Skip to content

Latest commit

 

History

History
220 lines (190 loc) · 8.67 KB

macros.org

File metadata and controls

220 lines (190 loc) · 8.67 KB

Contents

Namespace: thi.ng.math.macros

This namespace provides basic math expressions as macros to produce inline expanded, nested expressions to create more legible source code for involved maths and to avoid extraneous reduce calls caused by the higher arity versions of Clojure’s built-in maths functions.

The macros are useful to simplify code for many geometry & matrix operations and are defined for both Clojure & ClojureScript.

Because the ns only contains macros, importing it into a user ns (in a mixed CLJ/CLJS project) will require a similar approach as this one:

(ns user
  #?(:clj (:require [thi.ng.math.macros :as mm])
     :cljs (:require-macros [thi.ng.math.macros :as mm])))

The following maths functions are currently implemented and are especially useful (more legible and faster) for higher arities (up to 8):

fndescriptionmin aritymax arity
addlike clj +28
sublike clj -28
mullike clj *28
divlike clj /28
maddadd pairwise multiplies38
msubsubtract pairwise multiplies38
addmproduct of pairwise sums38
submproduct of pairwise subtracts38
adddivdivision of pairwise sums38
subdivdivision of pairwise subtracts38
maddsublike madd, but last arg or pair is subtracted as product48
addmsublike addm, but last arg or pair is subtracted as product48
msubaddlike msub, but last arg or pair is added as product48
submaddlike subm, but last arg or pair is added as product48

Expression macro builders

