(defmacro def-tuple-type
"Defines a type named TupleN for a N-element tuple."
[N]
(assert (integer? N))
(assert (pos? N))
(let [NAME (symbol (str "Tuple" N))
FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
`(deftype ~NAME ~FIELDS :as ~'this
clojure.lang.Associative
(~'containsKey [~'n] (and (integer? ~'n)
(< -1 ~'n ~N)))
(~'entryAt [~'k] (case ~'k
~@(interleave
(range 0 N)
(map (fn [e] `(clojure.lang.MapEntry. ~e ~(nth FIELDS e)))
(range 0 N)))))
(~'assoc [~'k ~'v] (case ~'k
~@(interleave
(range 0 N)
(map (fn [e] `(new ~NAME ~@(assoc FIELDS e 'v)))
(range 0 N)))))
clojure.lang.Counted
(~'count [] ~N)
clojure.lang.ILookup
(~'valAt [~'n] (case ~'n
~@(interleave (range 0 N) FIELDS)
nil))
(~'valAt [~'n not-found#] (case ~'n
~@(interleave (range 0 N) FIELDS)
not-found#))
clojure.lang.IFn
(~'invoke [~'n] (. ~'this valAt ~'n))
(~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
clojure.lang.IPersistentCollection
(~'cons [~'x] (cons ~'x (seq ~'this)))
(~'empty [] (new ~NAME ~@(repeat N nil)))
(~'equiv [~'x] (and (= ~N (count ~'x))
~@(map (fn [e] `(= ~(get FIELDS e) (nth ~'x ~e)))
(range 0 N))))
(~'seq [] (list ~@FIELDS))
clojure.lang.IPersistentVector
(~'length [] 2)
(~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
clojure.lang.Indexed
(~'nth [~'n] (. ~'this ~'valAt ~'n))
clojure.lang.Reversible
(~'rseq [] (reverse (. ~'this ~'seq)))
clojure.lang.Seqable
clojure.lang.Sequential
java.io.Serializable
java.lang.Comparable
(~'compareTo [~'x] (compare ~FIELDS ~'x))
java.lang.Iterable
(~'iterator [] (.. ~'this ~'seq ~'iterator))
java.lang.Runnable
(~'run [] nil)
java.util.Collection
java.util.List
(~'size [] 2)
(~'indexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(range 0 N))
(range 0 N))
:else -1))
(~'lastIndexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(reverse (range 0 N)))
(reverse (range 0 N)))
:else -1))
java.util.RandomAccess
java.util.concurrent.Callable
(~'call [] nil))))
(def-tuple-type 2) ;; Tuple2
(def-tuple-type 3) ;; Tuple3
(def-tuple-type 4) ;; Tuple4
(def-tuple-type 5) ;; Tuple5
(defn tuple
"Creates and returns a new 2, 3, 4, or 5-element tuple.
Tuples support the same methods as vectors."
([a b] (Tuple2 a b))
([a b c] (Tuple3 a b c))
([a b c d] (Tuple4 a b c d))
([a b c d e] (Tuple5 a b c d e)))
(use 'clojure.test)
(deftest t-equality
(is (= (tuple 8 9) [8 9])))
(deftest t-compare
(is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
(is (= 0 (compare (tuple 1 2) (tuple 1 2))))
(is (= 0 (compare (tuple :a :b :c) [:a :b :c]))))
(deftest t-assoc
(is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))
(deftest t-get
(is (= :d (get (tuple :a :b :c :d) 3)))
(is (= :notfound (get (tuple :a :b) 3 :notfound))))
(deftest t-count
(is (= 5 (count (tuple :a :b :c :d :e)))))
(deftest t-invoke
(is (= :d ((tuple :a :b :c :d) 3))))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (vector i i i) 2))))
(println "3-TUPLES")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (tuple i i i) 2))))
(println "5-VECTORS")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (vector i i i i i) 4))))
(println "5-TUPLES")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (tuple i i i i i) 4)))))
(defmacro def-tuple-type
"Defines a type, named NAME, for a N-element tuple with elements of
type TYPE, which may be primitive."
([NAME N] (def-tuple-type NAME N java.lang.Object))
([NAME N TYPE]
(assert (integer? N))
(assert (pos? N))
(let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
{:tag TYPE})
(range 0 N)))
FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
`(deftype ~NAME ~TAGGED-FIELDS :as ~'this
clojure.lang.Associative
(~'containsKey [~'n] (and (integer? ~'n)
(< -1 ~'n ~N)))
(~'entryAt [~'k] (case ~'k
~@(interleave
(range 0 N)
(map (fn [e] `(clojure.lang.MapEntry.
~e ~(nth FIELDS e)))
(range 0 N)))))
(~'assoc [~'k ~'v] (case ~'k
~@(interleave
(range 0 N)
(map (fn [e]
`(new ~NAME
~@(assoc FIELDS e 'v)))
(range 0 N)))))
clojure.lang.Counted
(~'count [] ~N)
clojure.lang.ILookup
(~'valAt [~'n] (case ~'n
~@(interleave (range 0 N) FIELDS)
nil))
(~'valAt [~'n not-found#] (case ~'n
~@(interleave (range 0 N) FIELDS)
not-found#))
clojure.lang.IFn
(~'invoke [~'n] (. ~'this valAt ~'n))
(~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
clojure.lang.IPersistentCollection
(~'cons [~'x] (cons ~'x (seq ~'this)))
(~'empty [] (new ~NAME ~@(repeat N nil)))
(~'equiv [~'x] (and (= ~N (count ~'x))
~@(map (fn [e] `(= ~(get FIELDS e)
(nth ~'x ~e)))
(range 0 N))))
(~'seq [] (list ~@FIELDS))
clojure.lang.IPersistentVector
(~'length [] 2)
(~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
clojure.lang.Indexed
(~'nth [~'n] (. ~'this ~'valAt ~'n))
clojure.lang.Reversible
(~'rseq [] (reverse (. ~'this ~'seq)))
clojure.lang.Seqable
clojure.lang.Sequential
java.io.Serializable
java.lang.Comparable
(~'compareTo [~'x] (compare ~FIELDS ~'x))
java.lang.Iterable
(~'iterator [] (.. ~'this ~'seq ~'iterator))
java.lang.Runnable
(~'run [] nil)
java.util.Collection
java.util.List
(~'size [] 2)
(~'indexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(range 0 N))
(range 0 N))
:else -1))
(~'lastIndexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(reverse (range 0 N)))
(reverse (range 0 N)))
:else -1))
java.util.RandomAccess
java.util.concurrent.Callable
(~'call [] nil)))))
(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)
(defn tuple
"Creates and returns a new 2, 3, 4, or 5-element tuple.
Tuples support the same methods as vectors."
([a b] (Tuple2 a b))
([a b c] (Tuple3 a b c))
([a b c d] (Tuple4 a b c d))
([a b c d e] (Tuple5 a b c d e)))
(def-tuple-type Double3D 3 double)
(use 'clojure.test)
(deftest t-equality
(is (= (tuple 8 9) [8 9])))
(deftest t-compare
(is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
(is (= 0 (compare (tuple 1 2) (tuple 1 2))))
(is (= 0 (compare (tuple :a :b :c) [:a :b :c]))))
(deftest t-assoc
(is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))
(deftest t-nth
(is (= :d (nth (tuple :a :b :c :d) 3)))
(is (= :notfound (nth (tuple :a :b) 3 :notfound))))
(deftest t-count
(is (= 5 (count (tuple :a :b :c :d :e)))))
(deftest t-invoke
(is (= :d ((tuple :a :b :c :d) 3))))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS of Objects")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (vector i i i) 2))))
(println "3-TUPLES of Objects")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (tuple i i i) 2))))
(println "3-TUPLES of doubles")
(dotimes [j 5]
(time (dotimes [i 1000000]
(get (Double3D 2.0 3.0 4.0) 2)))))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (vector i i i) 2))))
(println "3-TUPLES of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (tuple i i i) 2))))
(println "3-TUPLES of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (Double3D 2.0 3.0 4.0) 2))))
(println "arrays of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(aget (double-array 3 2.0) 2)))))
(defmacro def-tuple-type
"Defines a type, named NAME, for a N-element tuple with elements of
type TYPE, which may be primitive."
([NAME N] (def-tuple-type NAME N java.lang.Object))
([NAME N TYPE]
(assert (integer? N))
(assert (pos? N))
(let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
{:tag TYPE})
(range 0 N)))
FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
`(deftype ~NAME ~TAGGED-FIELDS :as ~'this
clojure.lang.Associative
(~'containsKey [~'n] (and (integer? ~'n)
(< -1 ~'n ~N)))
(~'entryAt [~'k] (cond ~@(interleave
(map (fn [e] `(= ~e ~'k))
(range 0 N))
(map (fn [e] `(clojure.lang.MapEntry.
~e ~(nth FIELDS e)))
(range 0 N)))
:else (throw (IllegalArgumentException.))))
(~'assoc [~'k ~'v] (cond ~@(interleave
(map (fn [e] `(= ~e ~'k))
(range 0 N))
(map (fn [e]
`(new ~NAME
~@(assoc FIELDS e 'v)))
(range 0 N)))
:else (throw (IllegalArgumentException.))))
clojure.lang.Counted
(~'count [] ~N)
clojure.lang.ILookup
(~'valAt [~'n] (cond ~@(interleave
(map (fn [e] `(= ~e ~'n))
(range 0 N))
FIELDS)
:else nil))
(~'valAt [~'n not-found#] (cond
~@(interleave
(map (fn [e] `(= ~e ~'n))
(range 0 N))
FIELDS)
:else not-found#))
clojure.lang.IFn
(~'invoke [~'n] (. ~'this valAt ~'n))
(~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
clojure.lang.IPersistentCollection
(~'cons [~'x] (cons ~'x (seq ~'this)))
(~'empty [] (new ~NAME ~@(repeat N nil)))
(~'equiv [~'x] (and (= ~N (count ~'x))
~@(map (fn [e] `(= ~(get FIELDS e)
(nth ~'x ~e)))
(range 0 N))))
(~'seq [] (list ~@FIELDS))
clojure.lang.IPersistentVector
(~'length [] 2)
(~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
clojure.lang.Indexed
(~'nth [~'n] (. ~'this ~'valAt ~'n))
clojure.lang.Reversible
(~'rseq [] (reverse (. ~'this ~'seq)))
clojure.lang.Seqable
clojure.lang.Sequential
java.io.Serializable
java.lang.Comparable
(~'compareTo [~'x] (compare ~FIELDS ~'x))
java.lang.Iterable
(~'iterator [] (.. ~'this ~'seq ~'iterator))
java.lang.Runnable
(~'run [] nil)
java.util.Collection
java.util.List
(~'size [] 2)
(~'indexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(range 0 N))
(range 0 N))
:else -1))
(~'lastIndexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(reverse (range 0 N)))
(reverse (range 0 N)))
:else -1))
java.util.RandomAccess
java.util.concurrent.Callable
(~'call [] nil)))))
(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)
(defn tuple
"Creates and returns a new 2, 3, 4, or 5-element tuple.
Tuples support the same methods as vectors."
([a b] (Tuple2 a b))
([a b c] (Tuple3 a b c))
([a b c d] (Tuple4 a b c d))
([a b c d e] (Tuple5 a b c d e)))
(def-tuple-type Double3D 3 double)
(use 'clojure.test)
(deftest t-equality
(is (= (tuple 8 9) [8 9])))
(deftest t-compare
(is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
(is (= 0 (compare (tuple 1 2) (tuple 1 2))))
(is (= 0 (compare (tuple :a :b :c) [:a :b :c]))))
(deftest t-assoc
(is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))
(deftest t-nth
(is (= :d (nth (tuple :a :b :c :d) 3)))
(is (= :notfound (nth (tuple :a :b) 3 :notfound))))
(deftest t-count
(is (= 5 (count (tuple :a :b :c :d :e)))))
(deftest t-invoke
(is (= :d ((tuple :a :b :c :d) 3))))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (vector i i i) 2))))
(println "3-TUPLES of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (tuple i i i) 2))))
(println "3-TUPLES of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (Double3D 2.0 3.0 4.0) 2))))
(println "arrays of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(aget (double-array 3 2.0) 2)))))
(defmacro def-tuple-type
"Defines a type, named NAME, for a N-element tuple with elements of
type TYPE, which may be primitive."
([NAME N] `(def-tuple-type ~NAME ~N java.lang.Object))
([NAME N TYPE]
(assert (integer? N))
(assert (pos? N))
(let [TAGGED-FIELDS (vec (map #(with-meta (symbol (str "e" %))
{:tag TYPE})
(range 0 N)))
FIELDS (vec (map #(symbol (str "e" %)) (range 0 N)))]
`(deftype ~NAME ~TAGGED-FIELDS :as ~'this
clojure.lang.Associative
(~'containsKey [~'n] (and (integer? ~'n)
(< -1 ~'n ~N)))
(~'entryAt [~'k] (cond ~@(interleave
(map (fn [e] `(= ~e ~'k))
(range 0 N))
(map (fn [e] `(clojure.lang.MapEntry.
~e ~(nth FIELDS e)))
(range 0 N)))
:else (throw (IllegalArgumentException.))))
(~'assoc [~'k ~'v] (cond ~@(interleave
(map (fn [e] `(= ~e ~'k))
(range 0 N))
(map (fn [e]
`(new ~NAME
~@(assoc FIELDS e 'v)))
(range 0 N)))
:else (throw (IllegalArgumentException.))))
clojure.lang.Counted
(~'count [] ~N)
clojure.lang.ILookup
(~'valAt [~'n] (cond ~@(interleave
(map (fn [e] `(= ~e ~'n))
(range 0 N))
FIELDS)
:else nil))
(~'valAt [~'n not-found#] (cond
~@(interleave
(map (fn [e] `(= ~e ~'n))
(range 0 N))
FIELDS)
:else not-found#))
clojure.lang.IFn
(~'invoke [~'n] (. ~'this valAt ~'n))
(~'invoke [~'n not-found#] (. ~'this valAt ~'n not-found#))
clojure.lang.IPersistentCollection
(~'cons [~'x] (cons ~'x (seq ~'this)))
(~'empty [] (new ~NAME ~@(repeat N nil)))
(~'equiv [~'x] (and (= ~N (count ~'x))
~@(map (fn [e] `(= ~(get FIELDS e)
(nth ~'x ~e)))
(range 0 N))))
(~'seq [] (list ~@FIELDS))
clojure.lang.IPersistentVector
(~'length [] 2)
(~'assocN [~'n ~'v] (. ~'this ~'assoc ~'n ~'v))
clojure.lang.Indexed
(~'nth [~'n] (. ~'this ~'valAt ~'n))
clojure.lang.Reversible
(~'rseq [] (reverse (. ~'this ~'seq)))
clojure.lang.Seqable
clojure.lang.Sequential
java.io.Serializable
java.lang.Comparable
(~'compareTo [~'x] (compare ~FIELDS ~'x))
java.lang.Iterable
(~'iterator [] (.. ~'this ~'seq ~'iterator))
java.lang.Runnable
(~'run [] nil)
java.util.Collection
java.util.List
(~'size [] 2)
(~'indexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(range 0 N))
(range 0 N))
:else -1))
(~'lastIndexOf [~'x] (cond
~@(interleave
(map (fn [n] `(= ~'x ~(get FIELDS n)))
(reverse (range 0 N)))
(reverse (range 0 N)))
:else -1))
java.util.RandomAccess
java.util.concurrent.Callable
(~'call [] nil)))))
(def-tuple-type Tuple2 2)
(def-tuple-type Tuple3 3)
(def-tuple-type Tuple4 4)
(def-tuple-type Tuple5 5)
(defn tuple
"Creates and returns a new 2, 3, 4, or 5-element tuple.
Tuples support the same methods as vectors."
([a b] (Tuple2 a b))
([a b c] (Tuple3 a b c))
([a b c d] (Tuple4 a b c d))
([a b c d e] (Tuple5 a b c d e)))
(def-tuple-type Double3D 3 double)
(use 'clojure.test)
(deftest t-equality
(is (= (tuple 8 9) [8 9])))
(deftest t-compare
(is (= -1 (compare (tuple 7 8 9) (tuple 7 8 10))))
(is (= 0 (compare (tuple 1 2) (tuple 1 2))))
(is (= 0 (compare (tuple :a :b :c) [:a :b :c]))))
(deftest t-assoc
(is (= (tuple :a :c) (assoc (tuple :a :b) 1 :c))))
(deftest t-nth
(is (= :d (nth (tuple :a :b :c :d) 3)))
(is (= :notfound (nth (tuple :a :b) 3 :notfound))))
(deftest t-count
(is (= 5 (count (tuple :a :b :c :d :e)))))
(deftest t-invoke
(is (= :d ((tuple :a :b :c :d) 3))))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (vector i i i) 2))))
(println "3-TUPLES of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (tuple i i i) 2))))
(println "3-TUPLES of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (Double3D 2.0 3.0 4.0) 2))))
(println "arrays of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(aget (double-array 3 2.0) 2)))))
(defn da [x y z]
(let [x (double x) y (double y) z (double z)]
(let [a #^doubles (double-array 3)]
(aset a 0 x)
(aset a 1 y)
(aset a 2 z)
a)))
(defn oa [x y z]
(let [a #^objects (make-array Object 3)]
(aset a 0 x)
(aset a 1 y)
(aset a 2 z)
a)))
(defn performance
"Prints micro-benchmark comparison of create/get with
vectors vs tuples"
[]
(println "3-VECTORS of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (vector i i i) 2))))
(println "3-TUPLES of Objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (tuple i i i) 2))))
(println "3-TUPLES of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(nth (Double3D 2.0 3.0 4.0) 2))))
(println "arrays of doubles")
(dotimes [j 6]
(time (dotimes [i 1000000]
(aget #^doubles (da 2.0 3.0 4.0) 2))))
(println "arrays of objects")
(dotimes [j 6]
(time (dotimes [i 1000000]
(aget #^objects (oa 2.0 3.0 4.0) 2)))))