Skip to content

Commit 4933709

Browse files
committed
IReduce, IChunkedSeq, tests, kaocha-cljs
1 parent bb1aba4 commit 4933709

8 files changed

Lines changed: 265 additions & 32 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,7 @@ web/target-cljs
2020
.cpcache
2121
dev/playground.clj
2222
.DS_Store
23-
TODO.txt
23+
TODO.txt
24+
.cljs_node_repl
25+
node_modules
26+
package-lock.json

deps.edn

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
:extra-paths ["test"]
2323
:extra-deps {
2424
org.clojure/clojurescript {:mvn/version "1.10.516"}
25-
lambdaisland/kaocha {:mvn/version "0.0-389"}
25+
lambdaisland/kaocha {:mvn/version "0.0-389"}
26+
lambdaisland/kaocha-cljs {:mvn/version "0.0-16"}
2627
}
2728
}
2829

project.clj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@
9191
]}
9292

9393
:profiles {
94-
:1.9 { :dependencies [[org.clojure/clojure "1.9.0" :scope "provided"]
95-
[org.clojure/clojurescript "1.9.946" :scope "provided"]] }
94+
:1.9 { :dependencies [[org.clojure/clojure "1.9.0" :scope "provided"]
95+
[org.clojure/clojurescript "1.9.946" :scope "provided"]] }
9696
:dev { :source-paths ["bench/src" "test" "dev"]
97-
:dependencies [[org.clojure/tools.nrepl "0.2.13"]
97+
:dependencies [[org.clojure/tools.nrepl "0.2.13"]
9898
[org.clojure/tools.namespace "0.2.11"]
99-
[lambdaisland/kaocha "0.0-389"]] }
99+
[lambdaisland/kaocha "0.0-389"]
100+
[lambdaisland/kaocha-cljs "0.0-16"]] }
100101
:aot { :aot [#"datascript\.(?!query-v3).*"]
101102
:jvm-opts ["-Dclojure.compiler.direct-linking=true"] }
102103
}

