(.using [library [lux (.except nat) [abstract [equivalence (.only Equivalence)] [order (.only Order)] [monoid (.only Monoid)] [codec (.only Codec)] [monad (.only do)]] [control ["[0]" function] ["[0]" maybe] ["[0]" try] ["<>" parser (.only) ["<[0]>" code (.only Parser)]]] [data ["[0]" product] ["[0]" text (.open: "[1]#[0]" monoid)]] [macro [syntax (.only syntax)] ["[0]" code]]]] [// ["n" nat (.open: "[1]#[0]" decimal)]]) (type: .public Ratio (Record [#numerator Nat #denominator Nat])) (def .public (nat value) (-> Ratio (Maybe Nat)) (case (the #denominator value) 1 {.#Some (the #numerator value)} _ {.#None})) (def (normal (open "_[0]")) (-> Ratio Ratio) (let [common (n.gcd _#numerator _#denominator)] [..#numerator (n./ common _#numerator) ..#denominator (n./ common _#denominator)])) (def .public ratio (syntax (_ [numerator .any ?denominator (<>.maybe .any)]) (in (list (` ((~! ..normal) [..#numerator (~ numerator) ..#denominator (~ (maybe.else (' 1) ?denominator))])))))) (def .public (= parameter subject) (-> Ratio Ratio Bit) (and (n.= (the #numerator parameter) (the #numerator subject)) (n.= (the #denominator parameter) (the #denominator subject)))) (def .public equivalence (Equivalence Ratio) (implementation (def = ..=))) (def (equalized parameter subject) (-> Ratio Ratio [Nat Nat]) [(n.* (the #denominator subject) (the #numerator parameter)) (n.* (the #denominator parameter) (the #numerator subject))]) (def .public (< parameter subject) (-> Ratio Ratio Bit) (let [[parameter' subject'] (..equalized parameter subject)] (n.< parameter' subject'))) (def .public (<= parameter subject) (-> Ratio Ratio Bit) (or (< parameter subject) (= parameter subject))) (def .public (> parameter subject) (-> Ratio Ratio Bit) (..< subject parameter)) (def .public (>= parameter subject) (-> Ratio Ratio Bit) (or (> parameter subject) (= parameter subject))) (def .public order (Order Ratio) (implementation (def equivalence ..equivalence) (def < ..<))) (def .public (+ parameter subject) (-> Ratio Ratio Ratio) (let [[parameter' subject'] (..equalized parameter subject)] (normal [(n.+ parameter' subject') (n.* (the #denominator parameter) (the #denominator subject))]))) (def .public (- parameter subject) (-> Ratio Ratio Ratio) (let [[parameter' subject'] (..equalized parameter subject)] (normal [(n.- parameter' subject') (n.* (the #denominator parameter) (the #denominator subject))]))) (def .public (* parameter subject) (-> Ratio Ratio Ratio) (normal [(n.* (the #numerator parameter) (the #numerator subject)) (n.* (the #denominator parameter) (the #denominator subject))])) (def .public (/ parameter subject) (-> Ratio Ratio Ratio) (let [[parameter' subject'] (..equalized parameter subject)] (normal [subject' parameter']))) (def .public (% parameter subject) (-> Ratio Ratio Ratio) (let [[parameter' subject'] (..equalized parameter subject) quot (n./ parameter' subject')] (..- (revised #numerator (n.* quot) parameter) subject))) (def .public (reciprocal (open "_[0]")) (-> Ratio Ratio) [..#numerator _#denominator ..#denominator _#numerator]) (def separator ":") (def .public codec (Codec Text Ratio) (implementation (def (encoded (open "_[0]")) (all text#composite (n#encoded _#numerator) ..separator (n#encoded _#denominator))) (def (decoded input) (case (text.split_by ..separator input) {.#Some [num denom]} (do try.monad [numerator (n#decoded num) denominator (n#decoded denom)] (in (normal [#numerator numerator #denominator denominator]))) {.#None} {.#Left (text#composite "Invalid syntax for ratio: " input)})))) (with_template [ ] [(def .public (Monoid Ratio) (implementation (def identity (..ratio )) (def composite )))] [0 ..+ addition] [1 ..* multiplication] )