#?(:clj
(do
(defmethod g/gcd [Ratio ::v/integral] [a b]
(g/div (.gcd (core/numerator a)
(biginteger b))
(core/denominator a)))
(defmethod g/gcd [::v/integral Ratio] [a b]
(g/div (.gcd (biginteger a)
(core/numerator b))
(core/denominator b)))
(defmethod g/gcd [Ratio Ratio] [a b]
(g/div (.gcd (core/numerator a)
(core/numerator b))
(g/lcm (core/denominator a)
(core/denominator b))))
(defmethod g/infinite? [Ratio] [_] false)
(doseq [[op f] [[g/exact-divide /]
[g/quotient quot]
[g/remainder rem]
[g/modulo mod]]]
(defmethod op [Ratio Ratio] [a b] (f a b))
(defmethod op [Ratio ::v/integral] [a b] (f a b))
(defmethod op [::v/integral Ratio] [a b] (f a b))))
:cljs
(let [ZERO (Fraction. 0)
ONE (Fraction. 1)]
(defn- pow [r m]
(let [n (numerator r)
d (denominator r)]
(if (neg? m)
(rationalize (g/expt d (g/negate m))
(g/expt n (g/negate m)))
(rationalize (g/expt n m)
(g/expt d m)))))
(defmethod v/= [::v/real Fraction] [l r] (= r l))
(defmethod g/add [Fraction Fraction] [a b] (promote (.add ^js a b)))
(defmethod g/sub [Fraction Fraction] [a b] (promote (.sub ^js a b)))
(defmethod g/mul [Fraction Fraction] [a b]
(promote (.mul ^js a b)))
(defmethod g/div [Fraction Fraction] [a b]
(promote (.div ^js a b)))
(defmethod g/exact-divide [Fraction Fraction] [a b]
(promote (.div ^js a b)))
(defmethod g/zero? [Fraction] [^Fraction c] (.equals c ZERO))
(defmethod g/one? [Fraction] [^Fraction c] (.equals c ONE))
(defmethod g/identity? [Fraction] [^Fraction c] (.equals c ONE))
(defmethod g/zero-like [Fraction] [_] 0)
(defmethod g/one-like [Fraction] [_] 1)
(defmethod g/identity-like [Fraction] [_] 1)
(defmethod g/negate [Fraction] [a] (promote (.neg ^js a)))
(defmethod g/negative? [Fraction] [a] (neg? (obj/get a "s")))
(defmethod g/infinite? [Fraction] [_] false)
(defmethod g/invert [Fraction] [a] (promote (.inverse ^js a)))
(defmethod g/square [Fraction] [a] (promote (.mul ^js a a)))
(defmethod g/cube [Fraction] [a] (promote (.pow ^js a 3)))
(defmethod g/abs [Fraction] [a] (promote (.abs ^js a)))
(defmethod g/magnitude [Fraction] [a] (promote (.abs ^js a)))
(defmethod g/gcd [Fraction Fraction] [a b]
(promote (.gcd ^js a b)))
(defmethod g/lcm [Fraction Fraction] [a b]
(promote (.lcm ^js a b)))
(defmethod g/expt [Fraction ::v/integral] [a b] (pow a b))
(defmethod g/sqrt [Fraction] [a]
(if (neg? a)
(g/sqrt (c/complex (.valueOf a)))
(g/div (g/sqrt (u/double (numerator a)))
(g/sqrt (u/double (denominator a))))))
(defmethod g/modulo [Fraction Fraction] [a b]
(promote
(.mod (.add (.mod ^js a b) b) b)))
(defmethod g/expt [Fraction Fraction] [a b]
(if (g/one? (denominator b))
(promote (.pow ^js a (numerator b)))
(g/expt (.valueOf a)
(.valueOf b))))
(defmethod g/quotient [Fraction Fraction] [a b]
(promote
(let [x (.div ^js a b)]
(if (pos? (obj/get x "s"))
(.floor ^js x)
(.ceil ^js x)))))
(defmethod g/remainder [Fraction Fraction] [a b]
(promote (.mod ^js a b)))
(defn- downcast-fraction
"Anything that `upcast-number` doesn't catch will hit this and pull a floating
point value out of the ratio."
[op]
(defmethod op [Fraction ::v/real] [a b]
(op (.valueOf ^js a) b))
(defmethod op [::v/real Fraction] [a b]
(op a (.valueOf ^js b))))
(defn- upcast-number
"Integrals can stay exact, so they become ratios before op."
[op]
(defmethod op [Fraction ::v/integral] [a b]
(op a (Fraction. b 1)))
(defmethod op [::v/integral Fraction] [a b]
(op (Fraction. a 1) b)))
(upcast-number g/exact-divide)
(downcast-fraction g/expt)
(doseq [op [g/add g/mul g/sub g/gcd g/lcm
g/modulo g/remainder
g/quotient g/div]]
(upcast-number op)
(downcast-fraction op))))