(ns emmy.numbers
"This namespace extends of all appropriate Emmy generic operations
from [[emmy.generic]] and [[emmy.value]] to the Clojure(script)
numeric tower.
For other numeric extensions, see [[emmy.ratio]]
and [[emmy.complex]]."
(:refer-clojure :exclude [zero? / + - *])
(:require #?(:cljs [emmy.ratio :as r])
[clojure.core :as core]
[emmy.complex :refer [complex]]
;; Required to enable the generic gcd implementation.
[emmy.euclid]
[emmy.generic :as g]
[emmy.util :as u]
[emmy.value :as v])
#?(:cljs
(:import (goog.math Long Integer))
:clj
(:import (clojure.lang BigInt)
(java.math BigInteger)
(org.apache.commons.math3.util ArithmeticUtils))))
(def ^:private boolean-type #?(:clj Boolean :cljs js/Boolean))
java.lang.Boolean
(defmethod g/zero? [boolean-type] [b] (not b))
#object[clojure.lang.MultiFn 0x78e2923f "
clojure.lang.MultiFn@78e2923f"
]
(defmethod g/one? [boolean-type] [b] b)
#object[clojure.lang.MultiFn 0x710fa3bd "
clojure.lang.MultiFn@710fa3bd"
]
(defmethod g/identity? [boolean-type] [b] b)
#object[clojure.lang.MultiFn 0x3e853a24 "
clojure.lang.MultiFn@3e853a24"
]
(defmethod g/zero-like [boolean-type] [_] false)
#object[clojure.lang.MultiFn 0x79337717 "
clojure.lang.MultiFn@79337717"
]
(defmethod g/one-like [boolean-type] [_] true)
#object[clojure.lang.MultiFn 0x46a24adf "
clojure.lang.MultiFn@46a24adf"
]
(defmethod g/identity-like [boolean-type] [_] true)
#object[clojure.lang.MultiFn 0x787144b0 "
clojure.lang.MultiFn@787144b0"
]
(defmethod g/zero-like [::v/floating-point] [_] 0.0)
#object[clojure.lang.MultiFn 0x79337717 "
clojure.lang.MultiFn@79337717"
]
(defmethod g/one-like [::v/floating-point] [_] 1.0)
#object[clojure.lang.MultiFn 0x46a24adf "
clojure.lang.MultiFn@46a24adf"
]
(defmethod g/identity-like [::v/floating-point] [_] 1.0)
#object[clojure.lang.MultiFn 0x787144b0 "
clojure.lang.MultiFn@787144b0"
]

"Backstop" implementations that apply to anything that descends from ::v/real.

