open System

/// Euclid's algorithm for hcf
let rec highestCommonFactor (x : int) (y : int) =
    let larger, smaller = Math.Max(x, y), Math.Min(x, y)
    let q, r = larger / smaller, larger % smaller
    if r = 0 then
        smaller
    else
        highestCommonFactor y r

/// Find the lowest terms of a fraction - eg. 2/4 --> 1/2
let lowestTerms n d = 
    if n <= 0 || d <= 0 then
        1, 1
    else
        let hcf = highestCommonFactor n d 
        n / hcf, d / hcf

/// Convert a floating point number to the nearest mixed number using the 
/// specified numerator and denominator - eg. 1,3,1.666 --> 1 2/3
let toMixedNumber numerator denominator x =
    if denominator <= 0 then
        raise (System.ArgumentException("Denominator must be greater than 0"))
    let a = x / (numerator|>float) * (denominator|>float)
    let b = a |> round |> int
    let c = b / denominator
    let d = b % denominator

    let d', denominator' = lowestTerms d denominator

    match c, d with
    | 0, 0 -> sprintf "0"
    | 0, d -> sprintf "%i/%i" d' denominator'
    | c, 0 -> sprintf "%i" c
    | c, d -> sprintf "%i %i/%i" c d' denominator'

//  ["0"; "1/3"; "2/3"; "1"; "1 1/3"; "1 2/3"; "2"; "2 1/3"; "2 2/3"; "3";
//   "3 1/3"; "3 2/3"; "4"; "4 1/3"; "4 2/3"; "5"; "5 1/3"; "5 2/3"; "6";
//   "6 1/3"; "6 2/3"; "7"; "7 1/3"; "7 2/3"; "8"; "8 1/3"; "8 2/3"; "9";
[0. .. (1./3.) .. 32.] |> List.map (fun x -> toMixedNumber 1 3 x)

//  ["0"; "1/6"; "1/3"; "1/2"; "2/3"; "5/6"; "1"; "1 1/6"; "1 1/3"; "1 1/2";
//   "1 2/3"; "1 5/6"; "2"; "2 1/6"; "2 1/3"; "2 1/2"; "2 2/3"; "2 5/6"; "3";
//   "3 1/6"; "3 1/3"; "3 1/2"; "3 2/3"; "3 5/6"; "4"; "4 1/6"; "4 1/3"; "4 1/2";
[0. .. (1./6.) .. 32.] |> List.map (fun x -> toMixedNumber 1 6 x)

//  ["0"; "1/10"; "1/5"; "3/10"; "2/5"; "1/2"; "3/5"; "7/10"; "4/5"; "9/10"; "1";
//   "1 1/10"; "1 1/5"; "1 3/10"; "1 2/5"; "1 1/2"; "1 3/5"; "1 7/10"; "1 4/5";
//   "1 9/10"; "2"; "2 1/10"; "2 1/5"; "2 3/10"; "2 2/5"; "2 1/2"; "2 3/5";
[0. .. (1./10.) .. 32.] |> List.map (fun x -> toMixedNumber 1 10 x)