Created
June 22, 2010 17:08
-
-
Save ejackson/448759 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns | |
#^{:doc "New definitions for a timeseries type"} | |
ts | |
(:use clojure.test) | |
(:import (clojure.lang PersistentTreeSet) | |
(java.io Writer))) | |
;;---------------------------------------------------------------------------- | |
(defprotocol Trim | |
"Trim a timeseries. There are performance advantages to having older+newer | |
rather than just trim in most implementations." | |
(trim-older [ts snip]) | |
(trim-newer [ts snip])) | |
;;---------------------------------------------------------------------------- | |
(defprotocol GettableData | |
"Kludge around my inability to figure out getting the right gensym" | |
(get-dataset [ts])) | |
;;---------------------------------------------------------------------------- | |
(defn circular-conj | |
"Conj maintaining the datatype and count" | |
([max coll x] | |
(let [d (- (count coll) max)] | |
(if (<= d 0) | |
(conj coll x) | |
(recur max | |
(into (empty coll) | |
(drop (inc d) coll)) | |
x)))) | |
([max coll x & xs] | |
(if xs | |
(recur max (circular-conj max coll x) (first xs) (next xs)) | |
(circular-conj max coll x)))) | |
;;---------------------------------------------------------------------------- | |
;; Idea is to write a macro that outputs the full deftype for Timeseries or | |
;; timeseries, but takes as an argument that datastructure which will become | |
;; the functions that we 'override' - in this case conj. | |
(defmacro create-timeseries-type [name cons-method] | |
`(deftype ~(symbol (str name)) [dataset#] | |
Object | |
(equals [this# o#] | |
(or (identical? this# o#) | |
(and (.isInstance ~(symbol (str name)) o#) | |
(= dataset# (.get-dataset o#))))) | |
(hashCode [this#] | |
(hash dataset#)) | |
(toString [this#] | |
(str "(ts " (apply pr-str dataset#)")")) | |
;;-------------- | |
clojure.lang.IPersistentCollection | |
(cons [this# a#] | |
(new ~(symbol (str name)) (~cons-method dataset# a#))) | |
(empty [this#] | |
(new ~name (-> dataset# empty))) | |
;; How does this differ from equals | |
(equiv [this# o#] | |
(.equals this# o#)) | |
(seq [this#] | |
(seq dataset#)) | |
;;-------------- | |
clojure.lang.IPersistentStack | |
(peek [this#] | |
(-> dataset# rseq first)) | |
(pop [this#] | |
(new ~name (disj dataset# (peek this#)))) | |
;;-------------- | |
clojure.lang.Sorted | |
(comparator [this#] | |
(.comparator dataset#)) | |
(entryKey [this# entry#] | |
(.entryKey dataset# entry#)) | |
(seq [this# ascending#] | |
(.seq dataset# ascending#)) | |
(seqFrom [this# key# ascending#] | |
(.seqFrom dataset# key# ascending#)) | |
;;-------------- | |
Trim | |
(trim-older [this# snip#] | |
(new ~name (reduce disj dataset# (subseq this# < snip#)))) | |
(trim-newer [this# snip#] | |
(new ~name (reduce disj dataset# (rsubseq this# > snip#)))) | |
;;-------------- | |
GettableData | |
(get-dataset [this#] dataset#))) | |
;;--------------------------------------------------------------------------- | |
;; Extra API | |
;;--------------------------------------------------------------------------- | |
;; Straight Timeseries | |
;; TODO: This recipe is seems like a candidate for a macro. | |
(create-timeseries-type | |
Timeseries | |
circular-conj) | |
(defn ts [& xs] | |
(Timeseries. (apply sorted-set xs))) | |
(defmethod print-method Timeseries [o, ^Writer w] | |
(#'clojure.core/print-sequential "(ts " #'clojure.core/pr-on " " ")" o w)) | |
(defmethod print-dup Timeseries [o, ^Writer w] | |
(print-method o w)) | |
;;--------------------------------------------------------------------------- | |
;; Circular Timeseries | |
(def circular-series-length 5) | |
(create-timeseries-type | |
CircularTimeseries | |
(partial circular-conj circular-series-length)) | |
(defn cts [& xs] | |
(CircularTimeseries. (apply sorted-set xs))) | |
(defmethod print-method CircularTimeseries [o, ^Writer w] | |
(#'clojure.core/print-sequential "(cts " #'clojure.core/pr-on " " ")" o w)) | |
(defmethod print-dup CircularTimeseries [o, ^Writer w] | |
(print-method o w)) | |
;;--------------------------------------------------------------------- | |
;; Tests below | |
(deftest interface-test | |
(let [a (ts 1 2 3) | |
b (ts 1 2 3) | |
c (ts 1 2) | |
d (ts 1) | |
e (ts) | |
f (ts 2 3) | |
g (ts 3)] | |
(testing "Testing conj..." | |
(are [y] (= a y) | |
(conj c 3) | |
(conj e 1 2 3))) | |
(testing "Testing peek..." | |
(are [x y] (= x y) | |
3 (peek a) | |
2 (peek c) | |
nil (peek e))) | |
(testing "Testing subseq..." | |
(are [x y z] (= x (subseq a y z)) | |
[1 2 3] > -2 | |
[1 2 3] > 0 | |
[2 3] > 1 | |
[1 2 3] >= 1 | |
nil > 3 ;; Technically this should be [] | |
nil > 4 | |
[1 2 3] < 4 | |
[1 2 3] <= 3 | |
[1 2] < 3 | |
[] < 0)) | |
(testing "Testing rsubseq..." | |
(are [x y z] (= x (rsubseq a y z)) | |
[3 2 1] > -2 | |
[3 2 1] > 0 | |
[3 2] > 1 | |
[3 2 1] >= 1 | |
[] > 3 | |
[] > 4 | |
[3 2 1] < 4 | |
[3 2 1] <= 3 | |
[2 1] < 3 | |
nil < 0)) ;; Technically this should be [] | |
;; trim | |
(testing "Testing trim..." | |
(are [x y] (= (Timeseries. (apply sorted-set x)) (trim-older a y)) | |
[1 2 3] 0 | |
[1 2 3] 1 | |
[2 3] 2 | |
[3] 3 | |
[] 4) | |
(are [x y] (= (Timeseries. (apply sorted-set x)) (trim-newer a y)) | |
[] 0 | |
[1] 1 | |
[1 2] 2 | |
[1 2 3] 3 | |
[1 2 3] 4)))) | |
(deftest internal-tests | |
(let [a (ts 1 2 3) | |
b (ts 1 2) | |
c (ts 1 2 3) | |
d (ts 1) | |
e (ts )] | |
(testing "Testing Object Interface..." | |
(are [x y] (= x y) | |
a a | |
a c) | |
(are [x y] (not (.equals x y)) | |
a b | |
b a) | |
;; hashCode | |
(are [x y] (= (.hashCode x) (.hashCode y)) | |
a a | |
a c) | |
;; toString | |
(is (= (.toString a) (.toString c)))) | |
(testing "clojure.lang.IPersistentCollection..." | |
;; Check the basic functionality | |
(are [x y] (.equals x y) | |
a (.cons b 3) | |
e (.empty a) | |
(seq [1 2 3]) (seq a)) | |
;; Type check these functions | |
(are [x] (= (type e) (type x)) | |
(.cons b 3) | |
(.empty a))) | |
(testing "clojure.lang.IPersistentStack..." | |
;; Check the basic functionality | |
(are [x y] (= x y) | |
3 (.peek a) | |
b (.pop a) | |
d (-> a .pop .pop) | |
;; Empties and nils | |
nil (.peek e) | |
e (.pop e)) | |
;; Type check these functions | |
(are [x] (= (type e) (type x)) | |
(.pop a))) | |
(testing "Sorted..." | |
(are [x y] (= x y) | |
(.seq a true) (seq a) | |
(.seq a true) (seq [1 2 3]) | |
(.seq a false) (seq [3 2 1]) | |
(.seqFrom a 2 true) (seq [2 3]) | |
(.seqFrom a 2 false) (seq [2 1]))))) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment