ToC

Generic Numerics

The first section introduces generic versions of Clojure's [[zero?]], [[+]], [[-]], [[*]] and [[/]] operations. Any type that can implement all of of these operations forms a mathematical Field.

There are, of course, other technical names for types that can only implement a subset of these operations, and more specializations of those names depending on whether or not the implementation of these binary operations is commutative or associative. (See Semigroup and Monoid first, and start exploring the realm of abstract algebra from there.)

This library takes a permissive stance on extensibility. Types extend the arithmetic operators by extending their unary or binary cases:

  • [[add]] for [[+]]
  • [[sub]] and [[negate]] for [[-]]
  • [[mul]] for [[*]]
  • [[invert]] and [[div]] for [[/]]

And the higher arity version reduces its list of arguments using this binary operation. This makes it possible and easy to make the arithmetic operators combine different types! It's up to you to do this in a mathematically responsible way.

Dispatch occurs via [[value/argument-kind]]. Documentation on how to extend each generic operation to some new type is sparse. Have a look at [[emmy.complex]] for an example of how to do this.

(defgeneric zero? 1
"Is true if `x` is an additive identity.")
#object[clojure.lang.MultiFn 0x78e2923f "
clojure.lang.MultiFn@78e2923f"
]
(defgeneric one? 1
"Is true if `x` is a multiplicative identity.")
#object[clojure.lang.MultiFn 0x710fa3bd "
clojure.lang.MultiFn@710fa3bd"
]
(defgeneric identity? 1
"Like `one?`, but this is true of square identity matrices as well.
No matrix is considered `one?` because its function as a multiplicative
identity depends on the shape of the other multiplicand.")
#object[clojure.lang.MultiFn 0x3e853a24 "
clojure.lang.MultiFn@3e853a24"
]
(defgeneric zero-like 1
"In general, this procedure returns the additive identity of the type of its
argument, if it exists. For numbers this is 0.")
#object[clojure.lang.MultiFn 0x79337717 "
clojure.lang.MultiFn@79337717"
]
(defgeneric one-like 1
"In general, this procedure returns the multiplicative identity of the type of
its argument, if it exists. For numbers this is 1.")
#object[clojure.lang.MultiFn 0x46a24adf "
clojure.lang.MultiFn@46a24adf"
]
(defgeneric identity-like 1
"Like `one-like` but works for square matrices.")
#object[clojure.lang.MultiFn 0x787144b0 "
clojure.lang.MultiFn@787144b0"
]
(defgeneric exact? 1
"Entries that are exact are available for `gcd`, among other operations.")
#object[clojure.lang.MultiFn 0x1864e04e "
clojure.lang.MultiFn@1864e04e"
]
(defgeneric freeze 1
"Freezing an expression means removing wrappers and other metadata from
subexpressions, so that the result is basically a pure S-expression with the
same structure as the input. Doing this will rob an expression of useful
information for further computation; so this is intended to be done just
before simplification and printing, to simplify those processes.")
#object[clojure.lang.MultiFn 0x7a0db79a "
clojure.lang.MultiFn@7a0db79a"
]
(defmethod freeze [#?(:clj String :cljs js/String)] [s] s)
#object[clojure.lang.MultiFn 0x7a0db79a "
clojure.lang.MultiFn@7a0db79a"
]
(defmethod freeze [nil] [_] nil)
#object[clojure.lang.MultiFn 0x7a0db79a "
clojure.lang.MultiFn@7a0db79a"
]
(defn numeric-zero?
"Returns `true` if `x` is both a [[number?]] and [[zero?]], false otherwise."
[x]
(and (v/number? x)
(zero? x)))
#object[emmy.generic$numeric_zero_QMARK_ 0x4b2edc16 "
emmy.generic$numeric_zero_QMARK_@4b2edc16"
]
(defgeneric ^:no-doc add 2
"Returns the sum of arguments `a` and `b`.
See [[+]] for a variadic version of [[add]]."
{:name '+
:dfdx (fn [_ _] 1)
:dfdy (fn [_ _] 1)})
#object[clojure.lang.MultiFn 0x7e1fb1be "
clojure.lang.MultiFn@7e1fb1be"
]
(defn +
"Generic implementation of `+`. Returns the sum of all supplied arguments. `(+)`
returns 0, the additive identity.
When applied between numbers, acts like `clojure.core/+`. Dispatch is open,
however, making it possible to 'add' types wherever the behavior is
mathematically sound.
For example:
```clojure
(+ [1 2 3] [2 3 4])
;;=> (up 3 5 7)
```"
([] 0)
([x] x)
([x y]
(cond (numeric-zero? x) y
(numeric-zero? y) x
:else (add x y)))
([x y & more]
(reduce + (+ x y) more)))
#object[emmy.generic$_PLUS_ 0x34146246 "
emmy.generic$_PLUS_@34146246"
]
(defgeneric negate 1
"Returns the negation of `a`.
Equivalent to `(- (g/zero-like a) a)`."
{:name '-
:dfdx (fn [_] -1)})
#object[clojure.lang.MultiFn 0x1b9c4031 "
clojure.lang.MultiFn@1b9c4031"
]
(defgeneric ^:no-doc sub 2
"Returns the difference of `a` and `b`.
Equivalent to `(+ a (negate b))`.
See [[-]] for a variadic version of [[sub]]."
{:name '-
:dfdx (fn [_ _] 1)
:dfdy (fn [_ _] -1)})
#object[clojure.lang.MultiFn 0x3b212ce4 "
clojure.lang.MultiFn@3b212ce4"
]
(defmethod sub :default [a b]
(add a (negate b)))
#object[clojure.lang.MultiFn 0x3b212ce4 "
clojure.lang.MultiFn@3b212ce4"
]
(defn -
"Generic implementation of `-`.
If one argument is supplied, returns the negation of `a`. Else returns the
difference of the first argument `a` and the sum of all remaining
arguments. `(-)` returns 0.
When applied between numbers, acts like `clojure.core/-`. Dispatch is open,
however, making it possible to 'subtract' types wherever the behavior is
mathematically sound.
For example:
```clojure
(- [1 2 3] [2 3 4])
;;=> (up -1 -1 -1)
(- [1 10])
;;=> (up -1 -10)
```"
([] 0)
([x] (negate x))
([x y]
(cond (numeric-zero? y) x
(numeric-zero? x) (negate y)
:else (sub x y)))
([x y & more]
(- x (apply + y more))))
#object[emmy.generic$_ 0x8bb5992 "
emmy.generic$_@8bb5992"
]
(defgeneric ^:no-doc mul 2
"Returns the product of `a` and `b`.
See [[*]] for a variadic version of [[mul]]."
{:name '*
:dfdx (fn [_ y] y)
:dfdy (fn [x _] x)})
#object[clojure.lang.MultiFn 0x4485ec13 "
clojure.lang.MultiFn@4485ec13"
]