(defmethod g/zero? [::v/real] [a] (core/zero? a))
#object[clojure.lang.MultiFn 0x78e2923f "
clojure.lang.MultiFn@78e2923f"
]
(defmethod g/one? [::v/real] [a] (== 1 a))
#object[clojure.lang.MultiFn 0x710fa3bd "
clojure.lang.MultiFn@710fa3bd"
]
(defmethod g/identity? [::v/real] [a] (== 1 a))
#object[clojure.lang.MultiFn 0x3e853a24 "
clojure.lang.MultiFn@3e853a24"
]
(defmethod g/zero-like [::v/real] [_] 0)
#object[clojure.lang.MultiFn 0x79337717 "
clojure.lang.MultiFn@79337717"
]
(defmethod g/one-like [::v/real] [_] 1)
#object[clojure.lang.MultiFn 0x46a24adf "
clojure.lang.MultiFn@46a24adf"
]
(defmethod g/identity-like [::v/real] [_] 1)
#object[clojure.lang.MultiFn 0x787144b0 "
clojure.lang.MultiFn@787144b0"
]
(defmethod g/exact? [::v/integral] [_] true)
#object[clojure.lang.MultiFn 0x1864e04e "
clojure.lang.MultiFn@1864e04e"
]
(defmethod g/exact? [::v/floating-point] [_] false)
#object[clojure.lang.MultiFn 0x1864e04e "
clojure.lang.MultiFn@1864e04e"
]
(defmethod g/freeze [::v/real] [a] a)
#object[clojure.lang.MultiFn 0x7a0db79a "
clojure.lang.MultiFn@7a0db79a"
]
(defmethod g/add [::v/real ::v/real] [a b] (#?(:clj +' :cljs core/+) a b))
#object[clojure.lang.MultiFn 0x7e1fb1be "
clojure.lang.MultiFn@7e1fb1be"
]
(defmethod g/mul [::v/real ::v/real] [a b] (#?(:clj *' :cljs core/*) a b))
#object[clojure.lang.MultiFn 0x4485ec13 "
clojure.lang.MultiFn@4485ec13"
]
(defmethod g/sub [::v/real ::v/real] [a b] (#?(:clj -' :cljs core/-) a b))
#object[clojure.lang.MultiFn 0x3b212ce4 "
clojure.lang.MultiFn@3b212ce4"
]
(defmethod g/negate [::v/real] [a] (core/- a))
#object[clojure.lang.MultiFn 0x1b9c4031 "
clojure.lang.MultiFn@1b9c4031"
]
(defmethod g/negative? [::v/real] [a] (neg? a))
#object[clojure.lang.MultiFn 0x6e310c69 "
clojure.lang.MultiFn@6e310c69"
]
(defmethod g/expt [::v/real ::v/real] [b x]
(if (and (neg? b)
(not (core/zero?
(g/fractional-part x))))
(g/exp (g/mul x (g/log b)))
(u/compute-expt b x)))
#object[clojure.lang.MultiFn 0x310104f9 "
clojure.lang.MultiFn@310104f9"
]
(defmethod g/abs [::v/real] [a] (u/compute-abs a))
#object[clojure.lang.MultiFn 0x721e14f0 "
clojure.lang.MultiFn@721e14f0"
]
(defmethod g/magnitude [::v/real] [a] (u/compute-abs a))
#object[clojure.lang.MultiFn 0x2df77656 "
clojure.lang.MultiFn@2df77656"
]
(defmethod g/div [::v/real ::v/real] [a b] (core// a b))
#object[clojure.lang.MultiFn 0x233dab3a "
clojure.lang.MultiFn@233dab3a"
]
(defmethod g/invert [::v/real] [a] (core// a))
#object[clojure.lang.MultiFn 0x165df7c1 "
clojure.lang.MultiFn@165df7c1"
]
(defmethod g/floor [::v/real] [a] (long (Math/floor a)))
#object[clojure.lang.MultiFn 0x6b6c4b9d "
clojure.lang.MultiFn@6b6c4b9d"
]
(defmethod g/ceiling [::v/real] [a] (long (Math/ceil a)))
#object[clojure.lang.MultiFn 0x775a6a7c "
clojure.lang.MultiFn@775a6a7c"
]
(defmethod g/integer-part [::v/real] [a]
#?(:clj (long a)
:cljs (Math/trunc a)))
#object[clojure.lang.MultiFn 0x12b60d1 "
clojure.lang.MultiFn@12b60d1"
]
(defmethod g/infinite? [::v/integral] [_] false)
#object[clojure.lang.MultiFn 0x585167ae "
clojure.lang.MultiFn@585167ae"
]
(defmethod g/infinite? [::v/real] [a]
#?(:clj (or (= a ##Inf)
(= a ##-Inf))
:cljs (infinite? a)))
#object[clojure.lang.MultiFn 0x585167ae "
clojure.lang.MultiFn@585167ae"
]

Complex Operations

(defmethod g/real-part [::v/real] [a] a)
#object[clojure.lang.MultiFn 0x4ed57a82 "
clojure.lang.MultiFn@4ed57a82"
]
(defmethod g/imag-part [::v/real] [_] 0)
#object[clojure.lang.MultiFn 0x41ca178e "
clojure.lang.MultiFn@41ca178e"
]
(defmethod g/angle [::v/real] [a]
(if (neg? a)
Math/PI
(g/zero-like a)))
#object[clojure.lang.MultiFn 0x1ab27ac4 "
clojure.lang.MultiFn@1ab27ac4"
]
(defmethod g/conjugate [::v/real] [a] a)
#object[clojure.lang.MultiFn 0x7fd6127a "
clojure.lang.MultiFn@7fd6127a"
]

