(ns emmy.numsymb
"Implementations of the generic operations for numeric types that have
optimizations available, and for the general symbolic case."
(:refer-clojure :exclude [abs])
(:require [emmy.complex :as c]
[emmy.euclid]
[emmy.generic :as g]
[emmy.numbers]
[emmy.ratio]
[emmy.util :as u]
[emmy.util.aggregate :as ua]
[emmy.util.logic :as ul]
[emmy.value :as v]))
(def ^:dynamic *incremental-simplifier*
"When bound to a simplifier (a function from symbolic expression => symbolic
expression), this simplifier will be called after every operation performed on
`emmy.abstract.number` instances.
`nil` by default."
nil)
nil
(def operator first)
#object[clojure.core$first__5449 0x6647cbf8 "
clojure.core$first__5449@6647cbf8"
]
(def operands rest)
#object[clojure.core$rest__5453 0x55adf6f7 "
clojure.core$rest__5453@55adf6f7"
]
(defn- is-expression?
"Returns a function which will decide if its argument is a sequence commencing
with s."
[s]
(fn [x]
(and (seq? x)
(= (operator x) s))))
#object[emmy.numsymb$is_expression_QMARK_ 0x6627b282 "
emmy.numsymb$is_expression_QMARK_@6627b282"
]
(def sum? (is-expression? '+))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x63406e5b "
emmy.numsymb$is_expression_QMARK_$fn__87286@63406e5b"
]
(def product? (is-expression? '*))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x3b0071c0 "
emmy.numsymb$is_expression_QMARK_$fn__87286@3b0071c0"
]
(def sqrt? (is-expression? 'sqrt))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x1a02219 "
emmy.numsymb$is_expression_QMARK_$fn__87286@1a02219"
]
(def expt? (is-expression? 'expt))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x5ac8230d "
emmy.numsymb$is_expression_QMARK_$fn__87286@5ac8230d"
]
(def quotient? (is-expression? '/))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x3a9aacb9 "
emmy.numsymb$is_expression_QMARK_$fn__87286@3a9aacb9"
]
(def arctan? (is-expression? 'atan))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x22c545b "
emmy.numsymb$is_expression_QMARK_$fn__87286@22c545b"
]
(def derivative? (is-expression? g/derivative-symbol))
#object[emmy.numsymb$is_expression_QMARK_$fn__87286 0x21c0eed "
emmy.numsymb$is_expression_QMARK_$fn__87286@21c0eed"
]
(defn iterated-derivative? [expr]
(and (seq? expr)
(expt? (operator expr))
(= g/derivative-symbol
(second
(operator expr)))))
#object[emmy.numsymb$iterated_derivative_QMARK_ 0x19d785e8 "
emmy.numsymb$iterated_derivative_QMARK_@19d785e8"
]
(defn- with-exactness-preserved
"Returns a wrapper around f that attempts to preserve exactness if the input is
numerically exact, else passes through to f."
[f sym-or-fn]
(let [process (if (symbol? sym-or-fn)
(fn [s] (list sym-or-fn s))
sym-or-fn)]
(fn [s]
(if (v/number? s)
(let [q (f s)]
(if-not (g/exact? s)
q
(if (g/exact? q)
q
(process s))))
(process s)))))
#object[emmy.numsymb$with_exactness_preserved 0x76c70d0f "
emmy.numsymb$with_exactness_preserved@76c70d0f"
]
(defn- mod-rem
"Modulo and remainder are very similar, so can benefit from a shared set of
simplifications."
[a b f sym]
(cond (and (v/number? a) (v/number? b)) (f a b)
(= a b) 0
(g/zero? a) 0
(g/one? b) a
:else (list sym a b)))
#object[emmy.numsymb$mod_rem 0x16290100 "
emmy.numsymb$mod_rem@16290100"
]

these are without constructor simplifications!

(defn- add [a b]
(cond (and (v/number? a) (v/number? b)) (g/add a b)
(v/number? a) (cond (g/zero? a) b
(sum? b) `(~'+ ~a ~@(operands b))
:else `(~'+ ~a ~b))
(v/number? b) (cond (g/zero? b) a
(sum? a) `(~'+ ~@(operands a) ~b)
:else `(~'+ ~a ~b))
(sum? a) (cond (sum? b) `(~'+ ~@(operands a) ~@(operands b))
:else `(~'+ ~@(operands a) ~b))
(sum? b) `(~'+ ~a ~@(operands b))
:else `(~'+ ~a ~b)))
#object[emmy.numsymb$add 0x74200e4e "
emmy.numsymb$add@74200e4e"
]
(defn- sub [a b]
(cond (and (v/number? a) (v/number? b)) (g/sub a b)
(v/number? a) (if (g/zero? a) `(~'- ~b) `(~'- ~a ~b))
(v/number? b) (if (g/zero? b) a `(~'- ~a ~b))
(= a b) 0
:else `(~'- ~a ~b)))
#object[emmy.numsymb$sub 0x51fd3de7 "
emmy.numsymb$sub@51fd3de7"
]
(defn- negate [x] (sub 0 x))
#object[emmy.numsymb$negate 0x1f1ceda2 "
emmy.numsymb$negate@1f1ceda2"
]
(defn- mul [a b]
(cond (and (v/number? a) (v/number? b)) (g/mul a b)
(v/number? a) (cond (g/zero? a) a
(g/one? a) b
(product? b) `(~'* ~a ~@(operands b))
:else `(~'* ~a ~b))
(v/number? b) (cond (g/zero? b) b
(g/one? b) a
(product? a) `(~'* ~@(operands a) ~b)
:else `(~'* ~a ~b))
(product? a) (cond (product? b) `(~'* ~@(operands a) ~@(operands b))
:else `(~'* ~@(operands a) ~b))
(product? b) `(~'* ~a ~@(operands b))
:else `(~'* ~a ~b)))
#object[emmy.numsymb$mul 0x4163ce40 "
emmy.numsymb$mul@4163ce40"
]
(defn- div [a b]
(cond (and (v/number? a) (v/number? b)) (g/div a b)
(v/number? a) (if (g/zero? a) a `(~'/ ~a ~b))
(v/number? b) (cond (g/zero? b) (u/arithmetic-ex "division by zero")
(g/one? b) a
:else `(~'/ ~a ~b))
:else `(~'/ ~a ~b)))
#object[emmy.numsymb$div 0x3545ea5c "
emmy.numsymb$div@3545ea5c"
]
(defn- invert [x] (div 1 x))
#object[emmy.numsymb$invert 0xdeffb16 "
emmy.numsymb$invert@deffb16"
]
(defn- modulo [a b]
(mod-rem a b modulo 'modulo))
#object[emmy.numsymb$modulo 0x756666f4 "
emmy.numsymb$modulo@756666f4"
]
(defn- remainder [a b]
(mod-rem a b remainder 'remainder))
#object[emmy.numsymb$remainder 0x2a28b7d6 "
emmy.numsymb$remainder@2a28b7d6"
]
(defn- floor [a]
(if (v/number? a)
(g/floor a)
(list 'floor a)))
#object[emmy.numsymb$floor 0x3025afc5 "
emmy.numsymb$floor@3025afc5"
]
(defn- ceiling [a]
(if (v/number? a)
(g/ceiling a)
(list 'ceiling a)))
#object[emmy.numsymb$ceiling 0x42e0d59c "
emmy.numsymb$ceiling@42e0d59c"
]
(defn- integer-part [a]
(if (v/number? a)
(g/integer-part a)
(list 'integer-part a)))
#object[emmy.numsymb$integer_part 0x4f75af2 "
emmy.numsymb$integer_part@4f75af2"
]
(defn- fractional-part [a]
(if (v/number? a)
(g/fractional-part a)
(list 'fractional-part a)))
#object[emmy.numsymb$fractional_part 0x6b0edbbd "
emmy.numsymb$fractional_part@6b0edbbd"
]

Trig Functions

(def ^:private pi Math/PI)
3.141592653589793
(def ^:private pi-over-4 (/ pi 4))
0.7853981633974483
(def ^:private two-pi (* 2 pi))
6.283185307179586
(def ^:private pi-over-2 (* 2 pi-over-4))
1.5707963267948966
(defn ^:private n:zero-mod-pi? [x]
(g/almost-integral? (/ x pi)))
#object[emmy.numsymb$n_COLON_zero_mod_pi_QMARK_ 0x76481a9e "
emmy.numsymb$n_COLON_zero_mod_pi_QMARK_@76481a9e"
]
(defn ^:private n:pi-over-2-mod-2pi? [x]
(g/almost-integral? (/ (- x pi-over-2 two-pi))))
#object[emmy.numsymb$n_COLON_pi_over_2_mod_2pi_QMARK_ 0x32c07dfe "
emmy.numsymb$n_COLON_pi_over_2_mod_2pi_QMARK_@32c07dfe"
]
(defn ^:private n:-pi-over-2-mod-2pi? [x]
(g/almost-integral? (/ (+ x pi-over-2) two-pi)))
#object[emmy.numsymb$n_COLON__pi_over_2_mod_2pi_QMARK_ 0xcde919e "
emmy.numsymb$n_COLON__pi_over_2_mod_2pi_QMARK_@cde919e"
]
(defn ^:private n:pi-mod-2pi? [x]
(g/almost-integral? (/ (- x pi) two-pi)))
#object[emmy.numsymb$n_COLON_pi_mod_2pi_QMARK_ 0x1190708b "
emmy.numsymb$n_COLON_pi_mod_2pi_QMARK_@1190708b"
]
(defn ^:private n:pi-over-2-mod-pi? [x]
(g/almost-integral? (/ (- x pi-over-2) pi)))
#object[emmy.numsymb$n_COLON_pi_over_2_mod_pi_QMARK_ 0x27ba14cd "
emmy.numsymb$n_COLON_pi_over_2_mod_pi_QMARK_@27ba14cd"
]
(defn ^:private n:zero-mod-2pi? [x]
(g/almost-integral? (/ x two-pi)))
#object[emmy.numsymb$n_COLON_zero_mod_2pi_QMARK_ 0x16a4b249 "
emmy.numsymb$n_COLON_zero_mod_2pi_QMARK_@16a4b249"
]
(defn ^:private n:-pi-over-4-mod-pi? [x]
(g/almost-integral? (/ (+ x pi-over-4) pi)))
#object[emmy.numsymb$n_COLON__pi_over_4_mod_pi_QMARK_ 0x6e0e8362 "
emmy.numsymb$n_COLON__pi_over_4_mod_pi_QMARK_@6e0e8362"
]
(defn ^:private n:pi-over-4-mod-pi? [x]
(g/almost-integral? (/ (- x pi-over-4) pi)))
#object[emmy.numsymb$n_COLON_pi_over_4_mod_pi_QMARK_ 0x7c08a200 "
emmy.numsymb$n_COLON_pi_over_4_mod_pi_QMARK_@7c08a200"
]
(def ^:no-doc zero-mod-pi? #{'-pi 'pi '-two-pi 'two-pi})
#{-pi -two-pi pi two-pi}
(def ^:no-doc pi-over-2-mod-2pi? #{'pi-over-2})
#{pi-over-2}
(def ^:no-doc -pi-over-2-mod-2pi? #{'-pi-over-2})
#{-pi-over-2}
(def ^:no-doc pi-mod-2pi? #{'-pi 'pi})
#{-pi pi}
(def ^:no-doc pi-over-2-mod-pi? #{'-pi-over-2 'pi-over-2})
#{-pi-over-2 pi-over-2}
(def ^:no-doc zero-mod-2pi? #{'-two-pi 'two-pi})
#{-two-pi two-pi}
(def ^:no-doc -pi-over-4-mod-pi? #{'-pi-over-4})
#{-pi-over-4}
(def ^:no-doc pi-over-4-mod-pi? #{'pi-over-4 '+pi-over-4})
#{+pi-over-4 pi-over-4}
(defn- sin
"Implementation of sine that attempts to apply optimizations at the call site.
If it's not possible to do this (if the expression is symbolic, say), returns
a symbolic form."
[x]
(cond (v/number? x) (if (g/exact? x)
(if (g/zero? x) 0 (list 'sin x))
(cond (n:zero-mod-pi? x) 0
(n:pi-over-2-mod-2pi? x) 1
(n:-pi-over-2-mod-2pi? x) -1
:else (Math/sin x)))
(symbol? x) (cond (zero-mod-pi? x) 0
(pi-over-2-mod-2pi? x) 1
(-pi-over-2-mod-2pi? x) -1
:else (list 'sin x))
:else (list 'sin x)))
#object[emmy.numsymb$sin 0xbe3dd30 "
emmy.numsymb$sin@be3dd30"
]
(defn- cos
"Implementation of cosine that attempts to apply optimizations at the call site.
If it's not possible to do this (if the expression is symbolic, say), returns
a symbolic form."
[x]
(cond (v/number? x) (if (g/exact? x)
(if (g/zero? x) 1 (list 'cos x))
(cond (n:pi-over-2-mod-pi? x) 0
(n:zero-mod-2pi? x) 1
(n:pi-mod-2pi? x) -1
:else (Math/cos x)))
(symbol? x) (cond (pi-over-2-mod-pi? x) 0
(zero-mod-2pi? x) 1
(pi-mod-2pi? x) -1
:else (list 'cos x))
:else (list 'cos x)))
#object[emmy.numsymb$cos 0x38e610ff "
emmy.numsymb$cos@38e610ff"
]
(defn- tan
"Implementation of tangent that attempts to apply optimizations at the call site.
If it's not possible to do this (if the expression is symbolic, say), returns
a symbolic form."
[x]
(cond (v/number? x) (if (g/exact? x)
(if (g/zero? x) 0 (list 'tan x))
(cond (n:zero-mod-pi? x) 0
(n:pi-over-4-mod-pi? x) 1
(n:-pi-over-4-mod-pi? x) -1
(n:pi-over-2-mod-pi? x) (u/illegal "Undefined: tan")
:else (Math/tan x)))
(symbol? x) (cond (zero-mod-pi? x) 0
(pi-over-4-mod-pi? x) 1
(-pi-over-4-mod-pi? x) -1
(pi-over-2-mod-pi? x) (u/illegal "Undefined: tan")
:else (list 'tan x))
:else (list 'tan x)))
#object[emmy.numsymb$tan 0x22699112 "
emmy.numsymb$tan@22699112"
]
(defn- csc [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/csc x)
(if (g/zero? x)
(u/illegal (str "Zero argument -- g/csc" x))
`(~'/ 1 ~(sin x))))
`(~'/ 1 ~(sin x))))
#object[emmy.numsymb$csc 0x1bc83ffd "
emmy.numsymb$csc@1bc83ffd"
]
(defn- sec [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/sec x)
(if (g/zero? x)
1
`(~'/ 1 ~(cos x))))
`(~'/ 1 ~(cos x))))
#object[emmy.numsymb$sec 0xed76be9 "
emmy.numsymb$sec@ed76be9"
]
(defn- asin [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/asin x)
(if (g/zero? x)
0
(list 'asin x)))
(list 'asin x)))
#object[emmy.numsymb$asin 0x6bee324 "
emmy.numsymb$asin@6bee324"
]
(defn- acos [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/acos x)
(if (g/one? x)
0
(list 'acos x)))
(list 'acos x)))
#object[emmy.numsymb$acos 0x68b103e0 "
emmy.numsymb$acos@68b103e0"
]
(defn- atan
([y]
(if (v/number? y)
(if-not (g/exact? y)
(g/atan y)
(if (g/zero? y)
0
(list 'atan y)))
(list 'atan y)))
([y x]
(cond (g/one? x) (atan y)
(g/exact-zero? y)
(if (v/number? x)
(if (g/negative? x) 'pi 0)
(and (ul/assume! `(~'non-negative? ~x) 'numsymb-atan)
0))
(g/exact-zero? x)
(if (v/number? y)
(if (g/negative? y)
'(- (/ pi 2))
'(/ pi 2))
(and (ul/assume! `(~'non-negative? ~y) 'numsymb-atan)
'(/ pi 2)))
(and (v/number? x)
(v/number? y)
(or (not (g/exact? x))
(not (g/exact? y))))
(g/atan y x)
:else (list 'atan y x))))
#object[emmy.numsymb$atan 0x32dcfedc "
emmy.numsymb$atan@32dcfedc"
]
(defn- cosh [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/cosh x)
(if (g/zero? x)
1
(list 'cosh x)))
(list 'cosh x)))
#object[emmy.numsymb$cosh 0x7e1434d "
emmy.numsymb$cosh@7e1434d"
]
(defn- sinh [x]
(if (v/number? x)
(if-not (g/exact? x)
(g/sinh x)
(if (g/zero? x)
0
(list 'sinh x)))
(list 'sinh x)))
#object[emmy.numsymb$sinh 0x76b72756 "
emmy.numsymb$sinh@76b72756"
]
(defn- tanh [x]
(div (sinh x)
(cosh x)))
#object[emmy.numsymb$tanh 0x16799463 "
emmy.numsymb$tanh@16799463"
]
(defn- coth [x]
(div (cosh x)
(sinh x)))
#object[emmy.numsymb$coth 0x4306b26e "
emmy.numsymb$coth@4306b26e"
]
(defn- sech [x]
(div 1 (cosh x)))
#object[emmy.numsymb$sech 0x4ef07a68 "
emmy.numsymb$sech@4ef07a68"
]
(defn- csch [x]
(div 1 (sinh x)))
#object[emmy.numsymb$csch 0x1327977 "
emmy.numsymb$csch@1327977"
]
(defn- acot [x]
(sub '(/ pi 2) (atan x)))
#object[emmy.numsymb$acot 0x5bb23fbe "
emmy.numsymb$acot@5bb23fbe"
]
(defn- abs
"Symbolic expression handler for abs."
[x]
(if (v/number? x)
(g/abs x)
(list 'abs x)))
#object[emmy.numsymb$abs 0x2dd17364 "
emmy.numsymb$abs@2dd17364"
]
(defn- gcd [a b]
(cond (and (v/number? a) (v/number? b)) (g/gcd a b)
(v/number? a) (cond (g/zero? a) b
(g/one? a) 1
:else (list 'gcd a b))
(v/number? b) (cond (g/zero? b) a
(g/one? b) 1
:else (list 'gcd a b))
(= a b) a
:else (list 'gcd a b)))
#object[emmy.numsymb$gcd 0x60bf6f8c "
emmy.numsymb$gcd@60bf6f8c"
]
(defn- lcm [a b]
(cond (and (v/number? a) (v/number? b)) (g/lcm a b)
(v/number? a) (cond (g/zero? a) 0
(g/one? a) b
:else (list 'lcm a b))
(v/number? b) (cond (g/zero? b) 0
(g/one? b) a
:else (list 'lcm a b))
(= a b) a
:else (list 'lcm a b)))
#object[emmy.numsymb$lcm 0x72545b2d "
emmy.numsymb$lcm@72545b2d"
]
(def sqrt
"Square root implementation that attempts to preserve exact numbers wherever
possible. If the incoming value is not exact, simply computes sqrt."
(with-exactness-preserved g/sqrt 'sqrt))
#object[emmy.numsymb$with_exactness_preserved$fn__87295 0x3851d516 "
emmy.numsymb$with_exactness_preserved$fn__87295@3851d516"
]
(def ^:private log
"Attempts to preserve exact precision if the argument is exact; else, evaluates
symbolically or numerically."
(with-exactness-preserved g/log 'log))
#object[emmy.numsymb$with_exactness_preserved$fn__87295 0x1ebdcb2b "
emmy.numsymb$with_exactness_preserved$fn__87295@1ebdcb2b"
]
(def ^:private exp
"Attempts to preserve exact precision if the argument is exact; else, evaluates
symbolically or numerically."
(with-exactness-preserved g/exp 'exp))
#object[emmy.numsymb$with_exactness_preserved$fn__87295 0x5e5147f "
emmy.numsymb$with_exactness_preserved$fn__87295@5e5147f"
]
(defn- expt
"Attempts to preserve exact precision if either argument is exact; else,
evaluates symbolically or numerically."
[b e]
(cond (and (v/number? b) (v/number? e)) (g/expt b e)
(v/number? b) (cond (g/one? b) 1
:else `(~'expt ~b ~e))
(v/number? e) (cond (g/zero? e) 1
(g/one? e) b
(and (integer? e) (even? e) (sqrt? b))
(expt (first (operands b)) (quot e 2))
(and (expt? b)
(v/number? (second (operands b)))
(integer? (* (second (operands b)) e)))
(expt (first (operands b))
(* (second (operands b)) e))
(< e 0) (invert (expt b (- e)))
:else `(~'expt ~b ~e))
:else `(~'expt ~b ~e)))
#object[emmy.numsymb$expt 0x55b8aadd "
emmy.numsymb$expt@55b8aadd"
]

Complex Operations

(def ^:private conjugate-transparent-operators
#{'negate 'invert 'square 'cube
'sqrt
'exp 'exp2 'exp10
'log 'log2 'log10
'sin 'cos 'tan 'sec 'csc
'asin 'acos 'atan
'sinh 'cosh 'tanh 'sech 'csch
'+ '- '* '/ 'expt 'up 'down})
#{* + - / acos asin atan cos cosh csc csch cube down exp exp10 exp2 expt invert log log10 11 more elided}
(defn- make-rectangular [r i]
(cond (g/exact-zero? i) r
(and (v/real? r) (v/real? i))
(g/make-rectangular r i)
:else (add r (mul c/I i))))
#object[emmy.numsymb$make_rectangular 0x63f72c19 "
emmy.numsymb$make_rectangular@63f72c19"
]
(defn- make-polar [m a]
(cond (g/exact-zero? m) m
(g/exact-zero? a) m
(and (v/real? m) (v/real? a)) (g/make-polar m a)
:else (mul m (add
(cos a)
(mul c/I (sin a))))))
#object[emmy.numsymb$make_polar 0x136357df "
emmy.numsymb$make_polar@136357df"
]
(defn- conjugate [z]
(cond (v/number? z) (g/conjugate z)
(and (seq? z)
(contains? conjugate-transparent-operators
(operator z)))
(cons (operator z) (map conjugate (operands z)))
:else (list 'conjugate z)))
#object[emmy.numsymb$conjugate 0x3e08ff20 "
emmy.numsymb$conjugate@3e08ff20"
]
(def ^:private magnitude
(with-exactness-preserved g/magnitude
(fn [a] (sqrt (mul (conjugate a) a)))))
#object[emmy.numsymb$with_exactness_preserved$fn__87295 0x2ef6547e "
emmy.numsymb$with_exactness_preserved$fn__87295@2ef6547e"
]
(defn- real-part [z]
(if (v/number? z)
(g/real-part z)
(mul (g/div 1 2)
(add z (conjugate z)))))
#object[emmy.numsymb$real_part 0x74bb225d "
emmy.numsymb$real_part@74bb225d"
]
(defn- imag-part [z]
(if (v/number? z)
(g/imag-part z)
(mul (g/div 1 2)
(mul (c/complex 0 -1)
(sub z (conjugate z))))))
#object[emmy.numsymb$imag_part 0x7d6f4eba "
emmy.numsymb$imag_part@7d6f4eba"
]
(def ^:private angle
(with-exactness-preserved g/angle
(fn [z]
(atan (imag-part z)
(real-part z)))))
#object[emmy.numsymb$with_exactness_preserved$fn__87295 0x4e75fc8 "
emmy.numsymb$with_exactness_preserved$fn__87295@4e75fc8"
]
(defn dot-product
"Returns the symbolic dot product of the two supplied numbers `z1` and `z2`.
If both are numbers, defers to [[emmy.generic/dot-product]]. Else,
returns
$$\\Re(z_1)\\Re(z_2) + \\Im(z_1)\\Im(z_2)$$"
[z1 z2]
(cond (and (v/number? z1) (v/number? z2))
(g/dot-product z1 z2)
(v/real? z1) (mul z1 (real-part z2))
(v/real? z2) (mul (real-part z1) z2)
:else (add
(mul (real-part z1)
(real-part z2))
(mul (imag-part z1)
(imag-part z2)))))
#object[emmy.numsymb$dot_product 0x182ebe8e "
emmy.numsymb$dot_product@182ebe8e"
]
(defn ^:no-doc derivative
"Returns the symbolic derivative of the expression `expr`, which should
represent a function like `f`.
If the expression is already a derivative like `(D f)` or `((expt D 2) f)`,
`derivative` will increase the power of the exponent.
For example:
```clojure
(derivative 'f) ;;=> (D f)
(derivative '(D f)) ;;=> ((expt D 2) f)
(derivative '((expt D 2) f)) ;;=> ((expt D 3) f)
```"
[expr]
(cond (derivative? expr)
(let [f (first (operands expr))]
(list (expt g/derivative-symbol 2)
f))
(iterated-derivative? expr)
(let [pow (nth (operator expr) 2)
f (first (operands expr))]
(list (expt g/derivative-symbol (inc pow))
f))
:else
(list g/derivative-symbol expr)))
#object[emmy.numsymb$derivative 0x35aade1c "
emmy.numsymb$derivative@35aade1c"
]

Boolean Operations

(defn- sym:and
"For symbolic arguments, returns a symbolic expression representing the logical
conjuction of `l` and `r`.
If either side is `true?`, returns the other side. If either side is `false?`,
returns `false`."
[l r]
(cond (true? l) r
(false? l) l
(true? r) l
(false? r) r
(= l r) r
:else (list 'and l r)))
#object[emmy.numsymb$sym_COLON_and 0x32375c8d "
emmy.numsymb$sym_COLON_and@32375c8d"
]
(defn- sym:or
"For symbolic arguments, returns a symbolic expression representing the logical
disjunction of `l` and `r`.
If either side is `true?`, returns `true`. If either side is `false?`,
returns the other side."
[l r]
(cond (true? l) l
(false? l) r
(true? r) r
(false? r) l
(= l r) r
:else (list 'or l r)))
#object[emmy.numsymb$sym_COLON_or 0xc904c1c "
emmy.numsymb$sym_COLON_or@c904c1c"
]
(defn- sym:not
"For symbolic `x`, returns a symbolic expression representing the logical
negation of `x`. For boolean `x`, returns the negation of `x`."
[x]
(if (boolean? x)
(not x)
(list 'not x)))
#object[emmy.numsymb$sym_COLON_not 0x612759ce "
emmy.numsymb$sym_COLON_not@612759ce"
]
(defn- sym:bin= [l r]
(let [num-l? (v/number? l)
num-r? (v/number? r)]
(cond (and num-l? num-r?) (v/= l r)
(or num-l? num-r?) false
(= l r) true
:else (list '= l r))))
#object[emmy.numsymb$sym_COLON_bin_EQ_ 0x21e31c8 "
emmy.numsymb$sym_COLON_bin_EQ_@21e31c8"
]
(defn- sym:=
([] true)
([_] true)
([x y] (sym:bin= x y))
([x y & more]
(let [xs (cons x (cons y more))
pairs (partition 2 1 xs)]
(reduce (fn [acc [x y]]
(if-let [eq (sym:bin= x y)]
(sym:and acc eq)
(reduced false)))
true
pairs))))
#object[emmy.numsymb$sym_COLON__EQ_ 0xac5d632 "
emmy.numsymb$sym_COLON__EQ_@ac5d632"
]
(defn- sym:zero? [x]
(if (v/number? x)
(g/zero? x)
(list '= 0 x)))
#object[emmy.numsymb$sym_COLON_zero_QMARK_ 0x4bb8bda "
emmy.numsymb$sym_COLON_zero_QMARK_@4bb8bda"
]
(defn- sym:one? [x]
(if (v/number? x)
(g/one? x)
(list '= 1 x)))
#object[emmy.numsymb$sym_COLON_one_QMARK_ 0x5f095baa "
emmy.numsymb$sym_COLON_one_QMARK_@5f095baa"
]

Table

(def ^:private symbolic-operator-table
{'zero? sym:zero?
'one? sym:one?
'identity? sym:one?
'= sym:=
'not sym:not
'and (ua/monoid sym:and true false?)
'or (ua/monoid sym:or false true?)
'negate negate
'invert invert
'+ (ua/monoid add 0)
'- (ua/group sub add negate 0)
'* (ua/monoid mul 1 g/zero?)
'/ (ua/group div mul invert 1 g/zero?)
'modulo modulo
'remainder remainder
'gcd (ua/monoid gcd 0)
'lcm (ua/monoid lcm 1 g/zero?)
'floor floor
'ceiling ceiling
'integer-part integer-part
'fractional-part fractional-part
'sin sin
'cos cos
'tan tan
'sec sec
'csc csc
'asin asin
'acos acos
'acot acot
'atan atan
'sinh sinh
'cosh cosh
'tanh tanh
'coth coth
'sech sech
'csch csch
'cube #(expt % 3)
'square #(expt % 2)
'abs abs
'sqrt sqrt
'log log
'exp exp
'expt expt
'make-rectangular make-rectangular
'make-polar make-polar
'real-part real-part
'imag-part imag-part
'conjugate conjugate
'magnitude magnitude
'dot-product dot-product
'inner-product dot-product
'angle angle
'derivative derivative})
{* #object[emmy.util.aggregate$monoid$fn__78706 0x2c6aa1f2 "
emmy.util.aggregate$monoid$fn__78706@2c6aa1f2"
]
+ #object[emmy.util.aggregate$monoid$fn__78706 0x83a960b "
emmy.util.aggregate$monoid$fn__78706@83a960b"
]
- #object[emmy.util.aggregate$group$fn__78709 0x5087298d "
emmy.util.aggregate$group$fn__78709@5087298d"
]
/ #object[emmy.util.aggregate$group$fn__78709 0x45c6fc3c "
emmy.util.aggregate$group$fn__78709@45c6fc3c"
]
= #object[emmy.numsymb$sym_COLON__EQ_ 0xac5d632 "
emmy.numsymb$sym_COLON__EQ_@ac5d632"
]
abs #object[emmy.numsymb$abs 0x2dd17364 "
emmy.numsymb$abs@2dd17364"
]
acos #object[emmy.numsymb$acos 0x68b103e0 "
emmy.numsymb$acos@68b103e0"
]
acot #object[emmy.numsymb$acot 0x5bb23fbe "
emmy.numsymb$acot@5bb23fbe"
]
and #object[emmy.util.aggregate$monoid$fn__78706 0x3cdab047 "
emmy.util.aggregate$monoid$fn__78706@3cdab047"
]
angle #object[emmy.numsymb$with_exactness_preserved$fn__87295 0x4e75fc8 "
emmy.numsymb$with_exactness_preserved$fn__87295@4e75fc8"
]
43 more elided}
(defn symbolic-operator
"Given a symbol (like `'+`) returns an applicable operator if there is a
corresponding symbolic operator construction available."
[s]
(symbolic-operator-table s))
#object[emmy.numsymb$symbolic_operator 0x608ce319 "
emmy.numsymb$symbolic_operator@608ce319"
]