In the binary arity of [[*]] we test for exact (numerical) zero because it is possible to produce a wrong-type zero here, as follows:

|0| |0|
|a b c| |0| |0| |0|
|d e f| |0| = |0|, not |0|

We are less worried about the zero? below, because any invertible matrix is square.

(defn *
"Generic implementation of `*`. Returns the product of all supplied
arguments. `(*)` returns 1, the multiplicative identity.
When applied between numbers, acts like `clojure.core/*`. Dispatch is open,
however, making it possible to 'multiply' types wherever the behavior is
mathematically sound.
For example:
```clojure
(* 2 #emmy/complex \"3 + 1i\")
;;=> #emmy/complex \"6 + 2i\"
```"
([] 1)
([x] x)
([x y]
(let [numx? (v/numerical? x)
numy? (v/numerical? y)]
(cond (and numx? (zero? x)) (zero-like y)
(and numy? (zero? y)) (zero-like x)
(and numx? (one? x)) y
(and numy? (one? y)) x
:else (mul x y))))
([x y & more]
(reduce * (* x y) more)))
#object[emmy.generic$_STAR_ 0x1b6c852f "
emmy.generic$_STAR_@1b6c852f"
]
(declare div square)
#object[clojure.lang.MultiFn 0x233dab3a "
clojure.lang.MultiFn@233dab3a"
]
(defgeneric invert 1
"Returns the multiplicative inverse of `a`.
Equivalent to `(/ 1 a)`."
{:name '/
:dfdx (fn [x] (div -1 (square x)))})
#object[clojure.lang.MultiFn 0x165df7c1 "
clojure.lang.MultiFn@165df7c1"
]
(def ^{:dynamic true
:no-doc true}
*in-default-invert*
false)
false
(defmethod invert :default [a]
(binding [*in-default-invert* true]
(div 1 a)))
#object[clojure.lang.MultiFn 0x165df7c1 "
clojure.lang.MultiFn@165df7c1"
]
(defgeneric div 2
"Returns the result of dividing `a` and `b`.
Equivalent to `(* a (negate b))`.
See [[/]] for a variadic version of [[div]]."
{:name '/
:dfdx (fn [_ y] (div 1 y))
:dfdy (fn [x y] (div (negate x)
(square y)))})
#object[clojure.lang.MultiFn 0x233dab3a "
clojure.lang.MultiFn@233dab3a"
]
(defmethod div :default [a b]
(if *in-default-invert*
(throw
(ex-info "No implementation of [[invert]] or [[div]]."
{:method 'div :args [a b]}))
(mul a (invert b))))
#object[clojure.lang.MultiFn 0x233dab3a "
clojure.lang.MultiFn@233dab3a"
]
(defn /
"Generic implementation of `/`.
If one argument is supplied, returns the multiplicative inverse of `a`. Else
returns the result of dividing first argument `a` by the product of all
remaining arguments. `(/)` returns 1, the multiplicative identity.
When applied between numbers, acts like `clojure.core//`. Dispatch is open,
however, making it possible to 'divide' types wherever the behavior is
mathematically sound.
For example:
```clojure
(/ [2 4 6] 2)
;;=> (up 1 2 3)
```"
([] 1)
([x] (invert x))
([x y]
(if (and (v/number? y) (one? y))
x
(div x y)))
([x y & more]
(/ x (apply * y more))))
#object[emmy.generic$_SLASH_ 0x49ce5701 "
emmy.generic$_SLASH_@49ce5701"
]
(def divide
"Alias for [[/]]."
/)
#object[emmy.generic$_SLASH_ 0x49ce5701 "
emmy.generic$_SLASH_@49ce5701"
]
(defgeneric exact-divide 2
"Similar to the binary case of [[/]], but throws if `(g/exact? <result>)`
returns false.")
#object[clojure.lang.MultiFn 0x198b6b28 "
clojure.lang.MultiFn@198b6b28"
]

Exponentiation, Log, Roots

This next batch of generics covers various forms of exponentation and its inverse.