(defmacro defmathop
  "Constructs macro to build inlined nested expressions which when
  call will apply f successively to all args. Supports arities 2-8."
  [name f]
  `(defmacro ~name
     ([a# b#]
        `(~~f ~a# ~b#))
     ([a# b# c#]
        `(~~f (~~f ~a# ~b#) ~c#))
     ([a# b# c# d#]
        `(~~f (~~f (~~f ~a# ~b#) ~c#) ~d#))
     ([a# b# c# d# e#]
        `(~~f (~~f (~~f (~~f ~a# ~b#) ~c#) ~d#) ~e#))
     ([a# b# c# d# e# f#]
        `(~~f (~~f (~~f (~~f (~~f ~a# ~b#) ~c#) ~d#) ~e#) ~f#))
     ([a# b# c# d# e# f# g#]
        `(~~f (~~f (~~f (~~f (~~f (~~f ~a# ~b#) ~c#) ~d#) ~e#) ~f#) ~g#))
     ([a# b# c# d# e# f# g# h#]
        `(~~f (~~f (~~f (~~f (~~f (~~f (~~f ~a# ~b#) ~c#) ~d#) ~e#) ~f#) ~g#) ~h#))))

(defmacro defmathop2
  "Constructs macro to build inlined nested expressions which when
  call will apply f to inner pairs and f2 to combine results."
  [name f f2]
  `(defmacro ~name
     ([a# b# c#]
        `(~~f2 (~~f ~a# ~b#) ~c#))
     ([a# b# c# d#]
        `(~~f2 (~~f ~a# ~b#) (~~f ~c# ~d#)))
     ([a# b# c# d# e#]
        `(~~f2 (~~f2 (~~f ~a# ~b#) (~~f ~c# ~d#)) ~e#))
     ([a# b# c# d# e# f#]
        `(~~f2 (~~f2 (~~f ~a# ~b#) (~~f ~c# ~d#)) (~~f ~e# ~f#)))
     ([a# b# c# d# e# f# g#]
        `(~~f2 (~~f2 (~~f2 (~~f ~a# ~b#) (~~f ~c# ~d#)) (~~f ~e# ~f#)) ~g#))
     ([a# b# c# d# e# f# g# h#]
        `(~~f2 (~~f2 (~~f2 (~~f ~a# ~b#) (~~f ~c# ~d#)) (~~f ~e# ~f#)) (~~f ~g# ~h#)))))

(defmacro defmathop3
  "Takes f, f2 & f3 as syntax-quoted symbols. Constructs a macro which
  when called, applies f to all but the last 1 or 2 args. The
  remaining arg(s) are combined with the first result using f2.
  Furthermore, for arities 6 and 8, f3 is first applied to the last
  two args are before the final application of f2. For example:

      (defmathop* maddsub `madd `- `*)
      (maddsub 2 3 4 5) => (- (madd 2 3 4) 5)
      (maddsub 2 3 4 5 6) => (- (madd 2 3 4) (* 5 6))"
  [name f f2 f3]
  `(defmacro ~name
     ([a# b# c# d#]
        `(~~f2 (~~f ~a# ~b# ~c#) ~d#))
     ([a# b# c# d# e#]
        `(~~f2 (~~f ~a# ~b# ~c# ~d#) ~e#))
     ([a# b# c# d# e# f#]
        `(~~f2 (~~f ~a# ~b# ~c# ~d#) (~~f3 ~e# ~f#)))
     ([a# b# c# d# e# f# g#]
        `(~~f2 (~~f ~a# ~b# ~c# ~d# ~e# ~f#) ~g#))
     ([a# b# c# d# e# f# g# h#]
        `(~~f2 (~~f ~a# ~b# ~c# ~d# ~e# ~f#) (~~f3 ~g# ~h#)))))

Actual definition of maths operations

(defmathop add `+)
(defmathop sub `-)
(defmathop mul `*)
(defmathop div `/)
(defmathop2 madd `* `+)
(defmathop2 msub `* `-)
(defmathop2 addm `+ `*)
(defmathop2 subm `- `*)
(defmathop2 adddiv `+ `/)
(defmathop2 subdiv `- `/)
(defmathop3 maddsub `madd `- `*)
(defmathop3 addmsub `addm `- `*)
(defmathop3 msubadd `msub `+ `*)
(defmathop3 submadd `subm `+ `*)

Binary operations

(defmacro if*
  "Returns y if x pred returns truthy, else 0"
  [pred x y] `(if (~pred ~x) ~y 0))

(defmacro bitmask
  "Constructs a bit mask from given values & predicate fn applied to
  each. If pred returns truthy value the value's related bit is set.
  Bit values start at 1 and double for successive args (max 8)."
  ([pred a]
     `(if* ~pred ~a 0x01))
  ([pred a b]
     `(bit-or (bitmask ~pred ~a) (if* ~pred ~b 0x02)))
  ([pred a b c]
     `(bit-or (bitmask ~pred ~a ~b) (if* ~pred ~c 0x04)))
  ([pred a b c d]
     `(bit-or (bitmask ~pred ~a ~b ~c) (if* ~pred ~d 0x08)))
  ([pred a b c d e]
     `(bit-or (bitmask ~pred ~a ~b ~c ~d) (if* ~pred ~e 0x10)))
  ([pred a b c d e f]
     `(bit-or (bitmask ~pred ~a ~b ~c ~d ~e) (if* ~pred ~f 0x20)))
  ([pred a b c d e f g]
     `(bit-or (bitmask ~pred ~a ~b ~c ~d ~e ~f) (if* ~pred ~g 0x40)))
  ([pred a b c d e f g h]
     `(bit-or (bitmask ~pred ~a ~b ~c ~d ~e ~f ~g) (if* ~pred ~h 0x80))))

Interpolation

(defmacro mix
  "Linear, bi-linear & tri-linear interpolation"
  ([a b t]
   `(let [a# ~a]
      (submadd ~b a# ~t a#)))
  ([a b c d u v]
   `(let [u# ~u
          a# (mix ~a ~b u#)]
      (submadd (mix ~c ~d u#) a# ~v a#)))
  ([a b c d e f g h u v w]
   `(let [u# ~u
          v# ~v
          a# (mix ~a ~b ~c ~d u# v#)]
      (submadd (mix ~e ~f ~g ~h u# v#) a# ~w a#))))

Min / max

(defmacro min
  ([a b] `(let [a# ~a b# ~b] (if (<= a# b#) a# b#)))
  ([a b c] `(min (min ~a ~b) ~c))
  ([a b c d] `(min (min (min ~a ~b) ~c) ~d))
  ([a b c d e] `(min (min (min (min ~a ~b) ~c) ~d) ~e))
  ([a b c d e f] `(min (min (min (min (min ~a ~b) ~c) ~d) ~e) ~f))
  ([a b c d e f g] `(min (min (min (min (min (min ~a ~b) ~c) ~d) ~e) ~f) ~g))
  ([a b c d e f g h] `(min (min (min (min (min (min (min ~a ~b) ~c) ~d) ~e) ~f) ~g) ~h)))

(defmacro max
  ([a b] `(let [a# ~a b# ~b] (if (>= a# b#) a# b#)))
  ([a b c] `(max (max ~a ~b) ~c))
  ([a b c d] `(max (max (max ~a ~b) ~c) ~d))
  ([a b c d e] `(max (max (max (max ~a ~b) ~c) ~d) ~e))
  ([a b c d e f] `(max (max (max (max (max ~a ~b) ~c) ~d) ~e) ~f))
  ([a b c d e f g] `(max (max (max (max (max (max ~a ~b) ~c) ~d) ~e) ~f) ~g))
  ([a b c d e f g h] `(max (max (max (max (max (max (max ~a ~b) ~c) ~d) ~e) ~f) ~g) ~h)))

Complete namespace definition

(ns thi.ng.math.macros
  (:refer-clojure :exclude [min max]))

<<builders>>

<<ops>>

<<bin-ops>>

<<mix>>

<<minmax>>