Trig Operations

(defmethod g/sinc [::v/real] [a]
(cond (g/zero? a) 1
(g/infinite? a) 0
:else (g// (g/sin a) a)))
#object[clojure.lang.MultiFn 0x6279a650 "
clojure.lang.MultiFn@6279a650"
]
(defmethod g/sin [::v/real] [a] (Math/sin a))
#object[clojure.lang.MultiFn 0x163caf8e "
clojure.lang.MultiFn@163caf8e"
]
(defmethod g/cos [::v/real] [a] (Math/cos a))
#object[clojure.lang.MultiFn 0x3be6105f "
clojure.lang.MultiFn@3be6105f"
]
(defmethod g/tan [::v/real] [a] (Math/tan a))
#object[clojure.lang.MultiFn 0x428252da "
clojure.lang.MultiFn@428252da"
]
(defmethod g/cosh [::v/real] [a] (Math/cosh a))
#object[clojure.lang.MultiFn 0x8dc9785 "
clojure.lang.MultiFn@8dc9785"
]
(defmethod g/sinh [::v/real] [a] (Math/sinh a))
#object[clojure.lang.MultiFn 0x1d61d948 "
clojure.lang.MultiFn@1d61d948"
]
(defmethod g/tanh [::v/real] [a] (Math/tanh a))
#object[clojure.lang.MultiFn 0x42231f14 "
clojure.lang.MultiFn@42231f14"
]
(defmethod g/atan [::v/real] [a] (Math/atan a))
#object[clojure.lang.MultiFn 0x6a540c36 "
clojure.lang.MultiFn@6a540c36"
]
(defmethod g/atan [::v/real ::v/real] [a b] (Math/atan2 a b))
#object[clojure.lang.MultiFn 0x6a540c36 "
clojure.lang.MultiFn@6a540c36"
]

Operations which allow promotion to complex numbers when their arguments would otherwise result in a NaN if computed on the real line

(defmethod g/asin [::v/real] [a]
(if (> (g/abs a) 1)
(g/asin (complex a))
(Math/asin a)))
#object[clojure.lang.MultiFn 0x43c45b34 "
clojure.lang.MultiFn@43c45b34"
]
(defmethod g/acos [::v/real] [a]
(if (> (g/abs a) 1)
(g/acos (complex a))
(Math/acos a)))
#object[clojure.lang.MultiFn 0x1380d514 "
clojure.lang.MultiFn@1380d514"
]
(defmethod g/sqrt [::v/real] [a]
(if (neg? a)
(g/sqrt (complex a))
(u/compute-sqrt a)))
#object[clojure.lang.MultiFn 0x79e8dd4d "
clojure.lang.MultiFn@79e8dd4d"
]
(defmethod g/log [::v/real] [a]
(if (neg? a)
(g/log (complex a))
(Math/log a)))
#object[clojure.lang.MultiFn 0x20b5f4b2 "
clojure.lang.MultiFn@20b5f4b2"
]

Specialized methods provided by the host platforms.

#?(:clj (defmethod g/log10 [Double] [x]
(if (neg? x)
(g/log10 (complex x))
(Math/log10 x)))
:cljs (defmethod g/log10 [js/Number] [x]
(if (neg? x)
(g/log10 (complex x))
(Math/log10 x))))
#object[clojure.lang.MultiFn 0x73d3e049 "
clojure.lang.MultiFn@73d3e049"
]
(defmethod g/exp [::v/real] [a]
(if (core/zero? a)
1
(Math/exp a)))
#object[clojure.lang.MultiFn 0x7168c357 "
clojure.lang.MultiFn@7168c357"
]

We don't yet suport quotient, remainder with floating point, but there are a few cases where it does make sense. When two numbers are identical up to sign, it's fine to say that they divide evenly (and return 1 or -1). If that happens, we can return a 0 remainder.