(defgeneric exp 1
"Returns the base-e exponential of `x`. Equivalent to `(expt e x)`, given
some properly-defined `e` symbol."
{:dfdx exp})
#object[clojure.lang.MultiFn 0x7168c357 "
clojure.lang.MultiFn@7168c357"
]
(defgeneric exp2 1
"Returns the base-2 exponential of `x`. Equivalent to `(expt 2 x)`.")
#object[clojure.lang.MultiFn 0x22465147 "
clojure.lang.MultiFn@22465147"
]
(declare expt)
#object[clojure.lang.MultiFn 0x310104f9 "
clojure.lang.MultiFn@310104f9"
]
(defmethod exp2 :default [x] (expt 2 x))
#object[clojure.lang.MultiFn 0x22465147 "
clojure.lang.MultiFn@22465147"
]
(defgeneric exp10 1
"Returns the base-10 exponential of `x`. Equivalent to `(expt 10 x)`.")
#object[clojure.lang.MultiFn 0x35d7011b "
clojure.lang.MultiFn@35d7011b"
]
(defmethod exp10 :default [x] (expt 10 x))
#object[clojure.lang.MultiFn 0x35d7011b "
clojure.lang.MultiFn@35d7011b"
]
(defgeneric log 1
"Returns the natural logarithm of `x`."
{:dfdx invert})
#object[clojure.lang.MultiFn 0x20b5f4b2 "
clojure.lang.MultiFn@20b5f4b2"
]
(defgeneric log2 1
"Returns the base-2 logarithm of `x`, i.e., $log_2(x)$.")
#object[clojure.lang.MultiFn 0x824b5db "
clojure.lang.MultiFn@824b5db"
]
(let [l2 (Math/log 2)]
(defmethod log2 :default [x] (div (log x) l2)))
#object[clojure.lang.MultiFn 0x824b5db "
clojure.lang.MultiFn@824b5db"
]
(defgeneric log10 1
"Returns the base-10 logarithm of `x`, i.e., $log_10(x)$.")
#object[clojure.lang.MultiFn 0x73d3e049 "
clojure.lang.MultiFn@73d3e049"
]
(let [l10 (Math/log 10)]
(defmethod log10 :default [x] (div (log x) l10)))
#object[clojure.lang.MultiFn 0x73d3e049 "
clojure.lang.MultiFn@73d3e049"
]
(declare negative?)
#object[clojure.lang.MultiFn 0x6e310c69 "
clojure.lang.MultiFn@6e310c69"
]
(defgeneric expt 2
{:dfdx (fn [x y]
(mul y (expt x (sub y 1))))
:dfdy (fn [x y]
(if (and (v/number? x) (zero? x))
(if (v/number? y)
(if (not (negative? y))
0
(u/illegal "Derivative undefined: expt"))
0)
(mul (log x) (expt x y))))})
#object[clojure.lang.MultiFn 0x310104f9 "
clojure.lang.MultiFn@310104f9"
]
(defn ^:no-doc default-expt
"Default implementation of exponentiation for integral exponents `e`.
This implementation uses ['Exponentation by
Squaring'](https://en.wikipedia.org/wiki/Exponentiation_by_squaring), and will
work for any type that implements `g/mul`.
The multiplication operation is looked up once and cached at the beginning of
computation."
[s e]
{:pre [(v/native-integral? e)]}
(let [kind (v/kind s)]
(if-let [mul' (get-method mul [kind kind])]
(letfn [(expt' [base pow]
(loop [n pow
y (one-like base)
z base]
(let [t (even? n)
n (quot n 2)]
(cond
t (recur n y (mul' z z))
(zero? n) (mul' z y)
:else (recur n (mul' z y) (mul' z z))))))]
(cond (pos? e) (expt' s e)
(zero? e) (one-like s)
:else (invert (expt' s (negate e)))))
(u/illegal (str "No g/mul implementation registered for kind " kind)))))
#object[emmy.generic$default_expt 0x4a345ddc "
emmy.generic$default_expt@4a345ddc"
]
(def ^:no-doc relative-integer-tolerance (clojure.core/* 100 u/machine-epsilon))
2.220446049250313e-14
(def ^:no-doc absolute-integer-tolerance 1e-20)
1e-20
(defn almost-integral?
"Returns true if `x` is either:
- [[integral?]],
- a floating point number either < [[absolute-integer-tolerance]] (if near
zero) or within [[relative-integer-tolerance]] of the closest integer,
false otherwise."
[x]
(or (v/integral? x)
(and (float? x)
(let [x (double x)
z (Math/round x)]
(if (zero? z)
(< (Math/abs x) absolute-integer-tolerance)
(< (Math/abs (/ (- x z) z)) relative-integer-tolerance))))))
#object[emmy.generic$almost_integral_QMARK_ 0x5ae8f1b4 "
emmy.generic$almost_integral_QMARK_@5ae8f1b4"
]
(defn exact-zero?
"Returns true if the supplied argument is an exact numerical zero, false
otherwise."
[n]
(and (v/number? n)
(exact? n)
(zero? n)))
#object[emmy.generic$exact_zero_QMARK_ 0x573afc2b "
emmy.generic$exact_zero_QMARK_@573afc2b"
]

[[expt]] can be defined (as a default) in terms of repeated multiplication, if the exponent is a (native) integer. The native requirement is simply due to the [[default-expt]] implementation above, which uses functions like quot and even? that are not generic.

For all other arguments, default [[expt]] requires that [[exp]], [[mul]] and [[log]] be defined already on the type.

(defmethod expt :default [s e]
(if (v/native-integral? e)
(default-expt s e)
(exp (mul e (log s)))))
#object[clojure.lang.MultiFn 0x310104f9 "
clojure.lang.MultiFn@310104f9"
]
(defgeneric square 1
{:dfdx (fn [x] (mul 2 x))})
#object[clojure.lang.MultiFn 0x147eaac5 "
clojure.lang.MultiFn@147eaac5"
]
(defmethod square :default [x] (expt x 2))
#object[clojure.lang.MultiFn 0x147eaac5 "
clojure.lang.MultiFn@147eaac5"
]
(defgeneric cube 1
{:dfdx (fn [x] (mul 3 (square x)))})
#object[clojure.lang.MultiFn 0x7e61e389 "
clojure.lang.MultiFn@7e61e389"
]
(defmethod cube :default [x] (expt x 3))
#object[clojure.lang.MultiFn 0x7e61e389 "
clojure.lang.MultiFn@7e61e389"
]
(defgeneric sqrt 1
{:dfdx (fn [x]
(invert
(mul (sqrt x) 2)))})
#object[clojure.lang.MultiFn 0x79e8dd4d "
clojure.lang.MultiFn@79e8dd4d"
]
(defmethod sqrt :default [x]
(exp (mul (div 1 2)
(log x))))
#object[clojure.lang.MultiFn 0x79e8dd4d "
clojure.lang.MultiFn@79e8dd4d"
]

More Generics

(defgeneric negative? 1
"Returns true if the argument `a` is less than `(g/zero-like a)`,
false otherwise. The default implementation depends on a proper Comparable
implementation on the type.`")
#object[clojure.lang.MultiFn 0x6e310c69 "
clojure.lang.MultiFn@6e310c69"
]
(defmethod negative? :default [a]
(< (compare a (zero-like a)) 0))
#object[clojure.lang.MultiFn 0x6e310c69 "
clojure.lang.MultiFn@6e310c69"
]
(defgeneric infinite? 1
"Returns true if `a` is either numerically infinite (i.e., equal to `##Inf`) or
a compound number (complex or quaterion, for example) with some infinite
component.")
#object[clojure.lang.MultiFn 0x585167ae "
clojure.lang.MultiFn@585167ae"
]
(defmethod infinite? :default [_] false)
#object[clojure.lang.MultiFn 0x585167ae "
clojure.lang.MultiFn@585167ae"
]
(defgeneric abs 1)
#object[clojure.lang.MultiFn 0x721e14f0 "
clojure.lang.MultiFn@721e14f0"
]
(declare integer-part)
#object[clojure.lang.MultiFn 0x12b60d1 "
clojure.lang.MultiFn@12b60d1"
]
(defgeneric floor 1
"Returns the largest integer less than or equal to `a`.
Extensions beyond real numbers may behave differently; see the [Documentation
site](https://cljdoc.org/d/org.mentat/emmy/CURRENT/doc/basics/generics)
for more detail.")
#object[clojure.lang.MultiFn 0x6b6c4b9d "
clojure.lang.MultiFn@6b6c4b9d"
]
(defmethod floor :default [a]
(if (negative? a)
(sub (integer-part a) 1)
(integer-part a)))
#object[clojure.lang.MultiFn 0x6b6c4b9d "
clojure.lang.MultiFn@6b6c4b9d"
]
(defgeneric ceiling 1
"Returns the result of rounding `a` up to the next largest integer.
Extensions beyond real numbers may behave differently; see the [Documentation
site](https://cljdoc.org/d/org.mentat/emmy/CURRENT/doc/basics/generics)
for more detail.")
#object[clojure.lang.MultiFn 0x775a6a7c "
clojure.lang.MultiFn@775a6a7c"
]
(defmethod ceiling :default [a]
(negate (floor (negate a))))
#object[clojure.lang.MultiFn 0x775a6a7c "
clojure.lang.MultiFn@775a6a7c"
]
(defgeneric integer-part 1
"Returns the integer part of `a` by removing any fractional digits.")
#object[clojure.lang.MultiFn 0x12b60d1 "
clojure.lang.MultiFn@12b60d1"
]
(defgeneric fractional-part 1
"Returns the fractional part of the given value, defined as `x - ⌊x⌋`.
For positive numbers, this is identical to `(- a (integer-part a))`. For
negative `a`, because [[floor]] truncates toward negative infinity, you might
be surprised to find that [[fractional-part]] returns the distance between `a`
and the next-lowest integer:
```clojure
(= 0.6 (fractional-part -0.4))
```")
#object[clojure.lang.MultiFn 0x4430be99 "
clojure.lang.MultiFn@4430be99"
]
(defmethod fractional-part :default [a]
(sub a (floor a)))
#object[clojure.lang.MultiFn 0x4430be99 "
clojure.lang.MultiFn@4430be99"
]
(defgeneric quotient 2)
#object[clojure.lang.MultiFn 0x7a1d117a "
clojure.lang.MultiFn@7a1d117a"
]
(defn ^:no-doc modulo-default
"The default implementation for [[modulo]] depends on the identity:
x mod y == x - y ⌊x/y⌋
This is the Knuth definition described
by [Wikipedia](https://en.wikipedia.org/wiki/Modulo_operation)."
[a b]
(sub a (mul b (floor (div a b)))))
#object[emmy.generic$modulo_default 0x122f6958 "
emmy.generic$modulo_default@122f6958"
]
(defgeneric modulo 2
"Returns the result of the
mathematical [Modulo](https://en.wikipedia.org/wiki/Modulo_operation)
operation between `a` and `b` (using the Knuth definition listed).
The contract satisfied by [[modulo]] is:
```clojure
(= a (+ (* b (floor (/ a b)))
(modulo a b)))
```
For numbers, this differs from the contract offered by [[remainder]]
because `(floor (/ a b))` rounds toward negative infinity, while
the [[quotient]] operation in the contract for [[remainder]] rounds toward 0.
The result will be either `0` or of the same sign as the divisor `b`.")
#object[clojure.lang.MultiFn 0xf575ff8 "
clojure.lang.MultiFn@f575ff8"
]
(defmethod modulo :default [a b]
(modulo-default a b))
#object[clojure.lang.MultiFn 0xf575ff8 "
clojure.lang.MultiFn@f575ff8"
]
(defn ^:no-doc remainder-default [n d]
(let [divnd (div n d)]
(if (= (negative? n) (negative? d))
(mul d (sub divnd (floor divnd)))
(mul d (sub divnd (ceiling divnd))))))
#object[emmy.generic$remainder_default 0xf0c219d "
emmy.generic$remainder_default@f0c219d"
]
(defgeneric remainder 2
"Returns the remainder of dividing the dividend `a` by divisor `b`.
The contract satisfied by [[remainder]] is:
```clojure
(= a (+ (* b (quotient a b))
(remainder a b)))
```
For numbers, this differs from the contract offered by [[modulo]]
because [[quotient]] rounds toward 0, while `(floor (/ a b))` rounds toward
negative infinity.
The result will be either `0` or of the same sign as the dividend `a`.")
#object[clojure.lang.MultiFn 0x1d8867c7 "
clojure.lang.MultiFn@1d8867c7"
]
(defmethod remainder :default [n d]
(remainder-default n d))
#object[clojure.lang.MultiFn 0x1d8867c7 "
clojure.lang.MultiFn@1d8867c7"
]
(defgeneric gcd 2
"Returns the [greatest common
divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) of the two
inputs `a` and `b`.")
#object[clojure.lang.MultiFn 0x724dd6e2 "
clojure.lang.MultiFn@724dd6e2"
]
(defgeneric lcm 2
"Returns the [least common
multiple](https://en.wikipedia.org/wiki/Least_common_multiple) of the two
inputs `a` and `b`.")
#object[clojure.lang.MultiFn 0x7484b887 "
clojure.lang.MultiFn@7484b887"
]
(defmethod lcm :default [a b]
(let [g (gcd a b)]
(if (zero? g)
g
(abs
(* (exact-divide a g) b)))))
#object[clojure.lang.MultiFn 0x7484b887 "
clojure.lang.MultiFn@7484b887"
]

Trigonometric functions

Thanks to John D Cook's post 'Bootstrapping a minimal math library' for his inspiration on the defaults and order of functions in the following sections.

(declare sin)
#object[clojure.lang.MultiFn 0x163caf8e "
clojure.lang.MultiFn@163caf8e"
]
(defgeneric cos 1
"Returns the [cosine](https://en.wikipedia.org/wiki/Sine_and_cosine) of the
supplied argument `a`."
{:dfdx (fn [x] (negate (sin x)))})
#object[clojure.lang.MultiFn 0x3be6105f "
clojure.lang.MultiFn@3be6105f"
]
(defgeneric sin 1
"Returns the [sine](https://en.wikipedia.org/wiki/Sine_and_cosine) of the
supplied argument `a`."
{:dfdx cos})
#object[clojure.lang.MultiFn 0x163caf8e "
clojure.lang.MultiFn@163caf8e"
]

Given [[sin]] and [[cos]] implementations, it's possible to define the rest of the trigonometric functions with proper defaults.

(defgeneric tan 1
"Computes the trigonometric tangent function of the supplied argument `a`.
Equivalent to `(/ (sin a) (cos a))`."
{:dfdx (fn [x]
(invert
(square (cos x))))})
#object[clojure.lang.MultiFn 0x428252da "
clojure.lang.MultiFn@428252da"
]
(defmethod tan :default [x]
(div (sin x) (cos x)))
#object[clojure.lang.MultiFn 0x428252da "
clojure.lang.MultiFn@428252da"
]
(declare csc)
#object[clojure.lang.MultiFn 0x482bde86 "
clojure.lang.MultiFn@482bde86"
]
(defgeneric cot 1
"Computes the trigonometric cotangent function of the supplied argument `a`.
Equivalent to `(invert (tan a))`, or `(/ (cos a) (sin a))`."
{:dfdx (fn [x]
(negate
(square (csc x))))})
#object[clojure.lang.MultiFn 0x41999170 "
clojure.lang.MultiFn@41999170"
]
(defmethod cot :default [x]
(div (cos x) (sin x)))
#object[clojure.lang.MultiFn 0x41999170 "
clojure.lang.MultiFn@41999170"
]
(defgeneric sec 1
"Computes the secant of the supplied argument `a`.
Equivalent to `(invert (cos a))`."
{:dfdx (fn [x]
(mul (sec x)
(tan x)))})
#object[clojure.lang.MultiFn 0x3b1846b4 "
clojure.lang.MultiFn@3b1846b4"
]
(defmethod sec :default [x]
(invert (cos x)))
#object[clojure.lang.MultiFn 0x3b1846b4 "
clojure.lang.MultiFn@3b1846b4"
]
(defgeneric csc 1
"Computes the cosecant of the supplied argument `a`.
Equivalent to `(invert (sin a))`."
{:dfdx (fn [x]
(negate
(mul (cot x)
(csc x))))})
#object[clojure.lang.MultiFn 0x482bde86 "
clojure.lang.MultiFn@482bde86"
]
(defmethod csc :default [x]
(invert (sin x)))
#object[clojure.lang.MultiFn 0x482bde86 "
clojure.lang.MultiFn@482bde86"
]

Inverse Trig Functions

(defgeneric atan [1 2]
"Computes the inverse tangent of the supplied argument `a`. Given two
arguments `a` and `b`, returns the inverse tangent of the angle formed by the
point `(b, a)` in a 2-dimensional euclidean plane.
The two-argument version is sometimes
called [Atan2](https://en.wikipedia.org/wiki/Atan2)."
{:dfdx (fn
([x]
(invert (add 1 (square x))))
([y x]
(div x (add (square x)
(square y)))))
:dfdy (fn [y x]
(div (negate y)
(add (square x)
(square y))))})
#object[clojure.lang.MultiFn 0x6a540c36 "
clojure.lang.MultiFn@6a540c36"
]

Given an [[atan]] implementation, types automatically gain default methods for the rest of the inverse trig functions.

(defgeneric asin 1
"Computes the inverse sine of the supplied argument `a`.
Defaults to `atan(x/sqrt(1-x^2))`."
{:dfdx (fn [x]
(invert
(sqrt (sub 1 (square x)))))})
#object[clojure.lang.MultiFn 0x43c45b34 "
clojure.lang.MultiFn@43c45b34"
]
(defmethod asin :default [x]
(atan (div x (sqrt (sub 1 (square x))))))
#object[clojure.lang.MultiFn 0x43c45b34 "
clojure.lang.MultiFn@43c45b34"
]
(defgeneric acos 1
"Computes the inverse cosine of the supplied argument `a`.
Defaults to `atan(sqrt(1-x^2)/x)`."
{:dfdx (fn [x]
(negate
(invert
(sqrt (sub 1 (square x))))))})
#object[clojure.lang.MultiFn 0x1380d514 "
clojure.lang.MultiFn@1380d514"
]
(defmethod acos :default [x]
(atan (div (sqrt (sub 1 (square x))) x)))
#object[clojure.lang.MultiFn 0x1380d514 "
clojure.lang.MultiFn@1380d514"
]
(defgeneric acot 1
"Computes the [inverse
cotangent](https://mathworld.wolfram.com/InverseCotangent.html) of the supplied
argument `a`.
defaults to `pi/2 - atan(x)`."
{:dfdx (fn [x]
(negate
(invert
(add 1 (square x)))))})
#object[clojure.lang.MultiFn 0x7c9d3014 "
clojure.lang.MultiFn@7c9d3014"
]
(defmethod acot :default [x]
(sub (/ Math/PI 2) (atan x)))
#object[clojure.lang.MultiFn 0x7c9d3014 "
clojure.lang.MultiFn@7c9d3014"
]
(defgeneric asec 1
"Computes the [inverse
secant](https://mathworld.wolfram.com/InverseSecant.html) of the supplied
argument `a`.
defaults to `atan(sqrt(x^2 - 1))`."
{:dfdx (fn [x]
(invert
(mul x (sqrt (sub (square x) 1)))))})
#object[clojure.lang.MultiFn 0x2f775017 "
clojure.lang.MultiFn@2f775017"
]
(defmethod asec :default [x]
(atan (sqrt (sub (square x) 1))))
#object[clojure.lang.MultiFn 0x2f775017 "
clojure.lang.MultiFn@2f775017"
]
(defgeneric acsc 1
"Computes the [inverse
cosecant](https://mathworld.wolfram.com/InverseCosecant.html) of the supplied
argument `a`.
defaults to `atan(1 / sqrt(x^2 - 1))`."
{:dfdx (fn [x]
(negate
(invert
(mul x (sqrt (sub (square x) 1))))))})
#object[clojure.lang.MultiFn 0x711fac61 "
clojure.lang.MultiFn@711fac61"
]
(defmethod acsc :default [x]
(atan (invert
(sqrt (sub (square x) 1)))))
#object[clojure.lang.MultiFn 0x711fac61 "
clojure.lang.MultiFn@711fac61"
]

Hyperbolic Trig

(declare sinh)
#object[clojure.lang.MultiFn 0x1d61d948 "
clojure.lang.MultiFn@1d61d948"
]
(defgeneric cosh 1
"Computes the [hyperbolic
cosine](https://mathworld.wolfram.com/HyperbolicCosine.html) of the supplied
argument `a`.
defaults to `(e^x + e^{-x}) / 2`."
{:dfdx sinh})
#object[clojure.lang.MultiFn 0x8dc9785 "
clojure.lang.MultiFn@8dc9785"
]
(defmethod cosh :default [x]
(div (add (exp x)
(exp (negate x)))
2))
#object[clojure.lang.MultiFn 0x8dc9785 "
clojure.lang.MultiFn@8dc9785"
]
(defgeneric sinh 1
"Computes the [hyperbolic
sine](https://mathworld.wolfram.com/HyperbolicSine.html) of the supplied
argument `a`.
defaults to `(e^x - e^{-x}) / 2`."
{:dfdx cosh})
#object[clojure.lang.MultiFn 0x1d61d948 "
clojure.lang.MultiFn@1d61d948"
]
(defmethod sinh :default [x]
(div (sub (exp x)
(exp (negate x)))
2))
#object[clojure.lang.MultiFn 0x1d61d948 "
clojure.lang.MultiFn@1d61d948"
]
(defgeneric tanh 1
"Computes the [hyperbolic
tangent](https://mathworld.wolfram.com/HyperbolicTangent.html) of the supplied
argument `a`.
defaults to `sinh(x) / cosh(x)`."
{:dfdx (fn [x]
(sub 1 (square (tanh x))))})
#object[clojure.lang.MultiFn 0x42231f14 "
clojure.lang.MultiFn@42231f14"
]
(defmethod tanh :default [x]
(let [exp2x (exp (add x x))]
(div (sub exp2x 1)
(add exp2x 1))))
#object[clojure.lang.MultiFn 0x42231f14 "
clojure.lang.MultiFn@42231f14"
]
(defgeneric sech 1
"Computes the [hyperbolic
secant](https://mathworld.wolfram.com/HyperbolicSecant.html) of the supplied
argument `a`.
defaults to `1 / cosh(x)`."
{:dfdx (fn [x]
(negate
(mul (sech x)
(tanh x))))})
#object[clojure.lang.MultiFn 0x4493b2f0 "
clojure.lang.MultiFn@4493b2f0"
]
(defmethod sech :default [x]
(div 2 (add (exp x)
(exp (negate x)))))
#object[clojure.lang.MultiFn 0x4493b2f0 "
clojure.lang.MultiFn@4493b2f0"
]
(declare csch)
#object[clojure.lang.MultiFn 0x281f7558 "
clojure.lang.MultiFn@281f7558"
]
(defgeneric coth 1
"Computes the [hyperbolic
cotangent](https://mathworld.wolfram.com/HyperbolicCotangent.html) of the supplied
argument `a`.
defaults to `cosh(x) / sinh(x)`."
{:dfdx (fn [x]
(negate
(square (csch x))))})
#object[clojure.lang.MultiFn 0x57ab8ad4 "
clojure.lang.MultiFn@57ab8ad4"
]
(defmethod coth :default [x]
(let [exp2x (exp (add x x))]
(div (add exp2x 1)
(sub exp2x 1))))
#object[clojure.lang.MultiFn 0x57ab8ad4 "
clojure.lang.MultiFn@57ab8ad4"
]
(defgeneric csch 1
"Computes the [hyperbolic
cosecant](https://mathworld.wolfram.com/HyperbolicCosecant.html) of the supplied
argument `a`.
defaults to `1 / sinh(x)`."
{:dfdx (fn [x]
(negate
(mul (coth x)
(csch x))))})
#object[clojure.lang.MultiFn 0x281f7558 "
clojure.lang.MultiFn@281f7558"
]
(defmethod csch :default [x]
(div 2 (sub (exp x)
(exp (negate x)))))
#object[clojure.lang.MultiFn 0x281f7558 "
clojure.lang.MultiFn@281f7558"
]

Inverse Hyperbolic Functions

(defgeneric acosh 1
"Computes the [inverse hyperbolic
cosine](https://mathworld.wolfram.com/InverseHyperbolicCosine.html) of the supplied
argument `a`.
defaults to `2 ln(sqrt((x+1)/2) + sqrt((x-1)/2))`."
{:dfdx (fn [x]
(invert
(mul (sqrt (sub x 1))
(sqrt (add x 1)))))})
#object[clojure.lang.MultiFn 0x208e6245 "
clojure.lang.MultiFn@208e6245"
]
(defmethod acosh :default [x]
(mul 2 (log (add
(sqrt (div (add x 1) 2))
(sqrt (div (sub x 1) 2))))))
#object[clojure.lang.MultiFn 0x208e6245 "
clojure.lang.MultiFn@208e6245"
]
(defgeneric asinh 1
"Computes the [inverse hyperbolic
sine](https://mathworld.wolfram.com/InverseHyperbolicSine.html) of the
supplied argument `a`.
defaults to `ln(x + sqrt(1 + x^2))`."
{:dfdx (fn [x]
(invert
(sqrt
(add 1 (square x)))))})
#object[clojure.lang.MultiFn 0x2d117535 "
clojure.lang.MultiFn@2d117535"
]
(defmethod asinh :default [x]
(log
(add x (sqrt (add 1 (square x))))))
#object[clojure.lang.MultiFn 0x2d117535 "
clojure.lang.MultiFn@2d117535"
]
(defgeneric atanh 1
"Computes the [inverse hyperbolic
tangent](https://mathworld.wolfram.com/InverseHyperbolicTangent.html) of the
supplied argument `a`.
defaults to `1/2 ln((1+x)/(1-x))`."
{:dfdx (fn [x]
(invert
(sub 1 (square x))))})
#object[clojure.lang.MultiFn 0x38f36f68 "
clojure.lang.MultiFn@38f36f68"
]
(defmethod atanh :default [x]
(div (sub (log (add 1 x))
(log (sub 1 x)))
2))
#object[clojure.lang.MultiFn 0x38f36f68 "
clojure.lang.MultiFn@38f36f68"
]
(defgeneric acoth 1
"Computes the [inverse hyperbolic
cotangent](https://mathworld.wolfram.com/InverseHyperbolicCotangent.html) of
the supplied argument `a`.
defaults to `1/2 ln((x+1)/(x-1))`."
{:dfdx (fn [x]
(invert
(sub 1 (square x))))})
#object[clojure.lang.MultiFn 0x3403b0b3 "
clojure.lang.MultiFn@3403b0b3"
]
(defmethod acoth :default [x]
(div (sub (log (add x 1))
(log (sub x 1)))
2))
#object[clojure.lang.MultiFn 0x3403b0b3 "
clojure.lang.MultiFn@3403b0b3"
]
(defgeneric asech 1
"Computes the [inverse hyperbolic
secant](https://mathworld.wolfram.com/InverseHyperbolicSecant.html) of the
supplied argument `a`.
defaults to `ln((1 + sqrt(1-x^2)) / x)`."
{:dfdx (fn [x]
(let [x+1 (add x 1)]
(negate
(invert
(mul (mul x x+1)
(sqrt (div (sub 1 x)
x+1)))))))})
#object[clojure.lang.MultiFn 0x674c703c "
clojure.lang.MultiFn@674c703c"
]
(defmethod asech :default [x]
(log
(div (add 1 (sqrt (sub 1 (square x))))
x)))
#object[clojure.lang.MultiFn 0x674c703c "
clojure.lang.MultiFn@674c703c"
]
(defgeneric acsch 1
"Computes the [inverse hyperbolic
cosecant](https://mathworld.wolfram.com/InverseHyperbolicCosecant.html) of the
supplied argument `a`.
defaults to `ln((1 + sqrt(1+x^2)) / x)`."
{:dfdx (fn [x]
(negate
(invert
(mul x (sqrt (add (square x) 1))))))})
#object[clojure.lang.MultiFn 0x4152193d "
clojure.lang.MultiFn@4152193d"
]
(defmethod acsch :default [x]
(log
(div (add 1 (sqrt (add 1 (square x))))
x)))
#object[clojure.lang.MultiFn 0x4152193d "
clojure.lang.MultiFn@4152193d"
]

Sinc and friends

This section defines [[sinc]] and [[tanc]], and the hyperbolic variants [[sinhc]] and [[tanhc]].

(defgeneric sinc 1
"The unnormalized [sinc
function](https://en.wikipedia.org/wiki/Sinc_function), equivalent to
$\\frac{\\sin x}{x}$ but defined to be equal to 1 at $x = 0$.
### References
- [Wikipedia page](https://en.wikipedia.org/wiki/Sinc_function)
- [Mathworld page on Sinc](https://mathworld.wolfram.com/SincFunction.html)
- [Boost notes on [[sinc]]
and [[sinch]]](https://www.boost.org/doc/libs/1_65_0/libs/math/doc/html/math_toolkit/sinc/sinc_overview.html)"
{:dfdx (fn [x]
(if (zero? x)
x
(sub (div (cos x) x)
(div (sin x) (square x)))))})
#object[clojure.lang.MultiFn 0x6279a650 "
clojure.lang.MultiFn@6279a650"
]
(defmethod sinc :default [x]
(if (zero? x)
(one-like x)
(div (sin x) x)))
#object[clojure.lang.MultiFn 0x6279a650 "
clojure.lang.MultiFn@6279a650"
]

NOTE that we don't define cosc. This StackExchange post has a nice explanation of why the analogous cosc doesn't belong: "The motivation for functions such as Loading..., Loading..., Loading..., Loading... is to consider the behaviour of a ratio with limit 1 as Loading.... There is no such motivation for Loading..., since Loading...."

The Julia language does define a cosc, but strangely makes it equal to the derivative of sinc, by analogy with cos being the derivative of sin. This felt to me to be a step too far. Here's the Julia manual page for cosc.

Wikipedia does have a page on coshc, defined as Loading....

(defgeneric tanc 1
"`tanc` is defined, by analogy with [[sinc]], to be equal to $\\frac{\\tan
x}{x}$ for nonzero $x$ and equal to 1 at $x = 0$.
### References
- [Wikipedia page](https://en.wikipedia.org/wiki/Tanc_function)
- [Mathworld page on Sinc](https://mathworld.wolfram.com/TancFunction.html)"
{:dfdx (fn [x]
(if (zero? x)
x
(let [sx (sec x)]
(sub (div (* sx sx) x)
(div (tan x) (square x))))))})
#object[clojure.lang.MultiFn 0x4b560484 "
clojure.lang.MultiFn@4b560484"
]
(defmethod tanc :default [x]
(if (zero? x)
(one-like x)
(div (tan x) x)))
#object[clojure.lang.MultiFn 0x4b560484 "
clojure.lang.MultiFn@4b560484"
]

Hyperbolic Variants

(defgeneric sinhc 1
"The [sinhc function](https://en.wikipedia.org/wiki/Sinhc_function),
equivalent to $\\frac{\\sinh x}{x}$ but defined to be equal to 1 at $x = 0$.
### References
- [Wikipedia page](https://en.wikipedia.org/wiki/Sinhc_function)
- [Mathworld page on Sinhc](https://mathworld.wolfram.com/SinhcFunction.html)"
{:dfdx (fn [x]
(if (zero? x)
x
(sub (div (cosh x) x)
(div (sinh x) (square x)))))})
#object[clojure.lang.MultiFn 0x4f9f4042 "
clojure.lang.MultiFn@4f9f4042"
]
(defmethod sinhc :default [x]
(if (zero? x)
(one-like x)
(div (sinh x) x)))
#object[clojure.lang.MultiFn 0x4f9f4042 "
clojure.lang.MultiFn@4f9f4042"
]
(defgeneric tanhc 1
"The [tanhc function](https://en.wikipedia.org/wiki/Tanhc_function),
equivalent to $\\frac{\\tanh x}{x}$ but defined to be equal to 1 at $x = 0$.
### References
- [Wikipedia page](https://en.wikipedia.org/wiki/Tanhc_function)
- [Mathworld page on Tanhc](https://mathworld.wolfram.com/TanhcFunction.html)"
{:dfdx (fn [x]
(if (zero? x)
x
(let [sx (sech x)]
(sub (div (* sx sx) x)
(div (tanh x) (square x))))))})
#object[clojure.lang.MultiFn 0x1e55337d "
clojure.lang.MultiFn@1e55337d"
]
(defmethod tanhc :default [x]
(if (zero? x)
(one-like x)
(div (tanh x) x)))
#object[clojure.lang.MultiFn 0x1e55337d "
clojure.lang.MultiFn@1e55337d"
]

Complex Operators

(defgeneric make-rectangular 2)
#object[clojure.lang.MultiFn 0x41c0c051 "
clojure.lang.MultiFn@41c0c051"
]
(defgeneric make-polar 2)
#object[clojure.lang.MultiFn 0x73349281 "
clojure.lang.MultiFn@73349281"
]
(defgeneric real-part 1)
#object[clojure.lang.MultiFn 0x4ed57a82 "
clojure.lang.MultiFn@4ed57a82"
]
(defgeneric imag-part 1)
#object[clojure.lang.MultiFn 0x41ca178e "
clojure.lang.MultiFn@41ca178e"
]
(defgeneric magnitude 1)
#object[clojure.lang.MultiFn 0x2df77656 "
clojure.lang.MultiFn@2df77656"
]
(defgeneric angle 1)
#object[clojure.lang.MultiFn 0x1ab27ac4 "
clojure.lang.MultiFn@1ab27ac4"
]
(defgeneric conjugate 1)
#object[clojure.lang.MultiFn 0x7fd6127a "
clojure.lang.MultiFn@7fd6127a"
]

Operations on structures

(defgeneric transpose 1)
#object[clojure.lang.MultiFn 0x2ecf7620 "
clojure.lang.MultiFn@2ecf7620"
]
(defgeneric trace 1)
#object[clojure.lang.MultiFn 0x3ca928c1 "
clojure.lang.MultiFn@3ca928c1"
]
(defgeneric determinant 1)
#object[clojure.lang.MultiFn 0x213217f1 "
clojure.lang.MultiFn@213217f1"
]
(defgeneric dimension 1)
#object[clojure.lang.MultiFn 0x56adc8b2 "
clojure.lang.MultiFn@56adc8b2"
]
(defgeneric dot-product 2)
#object[clojure.lang.MultiFn 0x4f7d2d76 "
clojure.lang.MultiFn@4f7d2d76"
]
(defgeneric inner-product 2)
#object[clojure.lang.MultiFn 0x31b14dc1 "
clojure.lang.MultiFn@31b14dc1"
]
(defgeneric outer-product 2)
#object[clojure.lang.MultiFn 0x4ab81d57 "
clojure.lang.MultiFn@4ab81d57"
]
(defgeneric cross-product 2)
#object[clojure.lang.MultiFn 0x7f10d912 "
clojure.lang.MultiFn@7f10d912"
]

Structure Defaults

(defmethod transpose [::v/scalar] [a] a)
#object[clojure.lang.MultiFn 0x2ecf7620 "
clojure.lang.MultiFn@2ecf7620"
]
(defmethod trace [::v/scalar] [a] a)
#object[clojure.lang.MultiFn 0x3ca928c1 "
clojure.lang.MultiFn@3ca928c1"
]
(defmethod determinant [::v/scalar] [a] a)
#object[clojure.lang.MultiFn 0x213217f1 "
clojure.lang.MultiFn@213217f1"
]
(defmethod dimension [::v/scalar] [_] 1)
#object[clojure.lang.MultiFn 0x56adc8b2 "
clojure.lang.MultiFn@56adc8b2"
]
(defmethod dot-product [::v/scalar ::v/scalar] [l r] (mul l r))
#object[clojure.lang.MultiFn 0x4f7d2d76 "
clojure.lang.MultiFn@4f7d2d76"
]

Scalars include complex numbers, but for the purposes of dot/inner-products these are interpreted as pairs of real numbers, where conjugate is identity. So this seems to be a sane default.

(defmethod inner-product [::v/scalar ::v/scalar] [l r]
(dot-product l r))
#object[clojure.lang.MultiFn 0x31b14dc1 "
clojure.lang.MultiFn@31b14dc1"
]

Solvers

(defgeneric solve-linear 2
"For a given `a` and `b`, returns `x` such that `a*x = b`.
See[[solve-linear-right]] for a similar function that solves for `a = x*b`.")
#object[clojure.lang.MultiFn 0x48e9c356 "
clojure.lang.MultiFn@48e9c356"
]
(defgeneric solve-linear-right 2
"For a given `a` and `b`, returns `x` such that `a = x*b`.
See[[solve-linear]] for a similar function that solves for `a*x = b`.")
#object[clojure.lang.MultiFn 0x738d50cf "
clojure.lang.MultiFn@738d50cf"
]
(defn solve-linear-left
"Alias for [[solve-linear]]; present for compatibility with the original
`scmutils` codebase.
NOTE: In `scmutils`, `solve-linear-left` and `solve-linear` act identically in
all cases except matrices. `solve-linear-left` only accepted a column
matrix (or up structure) in the `b` position, while `solve-linear` accepted
either a column or row (up or down structure).
In Emmy, both functions accept either type."
[a b]
(solve-linear a b))
#object[emmy.generic$solve_linear_left 0x7955b94c "
emmy.generic$solve_linear_left@7955b94c"
]

Solver Defaults

(defmethod solve-linear [::v/scalar ::v/scalar] [x y] (div y x))
#object[clojure.lang.MultiFn 0x48e9c356 "
clojure.lang.MultiFn@48e9c356"
]
(defmethod solve-linear-right [::v/scalar ::v/scalar] [x y] (div x y))
#object[clojure.lang.MultiFn 0x738d50cf "
clojure.lang.MultiFn@738d50cf"
]

More advanced generic operations

(def ^:no-doc derivative-symbol 'D)
D
(defgeneric partial-derivative 2)
#object[clojure.lang.MultiFn 0x3d9e83a6 "
clojure.lang.MultiFn@3d9e83a6"
]
(defgeneric Lie-derivative 1)
#object[clojure.lang.MultiFn 0xd31ac27 "
clojure.lang.MultiFn@d31ac27"
]
(defgeneric simplify 1)
#object[clojure.lang.MultiFn 0x41843e3e "
clojure.lang.MultiFn@41843e3e"
]
(defmethod simplify :default [a] a)
#object[clojure.lang.MultiFn 0x41843e3e "
clojure.lang.MultiFn@41843e3e"
]

defgeneric installs a metadata-lookup handler for keywords by default; in this case, we want to override that feature so that keywords simplify to themselves.

(defmethod simplify [#?(:clj Keyword :cljs cljs.core/Keyword)] [a] a)
#object[clojure.lang.MultiFn 0x41843e3e "
clojure.lang.MultiFn@41843e3e"
]

This call registers a symbol for any non-multimethod we care about. These will be returned instead of the actual function body when the user calls (g/freeze fn), for example.

(v/add-object-symbols!
{+ '+
* '*
- '-
/ '/
clojure.core/+ '+
clojure.core/* '*
clojure.core/- '-
clojure.core// '/
clojure.core/mod 'modulo
clojure.core/quot 'quotient
clojure.core/rem 'remainder
clojure.core/neg? 'negative?
#?@(:cljs [cljs.core/infinite? 'infinite?])
clojure.core/< '<
clojure.core/<= '<=
clojure.core/> '>
clojure.core/>= '>=
clojure.core/= '=})
{#object[clojure.core$_ 0x10b040 "
clojure.core$_@10b040"
]
- #object[clojure.core$_LT_ 0x49d19500 "
clojure.core$_LT_@49d19500"
]
< #object[clojure.core$_LT__EQ_ 0x4b010f20 "
clojure.core$_LT__EQ_@4b010f20"
]
<= #object[clojure.core$neg_QMARK_ 0x30f27c01 "
clojure.core$neg_QMARK_@30f27c01"
]
negative? #object[emmy.generic$_SLASH_ 0x49ce5701 "
emmy.generic$_SLASH_@49ce5701"
]
/ #object[emmy.generic$_ 0x2883d8e2 "
emmy.generic$_@2883d8e2"
]
- #object[emmy.generic$_PLUS_ 0x6d4f0d24 "
emmy.generic$_PLUS_@6d4f0d24"
]
+ #object[clojure.core$_STAR_ 0x4c3c9924 "
clojure.core$_STAR_@4c3c9924"
]
* #object[clojure.core$_SLASH_ 0x4e173fa4 "
clojure.core$_SLASH_@4e173fa4"
]
/ #object[emmy.generic$_PLUS_ 0x34146246 "
emmy.generic$_PLUS_@34146246"
]
+ 11 more elided}