annotate src/clojure/contrib/math.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
rlm@10 1 ;;; math.clj: math functions that deal intelligently with the various
rlm@10 2 ;;; types in Clojure's numeric tower, as well as math functions
rlm@10 3 ;;; commonly found in Scheme implementations.
rlm@10 4
rlm@10 5 ;; by Mark Engelberg (mark.engelberg@gmail.com)
rlm@10 6 ;; January 17, 2009
rlm@10 7
rlm@10 8 ;; expt - (expt x y) is x to the yth power, returns an exact number
rlm@10 9 ;; if the base is an exact number, and the power is an integer,
rlm@10 10 ;; otherwise returns a double.
rlm@10 11 ;; abs - (abs n) is the absolute value of n
rlm@10 12 ;; gcd - (gcd m n) returns the greatest common divisor of m and n
rlm@10 13 ;; lcm - (lcm m n) returns the least common multiple of m and n
rlm@10 14
rlm@10 15 ;; The behavior of the next three functions on doubles is consistent
rlm@10 16 ;; with the behavior of the corresponding functions
rlm@10 17 ;; in Java's Math library, but on exact numbers, returns an integer.
rlm@10 18
rlm@10 19 ;; floor - (floor n) returns the greatest integer less than or equal to n.
rlm@10 20 ;; If n is an exact number, floor returns an integer,
rlm@10 21 ;; otherwise a double.
rlm@10 22 ;; ceil - (ceil n) returns the least integer greater than or equal to n.
rlm@10 23 ;; If n is an exact number, ceil returns an integer,
rlm@10 24 ;; otherwise a double.
rlm@10 25 ;; round - (round n) rounds to the nearest integer.
rlm@10 26 ;; round always returns an integer. round rounds up for values
rlm@10 27 ;; exactly in between two integers.
rlm@10 28
rlm@10 29
rlm@10 30 ;; sqrt - Implements the sqrt behavior I'm accustomed to from PLT Scheme,
rlm@10 31 ;; specifically, if the input is an exact number, and is a square
rlm@10 32 ;; of an exact number, the output will be exact. The downside
rlm@10 33 ;; is that for the common case (inexact square root), some extra
rlm@10 34 ;; computation is done to look for an exact square root first.
rlm@10 35 ;; So if you need blazingly fast square root performance, and you
rlm@10 36 ;; know you're just going to need a double result, you're better
rlm@10 37 ;; off calling java's Math/sqrt, or alternatively, you could just
rlm@10 38 ;; convert your input to a double before calling this sqrt function.
rlm@10 39 ;; If Clojure ever gets complex numbers, then this function will
rlm@10 40 ;; need to be updated (so negative inputs yield complex outputs).
rlm@10 41 ;; exact-integer-sqrt - Implements a math function from the R6RS Scheme
rlm@10 42 ;; standard. (exact-integer-sqrt k) where k is a non-negative integer,
rlm@10 43 ;; returns [s r] where k = s^2+r and k < (s+1)^2. In other words, it
rlm@10 44 ;; returns the floor of the square root and the "remainder".
rlm@10 45
rlm@10 46 (ns
rlm@10 47 ^{:author "Mark Engelberg",
rlm@10 48 :doc "Math functions that deal intelligently with the various
rlm@10 49 types in Clojure's numeric tower, as well as math functions
rlm@10 50 commonly found in Scheme implementations.
rlm@10 51
rlm@10 52 expt - (expt x y) is x to the yth power, returns an exact number
rlm@10 53 if the base is an exact number, and the power is an integer,
rlm@10 54 otherwise returns a double.
rlm@10 55 abs - (abs n) is the absolute value of n
rlm@10 56 gcd - (gcd m n) returns the greatest common divisor of m and n
rlm@10 57 lcm - (lcm m n) returns the least common multiple of m and n
rlm@10 58
rlm@10 59 The behavior of the next three functions on doubles is consistent
rlm@10 60 with the behavior of the corresponding functions
rlm@10 61 in Java's Math library, but on exact numbers, returns an integer.
rlm@10 62
rlm@10 63 floor - (floor n) returns the greatest integer less than or equal to n.
rlm@10 64 If n is an exact number, floor returns an integer,
rlm@10 65 otherwise a double.
rlm@10 66 ceil - (ceil n) returns the least integer greater than or equal to n.
rlm@10 67 If n is an exact number, ceil returns an integer,
rlm@10 68 otherwise a double.
rlm@10 69 round - (round n) rounds to the nearest integer.
rlm@10 70 round always returns an integer. round rounds up for values
rlm@10 71 exactly in between two integers.
rlm@10 72
rlm@10 73
rlm@10 74 sqrt - Implements the sqrt behavior I'm accustomed to from PLT Scheme,
rlm@10 75 specifically, if the input is an exact number, and is a square
rlm@10 76 of an exact number, the output will be exact. The downside
rlm@10 77 is that for the common case (inexact square root), some extra
rlm@10 78 computation is done to look for an exact square root first.
rlm@10 79 So if you need blazingly fast square root performance, and you
rlm@10 80 know you're just going to need a double result, you're better
rlm@10 81 off calling java's Math/sqrt, or alternatively, you could just
rlm@10 82 convert your input to a double before calling this sqrt function.
rlm@10 83 If Clojure ever gets complex numbers, then this function will
rlm@10 84 need to be updated (so negative inputs yield complex outputs).
rlm@10 85 exact-integer-sqrt - Implements a math function from the R6RS Scheme
rlm@10 86 standard. (exact-integer-sqrt k) where k is a non-negative integer,
rlm@10 87 returns [s r] where k = s^2+r and k < (s+1)^2. In other words, it
rlm@10 88 returns the floor of the square root and the "remainder".
rlm@10 89 "}
rlm@10 90 clojure.contrib.math)
rlm@10 91
rlm@10 92 (derive ::integer ::exact)
rlm@10 93 (derive java.lang.Integer ::integer)
rlm@10 94 (derive java.math.BigInteger ::integer)
rlm@10 95 (derive java.lang.Long ::integer)
rlm@10 96 (derive java.math.BigDecimal ::exact)
rlm@10 97 (derive clojure.lang.Ratio ::exact)
rlm@10 98 (derive java.lang.Double ::inexact)
rlm@10 99 (derive java.lang.Float ::inexact)
rlm@10 100
rlm@10 101 (defmulti ^{:arglists '([base pow])
rlm@10 102 :doc "(expt base pow) is base to the pow power.
rlm@10 103 Returns an exact number if the base is an exact number and the power is an integer, otherwise returns a double."}
rlm@10 104 expt (fn [x y] [(class x) (class y)]))
rlm@10 105
rlm@10 106 (defn- expt-int [base pow]
rlm@10 107 (loop [n pow, y (num 1), z base]
rlm@10 108 (let [t (bit-and n 1), n (bit-shift-right n 1)]
rlm@10 109 (cond
rlm@10 110 (zero? t) (recur n y (* z z))
rlm@10 111 (zero? n) (* z y)
rlm@10 112 :else (recur n (* z y) (* z z))))))
rlm@10 113
rlm@10 114 (defmethod expt [::exact ::integer] [base pow]
rlm@10 115 (cond
rlm@10 116 (pos? pow) (expt-int base pow)
rlm@10 117 (zero? pow) 1
rlm@10 118 :else (/ 1 (expt-int base (- pow)))))
rlm@10 119
rlm@10 120 (defmethod expt :default [base pow] (Math/pow base pow))
rlm@10 121
rlm@10 122 (defn abs "(abs n) is the absolute value of n" [n]
rlm@10 123 (cond
rlm@10 124 (not (number? n)) (throw (IllegalArgumentException.
rlm@10 125 "abs requires a number"))
rlm@10 126 (neg? n) (- n)
rlm@10 127 :else n))
rlm@10 128
rlm@10 129 (defmulti ^{:arglists '([n])
rlm@10 130 :doc "(floor n) returns the greatest integer less than or equal to n.
rlm@10 131 If n is an exact number, floor returns an integer, otherwise a double."}
rlm@10 132 floor class)
rlm@10 133 (defmethod floor ::integer [n] n)
rlm@10 134 (defmethod floor java.math.BigDecimal [n] (.. n (setScale 0 BigDecimal/ROUND_FLOOR) (toBigInteger)))
rlm@10 135 (defmethod floor clojure.lang.Ratio [n]
rlm@10 136 (if (pos? n) (quot (. n numerator) (. n denominator))
rlm@10 137 (dec (quot (. n numerator) (. n denominator)))))
rlm@10 138 (defmethod floor :default [n]
rlm@10 139 (Math/floor n))
rlm@10 140
rlm@10 141 (defmulti ^{:arglists '([n])
rlm@10 142 :doc "(ceil n) returns the least integer greater than or equal to n.
rlm@10 143 If n is an exact number, ceil returns an integer, otherwise a double."}
rlm@10 144 ceil class)
rlm@10 145 (defmethod ceil ::integer [n] n)
rlm@10 146 (defmethod ceil java.math.BigDecimal [n] (.. n (setScale 0 BigDecimal/ROUND_CEILING) (toBigInteger)))
rlm@10 147 (defmethod ceil clojure.lang.Ratio [n]
rlm@10 148 (if (pos? n) (inc (quot (. n numerator) (. n denominator)))
rlm@10 149 (quot (. n numerator) (. n denominator))))
rlm@10 150 (defmethod ceil :default [n]
rlm@10 151 (Math/ceil n))
rlm@10 152
rlm@10 153 (defmulti ^{:arglists '([n])
rlm@10 154 :doc "(round n) rounds to the nearest integer.
rlm@10 155 round always returns an integer. Rounds up for values exactly in between two integers."}
rlm@10 156 round class)
rlm@10 157 (defmethod round ::integer [n] n)
rlm@10 158 (defmethod round java.math.BigDecimal [n] (floor (+ n 0.5M)))
rlm@10 159 (defmethod round clojure.lang.Ratio [n] (floor (+ n 1/2)))
rlm@10 160 (defmethod round :default [n] (Math/round n))
rlm@10 161
rlm@10 162 (defn gcd "(gcd a b) returns the greatest common divisor of a and b" [a b]
rlm@10 163 (if (or (not (integer? a)) (not (integer? b)))
rlm@10 164 (throw (IllegalArgumentException. "gcd requires two integers"))
rlm@10 165 (loop [a (abs a) b (abs b)]
rlm@10 166 (if (zero? b) a,
rlm@10 167 (recur b (mod a b))))))
rlm@10 168
rlm@10 169 (defn lcm
rlm@10 170 "(lcm a b) returns the least common multiple of a and b"
rlm@10 171 [a b]
rlm@10 172 (when (or (not (integer? a)) (not (integer? b)))
rlm@10 173 (throw (IllegalArgumentException. "lcm requires two integers")))
rlm@10 174 (cond (zero? a) 0
rlm@10 175 (zero? b) 0
rlm@10 176 :else (abs (* b (quot a (gcd a b))))))
rlm@10 177
rlm@10 178 ; Length of integer in binary, used as helper function for sqrt.
rlm@10 179 (defmulti ^{:private true} integer-length class)
rlm@10 180 (defmethod integer-length java.lang.Integer [n]
rlm@10 181 (count (Integer/toBinaryString n)))
rlm@10 182 (defmethod integer-length java.lang.Long [n]
rlm@10 183 (count (Long/toBinaryString n)))
rlm@10 184 (defmethod integer-length java.math.BigInteger [n]
rlm@10 185 (count (. n toString 2)))
rlm@10 186
rlm@10 187 ;; Produces the largest integer less than or equal to the square root of n
rlm@10 188 ;; Input n must be a non-negative integer
rlm@10 189 (defn- integer-sqrt [n]
rlm@10 190 (cond
rlm@10 191 (> n 24)
rlm@10 192 (let [n-len (integer-length n)]
rlm@10 193 (loop [init-value (if (even? n-len)
rlm@10 194 (bit-shift-left 1 (bit-shift-right n-len 1))
rlm@10 195 (bit-shift-left 2 (bit-shift-right n-len 1)))]
rlm@10 196 (let [iterated-value (bit-shift-right (+ init-value (quot n init-value)) 1)]
rlm@10 197 (if (>= iterated-value init-value)
rlm@10 198 init-value
rlm@10 199 (recur iterated-value)))))
rlm@10 200 (> n 15) 4
rlm@10 201 (> n 8) 3
rlm@10 202 (> n 3) 2
rlm@10 203 (> n 0) 1
rlm@10 204 (> n -1) 0))
rlm@10 205
rlm@10 206 (defn exact-integer-sqrt "(exact-integer-sqrt n) expects a non-negative integer n, and returns [s r] where n = s^2+r and n < (s+1)^2. In other words, it returns the floor of the square root and the 'remainder'.
rlm@10 207 For example, (exact-integer-sqrt 15) is [3 6] because 15 = 3^2+6."
rlm@10 208 [n]
rlm@10 209 (if (or (not (integer? n)) (neg? n))
rlm@10 210 (throw (IllegalArgumentException. "exact-integer-sqrt requires a non-negative integer"))
rlm@10 211 (let [isqrt (integer-sqrt n),
rlm@10 212 error (- n (* isqrt isqrt))]
rlm@10 213 [isqrt error])))
rlm@10 214
rlm@10 215 (defmulti ^{:arglists '([n])
rlm@10 216 :doc "Square root, but returns exact number if possible."}
rlm@10 217 sqrt class)
rlm@10 218 (defmethod sqrt ::integer [n]
rlm@10 219 (if (neg? n) Double/NaN
rlm@10 220 (let [isqrt (integer-sqrt n),
rlm@10 221 error (- n (* isqrt isqrt))]
rlm@10 222 (if (zero? error) isqrt
rlm@10 223 (Math/sqrt n)))))
rlm@10 224
rlm@10 225 (defmethod sqrt clojure.lang.Ratio [n]
rlm@10 226 (if (neg? n) Double/NaN
rlm@10 227 (let [numerator (.numerator n),
rlm@10 228 denominator (.denominator n),
rlm@10 229 sqrtnum (sqrt numerator)]
rlm@10 230 (if (float? sqrtnum)
rlm@10 231 (Math/sqrt n)
rlm@10 232 (let [sqrtden (sqrt denominator)]
rlm@10 233 (if (float? sqrtnum)
rlm@10 234 (Math/sqrt n)
rlm@10 235 (/ sqrtnum sqrtden)))))))
rlm@10 236
rlm@10 237 (defmethod sqrt java.math.BigDecimal [n]
rlm@10 238 (if (neg? n) Double/NaN
rlm@10 239 (let [frac (rationalize n),
rlm@10 240 sqrtfrac (sqrt frac)]
rlm@10 241 (if (ratio? sqrtfrac)
rlm@10 242 (/ (BigDecimal. (.numerator sqrtfrac))
rlm@10 243 (BigDecimal. (.denominator sqrtfrac)))
rlm@10 244 sqrtfrac))))
rlm@10 245
rlm@10 246 (defmethod sqrt :default [n]
rlm@10 247 (Math/sqrt n))