src-java/datascript/SortedSet.java

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525
@SuppressWarnings("unchecked")
26-
public class SortedSet extends ASortedSet implements IEditableCollection, ITransientCollection, Reversible {
26+
public class SortedSet extends ASortedSet implements IEditableCollection, ITransientCollection, Reversible, IReduce {
2727

2828
static Leaf[] EARLY_EXIT = new Leaf[0],
2929
UNCHANGED = new Leaf[0];
@@ -168,10 +168,16 @@ public SortedSet withMeta(IPersistentMap meta) {
168168
// Reversible
169169
public ISeq rseq() { return rslice(null, null, _cmp); }
170170

171-
// IChunkedSeq
172-
// public IChunk chunkedFirst() {}
173-
// public ISeq chunkedNext() {}
174-
// public ISeq chunkedMore() {}
171+
// IReduce
172+
public Object reduce(IFn f) {
173+
Seq seq = seq();
174+
return seq == null ? f.invoke() : seq.reduce(f);
175+
}
176+
177+
public Object reduce(IFn f, Object start) {
178+
Seq seq = seq();
179+
return seq == null ? start : seq().reduce(f, start);
180+
}
175181

176182
// IPersistentCollection
177183
public SortedSet empty() {
@@ -870,9 +876,84 @@ public Object next() {
870876
}
871877
}
872878

879+
// ===== CHUNK =====
880+
static class Chunk implements IChunk {
881+
final Object[] _keys;
882+
final int _idx, _end;
883+
final boolean _asc;
884+
885+
Chunk(Seq seq) {
886+
_asc = seq._asc;
887+
_idx = seq._idx;
888+
_keys = seq._node._keys;
889+
if (_asc) {
890+
int end = seq._node._len - 1;
891+
if (seq._keyTo != null)
892+
while (end > _idx && seq._cmp.compare(_keys[end], seq._keyTo) > 0)
893+
--end;
894+
_end = end;
895+
} else {
896+
int end = 0;
897+
if (seq._keyTo != null)
898+
while (end < _idx && seq._cmp.compare(_keys[end], seq._keyTo) < 0)
899+
++end;
900+
_end = end;
901+
}
902+
}
903+
904+
Chunk(Object[] keys, int idx, int end, boolean asc) {
905+
_keys = keys;
906+
_idx = idx;
907+
_end = end;
908+
_asc = asc;
909+
}
910+
911+
public IChunk dropFirst() {
912+
if (_idx == _end)
913+
throw new IllegalStateException("dropFirst of empty chunk");
914+
return new Chunk(_keys, _asc ? _idx+1 : _idx-1, _end, _asc);
915+
}
916+
917+
public Object reduce(IFn f, Object start) {
918+
Object ret = f.invoke(start, _keys[_idx]);
919+
if (ret instanceof Reduced)
920+
return ((Reduced) ret).deref();
921+
if (_asc)
922+
for (int x = _idx + 1; x <= _end; ++x) {
923+
ret = f.invoke(ret, _keys[x]);
924+
if (ret instanceof Reduced)
925+
return ((Reduced) ret).deref();
926+
}
927+
else // !_asc
928+
for (int x = _idx - 1; x >= _end; --x) {
929+
ret = f.invoke(ret, _keys[x]);
930+
if (ret instanceof Reduced)
931+
return ((Reduced) ret).deref();
932+
}
933+
return ret;
934+
}
935+
936+
public Object nth(int i) {
937+
assert (i >= 0 && i < count());
938+
return _asc ? _keys[_idx + i] : _keys[_idx - i];
939+
}
940+
941+
public Object nth(int i, Object notFound) {
942+
if (i >= 0 && i < count())
943+
return nth(i);
944+
return notFound;
945+
}
946+
947+
public int count() {
948+
if (_asc) return _end - _idx + 1;
949+
else return _idx - _end + 1;
950+
}
951+
}
952+
953+
873954
// ===== SEQ =====
874955

875-
class Seq extends ASeq implements IReduce, Reversible {
956+
class Seq extends ASeq implements IReduce, Reversible, IChunkedSeq {
876957
Seq _parent;
877958
Leaf _node;
878959
int _idx;
@@ -975,11 +1056,25 @@ public Object reduce(IFn f, Object start) {
9751056
// Iterable
9761057
public Iterator iterator() { return new JavaIter(clone()); }
9771058

978-
// clojure.lang.IChunkedSeq
979-
// (chunkedFirst [this] (iter-chunk this))
980-
// (chunkedNext [this] (iter-chunked-next this))
981-
// (chunkedMore [this] (or (.chunkedNext this) ()))
1059+
// IChunkedSeq
1060+
public Chunk chunkedFirst() { return new Chunk(this); }
1061+
1062+
public Seq chunkedNext() {
1063+
if (_parent == null) return null;
1064+
Seq nextParent = _parent.next();
1065+
if (nextParent == null) return null;
1066+
Leaf node = nextParent.child();
1067+
Seq seq = new Seq(meta(), nextParent, node, _asc ? 0 : node._len - 1, _keyTo, _cmp, _asc);
1068+
return seq.over() ? null : seq;
1069+
}
1070+
1071+
public ISeq chunkedMore() {
1072+
Seq seq = chunkedNext();
1073+
if (seq == null) return PersistentList.EMPTY;
1074+
return seq;
1075+
}
9821076

1077+
// Reversible
9831078
boolean atBeginning() {
9841079
return _idx == 0 && (_parent == null || _parent.atBeginning());
9851080
}

src/datascript/btset.cljs

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,39 @@
605605
right (inc (-rpath (.-root set) (.-shift set)))]
606606
(iter set left right))))
607607

608+
;; replace with cljs.core/ArrayChunk after https://dev.clojure.org/jira/browse/CLJS-2470
609+
(deftype Chunk [arr off end]
610+
ICounted
611+
(-count [_] (- end off))
612+
613+
IIndexed
614+
(-nth [this i]
615+
(aget arr (+ off i)))
616+
(-nth [this i not-found]
617+
(if (and (>= i 0) (< i (- end off)))
618+
(aget arr (+ off i))
619+
not-found))
620+
621+
IChunk
622+
(-drop-first [this]
623+
(if (== off end)
624+
(throw (js/Error. "-drop-first of empty chunk"))
625+
(ArrayChunk. arr (inc off) end)))
626+
627+
IReduce
628+
(-reduce [this f]
629+
(if (== off end)
630+
(f)
631+
(-reduce (-drop-first this) f (aget arr off))))
632+
(-reduce [this f start]
633+
(loop [val start, n off]
634+
(if (< n end)
635+
(let [val' (f val (aget arr n))]
636+
(if (reduced? val')
637+
@val'
638+
(recur val' (inc n))))
639+
val))))
640+
608641
(defprotocol IIter
609642
(-copy [this left right]))
610643

@@ -634,26 +667,26 @@
634667
;; can use cached array to move forward
635668
(when (< (inc left) right)
636669
(Iter. set (inc left) right keys (inc idx)))
637-
(let [left (next-path set left)]
638-
(when (and (not= -1 left) (< left right))
639-
(-copy this left right))))))
670+
(let [left' (next-path set left)]
671+
(when (and (not= -1 left') (< left' right))
672+
(-copy this left' right))))))
640673

641674
IChunkedSeq
642675
(-chunked-first [this]
643676
(let [end-idx (if (= (bit-or left path-mask)
644-
(bit-or right path-mask))
677+
(bit-or right path-mask))
645678
(bit-and right path-mask)
646679
(da/alength keys))]
647-
(array-chunk keys idx end-idx)))
680+
(Chunk. keys idx end-idx)))
648681

649682
(-chunked-rest [this]
650683
(or (-chunked-next this) ()))
651684

652685
IChunkedNext
653686
(-chunked-next [this]
654-
(let [left (next-path set (+ left (- (da/alength keys) idx 1)))]
655-
(when (and (not= -1 left) (< left right))
656-
(-copy this left right))))
687+
(let [left' (next-path set (+ left (- (da/alength keys) idx 1)))]
688+
(when (and (not= -1 left') (< left' right))
689+
(-copy this left' right))))
657690

658691
IReduce
659692
(-reduce [this f]

test-java/datascript/Bench.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,19 @@ public static void test() throws Exception {
357357
System.out.println("40 .. 30 % 10: " + s.rslice(40, 30, cmp));
358358
System.out.println("40 .. 30 % 10: " + s.slice(30, 40, cmp).rseq());
359359

360-
// System.out.println(s.slice(30, 30, cmp).reduce(new AFn() { public Object invoke(Object x, Object y) { return ((Integer)x)+((Integer)y); }}));
360+
System.out.println("\nReduces");
361+
// System.out.println(s.slice(null, null).reduce(new AFn() { public Object invoke(Object x, Object y) { return ((Integer)x)+((Integer)y); }}));
362+
IFn plus = (IFn) Clojure.var("clojure.core", "+");
363+
System.out.println("reduced sum(0 .. 100) 4950 = " + s.slice(null, null).reduce(plus));
364+
System.out.println("reduced sum(100 .. 0) 4950 = " + s.rslice(null, null).reduce(plus));
365+
System.out.println("reduced sum(10 .. 20) 165 = " + s.slice(10, 20).reduce(plus));
366+
System.out.println("reduced sum(20 .. 10) 165 = " + s.rslice(20, 10).reduce(plus));
367+
368+
IFn reduce1 = (IFn) Clojure.var("clojure.core", "reduce1");
369+
System.out.println("chunked sum(0 .. 100) 4950 = " + reduce1.invoke(plus, 0, s.slice(null, null)));
370+
System.out.println("chunked sum(100 .. 0) 4950 = " + reduce1.invoke(plus, 0, s.rslice(null, null)));
371+
System.out.println("chunked sum(10 .. 20) 165 = " + reduce1.invoke(plus, 0, s.slice(10, 20)));
372+
System.out.println("chunked sum(20 .. 10) 165 = " + reduce1.invoke(plus, 0, s.rslice(20, 10)));
361373
}
362374

363375
public static void main(String args[]) throws Exception {

test/datascript/test/btset.cljc

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
(range from (inc to))
5050
(range from (dec to) -1)))
5151

52-
(deftest slice-test
52+
(deftest test-slice
5353
(dotimes [i iters]
5454
(testing "straight 3 layers"
5555
(let [s (into (btset/btset) (shuffle (irange 0 5000)))]
@@ -236,7 +236,95 @@
236236
5000 0
237237
4999 1
238238
2500 2500
239-
2500.9 2500.1)))))
239+
2500.9 2500.1)))
240+
241+
(testing "Slice with equal elements"
242+
(let [cmp10 (fn [a b] (compare (quot a 10) (quot b 10)))
243+
s10 (reduce #(btset/btset-conj %1 %2 compare) (btset/btset-by cmp10) (shuffle (irange 0 5000)))]
244+
(are [from to expected] (= expected (btset/slice s10 from to))
245+
30 30 (irange 30 39)
246+
130 4970 (irange 130 4979)
247+
-100 6000 (irange 0 5000))
248+
(are [from to expected] (= expected (btset/rslice s10 from to))
249+
30 30 (irange 39 30)
250+
4970 130 (irange 4979 130)
251+
6000 -100 (irange 5000 0)))
252+
253+
(let [cmp100 (fn [a b] (compare (quot a 100) (quot b 100)))
254+
s100 (reduce #(btset/btset-conj %1 %2 compare) (btset/btset-by cmp100) (shuffle (irange 0 5000)))]
255+
(are [from to expected] (= expected (btset/slice s100 from to))
256+
30 30 (irange 0 99)
257+
2550 2550 (irange 2500 2599)
258+
130 4850 (irange 100 4899)
259+
-100 6000 (irange 0 5000))
260+
(are [from to expected] (= expected (btset/rslice s100 from to))
261+
30 30 (irange 99 0)
262+
2550 2550 (irange 2599 2500)
263+
4850 130 (irange 4899 100)
264+
6000 -100 (irange 5000 0))))
265+
))
266+
267+
268+
(defn ireduce
269+
([f coll] (#?(:clj .reduce :cljs -reduce) coll f))
270+
([f val coll] (#?(:clj .reduce :cljs -reduce) coll f val)))
271+
272+
273+
(defn reduce-chunked [f val coll]
274+
(if-some [s (seq coll)]
275+
(if (chunked-seq? s)
276+
(recur f (#?(:clj .reduce :cljs -reduce) (chunk-first s) f val) (chunk-next s))
277+
(recur f (f val (first s)) (next s)))
278+
val))
279+
280+
281+
(deftest test-reduces
282+
(testing "IReduced"
283+
(testing "Empty"
284+
(let [s (btset/btset)]
285+
(is (= 0 (ireduce + s)))
286+
(is (= 0 (ireduce + 0 s)))))
287+
288+
(testing "~3 layers"
289+
(let [s (into (btset/btset) (irange 0 5000))]
290+
(is (= 12502500 (ireduce + s)))
291+
(is (= 12502500 (ireduce + 0 s)))
292+
(is (= 12502500 (ireduce + (seq s))))
293+
(is (= 12502500 (ireduce + 0 (seq s))))
294+
(is (= 7502500 (ireduce + (btset/slice s 1000 4000))))
295+
(is (= 7502500 (ireduce + 0 (btset/slice s 1000 4000))))
296+
#?@(:clj [(is (= 12502500 (ireduce + (rseq s))))
297+
(is (= 12502500 (ireduce + 0 (rseq s))))
298+
(is (= 7502500 (ireduce + (btset/rslice s 4000 1000))))
299+
(is (= 7502500 (ireduce + 0 (btset/rslice s 4000 1000))))])))
300+
301+
(testing "~1 layer"
302+
(let [s (into (btset/btset) (irange 0 10))]
303+
(is (= 55 (ireduce + s)))
304+
(is (= 55 (ireduce + 0 s)))
305+
(is (= 55 (ireduce + (seq s))))
306+
(is (= 55 (ireduce + 0 (seq s))))
307+
(is (= 35 (ireduce + (btset/slice s 2 8))))
308+
(is (= 35 (ireduce + 0 (btset/slice s 2 8))))
309+
#?@(:clj [(is (= 55 (ireduce + (rseq s))))
310+
(is (= 55 (ireduce + 0 (rseq s))))
311+
(is (= 35 (ireduce + (btset/rslice s 8 2))))
312+
(is (= 35 (ireduce + 0 (btset/rslice s 8 2))))]))))
313+
314+
(testing "IChunkedSeq"
315+
(testing "~3 layers"
316+
(let [s (into (btset/btset) (irange 0 5000))]
317+
(is (= 12502500 (reduce-chunked + 0 s)))
318+
(is (= 7502500 (reduce-chunked + 0 (btset/slice s 1000 4000))))
319+
(is (= 12502500 (reduce-chunked + 0 (rseq s))))
320+
(is (= 7502500 (reduce-chunked + 0 (btset/rslice s 4000 1000))))))
321+
322+
(testing "~1 layer"
323+
(let [s (into (btset/btset) (irange 0 10))]
324+
(is (= 55 (reduce-chunked + 0 s)))
325+
(is (= 35 (reduce-chunked + 0 (btset/slice s 2 8))))
326+
(is (= 55 (reduce-chunked + 0 (rseq s))))
327+
(is (= 35 (reduce-chunked + 0 (btset/rslice s 8 2))))))))
240328

241329

242330
(defn into-via-doseq [to from]
@@ -298,8 +386,4 @@
298386
#_(println "[ OK ] btset slice checked"))
299387

300388

301-
(deftest test-reduced
302-
(is (= [1 2] (into [] (take 2) (btset/btset 1 2)))))
303-
304-
305389
;; (t/test-ns 'datascript.test.btset)

0 commit comments

Comments
 (0)