(defn- careful-divide
"Minimum effort division. If `b` and `a` are equal or of opposite sign,
returns 1 or -1 respectively. If `a` is 1 or -1, returns `b` or `-b`
respectively. Else, returns nil."
[b a]
(cond (v/= a b) (g/one-like a)
(v/= a (g/negate b)) (g/negate (g/one-like a))
(g/one? a) b
(g/one? (g/negate a)) (g/negate b)
:else nil))
#object[emmy.numbers$careful_divide 0x633ad90f "
emmy.numbers$careful_divide@633ad90f"
]
(defmethod g/exact-divide [::v/scalar ::v/real] [b a]
(or (careful-divide b a)
(u/illegal
(str "exact-divide not allowed between: " b ", " a))))
#object[clojure.lang.MultiFn 0x198b6b28 "
clojure.lang.MultiFn@198b6b28"
]
(defmethod g/quotient [::v/scalar ::v/real] [b a]
(or (careful-divide b a)
(u/illegal
(str "quotient not allowed between: " b ", " a))))
#object[clojure.lang.MultiFn 0x7a1d117a "
clojure.lang.MultiFn@7a1d117a"
]
(defn ^:private exact-divide
"Checked implementation of g/exact-divide general enough to use for any type
that defines g/remainder and g/quotient."
[a b]
{:pre [(g/zero? (g/remainder a b))]}
(g/quotient a b))
#object[emmy.numbers$exact_divide 0x6a951a2e "
emmy.numbers$exact_divide@6a951a2e"
]
(defmethod g/exact-divide [::v/integral ::v/integral] [b a]
(exact-divide b a))
#object[clojure.lang.MultiFn 0x198b6b28 "
clojure.lang.MultiFn@198b6b28"
]
(defmethod g/integer-part [::v/integral] [a] a)
#object[clojure.lang.MultiFn 0x12b60d1 "
clojure.lang.MultiFn@12b60d1"
]
(defmethod g/fractional-part [::v/integral] [_] 0)
#object[clojure.lang.MultiFn 0x4430be99 "
clojure.lang.MultiFn@4430be99"
]
(defmethod g/floor [::v/integral] [a] a)
#object[clojure.lang.MultiFn 0x6b6c4b9d "
clojure.lang.MultiFn@6b6c4b9d"
]
(defmethod g/ceiling [::v/integral] [a] a)
#object[clojure.lang.MultiFn 0x775a6a7c "
clojure.lang.MultiFn@775a6a7c"
]

All JVM and JS types that respond to ::native-integral behave correctly with Clojure's native quot, rem, mod.

(defmethod g/quotient [::v/native-integral ::v/native-integral] [a b] (quot a b))
#object[clojure.lang.MultiFn 0x7a1d117a "
clojure.lang.MultiFn@7a1d117a"
]
(defmethod g/remainder [::v/real ::v/real] [a b] (rem a b))
#object[clojure.lang.MultiFn 0x1d8867c7 "
clojure.lang.MultiFn@1d8867c7"
]
(defmethod g/modulo [::v/real ::v/real] [a b] (mod a b))
#object[clojure.lang.MultiFn 0xf575ff8 "
clojure.lang.MultiFn@f575ff8"
]

This section defines methods that act differently between ClojureScript and Clojure. The clojure methods are all slightly more refined based on Java's type system.

#?(:clj
;; Efficient, native GCD on the JVM.
(do (defmethod g/gcd [BigInteger BigInteger] [a b]
(.gcd ^BigInteger a
^BigInteger b))
(defmethod g/gcd [BigInt BigInt] [a b]
(.gcd (biginteger a)
(biginteger b)))
(defmethod g/gcd [Long Long] [a b]
(ArithmeticUtils/gcd ^long a ^long b))
(defmethod g/gcd [Integer Integer] [a b]
(ArithmeticUtils/gcd ^int a ^int b))
(doseq [from [Long BigInt Integer]]
(defmethod g/gcd [BigInteger from] [a b]
(.gcd ^BigInteger a (biginteger b)))
(defmethod g/gcd [from BigInteger] [a b]
(.gcd (biginteger a) b)))
(doseq [from [Long Integer]]
(defmethod g/gcd [BigInt from] [a b]
(.gcd (biginteger a) (biginteger b)))
(defmethod g/gcd [from BigInt] [a b]
(.gcd (biginteger a) (biginteger b))))))
nil

ClojureScript and Javascript have a number of numeric types available that don't respond true to number? These each require their own block of method implementations.