From b51137b2a3209ec411ae26474b6bccb6144b0c1f Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 19 Sep 2023 16:41:27 +0800 Subject: [PATCH 01/28] Initial implementation for cljs storage support --- project.clj | 10 +- .../me/tonsky/persistent_sorted_set.cljs | 1125 +---------------- .../me/tonsky/persistent_sorted_set/impl.cljs | 1107 ++++++++++++++++ .../persistent_sorted_set/protocol.cljs | 6 + 4 files changed, 1185 insertions(+), 1063 deletions(-) create mode 100644 src-clojure/me/tonsky/persistent_sorted_set/impl.cljs create mode 100644 src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs diff --git a/project.clj b/project.clj index eb3a981..52e5130 100644 --- a/project.clj +++ b/project.clj @@ -2,11 +2,11 @@ :description "Fast B-tree based persistent sorted set for Clojure/Script" :license {:name "MIT"} :url "https://github.com/tonsky/persistent-sorted-set" - + :dependencies [[org.clojure/clojure "1.11.1" :scope "provided"] [org.clojure/clojurescript "1.11.60" :scope "provided"]] - + :plugins [[lein-cljsbuild "1.1.7"]] @@ -14,15 +14,15 @@ :java-source-paths ["src-java"] :test-paths ["test-clojure"] - :javac-options ["-Xlint:unchecked" "-Xlint:-options" "-target" "8" "-source" "8" "-bootclasspath" ~(str (or (System/getenv "JAVA8_HOME") (throw (Exception. "Please set JAVA8_HOME"))) "/jre/lib/rt.jar")] + ;; :javac-options ["-Xlint:unchecked" "-Xlint:-options" "-target" "8" "-source" "8" "-bootclasspath" ~(str (or (System/getenv "JAVA8_HOME") (throw (Exception. "Please set JAVA8_HOME"))) "/jre/lib/rt.jar")] :jvm-opts ["-ea"] :profiles {:1.9 - {:dependencies + {:dependencies [[org.clojure/clojure "1.9.0" :scope "provided"] [org.clojure/clojurescript "1.9.946" :scope "provided"]]}} - + :deploy-repositories {"clojars" {:url "https://clojars.org/repo" diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 2f3b05d..49c926a 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -1,1056 +1,38 @@ (ns ^{:doc - "A B-tree based persistent sorted set. Supports transients, custom comparators, fast iteration, efficient slices (iterator over a part of the set) and reverse slices. Almost a drop-in replacement for [[clojure.core/sorted-set]], the only difference being this one can’t store nil." - :author "Nikita Prokopov"} - me.tonsky.persistent-sorted-set - (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) + "A B-tree based persistent sorted set. Supports transients, custom comparators, fast iteration, efficient slices (iterator over a part of the set) and reverse slices. Almost a drop-in replacement for [[clojure.core/sorted-set]], the only difference being this one can’t store nil." + :author "Nikita Prokopov"} + me.tonsky.persistent-sorted-set + (:refer-clojure :exclude [conj disj sorted-set sorted-set-by]) (:require - [me.tonsky.persistent-sorted-set.arrays :as arrays]) + [me.tonsky.persistent-sorted-set.arrays :as arrays] + [me.tonsky.persistent-sorted-set.impl :as impl :refer [BTSet]] + [me.tonsky.persistent-sorted-set.protocol :refer [IStorage]]) (:require-macros - [me.tonsky.persistent-sorted-set.arrays :as arrays])) - -; B+ tree -; ------- - -; Leaf: keys[] :: array of values - -; Node: pointers[] :: links to children nodes -; keys[] :: max value for whole subtree -; node.keys[i] == max(node.pointers[i].keys) -; All arrays are 16..32 elements, inclusive - -; BTSet: root :: Node or Leaf -; shift :: depth - 1 -; cnt :: size of a set, integer, rolling -; comparator :: comparator used for ordering -; meta :: clojure meta map -; _hash :: hash code, same as for clojure collections, on-demand, cached - -; Path: conceptually a vector of indexes from root to leaf value, but encoded in a single number. -; E.g. we have path [7 30 11] representing root.pointers[7].pointers[30].keys[11]. -; In our case level-shift is 5, meaning each index will take 5 bits: -; (7 << 10) | (30 << 5) | (11 << 0) = 8139 -; 00111 11110 01011 - -; Iter: set :: Set this iterator belongs to -; left :: Current path -; right :: Right bound path (exclusive) -; keys :: Cached ref for keys array for a leaf -; idx :: Cached idx in keys array -; Keys and idx are cached for fast iteration inside a leaf" - -(def ^:const max-safe-path - "js limitation for bit ops" - (js/Math.pow 2 31)) - -(def ^:const bits-per-level - "tunable param" - 5) - -(def ^:const max-len - (js/Math.pow 2 bits-per-level)) ;; 32 - -(def ^:const min-len - (/ max-len 2)) ;; 16 - -(def ^:private ^:const avg-len - (arrays/half (+ max-len min-len))) ;; 24 - -(def ^:const max-safe-level - (js/Math.floor (/ 31 bits-per-level))) ;; 6 - -(def ^:const bit-mask - (- max-len 1)) ;; 0b011111 = 5 bit - -(def factors - (arrays/into-array (map #(js/Math.pow 2 %) (range 0 52 bits-per-level)))) - -(def ^:const empty-path 0) - -(defn- path-get ^number [^number path ^number level] - (if (< level max-safe-level) - (-> path - (unsigned-bit-shift-right (* level bits-per-level)) - (bit-and bit-mask)) - (-> path - (/ (arrays/aget factors level)) - (js/Math.floor) - (bit-and bit-mask)))) - -(defn- path-set ^number [^number path ^number level ^number idx] - (let [smol? (and (< path max-safe-path) (< level max-safe-level)) - old (path-get path level) - minus (if smol? - (bit-shift-left old (* level bits-per-level)) - (* old (arrays/aget factors level))) - plus (if smol? - (bit-shift-left idx (* level bits-per-level)) - (* idx (arrays/aget factors level)))] - (-> path - (- minus) - (+ plus)))) - -(defn- path-inc ^number [^number path] - (inc path)) - -(defn- path-dec ^number [^number path] - (dec path)) - -(defn- path-cmp ^number [^number path1 ^number path2] - (- path1 path2)) - -(defn- path-lt ^boolean [^number path1 ^number path2] - (< path1 path2)) - -(defn- path-lte ^boolean [^number path1 ^number path2] - (<= path1 path2)) - -(defn- path-eq ^boolean [^number path1 ^number path2] - (== path1 path2)) - -(defn- path-same-leaf ^boolean [^number path1 ^number path2] - (if (and - (< path1 max-safe-path) - (< path2 max-safe-path)) - (== - (unsigned-bit-shift-right path1 bits-per-level) - (unsigned-bit-shift-right path2 bits-per-level)) - (== - (Math/floor (/ path1 max-len)) - (Math/floor (/ path2 max-len))))) - -(defn- path-str [^number path] - (loop [res () - path path] - (if (not= path 0) - (recur (cljs.core/conj res (mod path max-len)) (Math/floor (/ path max-len))) - (vec res)))) - -(defn- binary-search-l [cmp arr r k] - (loop [l 0 - r (long r)] - (if (<= l r) - (let [m (arrays/half (+ l r)) - mk (arrays/aget arr m)] - (if (neg? (cmp mk k)) - (recur (inc m) r) - (recur l (dec m)))) - l))) - -(defn- binary-search-r [cmp arr r k] - (loop [l 0 - r (long r)] - (if (<= l r) - (let [m (arrays/half (+ l r)) - mk (arrays/aget arr m)] - (if (pos? (cmp mk k)) - (recur l (dec m)) - (recur (inc m) r))) - l))) - -(defn- lookup-exact [cmp arr key] - (let [arr-l (arrays/alength arr) - idx (binary-search-l cmp arr (dec arr-l) key)] - (if (and (< idx arr-l) - (== 0 (cmp (arrays/aget arr idx) key))) - idx - -1))) - -(defn- lookup-range [cmp arr key] - (let [arr-l (arrays/alength arr) - idx (binary-search-l cmp arr (dec arr-l) key)] - (if (== idx arr-l) - -1 - idx))) - -;; Array operations - -(defn- cut-n-splice [arr cut-from cut-to splice-from splice-to xs] - (let [xs-l (arrays/alength xs) - l1 (- splice-from cut-from) - l2 (- cut-to splice-to) - l1xs (+ l1 xs-l) - new-arr (arrays/make-array (+ l1 xs-l l2))] - (arrays/acopy arr cut-from splice-from new-arr 0) - (arrays/acopy xs 0 xs-l new-arr l1) - (arrays/acopy arr splice-to cut-to new-arr l1xs) - new-arr)) - -(defn- splice [arr splice-from splice-to xs] - (cut-n-splice arr 0 (arrays/alength arr) splice-from splice-to xs)) - -(defn- insert [arr idx xs] - (cut-n-splice arr 0 (arrays/alength arr) idx idx xs)) - -(defn- merge-n-split [a1 a2] - (let [a1-l (arrays/alength a1) - a2-l (arrays/alength a2) - total-l (+ a1-l a2-l) - r1-l (arrays/half total-l) - r2-l (- total-l r1-l) - r1 (arrays/make-array r1-l) - r2 (arrays/make-array r2-l)] - (if (<= a1-l r1-l) - (do - (arrays/acopy a1 0 a1-l r1 0) - (arrays/acopy a2 0 (- r1-l a1-l) r1 a1-l) - (arrays/acopy a2 (- r1-l a1-l) a2-l r2 0)) - (do - (arrays/acopy a1 0 r1-l r1 0) - (arrays/acopy a1 r1-l a1-l r2 0) - (arrays/acopy a2 0 a2-l r2 (- a1-l r1-l)))) - (arrays/array r1 r2))) - -(defn- ^boolean eq-arr [cmp a1 a1-from a1-to a2 a2-from a2-to] - (let [len (- a1-to a1-from)] - (and - (== len (- a2-to a2-from)) - (loop [i 0] - (cond - (== i len) - true - - (not (== 0 (cmp - (arrays/aget a1 (+ i a1-from)) - (arrays/aget a2 (+ i a2-from))))) - false - - :else - (recur (inc i))))))) - -(defn- check-n-splice [cmp arr from to new-arr] - (if (eq-arr cmp arr from to new-arr 0 (arrays/alength new-arr)) - arr - (splice arr from to new-arr))) - -(defn- return-array - "Drop non-nil references and return array of arguments" - ([a1] - (arrays/array a1)) - ([a1 a2] - (if a1 - (if a2 - (arrays/array a1 a2) - (arrays/array a1)) - (arrays/array a2))) - ([a1 a2 a3] - (if a1 - (if a2 - (if a3 - (arrays/array a1 a2 a3) - (arrays/array a1 a2)) - (if a3 - (arrays/array a1 a3) - (arrays/array a1))) - (if a2 - (if a3 - (arrays/array a2 a3) - (arrays/array a2)) - (arrays/array a3))))) - -;; - -(defprotocol INode - (node-lim-key [_]) - (node-len [_]) - (node-merge [_ next]) - (node-merge-n-split [_ next]) - (node-lookup [_ cmp key]) - (node-conj [_ cmp key]) - (node-disj [_ cmp key root? left right])) - -(defn- rotate [node root? left right] - (cond - ;; root never merges - root? - (return-array node) - - ;; enough keys, nothing to merge - (> (node-len node) min-len) - (return-array left node right) - - ;; left and this can be merged to one - (and left (<= (node-len left) min-len)) - (return-array (node-merge left node) right) - - ;; right and this can be merged to one - (and right (<= (node-len right) min-len)) - (return-array left (node-merge node right)) - - ;; left has fewer nodes, redestribute with it - (and left (or (nil? right) - (< (node-len left) (node-len right)))) - (let [nodes (node-merge-n-split left node)] - (return-array (arrays/aget nodes 0) (arrays/aget nodes 1) right)) - - ;; right has fewer nodes, redestribute with it - :else - (let [nodes (node-merge-n-split node right)] - (return-array left (arrays/aget nodes 0) (arrays/aget nodes 1))))) - -(deftype Node [keys pointers] - INode - (node-lim-key [_] - (arrays/alast keys)) - - (node-len [_] - (arrays/alength keys)) - - (node-merge [_ next] - (Node. (arrays/aconcat keys (.-keys next)) - (arrays/aconcat pointers (.-pointers next)))) - - (node-merge-n-split [_ next] - (let [ks (merge-n-split keys (.-keys next)) - ps (merge-n-split pointers (.-pointers next))] - (return-array (Node. (arrays/aget ks 0) (arrays/aget ps 0)) - (Node. (arrays/aget ks 1) (arrays/aget ps 1))))) - - (node-lookup [_ cmp key] - (let [idx (lookup-range cmp keys key)] - (when-not (== -1 idx) - (node-lookup (arrays/aget pointers idx) cmp key)))) - - (node-conj [_ cmp key] - (let [idx (binary-search-l cmp keys (- (arrays/alength keys) 2) key) - nodes (node-conj (arrays/aget pointers idx) cmp key)] - (when nodes - (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) - new-pointers (splice pointers idx (inc idx) nodes)] - (if (<= (arrays/alength new-pointers) max-len) - ;; ok as is - (arrays/array (Node. new-keys new-pointers)) - ;; gotta split it up - (let [middle (arrays/half (arrays/alength new-pointers))] - (arrays/array - (Node. (.slice new-keys 0 middle) - (.slice new-pointers 0 middle)) - (Node. (.slice new-keys middle) - (.slice new-pointers middle))))))))) - - (node-disj [_ cmp key root? left right] - (let [idx (lookup-range cmp keys key)] - (when-not (== -1 idx) ;; short-circuit, key not here - (let [child (arrays/aget pointers idx) - left-child (when (>= (dec idx) 0) - (arrays/aget pointers (dec idx))) - right-child (when (< (inc idx) (arrays/alength pointers)) - (arrays/aget pointers (inc idx))) - disjned (node-disj child cmp key false left-child right-child)] - (when disjned ;; short-circuit, key not here - (let [left-idx (if left-child (dec idx) idx) - right-idx (if right-child (+ 2 idx) (+ 1 idx)) - new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) - new-pointers (splice pointers left-idx right-idx disjned)] - (rotate (Node. new-keys new-pointers) root? left right)))))))) - -(deftype Leaf [keys] - INode - (node-lim-key [_] - (arrays/alast keys)) -;; Object -;; (toString [_] (pr-str* (vec keys))) - - (node-len [_] - (arrays/alength keys)) - - (node-merge [_ next] - (Leaf. (arrays/aconcat keys (.-keys next)))) - - (node-merge-n-split [_ next] - (let [ks (merge-n-split keys (.-keys next))] - (return-array (Leaf. (arrays/aget ks 0)) - (Leaf. (arrays/aget ks 1))))) - - (node-lookup [_ cmp key] - (let [idx (lookup-exact cmp keys key)] - (when-not (== -1 idx) - (arrays/aget keys idx)))) - - (node-conj [_ cmp key] - (let [idx (binary-search-l cmp keys (dec (arrays/alength keys)) key) - keys-l (arrays/alength keys)] - (cond - ;; element already here - (and (< idx keys-l) - (== 0 (cmp key (arrays/aget keys idx)))) - nil - - ;; splitting - (== keys-l max-len) - (let [middle (arrays/half (inc keys-l))] - (if (> idx middle) - ;; new key goes to the second half - (arrays/array - (Leaf. (.slice keys 0 middle)) - (Leaf. (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) - ;; new key goes to the first half - (arrays/array - (Leaf. (cut-n-splice keys 0 middle idx idx (arrays/array key))) - (Leaf. (.slice keys middle keys-l))))) - - ;; ok as is - :else - (arrays/array (Leaf. (splice keys idx idx (arrays/array key))))))) - - (node-disj [_ cmp key root? left right] - (let [idx (lookup-exact cmp keys key)] - (when-not (== -1 idx) ;; key is here - (let [new-keys (splice keys idx (inc idx) (arrays/array))] - (rotate (Leaf. new-keys) root? left right)))))) - -;; BTSet - -(declare conj disj btset-iter) - -(def ^:private ^:const uninitialized-hash nil) - -(deftype BTSet [root shift cnt comparator meta ^:mutable _hash] - Object - (toString [this] (pr-str* this)) - - ICloneable - (-clone [_] (BTSet. root shift cnt comparator meta _hash)) - - IWithMeta - (-with-meta [_ new-meta] (BTSet. root shift cnt comparator new-meta _hash)) - - IMeta - (-meta [_] meta) - - IEmptyableCollection - (-empty [_] (BTSet. (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash)) - - IEquiv - (-equiv [this other] - (and - (set? other) - (== cnt (count other)) - (every? #(contains? this %) other))) - - IHash - (-hash [this] (caching-hash this hash-unordered-coll _hash)) - - ICollection - (-conj [this key] (conj this key comparator)) - - ISet - (-disjoin [this key] (disj this key comparator)) - - ILookup - (-lookup [_ k] - (node-lookup root comparator k)) - (-lookup [_ k not-found] - (or (node-lookup root comparator k) not-found)) - - ISeqable - (-seq [this] (btset-iter this)) - - IReduce - (-reduce [this f] - (if-let [i (btset-iter this)] - (-reduce i f) - (f))) - (-reduce [this f start] - (if-let [i (btset-iter this)] - (-reduce i f start) - start)) - - IReversible - (-rseq [this] - (rseq (btset-iter this))) - - ; ISorted - ; (-sorted-seq [this ascending?]) - ; (-sorted-seq-from [this k ascending?]) - ; (-entry-key [this entry] entry) - ; (-comparator [this] comparator) - - ICounted - (-count [_] cnt) - - IEditableCollection - (-as-transient [this] this) - - ITransientCollection - (-conj! [this key] (conj this key comparator)) - (-persistent! [this] this) - - ITransientSet - (-disjoin! [this key] (disj this key comparator)) - - IFn - (-invoke [this k] (-lookup this k)) - (-invoke [this k not-found] (-lookup this k not-found)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "#{" " " "}" opts (seq this)))) - -(defn- keys-for [set path] - (loop [level (.-shift set) - node (.-root set)] - (if (pos? level) - (recur - (dec level) - (arrays/aget (.-pointers node) (path-get path level))) - (.-keys node)))) - -(defn- alter-btset [set root shift cnt] - (BTSet. root shift cnt (.-comparator set) (.-meta set) uninitialized-hash)) - - -;; iteration - -(defn- -next-path [node ^number path ^number level] - (let [idx (path-get path level)] - (if (pos? level) - ;; inner node - (let [sub-path (-next-path (arrays/aget (.-pointers node) idx) path (dec level))] - (if (nil? sub-path) - ;; nested node overflow - (if (< (inc idx) (arrays/alength (.-pointers node))) - ;; advance current node idx, reset subsequent indexes - (path-set empty-path level (inc idx)) - ;; current node overflow - nil) - ;; keep current idx - (path-set sub-path level idx))) - ;; leaf - (if (< (inc idx) (arrays/alength (.-keys node))) - ;; advance leaf idx - (path-set empty-path 0 (inc idx)) - ;; leaf overflow - nil)))) - -(defn- -rpath - "Returns rightmost path possible starting from node and going deeper" - [node ^number path ^number level] - (loop [node node - path path - level level] - (if (pos? level) - ;; inner node - (recur - (arrays/alast (.-pointers node)) - (path-set path level (dec (arrays/alength (.-pointers node)))) - (dec level)) - ;; leaf - (path-set path 0 (dec (arrays/alength (.-keys node))))))) - -(defn- next-path - "Returns path representing next item after `path` in natural traversal order. - Will overflow at leaf if at the end of the tree" - [set ^number path] - (if (neg? path) - empty-path - (or - (-next-path (.-root set) path (.-shift set)) - (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) - -(defn- -prev-path [node ^number path ^number level] - (let [idx (path-get path level)] - (cond - ;; leaf overflow - (and (== 0 level) (== 0 idx)) - nil - - ;; leaf - (== 0 level) - (path-set empty-path 0 (dec idx)) - - ;; branch that was overflow before - (>= idx (node-len node)) - (-rpath node path level) - - :else - (let [path' (-prev-path (arrays/aget (.-pointers node) idx) path (dec level))] - (cond - ;; no sub-overflow, keep current idx - (some? path') - (path-set path' level idx) - - ;; nested overflow + this node overflow - (== 0 idx) - nil - - ;; nested overflow, advance current idx, reset subsequent indexes - :else - (let [path' (-rpath (arrays/aget (.-pointers node) (dec idx)) path (dec level))] - (path-set path' level (dec idx)))))))) - -(defn- prev-path - "Returns path representing previous item before `path` in natural traversal order. - Will overflow at leaf if at beginning of tree" - [set ^number path] - (if (> (path-get path (inc (.-shift set))) 0) ;; overflow - (-rpath (.-root set) path (.-shift set)) - (or - (-prev-path (.-root set) path (.-shift set)) - (path-dec empty-path)))) - -(declare iter riter) - -(defn- btset-iter - "Iterator that represents the whole set" - [set] - (when (pos? (node-len (.-root set))) - (let [left empty-path - rpath (-rpath (.-root set) empty-path (.-shift set)) - right (next-path set rpath)] - (iter set left right)))) - -;; replace with cljs.core/ArrayChunk after https://dev.clojure.org/jira/browse/CLJS-2470 -(deftype Chunk [arr off end] - ICounted - (-count [_] (- end off)) - - IIndexed - (-nth [this i] - (aget arr (+ off i))) - - (-nth [this i not-found] - (if (and (>= i 0) (< i (- end off))) - (aget arr (+ off i)) - not-found)) - - IChunk - (-drop-first [this] - (if (== off end) - (throw (js/Error. "-drop-first of empty chunk")) - (ArrayChunk. arr (inc off) end))) - - IReduce - (-reduce [this f] - (if (== off end) - (f) - (-reduce (-drop-first this) f (aget arr off)))) - - (-reduce [this f start] - (loop [val start, n off] - (if (< n end) - (let [val' (f val (aget arr n))] - (if (reduced? val') - @val' - (recur val' (inc n)))) - val)))) - -(defprotocol IIter - (-copy [this left right])) - -(defprotocol ISeek - (-seek - [this key] - [this key comparator])) - -(declare -seek* -rseek*) - -(deftype Iter [set left right keys idx] - IIter - (-copy [_ l r] - (Iter. set l r (keys-for set l) (path-get l 0))) - - IEquiv - (-equiv [this other] (equiv-sequential this other)) - - ISequential - ISeqable - (-seq [this] (when keys this)) - - ISeq - (-first [this] - (when keys - (arrays/aget keys idx))) - - (-rest [this] - (or (-next this) ())) - - INext - (-next [this] - (when keys - (if (< (inc idx) (arrays/alength keys)) - ;; can use cached array to move forward - (let [left' (path-inc left)] - (when (path-lt left' right) - (Iter. set left' right keys (inc idx)))) - (let [left' (next-path set left)] - (when (path-lt left' right) - (-copy this left' right)))))) - - IChunkedSeq - (-chunked-first [this] - (let [end-idx (if (path-same-leaf left right) - ;; right is in the same node - (path-get right 0) - ;; right is in a different node - (arrays/alength keys))] - (Chunk. keys idx end-idx))) - - (-chunked-rest [this] - (or (-chunked-next this) ())) - - IChunkedNext - (-chunked-next [this] - (let [last (path-set left 0 (dec (arrays/alength keys))) - left' (next-path set last)] - (when (path-lt left' right) - (-copy this left' right)))) - - IReduce - (-reduce [this f] - (if (nil? keys) - (f) - (let [first (-first this)] - (if-some [next (-next this)] - (-reduce next f first) - first)))) - - (-reduce [this f start] - (loop [left left - keys keys - idx idx - acc start] - (if (nil? keys) - acc - (let [new-acc (f acc (arrays/aget keys idx))] - (cond - (reduced? new-acc) - @new-acc - - (< (inc idx) (arrays/alength keys)) ;; can use cached array to move forward - (let [left' (path-inc left)] - (if (path-lt left' right) - (recur left' keys (inc idx) new-acc) - new-acc)) - - :else - (let [left' (next-path set left)] - (if (path-lt left' right) - (recur left' (keys-for set left') (path-get left' 0) new-acc) - new-acc))))))) - - IReversible - (-rseq [this] - (when keys - (riter set (prev-path set left) (prev-path set right)))) - - ISeek - (-seek [this key] - (-seek this key (.-comparator set))) - - (-seek [this key cmp] - (cond - (nil? key) - (throw (js/Error. "seek can't be called with a nil key!")) - - (nat-int? (cmp (arrays/aget keys idx) key)) - this - - :else - (when-some [left' (-seek* set key cmp)] - (Iter. set left' right (keys-for set left') (path-get left' 0))))) - - Object - (toString [this] (pr-str* this)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) - -(defn iter [set left right] - (Iter. set left right (keys-for set left) (path-get left 0))) - -;; reverse iteration - -(deftype ReverseIter [set left right keys idx] - IIter - (-copy [_ l r] - (ReverseIter. set l r (keys-for set r) (path-get r 0))) - - IEquiv - (-equiv [this other] (equiv-sequential this other)) - - ISequential - ISeqable - (-seq [this] (when keys this)) - - ISeq - (-first [this] - (when keys - (arrays/aget keys idx))) - - (-rest [this] - (or (-next this) ())) - - INext - (-next [this] - (when keys - (if (> idx 0) - ;; can use cached array to advance - (let [right' (path-dec right)] - (when (path-lt left right') - (ReverseIter. set left right' keys (dec idx)))) - (let [right' (prev-path set right)] - (when (path-lt left right') - (-copy this left right')))))) - - IReversible - (-rseq [this] - (when keys - (iter set (next-path set left) (next-path set right)))) - - ISeek - (-seek [this key] - (-seek this key (.-comparator set))) - - (-seek [this key cmp] - (cond - (nil? key) - (throw (js/Error. "seek can't be called with a nil key!")) - - (nat-int? (cmp key (arrays/aget keys idx))) - this - - :else - (let [right' (prev-path set (-rseek* set key cmp))] - (when (and - (nat-int? right') - (path-lte left right') - (path-lt right' right)) - (ReverseIter. set left right' (keys-for set right') (path-get right' 0)))))) - - Object - (toString [this] (pr-str* this)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) - -(defn riter [set left right] - (ReverseIter. set left right (keys-for set right) (path-get right 0))) - -;; distance - -(defn- -distance [node left right level] - (let [idx-l (path-get left level) - idx-r (path-get right level)] - (if (pos? level) - ;; inner node - (if (== idx-l idx-r) - (-distance (arrays/aget (.-pointers node) idx-l) left right (dec level)) - (loop [level level - res (- idx-r idx-l)] - (if (== 0 level) - res - (recur (dec level) (* res avg-len))))) - (- idx-r idx-l)))) - -(defn- distance [set path-l path-r] - (cond - (path-eq path-l path-r) - 0 - - (path-eq (path-inc path-l) path-r) - 1 - - (path-eq (next-path set path-l) path-r) - 1 - - :else - (-distance (.-root set) path-l path-r (.-shift set)))) - -(defn est-count [iter] - (distance (.-set iter) (.-left iter) (.-right iter))) - - -;; Slicing - -(defn- -seek* - "Returns path to first element >= key, - or -1 if all elements in a set < key" - [set key comparator] - (if (nil? key) - empty-path - (loop [node (.-root set) - path empty-path - level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (dec keys-l) key)] - (if (== keys-l idx) - nil - (path-set path 0 idx))) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (- keys-l 2) key)] - (recur - (arrays/aget (.-pointers node) idx) - (path-set path level idx) - (dec level)))))))) - -(defn- -rseek* - "Returns path to the first element that is > key. - If all elements in a set are <= key, returns `(-rpath set) + 1`. - It’s a virtual path that is bigger than any path in a tree" - [set key comparator] - (if (nil? key) - (path-inc (-rpath (.-root set) empty-path (.-shift set))) - (loop [node (.-root set) - path empty-path - level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (dec keys-l) key) - res (path-set path 0 idx)] - res) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (- keys-l 2) key) - res (path-set path level idx)] - (recur - (arrays/aget (.-pointers node) idx) - res - (dec level)))))))) - -(defn- -slice [set key-from key-to comparator] - (when-some [path (-seek* set key-from comparator)] - (let [till-path (-rseek* set key-to comparator)] - (when (path-lt path till-path) - (Iter. set path till-path (keys-for set path) (path-get path 0)))))) - -(defn- arr-map-inplace [f arr] - (let [len (arrays/alength arr)] - (loop [i 0] - (when (< i len) - (arrays/aset arr i (f (arrays/aget arr i))) - (recur (inc i)))) - arr)) - - -(defn- arr-partition-approx - "Splits `arr` into arrays of size between min-len and max-len, - trying to stick to (min+max)/2" - [min-len max-len arr] - (let [chunk-len avg-len - len (arrays/alength arr) - acc (transient [])] - (when (pos? len) - (loop [pos 0] - (let [rest (- len pos)] - (cond - (<= rest max-len) - (conj! acc (.slice arr pos)) - (>= rest (+ chunk-len min-len)) - (do - (conj! acc (.slice arr pos (+ pos chunk-len))) - (recur (+ pos chunk-len))) - :else - (let [piece-len (arrays/half rest)] - (conj! acc (.slice arr pos (+ pos piece-len))) - (recur (+ pos piece-len))))))) - (to-array (persistent! acc)))) - - -(defn- sorted-arr-distinct? [arr cmp] - (let [al (arrays/alength arr)] - (if (<= al 1) - true - (loop [i 1 - p (arrays/aget arr 0)] - (if (>= i al) - true - (let [e (arrays/aget arr i)] - (if (== 0 (cmp e p)) - false - (recur (inc i) e)))))))) - - -(defn- sorted-arr-distinct - "Filter out repetitive values in a sorted array. - Optimized for no-duplicates case" - [arr cmp] - (if (sorted-arr-distinct? arr cmp) - arr - (let [al (arrays/alength arr)] - (loop [acc (transient [(arrays/aget arr 0)]) - i 1 - p (arrays/aget arr 0)] - (if (>= i al) - (into-array (persistent! acc)) - (let [e (arrays/aget arr i)] - (if (== 0 (cmp e p)) - (recur acc (inc i) e) - (recur (conj! acc e) (inc i) e)))))))) - - -;; Public interface - -(defn conj - "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." - [set key cmp] - (let [roots (node-conj (.-root set) cmp key)] - (cond - ;; tree not changed - (nil? roots) - set - - ;; keeping single root - (== (arrays/alength roots) 1) - (alter-btset set - (arrays/aget roots 0) - (.-shift set) - (inc (.-cnt set))) - - ;; introducing new root - :else - (alter-btset set - (Node. (arrays/amap node-lim-key roots) roots) - (inc (.-shift set)) - (inc (.-cnt set)))))) - - -(defn disj - "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." - [set key cmp] - (let [new-roots (node-disj (.-root set) cmp key true nil nil)] - (if (nil? new-roots) ;; nothing changed, key wasn't in the set - set - (let [new-root (arrays/aget new-roots 0)] - (if (and (instance? Node new-root) - (== 1 (arrays/alength (.-pointers new-root)))) - - ;; root has one child, make him new root - (alter-btset set - (arrays/aget (.-pointers new-root) 0) - (dec (.-shift set)) - (dec (.-cnt set))) - - ;; keeping root level - (alter-btset set - new-root - (.-shift set) - (dec (.-cnt set)))))))) + [me.tonsky.persistent-sorted-set.arrays :as arrays])) +(def conj impl/conj) +(def disj impl/disj) (defn slice "An iterator for part of the set with provided boundaries. `(slice set from to)` returns iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([set key-from key-to] - (-slice set key-from key-to (.-comparator set))) - ([set key-from key-to comparator] - (-slice set key-from key-to comparator))) + ([^BTSet set key-from key-to] + (impl/-slice set key-from key-to (.-comparator set))) + ([^BTSet set key-from key-to comparator] + (impl/-slice set key-from key-to comparator))) (defn rslice "A reverse iterator for part of the set with provided boundaries. `(rslice set from to)` returns backwards iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([set key] - (some-> (-slice set key key (.-comparator set)) rseq)) - ([set key-from key-to] - (some-> (-slice set key-to key-from (.-comparator set)) rseq)) - ([set key-from key-to comparator] - (some-> (-slice set key-to key-from comparator) rseq))) + ([^BTSet set key] + (some-> (impl/-slice set key key (.-comparator set)) rseq)) + ([^BTSet set key-from key-to] + (some-> (impl/-slice set key-to key-from (.-comparator set)) rseq)) + ([^BTSet set key-from key-to comparator] + (some-> (impl/-slice set key-to key-from comparator) rseq))) (defn seek @@ -1058,9 +40,9 @@ `(seek (seq set) to)` returns iterator for all Xs where to <= X. Optionally pass in comparator that will override the one that set uses." ([seq to] - (-seek seq to)) + (impl/-seek seq to)) ([seq to cmp] - (-seek seq to cmp))) + (impl/-seek seq to cmp))) (defn from-sorted-array @@ -1069,44 +51,71 @@ (from-sorted-array cmp arr (arrays/alength arr) {})) ([cmp arr _len] (from-sorted-array cmp arr _len {})) - ([cmp arr _len _opts] + ([cmp arr _len opts] (let [leaves (->> arr - (arr-partition-approx min-len max-len) - (arr-map-inplace #(Leaf. %)))] + (impl/arr-partition-approx impl/min-len impl/max-len) + (impl/arr-map-inplace #(impl/Leaf. %))) + storage (:storage opts)] (loop [current-level leaves shift 0] (case (count current-level) - 0 (BTSet. (Leaf. (arrays/array)) 0 0 cmp nil uninitialized-hash) - 1 (BTSet. (first current-level) shift (arrays/alength arr) cmp nil uninitialized-hash) + 0 (impl/BTSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil + impl/uninitialized-hash impl/uninitialized-address) + 1 (impl/BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil + impl/uninitialized-hash impl/uninitialized-address) (recur - (->> current-level - (arr-partition-approx min-len max-len) - (arr-map-inplace #(Node. (arrays/amap node-lim-key %) %))) - (inc shift))))))) - + (->> current-level + (impl/arr-partition-approx impl/min-len impl/max-len) + (impl/arr-map-inplace #(impl/Node. (arrays/amap impl/node-lim-key %) % nil))) + (inc shift))))))) (defn from-sequential "Create a set with custom comparator and a collection of keys. Useful when you don’t want to call [[clojure.core/apply]] on [[sorted-set-by]]." [cmp seq] - (let [arr (-> (into-array seq) (arrays/asort cmp) (sorted-arr-distinct cmp))] + (let [arr (-> (into-array seq) (arrays/asort cmp) (impl/sorted-arr-distinct cmp))] (from-sorted-array cmp arr))) - (defn sorted-set* "Create a set with custom comparator, metadata and settings" [opts] - (BTSet. (Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) uninitialized-hash)) - + (impl/BTSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) + impl/uninitialized-hash impl/uninitialized-address)) (defn sorted-set-by - ([cmp] (BTSet. (Leaf. (arrays/array)) 0 0 cmp nil uninitialized-hash)) + ([cmp] (impl/BTSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil + impl/uninitialized-hash impl/uninitialized-address)) ([cmp & keys] (from-sequential cmp keys))) - (defn sorted-set ([] (sorted-set-by compare)) ([& keys] (from-sequential compare keys))) +(defn restore-by + "Constructs lazily-loaded set from storage, root address and custom comparator. + Supports all operations that normal in-memory impl would, + will fetch missing nodes by calling IStorage::restore when needed" + ([cmp address ^IStorage storage] + (restore-by cmp address storage {})) + ([cmp address ^IStorage storage opts] + (when-let [root (.restore storage address)] + (impl/BTSet. storage root 0 0 cmp nil impl/uninitialized-hash address)))) + +(defn restore + "Constructs lazily-loaded set from storage and root address. + Supports all operations that normal in-memory impl would, + will fetch missing nodes by calling IStorage::restore when needed" + ([address storage] + (restore-by compare address storage {})) + ([address ^IStorage storage opts] + (restore-by compare address storage opts))) + +(defn store + "Store each not-yet-stored node by calling IStorage::store and remembering + returned address. Incremental, won’t store same node twice on subsequent calls. + Returns root address. Remember it and use it for restore" + [^BTSet set] + (.store set)) + (defn settings [set] - {:branching-factor max-len - :ref-type :strong}) \ No newline at end of file + {:branching-factor impl/max-len + :ref-type :strong}) diff --git a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs new file mode 100644 index 0000000..41b544c --- /dev/null +++ b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs @@ -0,0 +1,1107 @@ +(ns me.tonsky.persistent-sorted-set.impl + (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) + (:require + [me.tonsky.persistent-sorted-set.arrays :as arrays]) + (:require-macros + [me.tonsky.persistent-sorted-set.arrays :as arrays])) + +; B+ tree +; ------- + +; Leaf: keys[] :: array of values + +; Node: children[] :: links to children nodes +; keys[] :: max value for whole subtree +; node.keys[i] == max(node.children[i].keys) +; All arrays are 16..32 elements, inclusive + +; BTSet: root :: Node or Leaf +; shift :: depth - 1 +; cnt :: size of a set, integer, rolling +; comparator :: comparator used for ordering +; meta :: clojure meta map +; _hash :: hash code, same as for clojure collections, on-demand, cached + +; Path: conceptually a vector of indexes from root to leaf value, but encoded in a single number. +; E.g. we have path [7 30 11] representing root.children[7].children[30].keys[11]. +; In our case level-shift is 5, meaning each index will take 5 bits: +; (7 << 10) | (30 << 5) | (11 << 0) = 8139 +; 00111 11110 01011 + +; Iter: set :: Set this iterator belongs to +; left :: Current path +; right :: Right bound path (exclusive) +; keys :: Cached ref for keys array for a leaf +; idx :: Cached idx in keys array +; Keys and idx are cached for fast iteration inside a leaf" + +(def + ;; ^:const + max-safe-path + "js limitation for bit ops" + (js/Math.pow 2 31)) + +(def + ;; ^:const + bits-per-level + "tunable param" + 5) + +(def + ;; ^:const + max-len + (js/Math.pow 2 bits-per-level)) ;; 32 + +(def + ;; ^:const + min-len + (/ max-len 2)) ;; 16 + +(def ^:private + ;; ^:const + avg-len + (arrays/half (+ max-len min-len))) ;; 24 + +(def + ;; ^:const + max-safe-level + (js/Math.floor (/ 31 bits-per-level))) ;; 6 + +(def + ;; ^:const + bit-mask + (- max-len 1)) ;; 0b011111 = 5 bit + +(def factors + (arrays/into-array (map #(js/Math.pow 2 %) (range 0 52 bits-per-level)))) + +(def + ;; ^:const + empty-path 0) + +(defn- path-get ^number [^number path ^number level] + (if (< level max-safe-level) + (-> path + (unsigned-bit-shift-right (* level bits-per-level)) + (bit-and bit-mask)) + (-> path + (/ (arrays/aget factors level)) + (js/Math.floor) + (bit-and bit-mask)))) + +(defn- path-set ^number [^number path ^number level ^number idx] + (let [smol? (and (< path max-safe-path) (< level max-safe-level)) + old (path-get path level) + minus (if smol? + (bit-shift-left old (* level bits-per-level)) + (* old (arrays/aget factors level))) + plus (if smol? + (bit-shift-left idx (* level bits-per-level)) + (* idx (arrays/aget factors level)))] + (-> path + (- minus) + (+ plus)))) + +(defn- path-inc ^number [^number path] + (inc path)) + +(defn- path-dec ^number [^number path] + (dec path)) + +(defn- path-cmp ^number [^number path1 ^number path2] + (- path1 path2)) + +(defn- path-lt ^boolean [^number path1 ^number path2] + (< path1 path2)) + +(defn- path-lte ^boolean [^number path1 ^number path2] + (<= path1 path2)) + +(defn- path-eq ^boolean [^number path1 ^number path2] + (== path1 path2)) + +(defn- path-same-leaf ^boolean [^number path1 ^number path2] + (if (and + (< path1 max-safe-path) + (< path2 max-safe-path)) + (== + (unsigned-bit-shift-right path1 bits-per-level) + (unsigned-bit-shift-right path2 bits-per-level)) + (== + (Math/floor (/ path1 max-len)) + (Math/floor (/ path2 max-len))))) + +(defn- path-str [^number path] + (loop [res () + path path] + (if (not= path 0) + (recur (cljs.core/conj res (mod path max-len)) (Math/floor (/ path max-len))) + (vec res)))) + +(defn- binary-search-l [cmp arr r k] + (loop [l 0 + r (long r)] + (if (<= l r) + (let [m (arrays/half (+ l r)) + mk (arrays/aget arr m)] + (if (neg? (cmp mk k)) + (recur (inc m) r) + (recur l (dec m)))) + l))) + +(defn- binary-search-r [cmp arr r k] + (loop [l 0 + r (long r)] + (if (<= l r) + (let [m (arrays/half (+ l r)) + mk (arrays/aget arr m)] + (if (pos? (cmp mk k)) + (recur l (dec m)) + (recur (inc m) r))) + l))) + +(defn- lookup-exact [cmp arr key] + (let [arr-l (arrays/alength arr) + idx (binary-search-l cmp arr (dec arr-l) key)] + (if (and (< idx arr-l) + (== 0 (cmp (arrays/aget arr idx) key))) + idx + -1))) + +(defn- lookup-range [cmp arr key] + (let [arr-l (arrays/alength arr) + idx (binary-search-l cmp arr (dec arr-l) key)] + (if (== idx arr-l) + -1 + idx))) + +;; Array operations + +(defn- cut-n-splice [arr cut-from cut-to splice-from splice-to xs] + (let [xs-l (arrays/alength xs) + l1 (- splice-from cut-from) + l2 (- cut-to splice-to) + l1xs (+ l1 xs-l) + new-arr (arrays/make-array (+ l1 xs-l l2))] + (arrays/acopy arr cut-from splice-from new-arr 0) + (arrays/acopy xs 0 xs-l new-arr l1) + (arrays/acopy arr splice-to cut-to new-arr l1xs) + new-arr)) + +(defn- splice [arr splice-from splice-to xs] + (cut-n-splice arr 0 (arrays/alength arr) splice-from splice-to xs)) + +(defn- insert [arr idx xs] + (cut-n-splice arr 0 (arrays/alength arr) idx idx xs)) + +(defn- merge-n-split [a1 a2] + (let [a1-l (arrays/alength a1) + a2-l (arrays/alength a2) + total-l (+ a1-l a2-l) + r1-l (arrays/half total-l) + r2-l (- total-l r1-l) + r1 (arrays/make-array r1-l) + r2 (arrays/make-array r2-l)] + (if (<= a1-l r1-l) + (do + (arrays/acopy a1 0 a1-l r1 0) + (arrays/acopy a2 0 (- r1-l a1-l) r1 a1-l) + (arrays/acopy a2 (- r1-l a1-l) a2-l r2 0)) + (do + (arrays/acopy a1 0 r1-l r1 0) + (arrays/acopy a1 r1-l a1-l r2 0) + (arrays/acopy a2 0 a2-l r2 (- a1-l r1-l)))) + (arrays/array r1 r2))) + +(defn- ^boolean eq-arr [cmp a1 a1-from a1-to a2 a2-from a2-to] + (let [len (- a1-to a1-from)] + (and + (== len (- a2-to a2-from)) + (loop [i 0] + (cond + (== i len) + true + + (not (== 0 (cmp + (arrays/aget a1 (+ i a1-from)) + (arrays/aget a2 (+ i a2-from))))) + false + + :else + (recur (inc i))))))) + +(defn- check-n-splice [cmp arr from to new-arr] + (if (eq-arr cmp arr from to new-arr 0 (arrays/alength new-arr)) + arr + (splice arr from to new-arr))) + +(defn- return-array + "Drop non-nil references and return array of arguments" + ([a1] + (arrays/array a1)) + ([a1 a2] + (if a1 + (if a2 + (arrays/array a1 a2) + (arrays/array a1)) + (arrays/array a2))) + ([a1 a2 a3] + (if a1 + (if a2 + (if a3 + (arrays/array a1 a2 a3) + (arrays/array a1 a2)) + (if a3 + (arrays/array a1 a3) + (arrays/array a1))) + (if a2 + (if a3 + (arrays/array a2 a3) + (arrays/array a2)) + (arrays/array a3))))) + +;; + +(defprotocol INode + (node-lim-key [_]) + (node-len [_]) + (node-merge [_ next]) + (node-merge-n-split [_ next]) + (node-lookup [_ cmp key storage]) + (node-child [_ idx storage]) + (node-conj [_ cmp key storage]) + (node-disj [_ cmp key root? left right storage])) + +(defn- rotate [node root? left right] + (cond + ;; root never merges + root? + (return-array node) + + ;; enough keys, nothing to merge + (> (node-len node) min-len) + (return-array left node right) + + ;; left and this can be merged to one + (and left (<= (node-len left) min-len)) + (return-array (node-merge left node) right) + + ;; right and this can be merged to one + (and right (<= (node-len right) min-len)) + (return-array left (node-merge node right)) + + ;; left has fewer nodes, redestribute with it + (and left (or (nil? right) + (< (node-len left) (node-len right)))) + (let [nodes (node-merge-n-split left node)] + (return-array (arrays/aget nodes 0) (arrays/aget nodes 1) right)) + + ;; right has fewer nodes, redestribute with it + :else + (let [nodes (node-merge-n-split node right)] + (return-array left (arrays/aget nodes 0) (arrays/aget nodes 1))))) + +(defprotocol IStore + (store [this storage])) + +(defn- ensure-addresses! + [^Node node size] + (when (empty? (.-addresses node)) + (set! (.-addresses node) (arrays/make-array size)))) + +(deftype Node [keys children ^:mutable addresses] + IStore + (store [this ^IStorage storage] + (ensure-addresses! this (count children)) + + ;; Children first + (dorun + (map-indexed + (fn [idx child] + (let [address (.store storage child)] + (aset addresses idx address))) + children)) + (.store storage this)) + + INode + (node-lim-key [_] + (arrays/alast keys)) + + (node-len [_] + (arrays/alength keys)) + + (node-merge [this ^Node next] + (ensure-addresses! this (count children)) + (ensure-addresses! next (count (.-children next))) + (Node. (arrays/aconcat keys (.-keys next)) + (arrays/aconcat children (.-children next)) + (arrays/aconcat addresses (.-addresses next)))) + + (node-merge-n-split [this ^Node next] + (ensure-addresses! this (count children)) + (ensure-addresses! next (count (.-children next))) + (let [ks (merge-n-split keys (.-keys next)) + ps (merge-n-split children (.-children next)) + as (merge-n-split addresses (.-addresses next))] + (return-array (Node. (arrays/aget ks 0) + (arrays/aget ps 0) + (arrays/aget as 0)) + (Node. (arrays/aget ks 1) + (arrays/aget ps 1) + (arrays/aget as 1))))) + + (node-child [_ idx ^IStorage storage] + (when-not (= -1 idx) + (let [child (arrays/aget children idx) + address (when addresses (arrays/aget addresses idx))] + (if-not child + (let [child (.restore storage address)] ;; TODO: async + (arrays/aset children idx child)) + (when (and storage address) + (.accessed storage address))) + (arrays/aget children idx)))) + + (node-lookup [this cmp key storage] + (let [idx (lookup-range cmp keys key)] + (when-let [child (node-child this idx storage)] + (node-lookup child cmp key storage)))) + + (node-conj [this cmp key storage] + (ensure-addresses! this (count children)) + (let [idx (binary-search-l cmp keys (- (arrays/alength keys) 2) key) + child (node-child this idx storage) + nodes (node-conj child cmp key storage)] + (when nodes + (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) + new-children (splice children idx (inc idx) nodes) + new-addresses (splice addresses idx (inc idx) nodes)] + (if (<= (arrays/alength new-children) max-len) + ;; ok as is + (arrays/array (Node. new-keys new-children new-addresses)) + ;; gotta split it up + (let [middle (arrays/half (arrays/alength new-children))] + (arrays/array + (Node. (.slice new-keys 0 middle) + (.slice new-children 0 middle) + (.slice new-addresses 0 middle)) + (Node. (.slice new-keys middle) + (.slice new-children middle) + (.slice new-addresses middle))))))))) + + (node-disj [this cmp key root? left right storage] + (ensure-addresses! this (count children)) + (let [idx (lookup-range cmp keys key)] + (when-not (== -1 idx) ;; short-circuit, key not here + (let [child (node-child this idx storage) + left-child (when (>= (dec idx) 0) + (node-child this (dec idx) storage)) + right-child (when (< (inc idx) (arrays/alength children)) + (node-child this (inc idx) storage)) + disjned (node-disj child cmp key false left-child right-child storage)] + (when disjned ;; short-circuit, key not here + (let [left-idx (if left-child (dec idx) idx) + right-idx (if right-child (+ 2 idx) (+ 1 idx)) + new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) + new-children (splice children left-idx right-idx disjned) + new-addresses (splice addresses left-idx right-idx disjned)] + (rotate (Node. new-keys new-children new-addresses) root? left right)))))))) + +(deftype Leaf [keys] + IStore + (store [this storage] + (.store storage this)) + + INode + (node-lim-key [_] + (arrays/alast keys)) +;; Object +;; (toString [_] (pr-str* (vec keys))) + + (node-len [_] + (arrays/alength keys)) + + (node-merge [_ next] + (Leaf. (arrays/aconcat keys (.-keys next)))) + + (node-merge-n-split [_ next] + (let [ks (merge-n-split keys (.-keys next))] + (return-array (Leaf. (arrays/aget ks 0)) + (Leaf. (arrays/aget ks 1))))) + + (node-child [_this idx _storage] + (arrays/aget keys idx)) + + (node-lookup [this cmp key storage] + (let [idx (lookup-exact cmp keys key)] + (when-not (== -1 idx) + (node-child this idx storage)))) + + (node-conj [_ cmp key storage] + + (let [idx (binary-search-l cmp keys (dec (arrays/alength keys)) key) + keys-l (arrays/alength keys)] + (cond + ;; element already here + (and (< idx keys-l) + (== 0 (cmp key (arrays/aget keys idx)))) + nil + + ;; splitting + (== keys-l max-len) + (let [middle (arrays/half (inc keys-l))] + (if (> idx middle) + ;; new key goes to the second half + (arrays/array + (Leaf. (.slice keys 0 middle)) + (Leaf. (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) + ;; new key goes to the first half + (arrays/array + (Leaf. (cut-n-splice keys 0 middle idx idx (arrays/array key))) + (Leaf. (.slice keys middle keys-l))))) + + ;; ok as is + :else + (arrays/array (Leaf. (splice keys idx idx (arrays/array key))))))) + + (node-disj [_ cmp key root? left right storage] + (let [idx (lookup-exact cmp keys key)] + (when-not (== -1 idx) ;; key is here + (let [new-keys (splice keys idx (inc idx) (arrays/array))] + (rotate (Leaf. new-keys) root? left right)))))) + +;; BTSet + +(declare conj disj btset-iter) + +(def ^:private + ;; ^:const + uninitialized-hash nil) +(def ^:private + ;; ^:const + uninitialized-address nil) + +(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] + Object + (toString [this] (pr-str* this)) + + ICloneable + (-clone [_] (BTSet. storage root shift cnt comparator meta _hash _address)) + + IWithMeta + (-with-meta [_ new-meta] (BTSet. storage root shift cnt comparator new-meta _hash _address)) + + IMeta + (-meta [_] meta) + + IEmptyableCollection + (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) + + IEquiv + (-equiv [this other] + (and + (set? other) + (== cnt (count other)) + (every? #(contains? this %) other))) + + IHash + (-hash [this] (caching-hash this hash-unordered-coll _hash)) + + ICollection + (-conj [this key] (conj this key comparator)) + + ISet + (-disjoin [this key] (disj this key comparator)) + + IStore + (store [_this storage*] + (assert (some? (or storage storage*))) + (when (nil? _address) + (set! _address (.store root storage))) + _address) + + ILookup + (-lookup [_ k] + (node-lookup root comparator k storage)) + (-lookup [_ k not-found] + (or (node-lookup root comparator k storage) not-found)) + + ISeqable + (-seq [this] (btset-iter this)) + + IReduce + (-reduce [this f] + (if-let [i (btset-iter this)] + (-reduce i f) + (f))) + (-reduce [this f start] + (if-let [i (btset-iter this)] + (-reduce i f start) + start)) + + IReversible + (-rseq [this] + (rseq (btset-iter this))) + + ; ISorted + ; (-sorted-seq [this ascending?]) + ; (-sorted-seq-from [this k ascending?]) + ; (-entry-key [this entry] entry) + ; (-comparator [this] comparator) + + ICounted + (-count [_] cnt) + + IEditableCollection + (-as-transient [this] this) + + ITransientCollection + (-conj! [this key] (conj this key comparator)) + (-persistent! [this] this) + + ITransientSet + (-disjoin! [this key] (disj this key comparator)) + + IFn + (-invoke [this k] (-lookup this k)) + (-invoke [this k not-found] (-lookup this k not-found)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "#{" " " "}" opts (seq this)))) + +(defn- keys-for [set path] + (loop [level (.-shift set) + node (.-root set)] + (if (pos? level) + (recur + (dec level) + (arrays/aget (.-children node) (path-get path level))) + (.-keys node)))) + +(defn alter-btset [^BTSet set root shift cnt] + (BTSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) + +;; iteration + +(defn- -next-path [node ^number path ^number level] + (let [idx (path-get path level)] + (if (pos? level) + ;; inner node + (let [sub-path (-next-path (arrays/aget (.-children node) idx) path (dec level))] + (if (nil? sub-path) + ;; nested node overflow + (if (< (inc idx) (arrays/alength (.-children node))) + ;; advance current node idx, reset subsequent indexes + (path-set empty-path level (inc idx)) + ;; current node overflow + nil) + ;; keep current idx + (path-set sub-path level idx))) + ;; leaf + (if (< (inc idx) (arrays/alength (.-keys node))) + ;; advance leaf idx + (path-set empty-path 0 (inc idx)) + ;; leaf overflow + nil)))) + +(defn- -rpath + "Returns rightmost path possible starting from node and going deeper" + [node ^number path ^number level] + (loop [node node + path path + level level] + (if (pos? level) + ;; inner node + (recur + (arrays/alast (.-children node)) + (path-set path level (dec (arrays/alength (.-children node)))) + (dec level)) + ;; leaf + (path-set path 0 (dec (arrays/alength (.-keys node))))))) + +(defn- next-path + "Returns path representing next item after `path` in natural traversal order. + Will overflow at leaf if at the end of the tree" + [set ^number path] + (if (neg? path) + empty-path + (or + (-next-path (.-root set) path (.-shift set)) + (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) + +(defn- -prev-path [node ^number path ^number level] + (let [idx (path-get path level)] + (cond + ;; leaf overflow + (and (== 0 level) (== 0 idx)) + nil + + ;; leaf + (== 0 level) + (path-set empty-path 0 (dec idx)) + + ;; branch that was overflow before + (>= idx (node-len node)) + (-rpath node path level) + + :else + (let [path' (-prev-path (arrays/aget (.-children node) idx) path (dec level))] + (cond + ;; no sub-overflow, keep current idx + (some? path') + (path-set path' level idx) + + ;; nested overflow + this node overflow + (== 0 idx) + nil + + ;; nested overflow, advance current idx, reset subsequent indexes + :else + (let [path' (-rpath (arrays/aget (.-children node) (dec idx)) path (dec level))] + (path-set path' level (dec idx)))))))) + +(defn- prev-path + "Returns path representing previous item before `path` in natural traversal order. + Will overflow at leaf if at beginning of tree" + [set ^number path] + (if (> (path-get path (inc (.-shift set))) 0) ;; overflow + (-rpath (.-root set) path (.-shift set)) + (or + (-prev-path (.-root set) path (.-shift set)) + (path-dec empty-path)))) + +(declare iter riter) + +(defn- btset-iter + "Iterator that represents the whole set" + [set] + (when (pos? (node-len (.-root set))) + (let [left empty-path + rpath (-rpath (.-root set) empty-path (.-shift set)) + right (next-path set rpath)] + (iter set left right)))) + +;; replace with cljs.core/ArrayChunk after https://dev.clojure.org/jira/browse/CLJS-2470 +(deftype Chunk [arr off end] + ICounted + (-count [_] (- end off)) + + IIndexed + (-nth [this i] + (aget arr (+ off i))) + + (-nth [this i not-found] + (if (and (>= i 0) (< i (- end off))) + (aget arr (+ off i)) + not-found)) + + IChunk + (-drop-first [this] + (if (== off end) + (throw (js/Error. "-drop-first of empty chunk")) + (ArrayChunk. arr (inc off) end))) + + IReduce + (-reduce [this f] + (if (== off end) + (f) + (-reduce (-drop-first this) f (aget arr off)))) + + (-reduce [this f start] + (loop [val start, n off] + (if (< n end) + (let [val' (f val (aget arr n))] + (if (reduced? val') + @val' + (recur val' (inc n)))) + val)))) + +(defprotocol IIter + (-copy [this left right])) + +(defprotocol ISeek + (-seek + [this key] + [this key comparator])) + +(declare -seek* -rseek*) + +(deftype Iter [^BTSet set left right keys idx] + IIter + (-copy [_ l r] + (Iter. set l r (keys-for set l) (path-get l 0))) + + IEquiv + (-equiv [this other] (equiv-sequential this other)) + + ISequential + ISeqable + (-seq [this] (when keys this)) + + ISeq + (-first [this] + (when keys + (arrays/aget keys idx))) + + (-rest [this] + (or (-next this) ())) + + INext + (-next [this] + (when keys + (if (< (inc idx) (arrays/alength keys)) + ;; can use cached array to move forward + (let [left' (path-inc left)] + (when (path-lt left' right) + (Iter. set left' right keys (inc idx)))) + (let [left' (next-path set left)] + (when (path-lt left' right) + (-copy this left' right)))))) + + IChunkedSeq + (-chunked-first [this] + (let [end-idx (if (path-same-leaf left right) + ;; right is in the same node + (path-get right 0) + ;; right is in a different node + (arrays/alength keys))] + (Chunk. keys idx end-idx))) + + (-chunked-rest [this] + (or (-chunked-next this) ())) + + IChunkedNext + (-chunked-next [this] + (let [last (path-set left 0 (dec (arrays/alength keys))) + left' (next-path set last)] + (when (path-lt left' right) + (-copy this left' right)))) + + IReduce + (-reduce [this f] + (if (nil? keys) + (f) + (let [first (-first this)] + (if-some [next (-next this)] + (-reduce next f first) + first)))) + + (-reduce [this f start] + (loop [left left + keys keys + idx idx + acc start] + (if (nil? keys) + acc + (let [new-acc (f acc (arrays/aget keys idx))] + (cond + (reduced? new-acc) + @new-acc + + (< (inc idx) (arrays/alength keys)) ;; can use cached array to move forward + (let [left' (path-inc left)] + (if (path-lt left' right) + (recur left' keys (inc idx) new-acc) + new-acc)) + + :else + (let [left' (next-path set left)] + (if (path-lt left' right) + (recur left' (keys-for set left') (path-get left' 0) new-acc) + new-acc))))))) + + IReversible + (-rseq [this] + (when keys + (riter set (prev-path set left) (prev-path set right)))) + + ISeek + (-seek [this key] + (-seek this key (.-comparator set))) + + (-seek [this key cmp] + (cond + (nil? key) + (throw (js/Error. "seek can't be called with a nil key!")) + + (nat-int? (cmp (arrays/aget keys idx) key)) + this + + :else + (when-some [left' (-seek* set key cmp)] + (Iter. set left' right (keys-for set left') (path-get left' 0))))) + + Object + (toString [this] (pr-str* this)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) + +(defn iter [set left right] + (Iter. set left right (keys-for set left) (path-get left 0))) + +;; reverse iteration + +(deftype ReverseIter [^BTSet set left right keys idx] + IIter + (-copy [_ l r] + (ReverseIter. set l r (keys-for set r) (path-get r 0))) + + IEquiv + (-equiv [this other] (equiv-sequential this other)) + + ISequential + ISeqable + (-seq [this] (when keys this)) + + ISeq + (-first [this] + (when keys + (arrays/aget keys idx))) + + (-rest [this] + (or (-next this) ())) + + INext + (-next [this] + (when keys + (if (> idx 0) + ;; can use cached array to advance + (let [right' (path-dec right)] + (when (path-lt left right') + (ReverseIter. set left right' keys (dec idx)))) + (let [right' (prev-path set right)] + (when (path-lt left right') + (-copy this left right')))))) + + IReversible + (-rseq [this] + (when keys + (iter set (next-path set left) (next-path set right)))) + + ISeek + (-seek [this key] + (-seek this key (.-comparator set))) + + (-seek [this key cmp] + (cond + (nil? key) + (throw (js/Error. "seek can't be called with a nil key!")) + + (nat-int? (cmp key (arrays/aget keys idx))) + this + + :else + (let [right' (prev-path set (-rseek* set key cmp))] + (when (and + (nat-int? right') + (path-lte left right') + (path-lt right' right)) + (ReverseIter. set left right' (keys-for set right') (path-get right' 0)))))) + + Object + (toString [this] (pr-str* this)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) + +(defn riter [^BTSet set left right] + (ReverseIter. set left right (keys-for set right) (path-get right 0))) + +;; distance + +(defn- -distance [^Node node left right level] + (let [idx-l (path-get left level) + idx-r (path-get right level)] + (if (pos? level) + ;; inner node + (if (== idx-l idx-r) + (-distance (arrays/aget (.-children node) idx-l) left right (dec level)) + (loop [level level + res (- idx-r idx-l)] + (if (== 0 level) + res + (recur (dec level) (* res avg-len))))) + (- idx-r idx-l)))) + +(defn- distance [^BTSet set path-l path-r] + (cond + (path-eq path-l path-r) + 0 + + (path-eq (path-inc path-l) path-r) + 1 + + (path-eq (next-path set path-l) path-r) + 1 + + :else + (-distance (.-root set) path-l path-r (.-shift set)))) + +(defn est-count [iter] + (distance (.-set iter) (.-left iter) (.-right iter))) + +;; Slicing + +(defn- -seek* + "Returns path to first element >= key, + or -1 if all elements in a set < key" + [^BTSet set key comparator] + (if (nil? key) + empty-path + (loop [node (.-root set) + path empty-path + level (.-shift set)] + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (dec keys-l) key)] + (if (== keys-l idx) + nil + (path-set path 0 idx))) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (- keys-l 2) key)] + (recur + (arrays/aget (.-children node) idx) + (path-set path level idx) + (dec level)))))))) + +(defn- -rseek* + "Returns path to the first element that is > key. + If all elements in a set are <= key, returns `(-rpath set) + 1`. + It’s a virtual path that is bigger than any path in a tree" + [^BTSet set key comparator] + (if (nil? key) + (path-inc (-rpath (.-root set) empty-path (.-shift set))) + (loop [node (.-root set) + path empty-path + level (.-shift set)] + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (dec keys-l) key) + res (path-set path 0 idx)] + res) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (- keys-l 2) key) + res (path-set path level idx)] + (recur + (arrays/aget (.-children node) idx) + res + (dec level)))))))) + +(defn -slice [^BTSet set key-from key-to comparator] + (when-some [path (-seek* set key-from comparator)] + (let [till-path (-rseek* set key-to comparator)] + (when (path-lt path till-path) + (Iter. set path till-path (keys-for set path) (path-get path 0)))))) + +(defn arr-map-inplace [f arr] + (let [len (arrays/alength arr)] + (loop [i 0] + (when (< i len) + (arrays/aset arr i (f (arrays/aget arr i))) + (recur (inc i)))) + arr)) + +(defn arr-partition-approx + "Splits `arr` into arrays of size between min-len and max-len, + trying to stick to (min+max)/2" + [min-len max-len arr] + (let [chunk-len avg-len + len (arrays/alength arr) + acc (transient [])] + (when (pos? len) + (loop [pos 0] + (let [rest (- len pos)] + (cond + (<= rest max-len) + (conj! acc (.slice arr pos)) + (>= rest (+ chunk-len min-len)) + (do + (conj! acc (.slice arr pos (+ pos chunk-len))) + (recur (+ pos chunk-len))) + :else + (let [piece-len (arrays/half rest)] + (conj! acc (.slice arr pos (+ pos piece-len))) + (recur (+ pos piece-len))))))) + (to-array (persistent! acc)))) + +(defn- sorted-arr-distinct? [arr cmp] + (let [al (arrays/alength arr)] + (if (<= al 1) + true + (loop [i 1 + p (arrays/aget arr 0)] + (if (>= i al) + true + (let [e (arrays/aget arr i)] + (if (== 0 (cmp e p)) + false + (recur (inc i) e)))))))) + +(defn- sorted-arr-distinct + "Filter out repetitive values in a sorted array. + Optimized for no-duplicates case" + [arr cmp] + (if (sorted-arr-distinct? arr cmp) + arr + (let [al (arrays/alength arr)] + (loop [acc (transient [(arrays/aget arr 0)]) + i 1 + p (arrays/aget arr 0)] + (if (>= i al) + (into-array (persistent! acc)) + (let [e (arrays/aget arr i)] + (if (== 0 (cmp e p)) + (recur acc (inc i) e) + (recur (conj! acc e) (inc i) e)))))))) + +;; Public interface + +(defn conj + "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." + [^BTSet set key cmp] + (let [roots (node-conj (.-root set) cmp key (.-storage set))] + (cond + ;; tree not changed + (nil? roots) + set + + ;; keeping single root + (== (arrays/alength roots) 1) + (alter-btset set + (arrays/aget roots 0) + (.-shift set) + (inc (.-cnt set))) + + ;; introducing new root + :else + (alter-btset set + (Node. (arrays/amap node-lim-key roots) roots nil) + (inc (.-shift set)) + (inc (.-cnt set)))))) + +(defn disj + "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." + [^BTSet set key cmp] + (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] + (if (nil? new-roots) ;; nothing changed, key wasn't in the set + set + (let [new-root (arrays/aget new-roots 0)] + (if (and (instance? Node new-root) + (== 1 (arrays/alength (.-children new-root)))) + + ;; root has one child, make him new root + (alter-btset set + (arrays/aget (.-children new-root) 0) + (dec (.-shift set)) + (dec (.-cnt set))) + + ;; keeping root level + (alter-btset set + new-root + (.-shift set) + (dec (.-cnt set)))))))) diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs new file mode 100644 index 0000000..ae5fe7f --- /dev/null +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -0,0 +1,6 @@ +(ns me.tonsky.persistent-sorted-set.protocol) + +(defprotocol IStorage + (restore [address]) + (accessed [address]) + (store [node])) From e6719a40e8b7803733074a821913efa2013c52cd Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 01:02:05 +0800 Subject: [PATCH 02/28] fix: protocol calls --- .../me/tonsky/persistent_sorted_set.cljs | 36 ++++--- .../me/tonsky/persistent_sorted_set/impl.cljs | 102 +++++++++--------- .../persistent_sorted_set/protocol.cljs | 6 +- 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 49c926a..2a31724 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -5,8 +5,8 @@ (:refer-clojure :exclude [conj disj sorted-set sorted-set-by]) (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.impl :as impl :refer [BTSet]] - [me.tonsky.persistent-sorted-set.protocol :refer [IStorage]]) + [me.tonsky.persistent-sorted-set.impl :as impl :refer [PersistentSortedSet]] + [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -17,9 +17,10 @@ "An iterator for part of the set with provided boundaries. `(slice set from to)` returns iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([^BTSet set key-from key-to] + ([^PersistentSortedSet set key-from key-to] (impl/-slice set key-from key-to (.-comparator set))) - ([^BTSet set key-from key-to comparator] + ([^PersistentSortedSet set key-from key-to comparator] + ;; (js/console.trace) (impl/-slice set key-from key-to comparator))) @@ -27,11 +28,11 @@ "A reverse iterator for part of the set with provided boundaries. `(rslice set from to)` returns backwards iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([^BTSet set key] + ([^PersistentSortedSet set key] (some-> (impl/-slice set key key (.-comparator set)) rseq)) - ([^BTSet set key-from key-to] + ([^PersistentSortedSet set key-from key-to] (some-> (impl/-slice set key-to key-from (.-comparator set)) rseq)) - ([^BTSet set key-from key-to comparator] + ([^PersistentSortedSet set key-from key-to comparator] (some-> (impl/-slice set key-to key-from comparator) rseq))) @@ -59,9 +60,9 @@ (loop [current-level leaves shift 0] (case (count current-level) - 0 (impl/BTSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil + 0 (impl/PersistentSortedSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil impl/uninitialized-hash impl/uninitialized-address) - 1 (impl/BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil + 1 (impl/PersistentSortedSet. storage (first current-level) shift (arrays/alength arr) cmp nil impl/uninitialized-hash impl/uninitialized-address) (recur (->> current-level @@ -78,11 +79,11 @@ (defn sorted-set* "Create a set with custom comparator, metadata and settings" [opts] - (impl/BTSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) + (impl/PersistentSortedSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) impl/uninitialized-hash impl/uninitialized-address)) (defn sorted-set-by - ([cmp] (impl/BTSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil + ([cmp] (impl/PersistentSortedSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil impl/uninitialized-hash impl/uninitialized-address)) ([cmp & keys] (from-sequential cmp keys))) @@ -96,9 +97,12 @@ will fetch missing nodes by calling IStorage::restore when needed" ([cmp address ^IStorage storage] (restore-by cmp address storage {})) - ([cmp address ^IStorage storage opts] - (when-let [root (.restore storage address)] - (impl/BTSet. storage root 0 0 cmp nil impl/uninitialized-hash address)))) + ([cmp address ^IStorage storage {:keys [set-metadata]}] + (when-let [root (protocol/restore storage address)] + (impl/PersistentSortedSet. storage root + (:shift set-metadata) + (:count set-metadata) + cmp nil impl/uninitialized-hash address)))) (defn restore "Constructs lazily-loaded set from storage and root address. @@ -113,8 +117,8 @@ "Store each not-yet-stored node by calling IStorage::store and remembering returned address. Incremental, won’t store same node twice on subsequent calls. Returns root address. Remember it and use it for restore" - [^BTSet set] - (.store set)) + [^PersistentSortedSet set ^IStorage storage] + (impl/store set storage)) (defn settings [set] {:branching-factor impl/max-len diff --git a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs index 41b544c..6b58f63 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs @@ -1,7 +1,8 @@ (ns me.tonsky.persistent-sorted-set.impl (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) (:require - [me.tonsky.persistent-sorted-set.arrays :as arrays]) + [me.tonsky.persistent-sorted-set.arrays :as arrays] + [me.tonsky.persistent-sorted-set.protocol :as protocol :refer [IStorage]]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -15,7 +16,7 @@ ; node.keys[i] == max(node.children[i].keys) ; All arrays are 16..32 elements, inclusive -; BTSet: root :: Node or Leaf +; PersistentSortedSet: root :: Node or Leaf ; shift :: depth - 1 ; cnt :: size of a set, integer, rolling ; comparator :: comparator used for ordering @@ -318,10 +319,10 @@ (dorun (map-indexed (fn [idx child] - (let [address (.store storage child)] + (let [address (protocol/store storage child)] (aset addresses idx address))) children)) - (.store storage this)) + (protocol/store storage this)) INode (node-lim-key [_] @@ -355,10 +356,10 @@ (let [child (arrays/aget children idx) address (when addresses (arrays/aget addresses idx))] (if-not child - (let [child (.restore storage address)] ;; TODO: async + (let [child (protocol/restore storage address)] (arrays/aset children idx child)) (when (and storage address) - (.accessed storage address))) + (protocol/accessed storage address))) (arrays/aget children idx)))) (node-lookup [this cmp key storage] @@ -374,7 +375,7 @@ (when nodes (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) - new-addresses (splice addresses idx (inc idx) nodes)] + new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] (if (<= (arrays/alength new-children) max-len) ;; ok as is (arrays/array (Node. new-keys new-children new-addresses)) @@ -402,14 +403,13 @@ (let [left-idx (if left-child (dec idx) idx) right-idx (if right-child (+ 2 idx) (+ 1 idx)) new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) - new-children (splice children left-idx right-idx disjned) - new-addresses (splice addresses left-idx right-idx disjned)] - (rotate (Node. new-keys new-children new-addresses) root? left right)))))))) + new-children (splice children left-idx right-idx disjned)] + (rotate (Node. new-keys new-children addresses) root? left right)))))))) (deftype Leaf [keys] IStore (store [this storage] - (.store storage this)) + (protocol/store storage this)) INode (node-lim-key [_] @@ -437,7 +437,6 @@ (node-child this idx storage)))) (node-conj [_ cmp key storage] - (let [idx (binary-search-l cmp keys (dec (arrays/alength keys)) key) keys-l (arrays/alength keys)] (cond @@ -469,32 +468,32 @@ (let [new-keys (splice keys idx (inc idx) (arrays/array))] (rotate (Leaf. new-keys) root? left right)))))) -;; BTSet +;; PersistentSortedSet (declare conj disj btset-iter) -(def ^:private +(def ;; ^:const uninitialized-hash nil) -(def ^:private +(def ;; ^:const uninitialized-address nil) -(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] +(deftype PersistentSortedSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] Object (toString [this] (pr-str* this)) ICloneable - (-clone [_] (BTSet. storage root shift cnt comparator meta _hash _address)) + (-clone [_] (PersistentSortedSet. storage root shift cnt comparator meta _hash _address)) IWithMeta - (-with-meta [_ new-meta] (BTSet. storage root shift cnt comparator new-meta _hash _address)) + (-with-meta [_ new-meta] (PersistentSortedSet. storage root shift cnt comparator new-meta _hash _address)) IMeta (-meta [_] meta) IEmptyableCollection - (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) + (-empty [_] (PersistentSortedSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) IEquiv (-equiv [this other] @@ -514,10 +513,11 @@ IStore (store [_this storage*] - (assert (some? (or storage storage*))) - (when (nil? _address) - (set! _address (.store root storage))) - _address) + (let [storage (or storage storage*)] + (assert (some? storage)) + (when (nil? _address) + (set! _address (store root storage))) + _address)) ILookup (-lookup [_ k] @@ -569,25 +569,31 @@ (-pr-writer [this writer opts] (pr-sequential-writer writer pr-writer "#{" " " "}" opts (seq this)))) +(defn child + [node idx storage] + (if (instance? Node node) + (node-child node idx storage) + (arrays/aget (.-children node) idx))) + (defn- keys-for [set path] (loop [level (.-shift set) node (.-root set)] (if (pos? level) (recur (dec level) - (arrays/aget (.-children node) (path-get path level))) + (child node (path-get path level) (.-storage set))) (.-keys node)))) -(defn alter-btset [^BTSet set root shift cnt] - (BTSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) +(defn alter-btset [^PersistentSortedSet set root shift cnt] + (PersistentSortedSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) ;; iteration -(defn- -next-path [node ^number path ^number level] +(defn- -next-path [set node ^number path ^number level] (let [idx (path-get path level)] (if (pos? level) ;; inner node - (let [sub-path (-next-path (arrays/aget (.-children node) idx) path (dec level))] + (let [sub-path (-next-path set (child node idx (.-storage set)) path (dec level))] (if (nil? sub-path) ;; nested node overflow (if (< (inc idx) (arrays/alength (.-children node))) @@ -626,10 +632,10 @@ (if (neg? path) empty-path (or - (-next-path (.-root set) path (.-shift set)) + (-next-path set (.-root set) path (.-shift set)) (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) -(defn- -prev-path [node ^number path ^number level] +(defn- -prev-path [set node ^number path ^number level] (let [idx (path-get path level)] (cond ;; leaf overflow @@ -645,7 +651,7 @@ (-rpath node path level) :else - (let [path' (-prev-path (arrays/aget (.-children node) idx) path (dec level))] + (let [path' (-prev-path set (child node idx (.-storage set)) path (dec level))] (cond ;; no sub-overflow, keep current idx (some? path') @@ -657,7 +663,7 @@ ;; nested overflow, advance current idx, reset subsequent indexes :else - (let [path' (-rpath (arrays/aget (.-children node) (dec idx)) path (dec level))] + (let [path' (-rpath (child node (dec idx) (.-storage set)) path (dec level))] (path-set path' level (dec idx)))))))) (defn- prev-path @@ -667,7 +673,7 @@ (if (> (path-get path (inc (.-shift set))) 0) ;; overflow (-rpath (.-root set) path (.-shift set)) (or - (-prev-path (.-root set) path (.-shift set)) + (-prev-path set (.-root set) path (.-shift set)) (path-dec empty-path)))) (declare iter riter) @@ -726,7 +732,7 @@ (declare -seek* -rseek*) -(deftype Iter [^BTSet set left right keys idx] +(deftype Iter [^PersistentSortedSet set left right keys idx] IIter (-copy [_ l r] (Iter. set l r (keys-for set l) (path-get l 0))) @@ -843,7 +849,7 @@ ;; reverse iteration -(deftype ReverseIter [^BTSet set left right keys idx] +(deftype ReverseIter [^PersistentSortedSet set left right keys idx] IIter (-copy [_ l r] (ReverseIter. set l r (keys-for set r) (path-get r 0))) @@ -907,18 +913,18 @@ (-pr-writer [this writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) -(defn riter [^BTSet set left right] +(defn riter [^PersistentSortedSet set left right] (ReverseIter. set left right (keys-for set right) (path-get right 0))) ;; distance -(defn- -distance [^Node node left right level] +(defn- -distance [^PersistentSortedSet set ^Node node left right level] (let [idx-l (path-get left level) idx-r (path-get right level)] (if (pos? level) ;; inner node (if (== idx-l idx-r) - (-distance (arrays/aget (.-children node) idx-l) left right (dec level)) + (-distance set (child node idx-l (.-storage set)) left right (dec level)) (loop [level level res (- idx-r idx-l)] (if (== 0 level) @@ -926,7 +932,7 @@ (recur (dec level) (* res avg-len))))) (- idx-r idx-l)))) -(defn- distance [^BTSet set path-l path-r] +(defn- distance [^PersistentSortedSet set path-l path-r] (cond (path-eq path-l path-r) 0 @@ -938,7 +944,7 @@ 1 :else - (-distance (.-root set) path-l path-r (.-shift set)))) + (-distance set (.-root set) path-l path-r (.-shift set)))) (defn est-count [iter] (distance (.-set iter) (.-left iter) (.-right iter))) @@ -948,7 +954,7 @@ (defn- -seek* "Returns path to first element >= key, or -1 if all elements in a set < key" - [^BTSet set key comparator] + [^PersistentSortedSet set key comparator] (if (nil? key) empty-path (loop [node (.-root set) @@ -964,7 +970,7 @@ (let [keys (.-keys node) idx (binary-search-l comparator keys (- keys-l 2) key)] (recur - (arrays/aget (.-children node) idx) + (child node idx (.-storage set)) (path-set path level idx) (dec level)))))))) @@ -972,7 +978,7 @@ "Returns path to the first element that is > key. If all elements in a set are <= key, returns `(-rpath set) + 1`. It’s a virtual path that is bigger than any path in a tree" - [^BTSet set key comparator] + [^PersistentSortedSet set key comparator] (if (nil? key) (path-inc (-rpath (.-root set) empty-path (.-shift set))) (loop [node (.-root set) @@ -988,11 +994,11 @@ idx (binary-search-r comparator keys (- keys-l 2) key) res (path-set path level idx)] (recur - (arrays/aget (.-children node) idx) + (child node idx (.-storage set)) res (dec level)))))))) -(defn -slice [^BTSet set key-from key-to comparator] +(defn -slice [^PersistentSortedSet set key-from key-to comparator] (when-some [path (-seek* set key-from comparator)] (let [till-path (-rseek* set key-to comparator)] (when (path-lt path till-path) @@ -1042,7 +1048,7 @@ false (recur (inc i) e)))))))) -(defn- sorted-arr-distinct +(defn sorted-arr-distinct "Filter out repetitive values in a sorted array. Optimized for no-duplicates case" [arr cmp] @@ -1063,7 +1069,7 @@ (defn conj "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." - [^BTSet set key cmp] + [^PersistentSortedSet set key cmp] (let [roots (node-conj (.-root set) cmp key (.-storage set))] (cond ;; tree not changed @@ -1086,7 +1092,7 @@ (defn disj "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." - [^BTSet set key cmp] + [^PersistentSortedSet set key cmp] (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] (if (nil? new-roots) ;; nothing changed, key wasn't in the set set diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs index ae5fe7f..a3d5207 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -1,6 +1,6 @@ (ns me.tonsky.persistent-sorted-set.protocol) (defprotocol IStorage - (restore [address]) - (accessed [address]) - (store [node])) + (restore [this address]) + (accessed [this address]) + (store [this node])) From 5eb0cadf30193b953727cfd5ba22061c3f701c84 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 16:33:35 +0800 Subject: [PATCH 03/28] fix: add back javac-options --- project.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.clj b/project.clj index 52e5130..06b1ce9 100644 --- a/project.clj +++ b/project.clj @@ -14,7 +14,7 @@ :java-source-paths ["src-java"] :test-paths ["test-clojure"] - ;; :javac-options ["-Xlint:unchecked" "-Xlint:-options" "-target" "8" "-source" "8" "-bootclasspath" ~(str (or (System/getenv "JAVA8_HOME") (throw (Exception. "Please set JAVA8_HOME"))) "/jre/lib/rt.jar")] + :javac-options ["-Xlint:unchecked" "-Xlint:-options" "-target" "8" "-source" "8" "-bootclasspath" ~(str (or (System/getenv "JAVA8_HOME") (throw (Exception. "Please set JAVA8_HOME"))) "/jre/lib/rt.jar")] :jvm-opts ["-ea"] :profiles From 96244b1edafc7cae3c6aafe51cc00e1eecbf112e Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 16:35:03 +0800 Subject: [PATCH 04/28] Rename PersistentSortedSet back to BTSet --- .../me/tonsky/persistent_sorted_set.cljs | 24 ++++++------- .../me/tonsky/persistent_sorted_set/impl.cljs | 36 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 2a31724..65101aa 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -5,7 +5,7 @@ (:refer-clojure :exclude [conj disj sorted-set sorted-set-by]) (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.impl :as impl :refer [PersistentSortedSet]] + [me.tonsky.persistent-sorted-set.impl :as impl :refer [BTSet]] [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -17,9 +17,9 @@ "An iterator for part of the set with provided boundaries. `(slice set from to)` returns iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([^PersistentSortedSet set key-from key-to] + ([^BTSet set key-from key-to] (impl/-slice set key-from key-to (.-comparator set))) - ([^PersistentSortedSet set key-from key-to comparator] + ([^BTSet set key-from key-to comparator] ;; (js/console.trace) (impl/-slice set key-from key-to comparator))) @@ -28,11 +28,11 @@ "A reverse iterator for part of the set with provided boundaries. `(rslice set from to)` returns backwards iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." - ([^PersistentSortedSet set key] + ([^BTSet set key] (some-> (impl/-slice set key key (.-comparator set)) rseq)) - ([^PersistentSortedSet set key-from key-to] + ([^BTSet set key-from key-to] (some-> (impl/-slice set key-to key-from (.-comparator set)) rseq)) - ([^PersistentSortedSet set key-from key-to comparator] + ([^BTSet set key-from key-to comparator] (some-> (impl/-slice set key-to key-from comparator) rseq))) @@ -60,9 +60,9 @@ (loop [current-level leaves shift 0] (case (count current-level) - 0 (impl/PersistentSortedSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil + 0 (impl/BTSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil impl/uninitialized-hash impl/uninitialized-address) - 1 (impl/PersistentSortedSet. storage (first current-level) shift (arrays/alength arr) cmp nil + 1 (impl/BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil impl/uninitialized-hash impl/uninitialized-address) (recur (->> current-level @@ -79,11 +79,11 @@ (defn sorted-set* "Create a set with custom comparator, metadata and settings" [opts] - (impl/PersistentSortedSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) + (impl/BTSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) impl/uninitialized-hash impl/uninitialized-address)) (defn sorted-set-by - ([cmp] (impl/PersistentSortedSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil + ([cmp] (impl/BTSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil impl/uninitialized-hash impl/uninitialized-address)) ([cmp & keys] (from-sequential cmp keys))) @@ -99,7 +99,7 @@ (restore-by cmp address storage {})) ([cmp address ^IStorage storage {:keys [set-metadata]}] (when-let [root (protocol/restore storage address)] - (impl/PersistentSortedSet. storage root + (impl/BTSet. storage root (:shift set-metadata) (:count set-metadata) cmp nil impl/uninitialized-hash address)))) @@ -117,7 +117,7 @@ "Store each not-yet-stored node by calling IStorage::store and remembering returned address. Incremental, won’t store same node twice on subsequent calls. Returns root address. Remember it and use it for restore" - [^PersistentSortedSet set ^IStorage storage] + [^BTSet set ^IStorage storage] (impl/store set storage)) (defn settings [set] diff --git a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs index 6b58f63..af0e651 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs @@ -16,7 +16,7 @@ ; node.keys[i] == max(node.children[i].keys) ; All arrays are 16..32 elements, inclusive -; PersistentSortedSet: root :: Node or Leaf +; BTSet: root :: Node or Leaf ; shift :: depth - 1 ; cnt :: size of a set, integer, rolling ; comparator :: comparator used for ordering @@ -468,7 +468,7 @@ (let [new-keys (splice keys idx (inc idx) (arrays/array))] (rotate (Leaf. new-keys) root? left right)))))) -;; PersistentSortedSet +;; BTSet (declare conj disj btset-iter) @@ -479,21 +479,21 @@ ;; ^:const uninitialized-address nil) -(deftype PersistentSortedSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] +(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] Object (toString [this] (pr-str* this)) ICloneable - (-clone [_] (PersistentSortedSet. storage root shift cnt comparator meta _hash _address)) + (-clone [_] (BTSet. storage root shift cnt comparator meta _hash _address)) IWithMeta - (-with-meta [_ new-meta] (PersistentSortedSet. storage root shift cnt comparator new-meta _hash _address)) + (-with-meta [_ new-meta] (BTSet. storage root shift cnt comparator new-meta _hash _address)) IMeta (-meta [_] meta) IEmptyableCollection - (-empty [_] (PersistentSortedSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) + (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) IEquiv (-equiv [this other] @@ -584,8 +584,8 @@ (child node (path-get path level) (.-storage set))) (.-keys node)))) -(defn alter-btset [^PersistentSortedSet set root shift cnt] - (PersistentSortedSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) +(defn alter-btset [^BTSet set root shift cnt] + (BTSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) ;; iteration @@ -732,7 +732,7 @@ (declare -seek* -rseek*) -(deftype Iter [^PersistentSortedSet set left right keys idx] +(deftype Iter [^BTSet set left right keys idx] IIter (-copy [_ l r] (Iter. set l r (keys-for set l) (path-get l 0))) @@ -849,7 +849,7 @@ ;; reverse iteration -(deftype ReverseIter [^PersistentSortedSet set left right keys idx] +(deftype ReverseIter [^BTSet set left right keys idx] IIter (-copy [_ l r] (ReverseIter. set l r (keys-for set r) (path-get r 0))) @@ -913,12 +913,12 @@ (-pr-writer [this writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) -(defn riter [^PersistentSortedSet set left right] +(defn riter [^BTSet set left right] (ReverseIter. set left right (keys-for set right) (path-get right 0))) ;; distance -(defn- -distance [^PersistentSortedSet set ^Node node left right level] +(defn- -distance [^BTSet set ^Node node left right level] (let [idx-l (path-get left level) idx-r (path-get right level)] (if (pos? level) @@ -932,7 +932,7 @@ (recur (dec level) (* res avg-len))))) (- idx-r idx-l)))) -(defn- distance [^PersistentSortedSet set path-l path-r] +(defn- distance [^BTSet set path-l path-r] (cond (path-eq path-l path-r) 0 @@ -954,7 +954,7 @@ (defn- -seek* "Returns path to first element >= key, or -1 if all elements in a set < key" - [^PersistentSortedSet set key comparator] + [^BTSet set key comparator] (if (nil? key) empty-path (loop [node (.-root set) @@ -978,7 +978,7 @@ "Returns path to the first element that is > key. If all elements in a set are <= key, returns `(-rpath set) + 1`. It’s a virtual path that is bigger than any path in a tree" - [^PersistentSortedSet set key comparator] + [^BTSet set key comparator] (if (nil? key) (path-inc (-rpath (.-root set) empty-path (.-shift set))) (loop [node (.-root set) @@ -998,7 +998,7 @@ res (dec level)))))))) -(defn -slice [^PersistentSortedSet set key-from key-to comparator] +(defn -slice [^BTSet set key-from key-to comparator] (when-some [path (-seek* set key-from comparator)] (let [till-path (-rseek* set key-to comparator)] (when (path-lt path till-path) @@ -1069,7 +1069,7 @@ (defn conj "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." - [^PersistentSortedSet set key cmp] + [^BTSet set key cmp] (let [roots (node-conj (.-root set) cmp key (.-storage set))] (cond ;; tree not changed @@ -1092,7 +1092,7 @@ (defn disj "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." - [^PersistentSortedSet set key cmp] + [^BTSet set key cmp] (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] (if (nil? new-roots) ;; nothing changed, key wasn't in the set set From fc8402eee2f14b2f91360a56dd47d6ed336fb7a8 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 16:56:01 +0800 Subject: [PATCH 05/28] def BTSet --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 65101aa..3db6812 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -12,6 +12,7 @@ (def conj impl/conj) (def disj impl/disj) +(def BTSet BTset) (defn slice "An iterator for part of the set with provided boundaries. From f1aa6bdb6f4f1078cb21928d9a79925c2be3f03c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 17:01:23 +0800 Subject: [PATCH 06/28] fix typo --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 3db6812..0f44ac2 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -5,14 +5,14 @@ (:refer-clojure :exclude [conj disj sorted-set sorted-set-by]) (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.impl :as impl :refer [BTSet]] + [me.tonsky.persistent-sorted-set.impl :as impl] [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) (def conj impl/conj) (def disj impl/disj) -(def BTSet BTset) +(def BTSet impl/BTSet) (defn slice "An iterator for part of the set with provided boundaries. From 8020ddbe72cc270dd07db096b446b6d14c856775 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Sep 2023 18:28:37 +0800 Subject: [PATCH 07/28] add target classes --- .gitignore | 3 +-- .../me/tonsky/persistent_sorted_set/ANode.class | Bin 0 -> 5029 bytes .../APersistentSortedSet.class | Bin 0 -> 3628 bytes .../persistent_sorted_set/ArrayUtil.class | Bin 0 -> 1293 bytes .../tonsky/persistent_sorted_set/Branch.class | Bin 0 -> 16732 bytes .../me/tonsky/persistent_sorted_set/Chunk.class | Bin 0 -> 3103 bytes .../IPersistentSortedSet.class | Bin 0 -> 1337 bytes .../me/tonsky/persistent_sorted_set/ISeek.class | Bin 0 -> 405 bytes .../tonsky/persistent_sorted_set/IStorage.class | Bin 0 -> 654 bytes .../tonsky/persistent_sorted_set/JavaIter.class | Bin 0 -> 777 bytes .../me/tonsky/persistent_sorted_set/Leaf.class | Bin 0 -> 7246 bytes .../PersistentSortedSet.class | Bin 0 -> 12865 bytes .../tonsky/persistent_sorted_set/RefType.class | Bin 0 -> 1008 bytes .../me/tonsky/persistent_sorted_set/Seq.class | Bin 0 -> 6952 bytes .../persistent_sorted_set/Settings$1.class | Bin 0 -> 795 bytes .../tonsky/persistent_sorted_set/Settings.class | Bin 0 -> 3111 bytes .../tonsky/persistent_sorted_set/Stitch.class | Bin 0 -> 771 bytes ...n.core.classpath.extract-native-dependencies | 1 + 18 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 target/classes/me/tonsky/persistent_sorted_set/ANode.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/APersistentSortedSet.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/ArrayUtil.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Branch.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Chunk.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/ISeek.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/IStorage.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/JavaIter.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Leaf.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/RefType.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Seq.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Settings$1.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Settings.class create mode 100644 target/classes/me/tonsky/persistent_sorted_set/Stitch.class create mode 100644 target/stale/leiningen.core.classpath.extract-native-dependencies diff --git a/.gitignore b/.gitignore index 4e8bbdb..b862ed8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ .nrepl-port .lein-* -target .DS_Store .idea pom.xml bench-clojure/*.edn node_modules -.shadow-cljs \ No newline at end of file +.shadow-cljs diff --git a/target/classes/me/tonsky/persistent_sorted_set/ANode.class b/target/classes/me/tonsky/persistent_sorted_set/ANode.class new file mode 100644 index 0000000000000000000000000000000000000000..17b71bbcaff2fd8452416adca740ed80da042248 GIT binary patch literal 5029 zcmcInOLH5?5&i~PU_o#xJ|R+MXvr}xfdC~qvMf6$4U?io8xcgsrYu`hY-$NCDTu&= z*-?>@*%qTpgbfoQI&FZRc`qsIpvar<9xHbcmzNSfU0tt+1Z`$ z{`%|gnclzs`!DwaJc(aNF#){?`>>`W7e&xp#X!xRu1fzaxv zv11DiU&>z9uj#3xZf>N`u3a^9&a6PsN@2s)oo&ky2wysP+SrjVS8c;_3g!mI64{cG za>}N?xs%#5EW2PkhUu)@Wy>-0a$jo2-%G*aDc!aW%PDaG$%3t~6^%SmT%_)~f?06V z0t@j>3m}~vnLbbN7R!0!?avlWV|lx@W?1K>DS^HfN6&4Z(YM^Siv23y6gWncu4mm@ z3Az*pC4Mqng9-HJvvKJ9ZCF6lSior*{MP(@(dDHKy#(np^5HNBj3#nb#2 zG4ch+bBDGib+(~fxhn$6c(ZBmjjMaHT-wqt-6>nM67r*Q&u2I24I3)fMXflG_5<%lj*i`SXgF&$;Iv*seHQG>xF!t(Cijp(oH}s zzBb^UJLfBzP0zN)Q%&*Cp+}tZies^c8gs^bOXotl^0rBq569cu&#W*E`i9{KHdHjO z8AT>au53Db!DNxX)P;&>HtU$F&mpGX*)H^UkZsf$)bl(Me%FQGPVu%Su@=NjZTD6Z z@Ut#}wkM#i*$06bwv1Bw8f)~QU0`iNYP<6FC~510zpd4&(@mXq9-@DA;kFl||G%fz zGpC)Y2MKu4b}WHsyKp;Ge`i(CpT6sQannBp4IU!hXw2q{<*RHOylx`1WX{TaIpp6K z=`OI=Id`v&+!x6g<{^YW?NK1tv&KNl0H58zg1KX)#<8} z44cOFtn9*$@J{MH5qKr5w!Ut-qKWoIcm*eK^2l7S=yzG{9Ekm7_t$5gE7fR3SIXN~ z&R8nQ7Axx7J0iK#a1gI*n8Kq1V>R*?DlOnC%PLzMzJW;%4`IKCZ{iUZZ)tcNKhp4H zyrZ#8eoDi;cu&Rq8a}{J1g6?Cl{NHrY5AuD2ixsEVd-Y>iroF7z-SHPf2D!|G%Vn} z98PHX8B!WPLQ2KYHT(jN8}CR#KVFbPHVq+FHTXt(_IW=bUdX+ngre zn}$Bq?N>%zIvTz($#ZVMN_>avOU%!@W&<+ApYeCreC?vJSP;ui-JyRDJgCC{H8;c9 z<}K+t_GtWfGHz_?X5OvdJ+(wQqdV;2gZz!)F&QWM^rpY|d9lbJCxN|r!!B4xzFI>3 zpK-Qr3mr}V8Cnp>!?Eyh&>IUWm(w?)#<>0F*MFasmOL*HqKD5JMDQK#!D$9R%d|R! zB$mnjcj@8_wEUv${Q#7&5E0@lB!u`Ef`K}G&s6b!_Dj!t+Gmflb{krYG2cQBH?gIF!q zz4R(lX$b=`6@f*ZM3wbvuCokNWxdSxS%yo>y})qE zc?IXH-(TXIH(+)BGSm7M66rynXUHVvQ9_Js%JTdh+II)z hpF&k`LJ9s6f!dMfX;3Q~w0S}F9#5|^5hP!i{{tZ%Wh%;VGyYzofgD6ezi+IMaM|ybZa6xe8O;C)AbC?8`TRb1+no=zMx?F$Yp&>PZm8hmmIY71>Mm-+u?qF z+MKX-j{t&I&8ctfsBtaTPv6=;(=xq&1v}!8%~};E)qRH54BA;HY8f#t<5)3&(QwA} zi#e|B{U3<4h55k2ZLEoRKejU7n{lORPAOOx532NU?fAvZM#f8ZpHtA3_Vmmo?=CDX zI$@B5u4>*30aO&3VM@;pFlJYkn1vzT3zvA&rZr_x@>*(zdZ9ZS?P^zZo@9=I3nDiV$c`eDHa9EAIKFqjlc*$x9RM1*>Ipk2m zqOv6+k)kPE3t3dqQMO!8V`b2Az@vi2rK8RgNnd}lg1Q9|0e7sr1QEz|7S)%6kIFgB zp^*{2QU0?XUWqtbbn-=K8%dR>Ic?ca)^H3qU)%gKGh1d#rbSR`#dKf_Q$*=!T|V1k zWz|q|Dw7;DuCkz*5;?>v;wq>PleB##E*!4KNnu>FD{{F$I^=H znK0-2p5GChMosiq&u@s0_TEDcOS^;G?_@2}dk5+*x<}^K(Z(89u!eT#Tgl?V?@0Xv zEKyk?X8~RebI7?O+-^?v+(F$9s696kQ>3l{E~sm@51}HW?N_mbp*x=upmQO>u6Y&6 z&6ZWrTUo)nN`T$ZSiy#c0MAxIS0%t6q{1WY8_fWB1eh=u>;|qxZ-hvlj0}bd;n9m-KZ^V71OFhH~oIG=pUq`ffh_E75Tq zOFBw~u0$=v2PvzAoE<|Ppocim4s*r~`y@9Z`VX2Fs_H)4Wu}bI-SFy=k6M{2%LQ^i g8>9R>hU2tyV)jEvhd?l4GemTmbJ1|hG#U=TN)hRHCpY1!t}PHV?>>-tWUBBJ564;U)> zmz#U~8@=4r?cMUTojud=SF6>qpcWEjh}EoJTlYIIVa~19R|e6DqF(2ieAM{JFl^Zk z^PSnKJC9sfCmrkgzQm2D?s==5M$_3N+^)20H-T`pzLPz{45^y08~ac6cF*2W!}5H~ zHhhNp9(2dIn&l0r)z)3zcihzwJ6e_DVfgX2LDtGu5b>JRaSija#S1+gIDAgz3%t-t z#1NNpH;Gxy$;c<6p&+A}#5_t#ETAmoUJ{E~V$fP<*>`Mj|DfD9U5^}@w!h^$u5UKB zJku{*S*kEqQbcvC zag0dm^F>OWj)4*qggrv52-370#WjNBOl?Y*X{?lN2K7NHcQE&d6B| zV^DhHUGgn*c41wth-6;w?$MyR8X&i+SXQUH><5{wXVJQT5rW*?)&}c%n~w~#O1lqy*zN{oPYWD z?O%B2v+X;GXgp5}P?&z;r)s*=qN@UA(A5FDhJKij-s|M^dg)syj~nE1qm2KFMK=ZM zW?Cj3?Ro|A#k%j35~;R_c1Ers((+P1Df&x3r1gq)yqZrYy&j-9=uIZ?;1na%_yi_@W9zc^Xnb{N48s@ApX!X4j>XN( zTiY zTfZvS+11(=kG01acXf2eV=arjV)6RMSUld^zN~9P^$ASo^o|x#n=`w$JvL|c%B8W+ z`9cv>R%1NcyyBSXD*bKI9E<+OG*2j-e{^i^gcN%j$Qm3C^#D4!8FQ*;Ir3i}Z*8le z4IU@-`5u%Qmh=HQQFA$YU$_&KT1D5OP~^ngyI>ivQHT+RtEw}|jwyupBpMh`n%(7B zRqx9vQ$?S~bSjxF7l2@#<<9SIxFNKsVH}l^M*66X69F(eFCBtM+dI?zs(ytut1;fu z8C@2e0Mhys=+rr|R`^rP*8tX0c#@mRKyw`AFfft^PWtHsh1rLm0pjR0kIB>AvAR9Z zR5u8*RU_ni-0){q$yc^xz|)hF%-7t}9*?%R!<3I71mwPvGtH!@cdT3$?To@CCR8IN z9y(-O8BxsG~f(OcMJmVK_*=okOs zX+{3(jIHcggWU1aAZX1X-GRAWzbrT+%xDmU3EczL71IZUVB1mA*UxFf^!t_87g2cD z8PT>CX$kF!K?q=Wb6dx0$cIh|HS5TBU5#bH80&P4jr6SBXQ&p_`2B-OCa}yieG{A8 z+$s}M9R*#8+F&*Xk>Nqb%&v7#n!>{#8|96PdA3e4cN&S-n{@-b3J zhiE>%6QX(4$W)RltfwXl<(ZwG9i1}ha*N&#(IxaAQ|`2mjySXvU3E+>zPzKQE5}Rk z`{;uZeMtY1vHuaGqiJ@Cj-fdrI!+${N&kg;DfkYn7JU?=e-hw-OiN_q@ge$e8U8Ps z{R!;}(WlfMqQ$g?X~ckBHbi^rvk>*5mSdKOVk1Od0MDk-6{F=LI*nF@sDoC8*u&ls z#c8$0R)~G{R*24^Gehjh8wWTT;t*%a_?02f<{YMx{So1aHpIEY)qFZ>08<*{t?_11 zHJ|fBoX-UzF66MqMIkQck`UWm8lrRP+aWHaPK(P!T)_tjBd0QzrX)HKbw=yT*vvDV zW2JJ7c<>0O;uN@E-ho9@ zAjBhi6q7o;GDP2@i+x-Lb#paLf@^$SD<7kMTqnO{#7-{talL#TWbwfv9?L~VDLt}oPi@gM=q&VCG5AkW7XPYPo0N_KH%15nl)=y=5?H3mfL!%jfn$SB-Y*{ z>v^$LW7sfg2G)_88d+i)WrV zYd+Fxtfe*XY*ZkP$==Dz=$S}l@UFySI6aHPAe!~U0>T`%U%Jz02+cW5b%sI$KQh`3 zF~SKls$bG5;ICRcwGDekM`Ro`fW@s7fl(`HSjV=5)`=Nt?SC1V1XSIh(6x^ok3AIl zhcF<8SB7wraW`r|MsIaRPmO73uIax7bL>lGoxifR-N{;M;&!}StL2zz95!b_%W!Gz z7s;Gn;lvF*qKn*pOG}tXYx|mx6_997ufBOOrQp(zj<#5|eR@>lM=zL(5C!{!|MV+N zy*ZH{T9bb1jjmc1Ylrt>1)d7V36lFXCCWi)g(DJt4@;lsLhUnaLON;}_P+j*Y%X$y zT`A!nC?w9yPiFVX9L)``#Q0vZX}=u7sp z>Q$)PX0^A+IyC~>t-CH=2QCb!@;oZq-qIH98tzO?FXH>M)obGXwX0$ohG+1=bk({b zrbpY_8e8MB2^LR8UD}7gOtdi_@cH9tC^pu^I6p8cPpW6Uu@umF9*cKHbMb~+nkctK z%5}6Aq6IZG`cA;tkU&h~C4-b`v8<(8QnDEJT=3$~KEQ#TE>&0SPsn>)%k zP?7Z5<~H(_ZznI&R5u_5k^6Z1Vx~*Vvv1C~&W)tO?Iyz5#?m`V1Q*&HM2zv5`PyG4YyrR7iqpu!c+K) zgD(@m2iKIB*KMQVBNVF3%$-9X3w3y;5mzd#Q59uF#6zi;M&g8{mg?vr8l#EIBkn#}nS$j1uC z1vVAGj1eU@_mh7&<<#IecL$gfveC2&eM&>h#>oiLI3RDJ0y<1nY7iH=V50!Dl$u?z z%Q0BMHaK9j@skIj+bDmNwCs@Y^%Ru%M4KUP+o(XklSqp+rxSs666NEN<^VbZ;+m?F zo22p+*js=NOAr_PLi`Hbwc5Z+jIvuPSSH|aNi!bJuZJ}~Wn)t)<=ltpNLa;DFql~| zmZLQ~4;clP&*gTR#<$$XcMYf%;WYJ&&l=r6+9G#-=0PRx8g0|xzhIz1>M(DMPrh&raX_2PWOJ;!;7~>M!3PQ2IbO_~g z@Fu3S!zHvFf++{KbiwGZTA^bXh2*Dv>~7c^$aTK`@}2ZSp5u%Z`@zVFiQgjI+(IRf zQg}|?ZaPfEE0p=YCJF|$JV>^pIgm)H&{0$;3~!_%(z(kq%JQ+hC?`3(Oi^7*9u1TQ zZWcoxOQ5Nx&{Q*|(gIhH(K1>NzFV=LJdJ*UxAnA=Hc>l0Os8u(<&wX^3g=keRAEuL zw4%(FSeusEN>{b>pjc6DWlb4xp>k1MU^j^xbA)L9Eow~BFM-$P`$LKC1P1Iu29E6B?LT*BV$n$b}5kOnhs-asDn zp0F7=TN*rJp$H+tW89++rql>~Zl#dZobI-@VULJJfK;Yr;e^y{5bu9TIrJkcqH7UB ze~gH6ohBzlW;f+#0QdV8*CBC}ciD;RQxb<0b%gfj#a(G3uOg8WHZ~GGnITGMV(Dv2ThJ}MWC?8Y&5v}t`a3jr>&s|h3Kgsz4I}rAA zBidjp676*k*$AsaOLA1u4hoh=hvtWOKPr6DosubxRV0u$X(1b6(UJZb$ABznmu!gd3J+O`oq5UzI8cXQ&rO+F)29SiDr6D`OvN@8Y;bjj*KnbXz2N|crAzRx`5_paV?$MT>Fo&=Y z78A0w9h+HTCfoh6qbxhewIx@GX~HmMrUs~K47xNzNCP&ZbOFwG1t{Va&>=?MfCnm?+XbIp<1UIehnd#=qCuRHzC{J zjOD-vDxq6ZyWWN>>2_2f8+ckOZ*JI%rDVj_!WAE-=bIfeR@qXy{w!=v_65-cvK^ebq=Gs3!V{iqc1F1^rW@>p{=A7Y-uhnh=yoOvoYn5%ibxt1g5 z**w9#kSCg#@g(yqo^0OCQ_OpLs=1A)nLBy9`7F;cpXVcWihPwyBt3RhF;ZkVm0Pqn z*iA>FHrhi*H`U?=eOMsvrAlX+RGN1*UVJ?$Z+uv*Sag;}A&b6c(Lz~FttINF>(Q}? zW^_{+Ynmr0JJYycE3XBFBa@RH1U+;p1v1Yd>Y=*^3T}i0&dvL0Bk$t?!^!(ab5XXG z5(S^Fdz7jpmTk(9uV%E3GGhp0vHwQIalcdmc!w*ZM$uJ4BK!L#`|FOh>>u{ugw$6X z_Dd+P*eJU<03x%^mJM#tl0;9?4s4@?8$xzaezIhmt{ikm^w`L2ViUIGbeQzCY~W)g_5n+-2mz7nP$cR|zR5NC0MN5fGGF83f`(GK3LIRec0oAjdK@xs#B$S7 z$T(#7Cc8l5u!~Oy<)#gSZuFoO<^o^__%zIkN&p?uZ6CZ@wkE(q(!-%Z8a`1?fk|C9x2OgYZpIcTkz3vp4gX^pSSW) z6T(mQ>`w*KpKIt*Qhj|rIuY>LrIA`3wc90*9i-_-_oor@zo#srB(M)9U#<4Im-2j2 zUN}#d1euhV=-Hq0z7GF^%jpG*rYW)Tz|M($XH=djbsziwsvWCPBR7&Hef^#W2L->d z1ksF0VPDr{?RjdH?UTG9?x%;|qQd$EzSxi-&c|x0AeQY2pzFpKSCJp>~JpLSPS&h`zV#ZGi_LMT4^q$#weLa2t(E06&^ zRINFIYC(|rst_cIk$ZIy+(P%1CVNsYeyKxw!o^GM5}D~wr(Eyc_OY9i%mKNiS3oYw z9FR4{UVI=Hx&awNbON$VN?>17`Z{?@GDPF8cZf!V8>038VLDFNPQv2lMTrpY+ogsi ze0d+U&{h*JY-vc@gKHo-RB$#L!mfc7_cjM`l2Q3C-H!5I(wz?wEyn6n?+y?c=?tJ- z`+pcpM?R4Ea9C4m7mKZl8HmfLLc&G8m2KO$^!HJ%omsVZFV)(aRjVQPqS6+UfW)CP zgQ6WW`l{7Vsugd&)rtmJt%9#t<^?q11GJ=6;2;hSN$Dh=L`J&3fwW%7QPKm6Hjv^* zR7wpC^2cwb!*(W?uZ9;(OyW7@8G@j-=jTiZ+#(R9XN&IuO zm^YbYc(XZ)?=$D{{pQL1fO$G^F~7}Q&8vBvc^hvx@8gHe$N6FNxBQ6t8t?EB@AO#w zsAmK}<{8gVcqZ|ap4q(1b29JtwDD7(bNFe`CH#!%I{t;{F8-D05q{3|0zdD0mtXMg z;oo`v{CjT&|G`_sfAmh~KY8c!i{2=|-Djmvt3?WZl63v~J;#tvmT& z)`R?s^$dS%y~KO1clk4GFZcLDihPxdeRWFtrYghNs64(!%IjOHe7@Dn@4G~WeAlWh z-|Z^fw^ijJ;^g|?P#{aGw?cby70)90nP@(DrHR_iUfA434u4%#K6aDQqWXWf&n!(Xsc<#Dm6WL zxSA21u4V@3t0RM_s&542YF6-U_08bbYIbm)IwrVD%?&<*jSif4S}oM}84c45>S_)Z zTDep7qP_%AdF#PBVNTj|JB&7GIqy!<-(h!cA1pMcV7H)%@zaB?^SlAhWSl@?0u5$@ zV*JOe!OuDR3(Ps@3(V=J5=bIq(df$39vtN2h#|Qf@_9nUl~ByVUy|P*>_iNHrqU2X z#))4V0?vxMsRm$5OFtq1UYe%47+u+egHUiWaE>st7gJwo^1gh1DIj|&ubU1P*8W8! z_R=bsGq5)0{{?F^9oB~LrNu652lr6@|9#emCABuLs0SyDh*SeLL+x7_EXGJwS!-wa z;7HT{VgLk2!bQIXh<$DPD*&Y}`$$oL53aQ251c*OKwUdXc|ZgyiGE5m#KuYojuI+K zK}Z+rmw~abOuq_PduWJjc#|r6aHKsbvAVm==VF%`_u283!}P0gi9jx1Oido{A6|o_ z{c#afZa)={w#^6WaD_9H$U5?F)VDu;=507hbuIvm-a-@f`TvutD^4THk0Y8BDWDcm zo?1vnY7q@rC($UinCjIMilA?bYSw%ExYN>23OQ;xcIfv|{vHs+a?{?~1gB?kI4`#x z&c!xvVUsaQ-wD}3D`fp4w>D1FyWO(;isRkn-4ER;TTJ?jNbhD@3!@pATGpi|)CgR( z;%W%)G)$EH6c;JnYPhDh=0TdIaB!5k+u$&_j67;N<)~IFQKwNIj%a46HabPEq|?!U zhFYbW%OY<#_IF1V*_tosdfG+c5C1Q~75_cBdLWk%7Cl6har5kmIfmRn05P~_F|#gz z$`+d1WXL51TrbP5%bzCS7kNP0KlI^p*c^*pd7R@{)Rvpr8=k(MW)SH?CI)T8U4)tY z(jF(DT1|y&4VB?oX}CI*YVmupT1#`$euDazroDj7y%e-)GIW!V+Zy0tIB3TIS8z4+ zCNv}0=+3JJlBrF#qlM`0IC#_O)7XgCI&5%6Zc<>`BzI^6pgiKWy}O(Pbva9rdpQx0 zT&F9sJ+(E$)wsIt^bG-c)J7_-t#jJ8<3de}#T!$07s0(XQseHVv} zm(k^}(hkM1{K?C&-a_A8=N?qtM=C{&b8p3`;d)4grr@Hi3+sFIeauyK1s{gr0BX(# fj_`QglKBz7D}nP`!2hv+zNnus>*rtf^R@p0Qm;I~ literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/Chunk.class b/target/classes/me/tonsky/persistent_sorted_set/Chunk.class new file mode 100644 index 0000000000000000000000000000000000000000..69a553c58f9300bbe1a86681665b5f9b310519ce GIT binary patch literal 3103 zcma)8TT>im8h)DTp=X8;0m5*OOa#Jk3W_FVC1(U3Lr4r5g{aW!>4Xk5-Pqm3hFy2n zTCetUuQu5&PrJ&^USu_d(o~t<8!z?;c)S0@N{P?g4NMZK!h-$|@Asa*=RJJ$zn{PR z9>6d@N}&t6Bv-E`(IUez&dZ9!FeYThVHg*Zn8Zcdau~)XS#g-e>$1Hp!&DWf@rE2+ zN#Z0fX?Qb%w^BHOnG}h$8m=brb_$J{(eRFt-mStM<~0~89KJFKtt&LgJJaj3|iD zEm|wSf&*74ZW_zRV8L*13|_o;(=z2$owM_|6eu}aT25Xj!#7EpRgjol7L~3;=}yD< zEid54f7$kpYXytEl_zY+4o)e^X7}`N%6f04=L+?Yx_L{%{t4T$CQHR@mNzZh6zrP{ z40G{aV_6K27^ zS@Nt1s@&*8$yprHP|(oJD|=SHWYXQ^*{Ar%ospdg_v|Doh&jQ6f^;^w>uGm3w^L|W z&baB89Qw>#Fn);>W??fNsVTSQnbx>1xTeAj9FgGb7{W_Bx{=j!1cN&I(66HiM}w=Q z2faF;6Uved@4?gIBhXONv5ep97(l;*mU0$drggh$jomV>CCLm0=cnCe!?S&Jp+B?Y zmNJHCW$eK6jKFqoWDF;tDVBoGO(yUR$G4fp8D?+B2r|KfmGO{CM-z%tpA_*>s zLC@=WAAivCyg>Id#uPM_{W`l9L}oFc4*#f{wj> zWf>`WVK1&-szN$}6P8~0p0#>*l8o#Qdm+Cwz@KHw7@n>4Dyz5vyp7)jAV0(2dv~-WcUPhLBU~Y zsF7mjt5qY_8O|)(aAq-vGwV2)qC<4kn0PtBj?s`>|XxA8#xC)&B)&tc;t%}$4Ne|HJFTCFIN^L@Utckm28Lp#3Y>w1jZ+EBScftZFC z4Z{l1@e{O1NGP0Dp0f)?s9}~~XrvsDP!q`d1)PHdR+`Y&_NLUy2`N zEOs?@eig}Zk>^+|C;aqvwBDtdZAcd3t6CfJXwI3CL7x8y$GTaMZM<3JgKb4)}DpUMEH z340rX@8plaKAhs}G|ux@m)ZVjxi|St1Q^4gFpfVn>;A(1Uy%zDYewP`IyH1XLbY=E zC&VirK?@fIg|9~h4IGBNf(B0uG+(3r4jNnkhN@O>GoPdC0S?WqK9Cs*8AwV z+h0~1rNtDLP7|ItSd&+_lCy!z_*ibLoS;fc&JIx-IV~+j9M|&d{J&(Dy{yR=C0*q5 zEVW-{rN6zUxsi!JzFkHyhI)b8x|aLsyu;*Jg&O+??d9^R3|T5e`8X={Gm?d`gl1m- E1^ze5SpWb4 literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class b/target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class new file mode 100644 index 0000000000000000000000000000000000000000..c396ec60ec1859800d383d0c80afe75264cf7e90 GIT binary patch literal 1337 zcma)5U2oD*7=BJ+bTF{OfNgF%KhRZX?2U=hk|k!z5+ZYp-IX^>DJM%AP>v0S z`~}{6J1_hJ{wU*n3XT>E@#eJe^PcBHdF-lqtm27^rz)PQ zs4=YgJ=ft3Cwi-EU)x5{9$XkF?Jjo$a}hEM+@5jh^@p~w15cPQTTajGjszdGn-;$^ z8M2mpF|Y%I85;W8M}A|5$5?enqY=6e7D#2pB+GaIm1S~1=|itWsc0>Cz(0-pZ7$C2 zc8`$VNOH;4l+UjiDtfXvEkbuy|B~!4Sb^xDKuloTX z1mhbP4_?b{%Hsw0^2j2WM*%s8H+^md-oU@SF@|@Xb3c?l_jzC>Nip1?T}_5^Y~?0( zFl@x&X(0^jG0!+phGGnrn@hzxcdN~?Nkb2#&n&E&Vf3C>kU|D3R_U>=rfKJq1H&3w z3i4!A(NmlLvbaYcjr@DWPSIDZ@4u?wf-&4eN#h4WR4<(w`huj!VnwnFOUbEuay~*X zVVxxYMV8W9ov|g6HM(A2N=k@WB2o#JNu(y#s8FQZ_BWK{(Y~V?0VYaHr0@!=#8jbF nj#X@s;3l>rva)rx9VxpXEkeppfdtuVTvnNM=mEA#P{Gb$+-o#g literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/ISeek.class b/target/classes/me/tonsky/persistent_sorted_set/ISeek.class new file mode 100644 index 0000000000000000000000000000000000000000..5d3825ba52d7defd32c777a104f06caa30ecdc99 GIT binary patch literal 405 zcmZutO;3YB5PeIrSc|q+;~y}_uSAX>hzEi;F+njD&kb!S21?nwEGGUd<4q6#0DqKm zN>3Ut^JZt>?7TPMKc8O!F0tQ%jqNU4*s;*F&=)XEqPf7;NsuKkiIXSAlM_xdnre4l zsdSn literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/JavaIter.class b/target/classes/me/tonsky/persistent_sorted_set/JavaIter.class new file mode 100644 index 0000000000000000000000000000000000000000..608e829f303151046a19f67756409ed83f02cf8e GIT binary patch literal 777 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 F{{xU(k8%J2 literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class b/target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class new file mode 100644 index 0000000000000000000000000000000000000000..30f1d5e9d1bf2e75867cc425ee5c30f4f3b9cef9 GIT binary patch literal 12865 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 Tg6r@H?lOY>p8tF4BY6K0Q%8wf literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/Settings$1.class b/target/classes/me/tonsky/persistent_sorted_set/Settings$1.class new file mode 100644 index 0000000000000000000000000000000000000000..ff1d90c221ed23773b63bbfedb4b264db42114e2 GIT binary patch literal 795 zcmb7CTWi!n6#iy6O?G2yqgK1tORL7cR54f(9~42#u9kLpp;_!hA4)bEyJ@pCOJ=sx z7ypjWDu{^m2l%7JleDxif)$4Io$p*{=A2)@e;fmNjz>0bV4;c%YE{y1=5*1*t(@G> z#T^TGEi5yvdclW8N26{y@y6WKQmBI?Z^AQ`D9wf5RWj2&-c?+CTm1dtXu=svJL?RU zmr&*tq22A?gkqXfiD3*?t;|J-;V| zX*B92JdN8~CNl@EJZuGZhLth*Pu}SN^^JaTdK>R4gR?FK&(_jVDXtjY^A`Q#K94j* z^P=S#8vhDC`(5)eOs9Fn$IaHx<^|JUFqpw$tKWOW(E5)gOJ;E*!n7D7P1OkcodLmi z`}KyqObb+{g<@_k|L1FuvwIE!zKL+%x#N2-7^7$jG)k~xJGDDDd2ix Ona2{LUEG%xxAF%s;KMTj literal 0 HcmV?d00001 diff --git a/target/classes/me/tonsky/persistent_sorted_set/Settings.class b/target/classes/me/tonsky/persistent_sorted_set/Settings.class new file mode 100644 index 0000000000000000000000000000000000000000..baf639b077ea6200a7269f685577dfcbbde62bf7 GIT binary patch literal 3111 zcmb7GYf}?f7=BI?*btVxfRc(@BZhEEwN@>lMG%w<+7i5=ZRwI6VIkS1yBn2O`#Y*L zoqlU)I{jd6g&CQt?U#0@KdS9>c0&?@sR*-Y&z^lR&-=WW&ENmL_zl1rd>z3-T&#nR z3AIeBQ!hVK=O3%xY#q9hico7#!}SQ- za6`k*2-?;CRx7=I5VQ5MHq5-MO`b6zHAK$)uaNoYl2tLE4V#xYBYLoV@MI%z`7`Ld*}pEJ|R3*K!kpU+CeqE=f>fw5haPA-~`v6z(^ z!niF^J7QXjKEp7ZrcFy;E#?-bJ*N-|1moW8_>{U0ya50u z-;i>R^y-XJ@Qi6-K&?5`8rx1bosjOP9Z9H@4+@5rnU>&GLOKWH zw_mTHlCH~YaG01#CEtuFXRJ!5S~8Vs($f?7iOw3Qd&CWw=F~ zN&_QvLpx>8$LHpD*KM$+k*RVT9!Y0QB@s#Gi*{O0nkrWfTc`(B;R)1Vm5w_jRW=RClq_?ZIlMI!e(p9mocJ1yAWzMZAx5U_0d$Z>pSm1D=>O(lKqBDOWPbl3R2% z+*eutQXp1QF|B*~RhjhN_~H1Ugf$HhIg6+B`PE`U;G1faN=kkOUFO8fN+;$^4p%`e zVXr!QMN#E;7x6n)^1Do!l8JF6n@yRn9MTQ3I>5ff21FwI9Dzjta|9Cu&k_2W=lnQw?LZCJ zdP?f3<0rk5|7~dUEcKTx9Yv5kKuZM025KtG5xM|cQA-u&dZ0={z0nfU-4r^g(CH}* zmK0nX3R2;i!W#V+T1x;UqW^nn&rmy41zwcE2R!gSgbW-Y_%4JI^@wWWPm*>L)kUff zk*FA{N>Zu?fqy9w)SwHEzgVasZ~`Yw2o4c}ALFkGgdYAx4WFP|@3@ESyJg{nd{V+S z2?}o)P4LyJvS_p<8f*@47gWK&htphDx~gpkpKf!nQ1Y8l5*^}Qx$mU=(4S~gw{>Wb zp@n`xD5(7S{P@^Ud-2yG&_1g4&ftBn%H;|)FM*@a5PpKz#5(F8qka>S`3=;+^bVV& zq3!_Hom$HXYn_s^EezupijKftM8Y{rf#K!BV=!PMf0tE6l}=hocsu zm((!HO3gmXql~3 z9()#XtGgf%4dz)NL2uJQ5QX2yX_a9%r0(gWC8x=GxETE}n-9`&HOx!f# z3RF~-A4w%p-|rnq?<0R0rAPj5|5(QAG1J-MVWHC^V6c;zxyG4x{Z3KBMgI%|73f>wRQtX$o%8A?RU&kwst&*KiMYDhMpKs%)^z kc;@pVUFumHzuDbz$}Rt)&g?Evr)kpUI@ZV(RNe9VPrmG`fB*mh literal 0 HcmV?d00001 diff --git a/target/stale/leiningen.core.classpath.extract-native-dependencies b/target/stale/leiningen.core.classpath.extract-native-dependencies new file mode 100644 index 0000000..9be2eed --- /dev/null +++ b/target/stale/leiningen.core.classpath.extract-native-dependencies @@ -0,0 +1 @@ +[{:dependencies {com.cognitect/transit-java {:vsn "1.0.362", :native-prefix nil}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil}, org.nrepl/incomplete {:vsn "0.1.0", :native-prefix nil}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil}, com.googlecode.json-simple/json-simple {:vsn "1.1.1", :native-prefix nil}, org.clojure/google-closure-library {:vsn "0.0-20211011-0726fdeb", :native-prefix nil}, org.clojure/clojurescript {:vsn "1.11.60", :native-prefix nil}, com.fasterxml.jackson.core/jackson-core {:vsn "2.8.7", :native-prefix nil}, org.clojure/google-closure-library-third-party {:vsn "0.0-20211011-0726fdeb", :native-prefix nil}, org.javassist/javassist {:vsn "3.18.1-GA", :native-prefix nil}, org.msgpack/msgpack {:vsn "0.6.12", :native-prefix nil}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil}, nrepl {:vsn "0.8.3", :native-prefix nil}, com.google.javascript/closure-compiler-unshaded {:vsn "v20220502", :native-prefix nil}, javax.xml.bind/jaxb-api {:vsn "2.3.0", :native-prefix nil}}, :native-path "target/native"} {:native-path "target/native", :dependencies {com.cognitect/transit-java {:vsn "1.0.362", :native-prefix nil, :native? false}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil, :native? false}, org.nrepl/incomplete {:vsn "0.1.0", :native-prefix nil, :native? false}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil, :native? false}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil, :native? false}, com.googlecode.json-simple/json-simple {:vsn "1.1.1", :native-prefix nil, :native? false}, org.clojure/google-closure-library {:vsn "0.0-20211011-0726fdeb", :native-prefix nil, :native? false}, org.clojure/clojurescript {:vsn "1.11.60", :native-prefix nil, :native? false}, com.fasterxml.jackson.core/jackson-core {:vsn "2.8.7", :native-prefix nil, :native? false}, org.clojure/google-closure-library-third-party {:vsn "0.0-20211011-0726fdeb", :native-prefix nil, :native? false}, org.javassist/javassist {:vsn "3.18.1-GA", :native-prefix nil, :native? false}, org.msgpack/msgpack {:vsn "0.6.12", :native-prefix nil, :native? false}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil, :native? false}, nrepl {:vsn "0.8.3", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-unshaded {:vsn "v20220502", :native-prefix nil, :native? false}, javax.xml.bind/jaxb-api {:vsn "2.3.0", :native-prefix nil, :native? false}}}] \ No newline at end of file From 94bf28c3225ab0310d9049e8b123e47a59bb068b Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Sun, 26 Nov 2023 12:19:27 +0800 Subject: [PATCH 08/28] remove impl --- .../me/tonsky/persistent_sorted_set.cljs | 1163 ++++++++++++++++- .../me/tonsky/persistent_sorted_set/impl.cljs | 1113 ---------------- 2 files changed, 1134 insertions(+), 1142 deletions(-) delete mode 100644 src-clojure/me/tonsky/persistent_sorted_set/impl.cljs diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 0f44ac2..9ceab08 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -2,27 +2,1132 @@ "A B-tree based persistent sorted set. Supports transients, custom comparators, fast iteration, efficient slices (iterator over a part of the set) and reverse slices. Almost a drop-in replacement for [[clojure.core/sorted-set]], the only difference being this one can’t store nil." :author "Nikita Prokopov"} me.tonsky.persistent-sorted-set - (:refer-clojure :exclude [conj disj sorted-set sorted-set-by]) + (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.impl :as impl] [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) -(def conj impl/conj) -(def disj impl/disj) -(def BTSet impl/BTSet) +; B+ tree +; ------- + +; Leaf: keys[] :: array of values + +; Node: children[] :: links to children nodes +; keys[] :: max value for whole subtree +; node.keys[i] == max(node.children[i].keys) +; All arrays are 16..32 elements, inclusive + +; BTSet: root :: Node or Leaf +; shift :: depth - 1 +; cnt :: size of a set, integer, rolling +; comparator :: comparator used for ordering +; meta :: clojure meta map +; _hash :: hash code, same as for clojure collections, on-demand, cached + +; Path: conceptually a vector of indexes from root to leaf value, but encoded in a single number. +; E.g. we have path [7 30 11] representing root.children[7].children[30].keys[11]. +; In our case level-shift is 5, meaning each index will take 5 bits: +; (7 << 10) | (30 << 5) | (11 << 0) = 8139 +; 00111 11110 01011 + +; Iter: set :: Set this iterator belongs to +; left :: Current path +; right :: Right bound path (exclusive) +; keys :: Cached ref for keys array for a leaf +; idx :: Cached idx in keys array +; Keys and idx are cached for fast iteration inside a leaf" + +(def + ;; ^:const + max-safe-path + "js limitation for bit ops" + (js/Math.pow 2 31)) + +(def + ;; ^:const + bits-per-level + "tunable param" + 5) + +(def + ;; ^:const + max-len + (js/Math.pow 2 bits-per-level)) ;; 32 + +(def + ;; ^:const + min-len + (/ max-len 2)) ;; 16 + +(def ^:private + ;; ^:const + avg-len + (arrays/half (+ max-len min-len))) ;; 24 + +(def + ;; ^:const + max-safe-level + (js/Math.floor (/ 31 bits-per-level))) ;; 6 + +(def + ;; ^:const + bit-mask + (- max-len 1)) ;; 0b011111 = 5 bit + +(def factors + (arrays/into-array (map #(js/Math.pow 2 %) (range 0 52 bits-per-level)))) + +(def + ;; ^:const + empty-path 0) + +(defn- path-get ^number [^number path ^number level] + (if (< level max-safe-level) + (-> path + (unsigned-bit-shift-right (* level bits-per-level)) + (bit-and bit-mask)) + (-> path + (/ (arrays/aget factors level)) + (js/Math.floor) + (bit-and bit-mask)))) + +(defn- path-set ^number [^number path ^number level ^number idx] + (let [smol? (and (< path max-safe-path) (< level max-safe-level)) + old (path-get path level) + minus (if smol? + (bit-shift-left old (* level bits-per-level)) + (* old (arrays/aget factors level))) + plus (if smol? + (bit-shift-left idx (* level bits-per-level)) + (* idx (arrays/aget factors level)))] + (-> path + (- minus) + (+ plus)))) + +(defn- path-inc ^number [^number path] + (inc path)) + +(defn- path-dec ^number [^number path] + (dec path)) + +(defn- path-cmp ^number [^number path1 ^number path2] + (- path1 path2)) + +(defn- path-lt ^boolean [^number path1 ^number path2] + (< path1 path2)) + +(defn- path-lte ^boolean [^number path1 ^number path2] + (<= path1 path2)) + +(defn- path-eq ^boolean [^number path1 ^number path2] + (== path1 path2)) + +(defn- path-same-leaf ^boolean [^number path1 ^number path2] + (if (and + (< path1 max-safe-path) + (< path2 max-safe-path)) + (== + (unsigned-bit-shift-right path1 bits-per-level) + (unsigned-bit-shift-right path2 bits-per-level)) + (== + (Math/floor (/ path1 max-len)) + (Math/floor (/ path2 max-len))))) + +(defn- path-str [^number path] + (loop [res () + path path] + (if (not= path 0) + (recur (cljs.core/conj res (mod path max-len)) (Math/floor (/ path max-len))) + (vec res)))) + +(defn- binary-search-l [cmp arr r k] + (loop [l 0 + r (long r)] + (if (<= l r) + (let [m (arrays/half (+ l r)) + mk (arrays/aget arr m)] + (if (neg? (cmp mk k)) + (recur (inc m) r) + (recur l (dec m)))) + l))) + +(defn- binary-search-r [cmp arr r k] + (loop [l 0 + r (long r)] + (if (<= l r) + (let [m (arrays/half (+ l r)) + mk (arrays/aget arr m)] + (if (pos? (cmp mk k)) + (recur l (dec m)) + (recur (inc m) r))) + l))) + +(defn- lookup-exact [cmp arr key] + (let [arr-l (arrays/alength arr) + idx (binary-search-l cmp arr (dec arr-l) key)] + (if (and (< idx arr-l) + (== 0 (cmp (arrays/aget arr idx) key))) + idx + -1))) + +(defn- lookup-range [cmp arr key] + (let [arr-l (arrays/alength arr) + idx (binary-search-l cmp arr (dec arr-l) key)] + (if (== idx arr-l) + -1 + idx))) + +;; Array operations + +(defn- cut-n-splice [arr cut-from cut-to splice-from splice-to xs] + (let [xs-l (arrays/alength xs) + l1 (- splice-from cut-from) + l2 (- cut-to splice-to) + l1xs (+ l1 xs-l) + new-arr (arrays/make-array (+ l1 xs-l l2))] + (arrays/acopy arr cut-from splice-from new-arr 0) + (arrays/acopy xs 0 xs-l new-arr l1) + (arrays/acopy arr splice-to cut-to new-arr l1xs) + new-arr)) + +(defn- splice [arr splice-from splice-to xs] + (cut-n-splice arr 0 (arrays/alength arr) splice-from splice-to xs)) + +(defn- insert [arr idx xs] + (cut-n-splice arr 0 (arrays/alength arr) idx idx xs)) + +(defn- merge-n-split [a1 a2] + (let [a1-l (arrays/alength a1) + a2-l (arrays/alength a2) + total-l (+ a1-l a2-l) + r1-l (arrays/half total-l) + r2-l (- total-l r1-l) + r1 (arrays/make-array r1-l) + r2 (arrays/make-array r2-l)] + (if (<= a1-l r1-l) + (do + (arrays/acopy a1 0 a1-l r1 0) + (arrays/acopy a2 0 (- r1-l a1-l) r1 a1-l) + (arrays/acopy a2 (- r1-l a1-l) a2-l r2 0)) + (do + (arrays/acopy a1 0 r1-l r1 0) + (arrays/acopy a1 r1-l a1-l r2 0) + (arrays/acopy a2 0 a2-l r2 (- a1-l r1-l)))) + (arrays/array r1 r2))) + +(defn- ^boolean eq-arr [cmp a1 a1-from a1-to a2 a2-from a2-to] + (let [len (- a1-to a1-from)] + (and + (== len (- a2-to a2-from)) + (loop [i 0] + (cond + (== i len) + true + + (not (== 0 (cmp + (arrays/aget a1 (+ i a1-from)) + (arrays/aget a2 (+ i a2-from))))) + false + + :else + (recur (inc i))))))) + +(defn- check-n-splice [cmp arr from to new-arr] + (if (eq-arr cmp arr from to new-arr 0 (arrays/alength new-arr)) + arr + (splice arr from to new-arr))) + +(defn- return-array + "Drop non-nil references and return array of arguments" + ([a1] + (arrays/array a1)) + ([a1 a2] + (if a1 + (if a2 + (arrays/array a1 a2) + (arrays/array a1)) + (arrays/array a2))) + ([a1 a2 a3] + (if a1 + (if a2 + (if a3 + (arrays/array a1 a2 a3) + (arrays/array a1 a2)) + (if a3 + (arrays/array a1 a3) + (arrays/array a1))) + (if a2 + (if a3 + (arrays/array a2 a3) + (arrays/array a2)) + (arrays/array a3))))) + +;; + +(defprotocol INode + (node-lim-key [_]) + (node-len [_]) + (node-merge [_ next]) + (node-merge-n-split [_ next]) + (node-lookup [_ cmp key storage]) + (node-child [_ idx storage]) + (node-conj [_ cmp key storage]) + (node-disj [_ cmp key root? left right storage])) + +(defn- rotate [node root? left right] + (cond + ;; root never merges + root? + (return-array node) + + ;; enough keys, nothing to merge + (> (node-len node) min-len) + (return-array left node right) + + ;; left and this can be merged to one + (and left (<= (node-len left) min-len)) + (return-array (node-merge left node) right) + + ;; right and this can be merged to one + (and right (<= (node-len right) min-len)) + (return-array left (node-merge node right)) + + ;; left has fewer nodes, redestribute with it + (and left (or (nil? right) + (< (node-len left) (node-len right)))) + (let [nodes (node-merge-n-split left node)] + (return-array (arrays/aget nodes 0) (arrays/aget nodes 1) right)) + + ;; right has fewer nodes, redestribute with it + :else + (let [nodes (node-merge-n-split node right)] + (return-array left (arrays/aget nodes 0) (arrays/aget nodes 1))))) + +(defprotocol IStore + (store-aux [this storage])) + +(defn- ensure-addresses! + [^Node node size] + (when (empty? (.-addresses node)) + (set! (.-addresses node) (arrays/make-array size)))) + +(deftype Node [keys children ^:mutable addresses] + IStore + (store-aux [this ^IStorage storage] + (ensure-addresses! this (count children)) + + ;; Children first + (dorun + (map-indexed + (fn [idx child] + (when-let [address (protocol/store storage child)] + (aset addresses idx address))) + children)) + (protocol/store storage this)) + + INode + (node-lim-key [_] + (arrays/alast keys)) + + (node-len [_] + (arrays/alength keys)) + + (node-merge [this ^Node next] + (ensure-addresses! this (count children)) + (ensure-addresses! next (count (.-children next))) + (Node. (arrays/aconcat keys (.-keys next)) + (arrays/aconcat children (.-children next)) + (arrays/aconcat addresses (.-addresses next)))) + + (node-merge-n-split [this ^Node next] + (ensure-addresses! this (count children)) + (ensure-addresses! next (count (.-children next))) + (let [ks (merge-n-split keys (.-keys next)) + ps (merge-n-split children (.-children next)) + as (merge-n-split addresses (.-addresses next))] + (return-array (Node. (arrays/aget ks 0) + (arrays/aget ps 0) + (arrays/aget as 0)) + (Node. (arrays/aget ks 1) + (arrays/aget ps 1) + (arrays/aget as 1))))) + + (node-child [_ idx ^IStorage storage] + (when-not (= -1 idx) + (let [child (arrays/aget children idx) + address (when addresses (arrays/aget addresses idx))] + (if-not child + (let [child (protocol/restore storage address)] + (arrays/aset children idx child)) + (when (and storage address) + (protocol/accessed storage address))) + (arrays/aget children idx)))) + + (node-lookup [this cmp key storage] + (let [idx (lookup-range cmp keys key)] + (when-let [child (node-child this idx storage)] + (node-lookup child cmp key storage)))) + + (node-conj [this cmp key storage] + (ensure-addresses! this (count children)) + (let [idx (binary-search-l cmp keys (- (arrays/alength keys) 2) key) + child (node-child this idx storage) + nodes (node-conj child cmp key storage)] + (when nodes + (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) + new-children (splice children idx (inc idx) nodes) + new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] + (if (<= (arrays/alength new-children) max-len) + ;; ok as is + (arrays/array (Node. new-keys new-children new-addresses)) + ;; gotta split it up + (let [middle (arrays/half (arrays/alength new-children))] + (arrays/array + (Node. (.slice new-keys 0 middle) + (.slice new-children 0 middle) + (.slice new-addresses 0 middle)) + (Node. (.slice new-keys middle) + (.slice new-children middle) + (.slice new-addresses middle))))))))) + + (node-disj [this cmp key root? left right storage] + (ensure-addresses! this (count children)) + (let [idx (lookup-range cmp keys key)] + (when-not (== -1 idx) ;; short-circuit, key not here + (let [child (node-child this idx storage) + left-child (when (>= (dec idx) 0) + (node-child this (dec idx) storage)) + right-child (when (< (inc idx) (arrays/alength children)) + (node-child this (inc idx) storage)) + disjned (node-disj child cmp key false left-child right-child storage)] + (when disjned ;; short-circuit, key not here + (let [left-idx (if left-child (dec idx) idx) + right-idx (if right-child (+ 2 idx) (+ 1 idx)) + new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) + new-children (splice children left-idx right-idx disjned)] + (rotate (Node. new-keys new-children addresses) root? left right)))))))) + +(deftype Leaf [keys] + IStore + (store-aux [this storage] + (protocol/store storage this)) + + INode + (node-lim-key [_] + (arrays/alast keys)) +;; Object +;; (toString [_] (pr-str* (vec keys))) + + (node-len [_] + (arrays/alength keys)) + + (node-merge [_ next] + (Leaf. (arrays/aconcat keys (.-keys next)))) + + (node-merge-n-split [_ next] + (let [ks (merge-n-split keys (.-keys next))] + (return-array (Leaf. (arrays/aget ks 0)) + (Leaf. (arrays/aget ks 1))))) + + (node-child [_this idx _storage] + (arrays/aget keys idx)) + + (node-lookup [this cmp key storage] + (let [idx (lookup-exact cmp keys key)] + (when-not (== -1 idx) + (node-child this idx storage)))) + + (node-conj [_ cmp key storage] + (let [idx (binary-search-l cmp keys (dec (arrays/alength keys)) key) + keys-l (arrays/alength keys)] + (cond + ;; element already here + (and (< idx keys-l) + (== 0 (cmp key (arrays/aget keys idx)))) + nil + + ;; splitting + (== keys-l max-len) + (let [middle (arrays/half (inc keys-l))] + (if (> idx middle) + ;; new key goes to the second half + (arrays/array + (Leaf. (.slice keys 0 middle)) + (Leaf. (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) + ;; new key goes to the first half + (arrays/array + (Leaf. (cut-n-splice keys 0 middle idx idx (arrays/array key))) + (Leaf. (.slice keys middle keys-l))))) + + ;; ok as is + :else + (arrays/array (Leaf. (splice keys idx idx (arrays/array key))))))) + + (node-disj [_ cmp key root? left right storage] + (let [idx (lookup-exact cmp keys key)] + (when-not (== -1 idx) ;; key is here + (let [new-keys (splice keys idx (inc idx) (arrays/array))] + (rotate (Leaf. new-keys) root? left right)))))) + +;; BTSet + +(declare conj disj btset-iter) + +(def + ;; ^:const + uninitialized-hash nil) +(def + ;; ^:const + uninitialized-address nil) + +(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] + Object + (toString [this] (pr-str* this)) + + ICloneable + (-clone [_] (BTSet. storage root shift cnt comparator meta _hash _address)) + + IWithMeta + (-with-meta [_ new-meta] (BTSet. storage root shift cnt comparator new-meta _hash _address)) + + IMeta + (-meta [_] meta) + + IEmptyableCollection + (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) + + IEquiv + (-equiv [this other] + (and + (set? other) + (== cnt (count other)) + (every? #(contains? this %) other))) + + IHash + (-hash [this] (caching-hash this hash-unordered-coll _hash)) + + ICollection + (-conj [this key] (conj this key comparator)) + + ISet + (-disjoin [this key] (disj this key comparator)) + + IStore + (store-aux [_this storage*] + (let [storage (or storage storage*)] + (assert (some? storage)) + (when (nil? _address) + (set! _address (protocol/store storage root))) + _address)) + + ILookup + (-lookup [_ k] + (node-lookup root comparator k storage)) + (-lookup [_ k not-found] + (or (node-lookup root comparator k storage) not-found)) + + ISeqable + (-seq [this] (btset-iter this)) + + IReduce + (-reduce [this f] + (if-let [i (btset-iter this)] + (-reduce i f) + (f))) + (-reduce [this f start] + (if-let [i (btset-iter this)] + (-reduce i f start) + start)) + + IReversible + (-rseq [this] + (rseq (btset-iter this))) + + ; ISorted + ; (-sorted-seq [this ascending?]) + ; (-sorted-seq-from [this k ascending?]) + ; (-entry-key [this entry] entry) + ; (-comparator [this] comparator) + + ICounted + (-count [_] cnt) + + IEditableCollection + (-as-transient [this] this) + + ITransientCollection + (-conj! [this key] (conj this key comparator)) + (-persistent! [this] this) + + ITransientSet + (-disjoin! [this key] (disj this key comparator)) + + IFn + (-invoke [this k] (-lookup this k)) + (-invoke [this k not-found] (-lookup this k not-found)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "#{" " " "}" opts (seq this)))) + +(defn child + [node idx storage] + (if (instance? Node node) + (node-child node idx storage) + (arrays/aget (.-children node) idx))) + +(defn- keys-for [set path] + (loop [level (.-shift set) + node (.-root set)] + (if (pos? level) + (recur + (dec level) + (child node (path-get path level) (.-storage set))) + (.-keys node)))) + +(defn alter-btset [^BTSet set root shift cnt] + (BTSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) + +;; iteration + +(defn- -next-path [set node ^number path ^number level] + (let [idx (path-get path level)] + (if (pos? level) + ;; inner node + (let [sub-path (-next-path set (child node idx (.-storage set)) path (dec level))] + (if (nil? sub-path) + ;; nested node overflow + (if (< (inc idx) (arrays/alength (.-children node))) + ;; advance current node idx, reset subsequent indexes + (path-set empty-path level (inc idx)) + ;; current node overflow + nil) + ;; keep current idx + (path-set sub-path level idx))) + ;; leaf + (if (< (inc idx) (arrays/alength (.-keys node))) + ;; advance leaf idx + (path-set empty-path 0 (inc idx)) + ;; leaf overflow + nil)))) + +(defn- -rpath + "Returns rightmost path possible starting from node and going deeper" + [node ^number path ^number level] + (loop [node node + path path + level level] + (if (pos? level) + ;; inner node + (recur + (arrays/alast (.-children node)) + (path-set path level (dec (arrays/alength (.-children node)))) + (dec level)) + ;; leaf + (path-set path 0 (dec (arrays/alength (.-keys node))))))) + +(defn- next-path + "Returns path representing next item after `path` in natural traversal order. + Will overflow at leaf if at the end of the tree" + [set ^number path] + (if (neg? path) + empty-path + (or + (-next-path set (.-root set) path (.-shift set)) + (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) + +(defn- -prev-path [set node ^number path ^number level] + (let [idx (path-get path level)] + (cond + ;; leaf overflow + (and (== 0 level) (== 0 idx)) + nil + + ;; leaf + (== 0 level) + (path-set empty-path 0 (dec idx)) + + ;; branch that was overflow before + (>= idx (node-len node)) + (-rpath node path level) + + :else + (let [path' (-prev-path set (child node idx (.-storage set)) path (dec level))] + (cond + ;; no sub-overflow, keep current idx + (some? path') + (path-set path' level idx) + + ;; nested overflow + this node overflow + (== 0 idx) + nil + + ;; nested overflow, advance current idx, reset subsequent indexes + :else + (let [path' (-rpath (child node (dec idx) (.-storage set)) path (dec level))] + (path-set path' level (dec idx)))))))) + +(defn- prev-path + "Returns path representing previous item before `path` in natural traversal order. + Will overflow at leaf if at beginning of tree" + [set ^number path] + (if (> (path-get path (inc (.-shift set))) 0) ;; overflow + (-rpath (.-root set) path (.-shift set)) + (or + (-prev-path set (.-root set) path (.-shift set)) + (path-dec empty-path)))) + +(declare iter riter) + +(defn- btset-iter + "Iterator that represents the whole set" + [set] + (when (pos? (node-len (.-root set))) + (let [left empty-path + rpath (-rpath (.-root set) empty-path (.-shift set)) + right (next-path set rpath)] + (iter set left right)))) + +;; replace with cljs.core/ArrayChunk after https://dev.clojure.org/jira/browse/CLJS-2470 +(deftype Chunk [arr off end] + ICounted + (-count [_] (- end off)) + + IIndexed + (-nth [this i] + (aget arr (+ off i))) + + (-nth [this i not-found] + (if (and (>= i 0) (< i (- end off))) + (aget arr (+ off i)) + not-found)) + + IChunk + (-drop-first [this] + (if (== off end) + (throw (js/Error. "-drop-first of empty chunk")) + (ArrayChunk. arr (inc off) end))) + + IReduce + (-reduce [this f] + (if (== off end) + (f) + (-reduce (-drop-first this) f (aget arr off)))) + + (-reduce [this f start] + (loop [val start, n off] + (if (< n end) + (let [val' (f val (aget arr n))] + (if (reduced? val') + @val' + (recur val' (inc n)))) + val)))) + +(defprotocol IIter + (-copy [this left right])) + +(defprotocol ISeek + (-seek + [this key] + [this key comparator])) + +(declare -seek* -rseek*) + +(deftype Iter [^BTSet set left right keys idx] + IIter + (-copy [_ l r] + (Iter. set l r (keys-for set l) (path-get l 0))) + + IEquiv + (-equiv [this other] (equiv-sequential this other)) + + ISequential + ISeqable + (-seq [this] (when keys this)) + + ISeq + (-first [this] + (when keys + (arrays/aget keys idx))) + + (-rest [this] + (or (-next this) ())) + + INext + (-next [this] + (when keys + (if (< (inc idx) (arrays/alength keys)) + ;; can use cached array to move forward + (let [left' (path-inc left)] + (when (path-lt left' right) + (Iter. set left' right keys (inc idx)))) + (let [left' (next-path set left)] + (when (path-lt left' right) + (-copy this left' right)))))) + + IChunkedSeq + (-chunked-first [this] + (let [end-idx (if (path-same-leaf left right) + ;; right is in the same node + (path-get right 0) + ;; right is in a different node + (arrays/alength keys))] + (Chunk. keys idx end-idx))) + + (-chunked-rest [this] + (or (-chunked-next this) ())) + + IChunkedNext + (-chunked-next [this] + (let [last (path-set left 0 (dec (arrays/alength keys))) + left' (next-path set last)] + (when (path-lt left' right) + (-copy this left' right)))) + + IReduce + (-reduce [this f] + (if (nil? keys) + (f) + (let [first (-first this)] + (if-some [next (-next this)] + (-reduce next f first) + first)))) + + (-reduce [this f start] + (loop [left left + keys keys + idx idx + acc start] + (if (nil? keys) + acc + (let [new-acc (f acc (arrays/aget keys idx))] + (cond + (reduced? new-acc) + @new-acc + + (< (inc idx) (arrays/alength keys)) ;; can use cached array to move forward + (let [left' (path-inc left)] + (if (path-lt left' right) + (recur left' keys (inc idx) new-acc) + new-acc)) + + :else + (let [left' (next-path set left)] + (if (path-lt left' right) + (recur left' (keys-for set left') (path-get left' 0) new-acc) + new-acc))))))) + + IReversible + (-rseq [this] + (when keys + (riter set (prev-path set left) (prev-path set right)))) + + ISeek + (-seek [this key] + (-seek this key (.-comparator set))) + + (-seek [this key cmp] + (cond + (nil? key) + (throw (js/Error. "seek can't be called with a nil key!")) + + (nat-int? (cmp (arrays/aget keys idx) key)) + this + + :else + (when-some [left' (-seek* set key cmp)] + (Iter. set left' right (keys-for set left') (path-get left' 0))))) + + Object + (toString [this] (pr-str* this)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) + +(defn iter [set left right] + (Iter. set left right (keys-for set left) (path-get left 0))) + +;; reverse iteration + +(deftype ReverseIter [^BTSet set left right keys idx] + IIter + (-copy [_ l r] + (ReverseIter. set l r (keys-for set r) (path-get r 0))) + + IEquiv + (-equiv [this other] (equiv-sequential this other)) + + ISequential + ISeqable + (-seq [this] (when keys this)) + + ISeq + (-first [this] + (when keys + (arrays/aget keys idx))) + + (-rest [this] + (or (-next this) ())) + + INext + (-next [this] + (when keys + (if (> idx 0) + ;; can use cached array to advance + (let [right' (path-dec right)] + (when (path-lt left right') + (ReverseIter. set left right' keys (dec idx)))) + (let [right' (prev-path set right)] + (when (path-lt left right') + (-copy this left right')))))) + + IReversible + (-rseq [this] + (when keys + (iter set (next-path set left) (next-path set right)))) + + ISeek + (-seek [this key] + (-seek this key (.-comparator set))) + + (-seek [this key cmp] + (cond + (nil? key) + (throw (js/Error. "seek can't be called with a nil key!")) + + (nat-int? (cmp key (arrays/aget keys idx))) + this + + :else + (let [right' (prev-path set (-rseek* set key cmp))] + (when (and + (nat-int? right') + (path-lte left right') + (path-lt right' right)) + (ReverseIter. set left right' (keys-for set right') (path-get right' 0)))))) + + Object + (toString [this] (pr-str* this)) + + IPrintWithWriter + (-pr-writer [this writer opts] + (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) + +(defn riter [^BTSet set left right] + (ReverseIter. set left right (keys-for set right) (path-get right 0))) + +;; distance + +(defn- -distance [^BTSet set ^Node node left right level] + (let [idx-l (path-get left level) + idx-r (path-get right level)] + (if (pos? level) + ;; inner node + (if (== idx-l idx-r) + (-distance set (child node idx-l (.-storage set)) left right (dec level)) + (loop [level level + res (- idx-r idx-l)] + (if (== 0 level) + res + (recur (dec level) (* res avg-len))))) + (- idx-r idx-l)))) + +(defn- distance [^BTSet set path-l path-r] + (cond + (path-eq path-l path-r) + 0 + + (path-eq (path-inc path-l) path-r) + 1 + + (path-eq (next-path set path-l) path-r) + 1 + + :else + (-distance set (.-root set) path-l path-r (.-shift set)))) + +(defn est-count [iter] + (distance (.-set iter) (.-left iter) (.-right iter))) + +;; Slicing + +(defn- -seek* + "Returns path to first element >= key, + or -1 if all elements in a set < key" + [^BTSet set key comparator] + (if (nil? key) + empty-path + (loop [node (.-root set) + path empty-path + level (.-shift set)] + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (dec keys-l) key)] + (if (== keys-l idx) + nil + (path-set path 0 idx))) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (- keys-l 2) key)] + (recur + (child node idx (.-storage set)) + (path-set path level idx) + (dec level)))))))) + +(defn- -rseek* + "Returns path to the first element that is > key. + If all elements in a set are <= key, returns `(-rpath set) + 1`. + It’s a virtual path that is bigger than any path in a tree" + [^BTSet set key comparator] + (if (nil? key) + (path-inc (-rpath (.-root set) empty-path (.-shift set))) + (loop [node (.-root set) + path empty-path + level (.-shift set)] + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (dec keys-l) key) + res (path-set path 0 idx)] + res) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (- keys-l 2) key) + res (path-set path level idx)] + (recur + (child node idx (.-storage set)) + res + (dec level)))))))) + +(defn -slice [^BTSet set key-from key-to comparator] + (when-some [path (-seek* set key-from comparator)] + (let [till-path (-rseek* set key-to comparator)] + (when (path-lt path till-path) + (Iter. set path till-path (keys-for set path) (path-get path 0)))))) + +(defn arr-map-inplace [f arr] + (let [len (arrays/alength arr)] + (loop [i 0] + (when (< i len) + (arrays/aset arr i (f (arrays/aget arr i))) + (recur (inc i)))) + arr)) + +(defn arr-partition-approx + "Splits `arr` into arrays of size between min-len and max-len, + trying to stick to (min+max)/2" + [min-len max-len arr] + (let [chunk-len avg-len + len (arrays/alength arr) + acc (transient [])] + (when (pos? len) + (loop [pos 0] + (let [rest (- len pos)] + (cond + (<= rest max-len) + (conj! acc (.slice arr pos)) + (>= rest (+ chunk-len min-len)) + (do + (conj! acc (.slice arr pos (+ pos chunk-len))) + (recur (+ pos chunk-len))) + :else + (let [piece-len (arrays/half rest)] + (conj! acc (.slice arr pos (+ pos piece-len))) + (recur (+ pos piece-len))))))) + (to-array (persistent! acc)))) + +(defn- sorted-arr-distinct? [arr cmp] + (let [al (arrays/alength arr)] + (if (<= al 1) + true + (loop [i 1 + p (arrays/aget arr 0)] + (if (>= i al) + true + (let [e (arrays/aget arr i)] + (if (== 0 (cmp e p)) + false + (recur (inc i) e)))))))) + +(defn sorted-arr-distinct + "Filter out repetitive values in a sorted array. + Optimized for no-duplicates case" + [arr cmp] + (if (sorted-arr-distinct? arr cmp) + arr + (let [al (arrays/alength arr)] + (loop [acc (transient [(arrays/aget arr 0)]) + i 1 + p (arrays/aget arr 0)] + (if (>= i al) + (into-array (persistent! acc)) + (let [e (arrays/aget arr i)] + (if (== 0 (cmp e p)) + (recur acc (inc i) e) + (recur (conj! acc e) (inc i) e)))))))) + +;; Public interface + +(defn conj + "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." + [^BTSet set key cmp] + (let [roots (node-conj (.-root set) cmp key (.-storage set))] + (cond + ;; tree not changed + (nil? roots) + set + + ;; keeping single root + (== (arrays/alength roots) 1) + (alter-btset set + (arrays/aget roots 0) + (.-shift set) + (inc (.-cnt set))) + + ;; introducing new root + :else + (alter-btset set + (Node. (arrays/amap node-lim-key roots) roots nil) + (inc (.-shift set)) + (inc (.-cnt set)))))) + +(defn disj + "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." + [^BTSet set key cmp] + (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] + (if (nil? new-roots) ;; nothing changed, key wasn't in the set + set + (let [new-root (arrays/aget new-roots 0)] + (if (and (instance? Node new-root) + (== 1 (arrays/alength (.-children new-root)))) + + ;; root has one child, make him new root + (alter-btset set + (arrays/aget (.-children new-root) 0) + (dec (.-shift set)) + (dec (.-cnt set))) + + ;; keeping root level + (alter-btset set + new-root + (.-shift set) + (dec (.-cnt set)))))))) + +(def conj conj) +(def disj disj) +(def BTSet BTSet) (defn slice "An iterator for part of the set with provided boundaries. `(slice set from to)` returns iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." ([^BTSet set key-from key-to] - (impl/-slice set key-from key-to (.-comparator set))) + (-slice set key-from key-to (.-comparator set))) ([^BTSet set key-from key-to comparator] ;; (js/console.trace) - (impl/-slice set key-from key-to comparator))) + (-slice set key-from key-to comparator))) (defn rslice @@ -30,11 +1135,11 @@ `(rslice set from to)` returns backwards iterator for all Xs where from <= X <= to. Optionally pass in comparator that will override the one that set uses. Supports efficient [[clojure.core/rseq]]." ([^BTSet set key] - (some-> (impl/-slice set key key (.-comparator set)) rseq)) + (some-> (-slice set key key (.-comparator set)) rseq)) ([^BTSet set key-from key-to] - (some-> (impl/-slice set key-to key-from (.-comparator set)) rseq)) + (some-> (-slice set key-to key-from (.-comparator set)) rseq)) ([^BTSet set key-from key-to comparator] - (some-> (impl/-slice set key-to key-from comparator) rseq))) + (some-> (-slice set key-to key-from comparator) rseq))) (defn seek @@ -42,9 +1147,9 @@ `(seek (seq set) to)` returns iterator for all Xs where to <= X. Optionally pass in comparator that will override the one that set uses." ([seq to] - (impl/-seek seq to)) + (-seek seq to)) ([seq to cmp] - (impl/-seek seq to cmp))) + (-seek seq to cmp))) (defn from-sorted-array @@ -55,37 +1160,37 @@ (from-sorted-array cmp arr _len {})) ([cmp arr _len opts] (let [leaves (->> arr - (impl/arr-partition-approx impl/min-len impl/max-len) - (impl/arr-map-inplace #(impl/Leaf. %))) + (arr-partition-approx min-len max-len) + (arr-map-inplace #(Leaf. %))) storage (:storage opts)] (loop [current-level leaves shift 0] (case (count current-level) - 0 (impl/BTSet. storage (impl/Leaf. (arrays/array)) 0 0 cmp nil - impl/uninitialized-hash impl/uninitialized-address) - 1 (impl/BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil - impl/uninitialized-hash impl/uninitialized-address) + 0 (BTSet. storage (Leaf. (arrays/array)) 0 0 cmp nil + uninitialized-hash uninitialized-address) + 1 (BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil + uninitialized-hash uninitialized-address) (recur (->> current-level - (impl/arr-partition-approx impl/min-len impl/max-len) - (impl/arr-map-inplace #(impl/Node. (arrays/amap impl/node-lim-key %) % nil))) + (arr-partition-approx min-len max-len) + (arr-map-inplace #(Node. (arrays/amap node-lim-key %) % nil))) (inc shift))))))) (defn from-sequential "Create a set with custom comparator and a collection of keys. Useful when you don’t want to call [[clojure.core/apply]] on [[sorted-set-by]]." [cmp seq] - (let [arr (-> (into-array seq) (arrays/asort cmp) (impl/sorted-arr-distinct cmp))] + (let [arr (-> (into-array seq) (arrays/asort cmp) (sorted-arr-distinct cmp))] (from-sorted-array cmp arr))) (defn sorted-set* "Create a set with custom comparator, metadata and settings" [opts] - (impl/BTSet. (:storage opts) (impl/Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) - impl/uninitialized-hash impl/uninitialized-address)) + (BTSet. (:storage opts) (Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) + uninitialized-hash uninitialized-address)) (defn sorted-set-by - ([cmp] (impl/BTSet. nil (impl/Leaf. (arrays/array)) 0 0 cmp nil - impl/uninitialized-hash impl/uninitialized-address)) + ([cmp] (BTSet. nil (Leaf. (arrays/array)) 0 0 cmp nil + uninitialized-hash uninitialized-address)) ([cmp & keys] (from-sequential cmp keys))) (defn sorted-set @@ -100,10 +1205,10 @@ (restore-by cmp address storage {})) ([cmp address ^IStorage storage {:keys [set-metadata]}] (when-let [root (protocol/restore storage address)] - (impl/BTSet. storage root + (BTSet. storage root (:shift set-metadata) (:count set-metadata) - cmp nil impl/uninitialized-hash address)))) + cmp nil uninitialized-hash address)))) (defn restore "Constructs lazily-loaded set from storage and root address. @@ -119,8 +1224,8 @@ returned address. Incremental, won’t store same node twice on subsequent calls. Returns root address. Remember it and use it for restore" [^BTSet set ^IStorage storage] - (impl/store set storage)) + (store-aux set storage)) (defn settings [set] - {:branching-factor impl/max-len + {:branching-factor max-len :ref-type :strong}) diff --git a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs b/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs deleted file mode 100644 index af0e651..0000000 --- a/src-clojure/me/tonsky/persistent_sorted_set/impl.cljs +++ /dev/null @@ -1,1113 +0,0 @@ -(ns me.tonsky.persistent-sorted-set.impl - (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) - (:require - [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.protocol :as protocol :refer [IStorage]]) - (:require-macros - [me.tonsky.persistent-sorted-set.arrays :as arrays])) - -; B+ tree -; ------- - -; Leaf: keys[] :: array of values - -; Node: children[] :: links to children nodes -; keys[] :: max value for whole subtree -; node.keys[i] == max(node.children[i].keys) -; All arrays are 16..32 elements, inclusive - -; BTSet: root :: Node or Leaf -; shift :: depth - 1 -; cnt :: size of a set, integer, rolling -; comparator :: comparator used for ordering -; meta :: clojure meta map -; _hash :: hash code, same as for clojure collections, on-demand, cached - -; Path: conceptually a vector of indexes from root to leaf value, but encoded in a single number. -; E.g. we have path [7 30 11] representing root.children[7].children[30].keys[11]. -; In our case level-shift is 5, meaning each index will take 5 bits: -; (7 << 10) | (30 << 5) | (11 << 0) = 8139 -; 00111 11110 01011 - -; Iter: set :: Set this iterator belongs to -; left :: Current path -; right :: Right bound path (exclusive) -; keys :: Cached ref for keys array for a leaf -; idx :: Cached idx in keys array -; Keys and idx are cached for fast iteration inside a leaf" - -(def - ;; ^:const - max-safe-path - "js limitation for bit ops" - (js/Math.pow 2 31)) - -(def - ;; ^:const - bits-per-level - "tunable param" - 5) - -(def - ;; ^:const - max-len - (js/Math.pow 2 bits-per-level)) ;; 32 - -(def - ;; ^:const - min-len - (/ max-len 2)) ;; 16 - -(def ^:private - ;; ^:const - avg-len - (arrays/half (+ max-len min-len))) ;; 24 - -(def - ;; ^:const - max-safe-level - (js/Math.floor (/ 31 bits-per-level))) ;; 6 - -(def - ;; ^:const - bit-mask - (- max-len 1)) ;; 0b011111 = 5 bit - -(def factors - (arrays/into-array (map #(js/Math.pow 2 %) (range 0 52 bits-per-level)))) - -(def - ;; ^:const - empty-path 0) - -(defn- path-get ^number [^number path ^number level] - (if (< level max-safe-level) - (-> path - (unsigned-bit-shift-right (* level bits-per-level)) - (bit-and bit-mask)) - (-> path - (/ (arrays/aget factors level)) - (js/Math.floor) - (bit-and bit-mask)))) - -(defn- path-set ^number [^number path ^number level ^number idx] - (let [smol? (and (< path max-safe-path) (< level max-safe-level)) - old (path-get path level) - minus (if smol? - (bit-shift-left old (* level bits-per-level)) - (* old (arrays/aget factors level))) - plus (if smol? - (bit-shift-left idx (* level bits-per-level)) - (* idx (arrays/aget factors level)))] - (-> path - (- minus) - (+ plus)))) - -(defn- path-inc ^number [^number path] - (inc path)) - -(defn- path-dec ^number [^number path] - (dec path)) - -(defn- path-cmp ^number [^number path1 ^number path2] - (- path1 path2)) - -(defn- path-lt ^boolean [^number path1 ^number path2] - (< path1 path2)) - -(defn- path-lte ^boolean [^number path1 ^number path2] - (<= path1 path2)) - -(defn- path-eq ^boolean [^number path1 ^number path2] - (== path1 path2)) - -(defn- path-same-leaf ^boolean [^number path1 ^number path2] - (if (and - (< path1 max-safe-path) - (< path2 max-safe-path)) - (== - (unsigned-bit-shift-right path1 bits-per-level) - (unsigned-bit-shift-right path2 bits-per-level)) - (== - (Math/floor (/ path1 max-len)) - (Math/floor (/ path2 max-len))))) - -(defn- path-str [^number path] - (loop [res () - path path] - (if (not= path 0) - (recur (cljs.core/conj res (mod path max-len)) (Math/floor (/ path max-len))) - (vec res)))) - -(defn- binary-search-l [cmp arr r k] - (loop [l 0 - r (long r)] - (if (<= l r) - (let [m (arrays/half (+ l r)) - mk (arrays/aget arr m)] - (if (neg? (cmp mk k)) - (recur (inc m) r) - (recur l (dec m)))) - l))) - -(defn- binary-search-r [cmp arr r k] - (loop [l 0 - r (long r)] - (if (<= l r) - (let [m (arrays/half (+ l r)) - mk (arrays/aget arr m)] - (if (pos? (cmp mk k)) - (recur l (dec m)) - (recur (inc m) r))) - l))) - -(defn- lookup-exact [cmp arr key] - (let [arr-l (arrays/alength arr) - idx (binary-search-l cmp arr (dec arr-l) key)] - (if (and (< idx arr-l) - (== 0 (cmp (arrays/aget arr idx) key))) - idx - -1))) - -(defn- lookup-range [cmp arr key] - (let [arr-l (arrays/alength arr) - idx (binary-search-l cmp arr (dec arr-l) key)] - (if (== idx arr-l) - -1 - idx))) - -;; Array operations - -(defn- cut-n-splice [arr cut-from cut-to splice-from splice-to xs] - (let [xs-l (arrays/alength xs) - l1 (- splice-from cut-from) - l2 (- cut-to splice-to) - l1xs (+ l1 xs-l) - new-arr (arrays/make-array (+ l1 xs-l l2))] - (arrays/acopy arr cut-from splice-from new-arr 0) - (arrays/acopy xs 0 xs-l new-arr l1) - (arrays/acopy arr splice-to cut-to new-arr l1xs) - new-arr)) - -(defn- splice [arr splice-from splice-to xs] - (cut-n-splice arr 0 (arrays/alength arr) splice-from splice-to xs)) - -(defn- insert [arr idx xs] - (cut-n-splice arr 0 (arrays/alength arr) idx idx xs)) - -(defn- merge-n-split [a1 a2] - (let [a1-l (arrays/alength a1) - a2-l (arrays/alength a2) - total-l (+ a1-l a2-l) - r1-l (arrays/half total-l) - r2-l (- total-l r1-l) - r1 (arrays/make-array r1-l) - r2 (arrays/make-array r2-l)] - (if (<= a1-l r1-l) - (do - (arrays/acopy a1 0 a1-l r1 0) - (arrays/acopy a2 0 (- r1-l a1-l) r1 a1-l) - (arrays/acopy a2 (- r1-l a1-l) a2-l r2 0)) - (do - (arrays/acopy a1 0 r1-l r1 0) - (arrays/acopy a1 r1-l a1-l r2 0) - (arrays/acopy a2 0 a2-l r2 (- a1-l r1-l)))) - (arrays/array r1 r2))) - -(defn- ^boolean eq-arr [cmp a1 a1-from a1-to a2 a2-from a2-to] - (let [len (- a1-to a1-from)] - (and - (== len (- a2-to a2-from)) - (loop [i 0] - (cond - (== i len) - true - - (not (== 0 (cmp - (arrays/aget a1 (+ i a1-from)) - (arrays/aget a2 (+ i a2-from))))) - false - - :else - (recur (inc i))))))) - -(defn- check-n-splice [cmp arr from to new-arr] - (if (eq-arr cmp arr from to new-arr 0 (arrays/alength new-arr)) - arr - (splice arr from to new-arr))) - -(defn- return-array - "Drop non-nil references and return array of arguments" - ([a1] - (arrays/array a1)) - ([a1 a2] - (if a1 - (if a2 - (arrays/array a1 a2) - (arrays/array a1)) - (arrays/array a2))) - ([a1 a2 a3] - (if a1 - (if a2 - (if a3 - (arrays/array a1 a2 a3) - (arrays/array a1 a2)) - (if a3 - (arrays/array a1 a3) - (arrays/array a1))) - (if a2 - (if a3 - (arrays/array a2 a3) - (arrays/array a2)) - (arrays/array a3))))) - -;; - -(defprotocol INode - (node-lim-key [_]) - (node-len [_]) - (node-merge [_ next]) - (node-merge-n-split [_ next]) - (node-lookup [_ cmp key storage]) - (node-child [_ idx storage]) - (node-conj [_ cmp key storage]) - (node-disj [_ cmp key root? left right storage])) - -(defn- rotate [node root? left right] - (cond - ;; root never merges - root? - (return-array node) - - ;; enough keys, nothing to merge - (> (node-len node) min-len) - (return-array left node right) - - ;; left and this can be merged to one - (and left (<= (node-len left) min-len)) - (return-array (node-merge left node) right) - - ;; right and this can be merged to one - (and right (<= (node-len right) min-len)) - (return-array left (node-merge node right)) - - ;; left has fewer nodes, redestribute with it - (and left (or (nil? right) - (< (node-len left) (node-len right)))) - (let [nodes (node-merge-n-split left node)] - (return-array (arrays/aget nodes 0) (arrays/aget nodes 1) right)) - - ;; right has fewer nodes, redestribute with it - :else - (let [nodes (node-merge-n-split node right)] - (return-array left (arrays/aget nodes 0) (arrays/aget nodes 1))))) - -(defprotocol IStore - (store [this storage])) - -(defn- ensure-addresses! - [^Node node size] - (when (empty? (.-addresses node)) - (set! (.-addresses node) (arrays/make-array size)))) - -(deftype Node [keys children ^:mutable addresses] - IStore - (store [this ^IStorage storage] - (ensure-addresses! this (count children)) - - ;; Children first - (dorun - (map-indexed - (fn [idx child] - (let [address (protocol/store storage child)] - (aset addresses idx address))) - children)) - (protocol/store storage this)) - - INode - (node-lim-key [_] - (arrays/alast keys)) - - (node-len [_] - (arrays/alength keys)) - - (node-merge [this ^Node next] - (ensure-addresses! this (count children)) - (ensure-addresses! next (count (.-children next))) - (Node. (arrays/aconcat keys (.-keys next)) - (arrays/aconcat children (.-children next)) - (arrays/aconcat addresses (.-addresses next)))) - - (node-merge-n-split [this ^Node next] - (ensure-addresses! this (count children)) - (ensure-addresses! next (count (.-children next))) - (let [ks (merge-n-split keys (.-keys next)) - ps (merge-n-split children (.-children next)) - as (merge-n-split addresses (.-addresses next))] - (return-array (Node. (arrays/aget ks 0) - (arrays/aget ps 0) - (arrays/aget as 0)) - (Node. (arrays/aget ks 1) - (arrays/aget ps 1) - (arrays/aget as 1))))) - - (node-child [_ idx ^IStorage storage] - (when-not (= -1 idx) - (let [child (arrays/aget children idx) - address (when addresses (arrays/aget addresses idx))] - (if-not child - (let [child (protocol/restore storage address)] - (arrays/aset children idx child)) - (when (and storage address) - (protocol/accessed storage address))) - (arrays/aget children idx)))) - - (node-lookup [this cmp key storage] - (let [idx (lookup-range cmp keys key)] - (when-let [child (node-child this idx storage)] - (node-lookup child cmp key storage)))) - - (node-conj [this cmp key storage] - (ensure-addresses! this (count children)) - (let [idx (binary-search-l cmp keys (- (arrays/alength keys) 2) key) - child (node-child this idx storage) - nodes (node-conj child cmp key storage)] - (when nodes - (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) - new-children (splice children idx (inc idx) nodes) - new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] - (if (<= (arrays/alength new-children) max-len) - ;; ok as is - (arrays/array (Node. new-keys new-children new-addresses)) - ;; gotta split it up - (let [middle (arrays/half (arrays/alength new-children))] - (arrays/array - (Node. (.slice new-keys 0 middle) - (.slice new-children 0 middle) - (.slice new-addresses 0 middle)) - (Node. (.slice new-keys middle) - (.slice new-children middle) - (.slice new-addresses middle))))))))) - - (node-disj [this cmp key root? left right storage] - (ensure-addresses! this (count children)) - (let [idx (lookup-range cmp keys key)] - (when-not (== -1 idx) ;; short-circuit, key not here - (let [child (node-child this idx storage) - left-child (when (>= (dec idx) 0) - (node-child this (dec idx) storage)) - right-child (when (< (inc idx) (arrays/alength children)) - (node-child this (inc idx) storage)) - disjned (node-disj child cmp key false left-child right-child storage)] - (when disjned ;; short-circuit, key not here - (let [left-idx (if left-child (dec idx) idx) - right-idx (if right-child (+ 2 idx) (+ 1 idx)) - new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) - new-children (splice children left-idx right-idx disjned)] - (rotate (Node. new-keys new-children addresses) root? left right)))))))) - -(deftype Leaf [keys] - IStore - (store [this storage] - (protocol/store storage this)) - - INode - (node-lim-key [_] - (arrays/alast keys)) -;; Object -;; (toString [_] (pr-str* (vec keys))) - - (node-len [_] - (arrays/alength keys)) - - (node-merge [_ next] - (Leaf. (arrays/aconcat keys (.-keys next)))) - - (node-merge-n-split [_ next] - (let [ks (merge-n-split keys (.-keys next))] - (return-array (Leaf. (arrays/aget ks 0)) - (Leaf. (arrays/aget ks 1))))) - - (node-child [_this idx _storage] - (arrays/aget keys idx)) - - (node-lookup [this cmp key storage] - (let [idx (lookup-exact cmp keys key)] - (when-not (== -1 idx) - (node-child this idx storage)))) - - (node-conj [_ cmp key storage] - (let [idx (binary-search-l cmp keys (dec (arrays/alength keys)) key) - keys-l (arrays/alength keys)] - (cond - ;; element already here - (and (< idx keys-l) - (== 0 (cmp key (arrays/aget keys idx)))) - nil - - ;; splitting - (== keys-l max-len) - (let [middle (arrays/half (inc keys-l))] - (if (> idx middle) - ;; new key goes to the second half - (arrays/array - (Leaf. (.slice keys 0 middle)) - (Leaf. (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) - ;; new key goes to the first half - (arrays/array - (Leaf. (cut-n-splice keys 0 middle idx idx (arrays/array key))) - (Leaf. (.slice keys middle keys-l))))) - - ;; ok as is - :else - (arrays/array (Leaf. (splice keys idx idx (arrays/array key))))))) - - (node-disj [_ cmp key root? left right storage] - (let [idx (lookup-exact cmp keys key)] - (when-not (== -1 idx) ;; key is here - (let [new-keys (splice keys idx (inc idx) (arrays/array))] - (rotate (Leaf. new-keys) root? left right)))))) - -;; BTSet - -(declare conj disj btset-iter) - -(def - ;; ^:const - uninitialized-hash nil) -(def - ;; ^:const - uninitialized-address nil) - -(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] - Object - (toString [this] (pr-str* this)) - - ICloneable - (-clone [_] (BTSet. storage root shift cnt comparator meta _hash _address)) - - IWithMeta - (-with-meta [_ new-meta] (BTSet. storage root shift cnt comparator new-meta _hash _address)) - - IMeta - (-meta [_] meta) - - IEmptyableCollection - (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) - - IEquiv - (-equiv [this other] - (and - (set? other) - (== cnt (count other)) - (every? #(contains? this %) other))) - - IHash - (-hash [this] (caching-hash this hash-unordered-coll _hash)) - - ICollection - (-conj [this key] (conj this key comparator)) - - ISet - (-disjoin [this key] (disj this key comparator)) - - IStore - (store [_this storage*] - (let [storage (or storage storage*)] - (assert (some? storage)) - (when (nil? _address) - (set! _address (store root storage))) - _address)) - - ILookup - (-lookup [_ k] - (node-lookup root comparator k storage)) - (-lookup [_ k not-found] - (or (node-lookup root comparator k storage) not-found)) - - ISeqable - (-seq [this] (btset-iter this)) - - IReduce - (-reduce [this f] - (if-let [i (btset-iter this)] - (-reduce i f) - (f))) - (-reduce [this f start] - (if-let [i (btset-iter this)] - (-reduce i f start) - start)) - - IReversible - (-rseq [this] - (rseq (btset-iter this))) - - ; ISorted - ; (-sorted-seq [this ascending?]) - ; (-sorted-seq-from [this k ascending?]) - ; (-entry-key [this entry] entry) - ; (-comparator [this] comparator) - - ICounted - (-count [_] cnt) - - IEditableCollection - (-as-transient [this] this) - - ITransientCollection - (-conj! [this key] (conj this key comparator)) - (-persistent! [this] this) - - ITransientSet - (-disjoin! [this key] (disj this key comparator)) - - IFn - (-invoke [this k] (-lookup this k)) - (-invoke [this k not-found] (-lookup this k not-found)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "#{" " " "}" opts (seq this)))) - -(defn child - [node idx storage] - (if (instance? Node node) - (node-child node idx storage) - (arrays/aget (.-children node) idx))) - -(defn- keys-for [set path] - (loop [level (.-shift set) - node (.-root set)] - (if (pos? level) - (recur - (dec level) - (child node (path-get path level) (.-storage set))) - (.-keys node)))) - -(defn alter-btset [^BTSet set root shift cnt] - (BTSet. (.-storage set) root shift cnt (.-comparator set) (.-meta set) uninitialized-hash uninitialized-address)) - -;; iteration - -(defn- -next-path [set node ^number path ^number level] - (let [idx (path-get path level)] - (if (pos? level) - ;; inner node - (let [sub-path (-next-path set (child node idx (.-storage set)) path (dec level))] - (if (nil? sub-path) - ;; nested node overflow - (if (< (inc idx) (arrays/alength (.-children node))) - ;; advance current node idx, reset subsequent indexes - (path-set empty-path level (inc idx)) - ;; current node overflow - nil) - ;; keep current idx - (path-set sub-path level idx))) - ;; leaf - (if (< (inc idx) (arrays/alength (.-keys node))) - ;; advance leaf idx - (path-set empty-path 0 (inc idx)) - ;; leaf overflow - nil)))) - -(defn- -rpath - "Returns rightmost path possible starting from node and going deeper" - [node ^number path ^number level] - (loop [node node - path path - level level] - (if (pos? level) - ;; inner node - (recur - (arrays/alast (.-children node)) - (path-set path level (dec (arrays/alength (.-children node)))) - (dec level)) - ;; leaf - (path-set path 0 (dec (arrays/alength (.-keys node))))))) - -(defn- next-path - "Returns path representing next item after `path` in natural traversal order. - Will overflow at leaf if at the end of the tree" - [set ^number path] - (if (neg? path) - empty-path - (or - (-next-path set (.-root set) path (.-shift set)) - (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) - -(defn- -prev-path [set node ^number path ^number level] - (let [idx (path-get path level)] - (cond - ;; leaf overflow - (and (== 0 level) (== 0 idx)) - nil - - ;; leaf - (== 0 level) - (path-set empty-path 0 (dec idx)) - - ;; branch that was overflow before - (>= idx (node-len node)) - (-rpath node path level) - - :else - (let [path' (-prev-path set (child node idx (.-storage set)) path (dec level))] - (cond - ;; no sub-overflow, keep current idx - (some? path') - (path-set path' level idx) - - ;; nested overflow + this node overflow - (== 0 idx) - nil - - ;; nested overflow, advance current idx, reset subsequent indexes - :else - (let [path' (-rpath (child node (dec idx) (.-storage set)) path (dec level))] - (path-set path' level (dec idx)))))))) - -(defn- prev-path - "Returns path representing previous item before `path` in natural traversal order. - Will overflow at leaf if at beginning of tree" - [set ^number path] - (if (> (path-get path (inc (.-shift set))) 0) ;; overflow - (-rpath (.-root set) path (.-shift set)) - (or - (-prev-path set (.-root set) path (.-shift set)) - (path-dec empty-path)))) - -(declare iter riter) - -(defn- btset-iter - "Iterator that represents the whole set" - [set] - (when (pos? (node-len (.-root set))) - (let [left empty-path - rpath (-rpath (.-root set) empty-path (.-shift set)) - right (next-path set rpath)] - (iter set left right)))) - -;; replace with cljs.core/ArrayChunk after https://dev.clojure.org/jira/browse/CLJS-2470 -(deftype Chunk [arr off end] - ICounted - (-count [_] (- end off)) - - IIndexed - (-nth [this i] - (aget arr (+ off i))) - - (-nth [this i not-found] - (if (and (>= i 0) (< i (- end off))) - (aget arr (+ off i)) - not-found)) - - IChunk - (-drop-first [this] - (if (== off end) - (throw (js/Error. "-drop-first of empty chunk")) - (ArrayChunk. arr (inc off) end))) - - IReduce - (-reduce [this f] - (if (== off end) - (f) - (-reduce (-drop-first this) f (aget arr off)))) - - (-reduce [this f start] - (loop [val start, n off] - (if (< n end) - (let [val' (f val (aget arr n))] - (if (reduced? val') - @val' - (recur val' (inc n)))) - val)))) - -(defprotocol IIter - (-copy [this left right])) - -(defprotocol ISeek - (-seek - [this key] - [this key comparator])) - -(declare -seek* -rseek*) - -(deftype Iter [^BTSet set left right keys idx] - IIter - (-copy [_ l r] - (Iter. set l r (keys-for set l) (path-get l 0))) - - IEquiv - (-equiv [this other] (equiv-sequential this other)) - - ISequential - ISeqable - (-seq [this] (when keys this)) - - ISeq - (-first [this] - (when keys - (arrays/aget keys idx))) - - (-rest [this] - (or (-next this) ())) - - INext - (-next [this] - (when keys - (if (< (inc idx) (arrays/alength keys)) - ;; can use cached array to move forward - (let [left' (path-inc left)] - (when (path-lt left' right) - (Iter. set left' right keys (inc idx)))) - (let [left' (next-path set left)] - (when (path-lt left' right) - (-copy this left' right)))))) - - IChunkedSeq - (-chunked-first [this] - (let [end-idx (if (path-same-leaf left right) - ;; right is in the same node - (path-get right 0) - ;; right is in a different node - (arrays/alength keys))] - (Chunk. keys idx end-idx))) - - (-chunked-rest [this] - (or (-chunked-next this) ())) - - IChunkedNext - (-chunked-next [this] - (let [last (path-set left 0 (dec (arrays/alength keys))) - left' (next-path set last)] - (when (path-lt left' right) - (-copy this left' right)))) - - IReduce - (-reduce [this f] - (if (nil? keys) - (f) - (let [first (-first this)] - (if-some [next (-next this)] - (-reduce next f first) - first)))) - - (-reduce [this f start] - (loop [left left - keys keys - idx idx - acc start] - (if (nil? keys) - acc - (let [new-acc (f acc (arrays/aget keys idx))] - (cond - (reduced? new-acc) - @new-acc - - (< (inc idx) (arrays/alength keys)) ;; can use cached array to move forward - (let [left' (path-inc left)] - (if (path-lt left' right) - (recur left' keys (inc idx) new-acc) - new-acc)) - - :else - (let [left' (next-path set left)] - (if (path-lt left' right) - (recur left' (keys-for set left') (path-get left' 0) new-acc) - new-acc))))))) - - IReversible - (-rseq [this] - (when keys - (riter set (prev-path set left) (prev-path set right)))) - - ISeek - (-seek [this key] - (-seek this key (.-comparator set))) - - (-seek [this key cmp] - (cond - (nil? key) - (throw (js/Error. "seek can't be called with a nil key!")) - - (nat-int? (cmp (arrays/aget keys idx) key)) - this - - :else - (when-some [left' (-seek* set key cmp)] - (Iter. set left' right (keys-for set left') (path-get left' 0))))) - - Object - (toString [this] (pr-str* this)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) - -(defn iter [set left right] - (Iter. set left right (keys-for set left) (path-get left 0))) - -;; reverse iteration - -(deftype ReverseIter [^BTSet set left right keys idx] - IIter - (-copy [_ l r] - (ReverseIter. set l r (keys-for set r) (path-get r 0))) - - IEquiv - (-equiv [this other] (equiv-sequential this other)) - - ISequential - ISeqable - (-seq [this] (when keys this)) - - ISeq - (-first [this] - (when keys - (arrays/aget keys idx))) - - (-rest [this] - (or (-next this) ())) - - INext - (-next [this] - (when keys - (if (> idx 0) - ;; can use cached array to advance - (let [right' (path-dec right)] - (when (path-lt left right') - (ReverseIter. set left right' keys (dec idx)))) - (let [right' (prev-path set right)] - (when (path-lt left right') - (-copy this left right')))))) - - IReversible - (-rseq [this] - (when keys - (iter set (next-path set left) (next-path set right)))) - - ISeek - (-seek [this key] - (-seek this key (.-comparator set))) - - (-seek [this key cmp] - (cond - (nil? key) - (throw (js/Error. "seek can't be called with a nil key!")) - - (nat-int? (cmp key (arrays/aget keys idx))) - this - - :else - (let [right' (prev-path set (-rseek* set key cmp))] - (when (and - (nat-int? right') - (path-lte left right') - (path-lt right' right)) - (ReverseIter. set left right' (keys-for set right') (path-get right' 0)))))) - - Object - (toString [this] (pr-str* this)) - - IPrintWithWriter - (-pr-writer [this writer opts] - (pr-sequential-writer writer pr-writer "(" " " ")" opts (seq this)))) - -(defn riter [^BTSet set left right] - (ReverseIter. set left right (keys-for set right) (path-get right 0))) - -;; distance - -(defn- -distance [^BTSet set ^Node node left right level] - (let [idx-l (path-get left level) - idx-r (path-get right level)] - (if (pos? level) - ;; inner node - (if (== idx-l idx-r) - (-distance set (child node idx-l (.-storage set)) left right (dec level)) - (loop [level level - res (- idx-r idx-l)] - (if (== 0 level) - res - (recur (dec level) (* res avg-len))))) - (- idx-r idx-l)))) - -(defn- distance [^BTSet set path-l path-r] - (cond - (path-eq path-l path-r) - 0 - - (path-eq (path-inc path-l) path-r) - 1 - - (path-eq (next-path set path-l) path-r) - 1 - - :else - (-distance set (.-root set) path-l path-r (.-shift set)))) - -(defn est-count [iter] - (distance (.-set iter) (.-left iter) (.-right iter))) - -;; Slicing - -(defn- -seek* - "Returns path to first element >= key, - or -1 if all elements in a set < key" - [^BTSet set key comparator] - (if (nil? key) - empty-path - (loop [node (.-root set) - path empty-path - level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (dec keys-l) key)] - (if (== keys-l idx) - nil - (path-set path 0 idx))) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (- keys-l 2) key)] - (recur - (child node idx (.-storage set)) - (path-set path level idx) - (dec level)))))))) - -(defn- -rseek* - "Returns path to the first element that is > key. - If all elements in a set are <= key, returns `(-rpath set) + 1`. - It’s a virtual path that is bigger than any path in a tree" - [^BTSet set key comparator] - (if (nil? key) - (path-inc (-rpath (.-root set) empty-path (.-shift set))) - (loop [node (.-root set) - path empty-path - level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (dec keys-l) key) - res (path-set path 0 idx)] - res) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (- keys-l 2) key) - res (path-set path level idx)] - (recur - (child node idx (.-storage set)) - res - (dec level)))))))) - -(defn -slice [^BTSet set key-from key-to comparator] - (when-some [path (-seek* set key-from comparator)] - (let [till-path (-rseek* set key-to comparator)] - (when (path-lt path till-path) - (Iter. set path till-path (keys-for set path) (path-get path 0)))))) - -(defn arr-map-inplace [f arr] - (let [len (arrays/alength arr)] - (loop [i 0] - (when (< i len) - (arrays/aset arr i (f (arrays/aget arr i))) - (recur (inc i)))) - arr)) - -(defn arr-partition-approx - "Splits `arr` into arrays of size between min-len and max-len, - trying to stick to (min+max)/2" - [min-len max-len arr] - (let [chunk-len avg-len - len (arrays/alength arr) - acc (transient [])] - (when (pos? len) - (loop [pos 0] - (let [rest (- len pos)] - (cond - (<= rest max-len) - (conj! acc (.slice arr pos)) - (>= rest (+ chunk-len min-len)) - (do - (conj! acc (.slice arr pos (+ pos chunk-len))) - (recur (+ pos chunk-len))) - :else - (let [piece-len (arrays/half rest)] - (conj! acc (.slice arr pos (+ pos piece-len))) - (recur (+ pos piece-len))))))) - (to-array (persistent! acc)))) - -(defn- sorted-arr-distinct? [arr cmp] - (let [al (arrays/alength arr)] - (if (<= al 1) - true - (loop [i 1 - p (arrays/aget arr 0)] - (if (>= i al) - true - (let [e (arrays/aget arr i)] - (if (== 0 (cmp e p)) - false - (recur (inc i) e)))))))) - -(defn sorted-arr-distinct - "Filter out repetitive values in a sorted array. - Optimized for no-duplicates case" - [arr cmp] - (if (sorted-arr-distinct? arr cmp) - arr - (let [al (arrays/alength arr)] - (loop [acc (transient [(arrays/aget arr 0)]) - i 1 - p (arrays/aget arr 0)] - (if (>= i al) - (into-array (persistent! acc)) - (let [e (arrays/aget arr i)] - (if (== 0 (cmp e p)) - (recur acc (inc i) e) - (recur (conj! acc e) (inc i) e)))))))) - -;; Public interface - -(defn conj - "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." - [^BTSet set key cmp] - (let [roots (node-conj (.-root set) cmp key (.-storage set))] - (cond - ;; tree not changed - (nil? roots) - set - - ;; keeping single root - (== (arrays/alength roots) 1) - (alter-btset set - (arrays/aget roots 0) - (.-shift set) - (inc (.-cnt set))) - - ;; introducing new root - :else - (alter-btset set - (Node. (arrays/amap node-lim-key roots) roots nil) - (inc (.-shift set)) - (inc (.-cnt set)))))) - -(defn disj - "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." - [^BTSet set key cmp] - (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] - (if (nil? new-roots) ;; nothing changed, key wasn't in the set - set - (let [new-root (arrays/aget new-roots 0)] - (if (and (instance? Node new-root) - (== 1 (arrays/alength (.-children new-root)))) - - ;; root has one child, make him new root - (alter-btset set - (arrays/aget (.-children new-root) 0) - (dec (.-shift set)) - (dec (.-cnt set))) - - ;; keeping root level - (alter-btset set - new-root - (.-shift set) - (dec (.-cnt set)))))))) From f114487572cf659861725afe3b804cc68051362c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 27 Nov 2023 19:21:37 +0800 Subject: [PATCH 09/28] fix: make sure keys/addresses/chilren have the same length --- .../me/tonsky/persistent_sorted_set.cljs | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 9ceab08..14ca794 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -276,6 +276,10 @@ (node-conj [_ cmp key storage]) (node-disj [_ cmp key root? left right storage])) +(defn- set-child + [children idx child] + (aset children idx child)) + (defn- rotate [node root? left right] (cond ;; root never merges @@ -313,6 +317,28 @@ (when (empty? (.-addresses node)) (set! (.-addresses node) (arrays/make-array size)))) +(defn- set-address! + [addresses idx address] + (aset addresses idx address)) + +(declare Node) + +(defn new-node + [keys children addresses] + (let [addresses (if (nil? addresses) + (arrays/make-array (arrays/alength keys)) + addresses)] + (when (not= (count keys) (count children) (count addresses)) + (js/console.trace)) + + (assert (= (count keys) (count children) (count addresses)) + (str + "counts not matched" + {:keys-count (count keys) + :children-count (count children) + :addresses-count (count addresses)})) + (Node. keys children addresses))) + (deftype Node [keys children ^:mutable addresses] IStore (store-aux [this ^IStorage storage] @@ -321,10 +347,13 @@ ;; Children first (dorun (map-indexed - (fn [idx child] - (when-let [address (protocol/store storage child)] - (aset addresses idx address))) - children)) + (fn [idx address] + (when (nil? address) + (assert (not (nil? children))) + (assert (not (nil? (aget children idx)))) + (let [address (store-aux (aget children idx) storage)] + (set-address! addresses idx address)))) + addresses)) (protocol/store storage this)) INode @@ -335,11 +364,14 @@ (arrays/alength keys)) (node-merge [this ^Node next] + (assert (and (= (count keys) (count children)) + (= (count (.-keys next)) + (count (.-children next))))) (ensure-addresses! this (count children)) (ensure-addresses! next (count (.-children next))) - (Node. (arrays/aconcat keys (.-keys next)) - (arrays/aconcat children (.-children next)) - (arrays/aconcat addresses (.-addresses next)))) + (new-node (arrays/aconcat keys (.-keys next)) + (arrays/aconcat children (.-children next)) + (arrays/aconcat addresses (.-addresses next)))) (node-merge-n-split [this ^Node next] (ensure-addresses! this (count children)) @@ -347,15 +379,18 @@ (let [ks (merge-n-split keys (.-keys next)) ps (merge-n-split children (.-children next)) as (merge-n-split addresses (.-addresses next))] - (return-array (Node. (arrays/aget ks 0) - (arrays/aget ps 0) - (arrays/aget as 0)) - (Node. (arrays/aget ks 1) - (arrays/aget ps 1) - (arrays/aget as 1))))) - - (node-child [_ idx ^IStorage storage] + (return-array (new-node (arrays/aget ks 0) + (arrays/aget ps 0) + (arrays/aget as 0)) + (new-node (arrays/aget ks 1) + (arrays/aget ps 1) + (arrays/aget as 1))))) + + (node-child [_this idx ^IStorage storage] (when-not (= -1 idx) + (assert (or (and (seq children) (arrays/aget children idx)) ; child exists + (and (seq addresses) (arrays/aget addresses idx))) + (str "Neither child or address exists" {:keys keys :addresses addresses :idx idx :children children})) (let [child (arrays/aget children idx) address (when addresses (arrays/aget addresses idx))] (if-not child @@ -379,18 +414,19 @@ (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] + (assert (every? some? new-children) (str "children is nil: " new-children)) (if (<= (arrays/alength new-children) max-len) ;; ok as is - (arrays/array (Node. new-keys new-children new-addresses)) + (arrays/array (new-node new-keys new-children new-addresses)) ;; gotta split it up (let [middle (arrays/half (arrays/alength new-children))] (arrays/array - (Node. (.slice new-keys 0 middle) - (.slice new-children 0 middle) - (.slice new-addresses 0 middle)) - (Node. (.slice new-keys middle) - (.slice new-children middle) - (.slice new-addresses middle))))))))) + (new-node (.slice new-keys 0 middle) + (.slice new-children 0 middle) + (.slice new-addresses 0 middle)) + (new-node (.slice new-keys middle) + (.slice new-children middle) + (.slice new-addresses middle))))))))) (node-disj [this cmp key root? left right storage] (ensure-addresses! this (count children)) @@ -406,8 +442,9 @@ (let [left-idx (if left-child (dec idx) idx) right-idx (if right-child (+ 2 idx) (+ 1 idx)) new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) - new-children (splice children left-idx right-idx disjned)] - (rotate (Node. new-keys new-children addresses) root? left right)))))))) + new-children (splice children left-idx right-idx disjned) + new-addresses (splice addresses left-idx right-idx (arrays/make-array (count disjned)))] + (rotate (new-node new-keys new-children new-addresses) root? left right)))))))) (deftype Leaf [keys] IStore @@ -519,7 +556,7 @@ (let [storage (or storage storage*)] (assert (some? storage)) (when (nil? _address) - (set! _address (protocol/store storage root))) + (set! _address (store-aux root storage))) _address)) ILookup @@ -574,9 +611,8 @@ (defn child [node idx storage] - (if (instance? Node node) - (node-child node idx storage) - (arrays/aget (.-children node) idx))) + (when (instance? Node node) + (node-child node idx storage))) (defn- keys-for [set path] (loop [level (.-shift set) @@ -1089,7 +1125,8 @@ ;; introducing new root :else (alter-btset set - (Node. (arrays/amap node-lim-key roots) roots nil) + (new-node (arrays/amap node-lim-key roots) roots + (arrays/make-array (count roots))) (inc (.-shift set)) (inc (.-cnt set)))))) @@ -1115,10 +1152,6 @@ (.-shift set) (dec (.-cnt set)))))))) -(def conj conj) -(def disj disj) -(def BTSet BTSet) - (defn slice "An iterator for part of the set with provided boundaries. `(slice set from to)` returns iterator for all Xs where from <= X <= to. @@ -1173,7 +1206,7 @@ (recur (->> current-level (arr-partition-approx min-len max-len) - (arr-map-inplace #(Node. (arrays/amap node-lim-key %) % nil))) + (arr-map-inplace #(new-node (arrays/amap node-lim-key %) % nil))) (inc shift))))))) (defn from-sequential From 88784300bc2feb70fe25f9744ceca73c871e6f9e Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 28 Nov 2023 14:35:41 +0800 Subject: [PATCH 10/28] Remove debug asserts --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 14ca794..0d3b0cb 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -276,7 +276,7 @@ (node-conj [_ cmp key storage]) (node-disj [_ cmp key root? left right storage])) -(defn- set-child +(defn- set-child! [children idx child] (aset children idx child)) @@ -328,15 +328,6 @@ (let [addresses (if (nil? addresses) (arrays/make-array (arrays/alength keys)) addresses)] - (when (not= (count keys) (count children) (count addresses)) - (js/console.trace)) - - (assert (= (count keys) (count children) (count addresses)) - (str - "counts not matched" - {:keys-count (count keys) - :children-count (count children) - :addresses-count (count addresses)})) (Node. keys children addresses))) (deftype Node [keys children ^:mutable addresses] @@ -388,6 +379,7 @@ (node-child [_this idx ^IStorage storage] (when-not (= -1 idx) + ;; TODO: Remove when the implementation is stable (assert (or (and (seq children) (arrays/aget children idx)) ; child exists (and (seq addresses) (arrays/aget addresses idx))) (str "Neither child or address exists" {:keys keys :addresses addresses :idx idx :children children})) @@ -395,7 +387,7 @@ address (when addresses (arrays/aget addresses idx))] (if-not child (let [child (protocol/restore storage address)] - (arrays/aset children idx child)) + (set-child! children idx child)) (when (and storage address) (protocol/accessed storage address))) (arrays/aget children idx)))) @@ -414,7 +406,6 @@ (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] - (assert (every? some? new-children) (str "children is nil: " new-children)) (if (<= (arrays/alength new-children) max-len) ;; ok as is (arrays/array (new-node new-keys new-children new-addresses)) From 93b471d234f6ab28759e7b894cbc36d2dff0613c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 00:34:36 +0800 Subject: [PATCH 11/28] fix: can skip passing storage to set/store --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 0d3b0cb..6b1da5c 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -1247,8 +1247,11 @@ "Store each not-yet-stored node by calling IStorage::store and remembering returned address. Incremental, won’t store same node twice on subsequent calls. Returns root address. Remember it and use it for restore" - [^BTSet set ^IStorage storage] - (store-aux set storage)) + ([^BTSet set] + (assert (some? (.-storage set))) + (store-aux set (.-storage set))) + ([^BTSet set ^IStorage storage] + (store-aux set storage))) (defn settings [set] {:branching-factor max-len From 5801fbc080894ea543250011ba3a8df92433abdb Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 00:42:06 +0800 Subject: [PATCH 12/28] fix: make storage mutable --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 6b1da5c..713548b 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -510,7 +510,7 @@ ;; ^:const uninitialized-address nil) -(deftype BTSet [storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] +(deftype BTSet [^:mutable storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] Object (toString [this] (pr-str* this)) @@ -546,6 +546,8 @@ (store-aux [_this storage*] (let [storage (or storage storage*)] (assert (some? storage)) + (when (nil? storage) + (set! storage storage*)) (when (nil? _address) (set! _address (store-aux root storage))) _address)) From 37d818c2a530729bc9e05f63e12cf522ed0b85a2 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 00:45:14 +0800 Subject: [PATCH 13/28] fix: mutable storage --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 713548b..24fc8b5 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -544,13 +544,12 @@ IStore (store-aux [_this storage*] - (let [storage (or storage storage*)] - (assert (some? storage)) - (when (nil? storage) - (set! storage storage*)) - (when (nil? _address) - (set! _address (store-aux root storage))) - _address)) + (when (nil? storage) + (set! storage storage*)) + (when (nil? _address) + (assert (some? storage) "storage couldn't be nil") + (set! _address (store-aux root storage))) + _address) ILookup (-lookup [_ k] From 4c0876600351ff501fab057933809b899cbc260b Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 01:18:11 +0800 Subject: [PATCH 14/28] fix: restore node in rpath --- .../me/tonsky/persistent_sorted_set.cljs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 24fc8b5..4220ae2 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -643,16 +643,20 @@ (defn- -rpath "Returns rightmost path possible starting from node and going deeper" - [node ^number path ^number level] + [node ^number path ^number level storage] (loop [node node path path level level] (if (pos? level) ;; inner node - (recur - (arrays/alast (.-children node)) - (path-set path level (dec (arrays/alength (.-children node)))) - (dec level)) + (let [last-idx (dec (arrays/alength (.-children node))) + node-child (or (arrays/alast (.-children node)) + (child node last-idx storage))] + (recur + node-child + (path-set path level last-idx) + (dec level) + storage)) ;; leaf (path-set path 0 (dec (arrays/alength (.-keys node))))))) @@ -664,7 +668,7 @@ empty-path (or (-next-path set (.-root set) path (.-shift set)) - (path-inc (-rpath (.-root set) empty-path (.-shift set)))))) + (path-inc (-rpath (.-root set) empty-path (.-shift set) (.-storage set)))))) (defn- -prev-path [set node ^number path ^number level] (let [idx (path-get path level)] @@ -679,7 +683,7 @@ ;; branch that was overflow before (>= idx (node-len node)) - (-rpath node path level) + (-rpath node path level (.-storage set)) :else (let [path' (-prev-path set (child node idx (.-storage set)) path (dec level))] @@ -694,7 +698,7 @@ ;; nested overflow, advance current idx, reset subsequent indexes :else - (let [path' (-rpath (child node (dec idx) (.-storage set)) path (dec level))] + (let [path' (-rpath (child node (dec idx) (.-storage set)) path (dec level) (.-storage set))] (path-set path' level (dec idx)))))))) (defn- prev-path @@ -702,7 +706,7 @@ Will overflow at leaf if at beginning of tree" [set ^number path] (if (> (path-get path (inc (.-shift set))) 0) ;; overflow - (-rpath (.-root set) path (.-shift set)) + (-rpath (.-root set) path (.-shift set) (.-storage set)) (or (-prev-path set (.-root set) path (.-shift set)) (path-dec empty-path)))) @@ -714,7 +718,7 @@ [set] (when (pos? (node-len (.-root set))) (let [left empty-path - rpath (-rpath (.-root set) empty-path (.-shift set)) + rpath (-rpath (.-root set) empty-path (.-shift set) (.-storage set)) right (next-path set rpath)] (iter set left right)))) @@ -1011,7 +1015,7 @@ It’s a virtual path that is bigger than any path in a tree" [^BTSet set key comparator] (if (nil? key) - (path-inc (-rpath (.-root set) empty-path (.-shift set))) + (path-inc (-rpath (.-root set) empty-path (.-shift set) (.-storage set))) (loop [node (.-root set) path empty-path level (.-shift set)] From f777ffd33473cfc12e8fd0c4f68812cdbe8a4c9a Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 01:19:36 +0800 Subject: [PATCH 15/28] fix: wrong arguments --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 4220ae2..76cebf0 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -655,8 +655,7 @@ (recur node-child (path-set path level last-idx) - (dec level) - storage)) + (dec level))) ;; leaf (path-set path 0 (dec (arrays/alength (.-keys node))))))) From 068a99f4734e8bc62d52ec5f26fb6741b9b431da Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 01:49:30 +0800 Subject: [PATCH 16/28] enhance: lazy load indexes --- .../me/tonsky/persistent_sorted_set.cljs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 76cebf0..cb8f875 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -510,7 +510,16 @@ ;; ^:const uninitialized-address nil) -(deftype BTSet [^:mutable storage root shift cnt comparator meta ^:mutable _hash ^:mutable _address] +(defn- ensure-root-node! + "Restore root node if it's not loaded yet" + [storage root address] + (or root + (when address + (let [node (protocol/restore storage address)] + (set! root node) + node)))) + +(deftype BTSet [^:mutable storage ^:mutable root shift cnt comparator meta ^:mutable _hash ^:mutable _address] Object (toString [this] (pr-str* this)) @@ -546,6 +555,7 @@ (store-aux [_this storage*] (when (nil? storage) (set! storage storage*)) + (ensure-root-node! storage root _address) (when (nil? _address) (assert (some? storage) "storage couldn't be nil") (set! _address (store-aux root storage))) @@ -553,8 +563,10 @@ ILookup (-lookup [_ k] + (ensure-root-node! storage root _address) (node-lookup root comparator k storage)) (-lookup [_ k not-found] + (ensure-root-node! storage root _address) (or (node-lookup root comparator k storage) not-found)) ISeqable @@ -1232,11 +1244,7 @@ ([cmp address ^IStorage storage] (restore-by cmp address storage {})) ([cmp address ^IStorage storage {:keys [set-metadata]}] - (when-let [root (protocol/restore storage address)] - (BTSet. storage root - (:shift set-metadata) - (:count set-metadata) - cmp nil uninitialized-hash address)))) + (BTSet. storage nil (:shift set-metadata) (:count set-metadata) cmp nil uninitialized-hash address))) (defn restore "Constructs lazily-loaded set from storage and root address. From 095ef7340b4deb33245cf2e35abda3686c3662ac Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 01:50:36 +0800 Subject: [PATCH 17/28] fix: add missing mutable --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index cb8f875..426e3dd 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -512,7 +512,7 @@ (defn- ensure-root-node! "Restore root node if it's not loaded yet" - [storage root address] + [storage ^:mutable root address] (or root (when address (let [node (protocol/restore storage address)] From b98540bfe3064ab314b70666e6825d2c51f8dade Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 02:04:13 +0800 Subject: [PATCH 18/28] fix: add IRoot protocol --- .../me/tonsky/persistent_sorted_set.cljs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 426e3dd..0948b92 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -510,14 +510,8 @@ ;; ^:const uninitialized-address nil) -(defn- ensure-root-node! - "Restore root node if it's not loaded yet" - [storage ^:mutable root address] - (or root - (when address - (let [node (protocol/restore storage address)] - (set! root node) - node)))) +(defprotocol IRoot + (-ensure-root-node [_])) (deftype BTSet [^:mutable storage ^:mutable root shift cnt comparator meta ^:mutable _hash ^:mutable _address] Object @@ -551,6 +545,14 @@ ISet (-disjoin [this key] (disj this key comparator)) + IRoot + (-ensure-root-node [_this] + (or root + (when _address + (let [node (protocol/restore storage _address)] + (set! root node) + node)))) + IStore (store-aux [_this storage*] (when (nil? storage) From 86ccc18bfcc60f1a1b7a48e27b45bc51c7ef8582 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 02:06:34 +0800 Subject: [PATCH 19/28] typo --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 0948b92..2ee61fb 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -554,21 +554,21 @@ node)))) IStore - (store-aux [_this storage*] + (store-aux [this storage*] (when (nil? storage) (set! storage storage*)) - (ensure-root-node! storage root _address) + (-ensure-root-node this) (when (nil? _address) (assert (some? storage) "storage couldn't be nil") (set! _address (store-aux root storage))) _address) ILookup - (-lookup [_ k] - (ensure-root-node! storage root _address) + (-lookup [this k] + (-ensure-root-node this) (node-lookup root comparator k storage)) - (-lookup [_ k not-found] - (ensure-root-node! storage root _address) + (-lookup [this k not-found] + (-ensure-root-node this) (or (node-lookup root comparator k storage) not-found)) ISeqable From 647779d00fb0707403d92a874500cf30ad698266 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 7 Dec 2023 02:12:33 +0800 Subject: [PATCH 20/28] fix: ensure root node exists fix unit tests --- .../me/tonsky/persistent_sorted_set.cljs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 2ee61fb..e7044fa 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -622,7 +622,7 @@ (defn- keys-for [set path] (loop [level (.-shift set) - node (.-root set)] + node (-ensure-root-node set)] (if (pos? level) (recur (dec level) @@ -680,8 +680,8 @@ (if (neg? path) empty-path (or - (-next-path set (.-root set) path (.-shift set)) - (path-inc (-rpath (.-root set) empty-path (.-shift set) (.-storage set)))))) + (-next-path set (-ensure-root-node set) path (.-shift set)) + (path-inc (-rpath (-ensure-root-node set) empty-path (.-shift set) (.-storage set)))))) (defn- -prev-path [set node ^number path ^number level] (let [idx (path-get path level)] @@ -719,9 +719,9 @@ Will overflow at leaf if at beginning of tree" [set ^number path] (if (> (path-get path (inc (.-shift set))) 0) ;; overflow - (-rpath (.-root set) path (.-shift set) (.-storage set)) + (-rpath (-ensure-root-node set) path (.-shift set) (.-storage set)) (or - (-prev-path set (.-root set) path (.-shift set)) + (-prev-path set (-ensure-root-node set) path (.-shift set)) (path-dec empty-path)))) (declare iter riter) @@ -729,9 +729,9 @@ (defn- btset-iter "Iterator that represents the whole set" [set] - (when (pos? (node-len (.-root set))) + (when (pos? (node-len (-ensure-root-node set))) (let [left empty-path - rpath (-rpath (.-root set) empty-path (.-shift set) (.-storage set)) + rpath (-rpath (-ensure-root-node set) empty-path (.-shift set) (.-storage set)) right (next-path set rpath)] (iter set left right)))) @@ -992,7 +992,7 @@ 1 :else - (-distance set (.-root set) path-l path-r (.-shift set)))) + (-distance set (-ensure-root-node set) path-l path-r (.-shift set)))) (defn est-count [iter] (distance (.-set iter) (.-left iter) (.-right iter))) @@ -1005,7 +1005,7 @@ [^BTSet set key comparator] (if (nil? key) empty-path - (loop [node (.-root set) + (loop [node (-ensure-root-node set) path empty-path level (.-shift set)] (let [keys-l (node-len node)] @@ -1028,8 +1028,8 @@ It’s a virtual path that is bigger than any path in a tree" [^BTSet set key comparator] (if (nil? key) - (path-inc (-rpath (.-root set) empty-path (.-shift set) (.-storage set))) - (loop [node (.-root set) + (path-inc (-rpath (-ensure-root-node set) empty-path (.-shift set) (.-storage set))) + (loop [node (-ensure-root-node set) path empty-path level (.-shift set)] (let [keys-l (node-len node)] @@ -1118,7 +1118,7 @@ (defn conj "Analogue to [[clojure.core/conj]] with comparator that overrides the one stored in set." [^BTSet set key cmp] - (let [roots (node-conj (.-root set) cmp key (.-storage set))] + (let [roots (node-conj (-ensure-root-node set) cmp key (.-storage set))] (cond ;; tree not changed (nil? roots) @@ -1142,7 +1142,7 @@ (defn disj "Analogue to [[clojure.core/disj]] with comparator that overrides the one stored in set." [^BTSet set key cmp] - (let [new-roots (node-disj (.-root set) cmp key true nil nil (.-storage set))] + (let [new-roots (node-disj (-ensure-root-node set) cmp key true nil nil (.-storage set))] (if (nil? new-roots) ;; nothing changed, key wasn't in the set set (let [new-root (arrays/aget new-roots 0)] From ea0e50719b7fecddf044014dcdd19128c3004e85 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Wed, 13 Dec 2023 17:01:20 -0500 Subject: [PATCH 21/28] Remove accidental target classes introduced Also remove unnecessary changes to project.clj and .gitignore --- .gitignore | 3 ++- project.clj | 8 ++++---- .../me/tonsky/persistent_sorted_set/ANode.class | Bin 5029 -> 0 bytes .../APersistentSortedSet.class | Bin 3628 -> 0 bytes .../persistent_sorted_set/ArrayUtil.class | Bin 1293 -> 0 bytes .../tonsky/persistent_sorted_set/Branch.class | Bin 16732 -> 0 bytes .../me/tonsky/persistent_sorted_set/Chunk.class | Bin 3103 -> 0 bytes .../IPersistentSortedSet.class | Bin 1337 -> 0 bytes .../me/tonsky/persistent_sorted_set/ISeek.class | Bin 405 -> 0 bytes .../tonsky/persistent_sorted_set/IStorage.class | Bin 654 -> 0 bytes .../tonsky/persistent_sorted_set/JavaIter.class | Bin 777 -> 0 bytes .../me/tonsky/persistent_sorted_set/Leaf.class | Bin 7246 -> 0 bytes .../PersistentSortedSet.class | Bin 12865 -> 0 bytes .../tonsky/persistent_sorted_set/RefType.class | Bin 1008 -> 0 bytes .../me/tonsky/persistent_sorted_set/Seq.class | Bin 6952 -> 0 bytes .../persistent_sorted_set/Settings$1.class | Bin 795 -> 0 bytes .../tonsky/persistent_sorted_set/Settings.class | Bin 3111 -> 0 bytes .../tonsky/persistent_sorted_set/Stitch.class | Bin 771 -> 0 bytes ...n.core.classpath.extract-native-dependencies | 1 - 19 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/ANode.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/APersistentSortedSet.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/ArrayUtil.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Branch.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Chunk.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/ISeek.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/IStorage.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/JavaIter.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Leaf.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/RefType.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Seq.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Settings$1.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Settings.class delete mode 100644 target/classes/me/tonsky/persistent_sorted_set/Stitch.class delete mode 100644 target/stale/leiningen.core.classpath.extract-native-dependencies diff --git a/.gitignore b/.gitignore index b862ed8..4e8bbdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ .nrepl-port .lein-* +target .DS_Store .idea pom.xml bench-clojure/*.edn node_modules -.shadow-cljs +.shadow-cljs \ No newline at end of file diff --git a/project.clj b/project.clj index 06b1ce9..eb3a981 100644 --- a/project.clj +++ b/project.clj @@ -2,11 +2,11 @@ :description "Fast B-tree based persistent sorted set for Clojure/Script" :license {:name "MIT"} :url "https://github.com/tonsky/persistent-sorted-set" - + :dependencies [[org.clojure/clojure "1.11.1" :scope "provided"] [org.clojure/clojurescript "1.11.60" :scope "provided"]] - + :plugins [[lein-cljsbuild "1.1.7"]] @@ -19,10 +19,10 @@ :profiles {:1.9 - {:dependencies + {:dependencies [[org.clojure/clojure "1.9.0" :scope "provided"] [org.clojure/clojurescript "1.9.946" :scope "provided"]]}} - + :deploy-repositories {"clojars" {:url "https://clojars.org/repo" diff --git a/target/classes/me/tonsky/persistent_sorted_set/ANode.class b/target/classes/me/tonsky/persistent_sorted_set/ANode.class deleted file mode 100644 index 17b71bbcaff2fd8452416adca740ed80da042248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5029 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 diff --git a/target/classes/me/tonsky/persistent_sorted_set/Chunk.class b/target/classes/me/tonsky/persistent_sorted_set/Chunk.class deleted file mode 100644 index 69a553c58f9300bbe1a86681665b5f9b310519ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3103 zcma)8TT>im8h)DTp=X8;0m5*OOa#Jk3W_FVC1(U3Lr4r5g{aW!>4Xk5-Pqm3hFy2n zTCetUuQu5&PrJ&^USu_d(o~t<8!z?;c)S0@N{P?g4NMZK!h-$|@Asa*=RJJ$zn{PR z9>6d@N}&t6Bv-E`(IUez&dZ9!FeYThVHg*Zn8Zcdau~)XS#g-e>$1Hp!&DWf@rE2+ zN#Z0fX?Qb%w^BHOnG}h$8m=brb_$J{(eRFt-mStM<~0~89KJFKtt&LgJJaj3|iD zEm|wSf&*74ZW_zRV8L*13|_o;(=z2$owM_|6eu}aT25Xj!#7EpRgjol7L~3;=}yD< zEid54f7$kpYXytEl_zY+4o)e^X7}`N%6f04=L+?Yx_L{%{t4T$CQHR@mNzZh6zrP{ z40G{aV_6K27^ zS@Nt1s@&*8$yprHP|(oJD|=SHWYXQ^*{Ar%ospdg_v|Doh&jQ6f^;^w>uGm3w^L|W z&baB89Qw>#Fn);>W??fNsVTSQnbx>1xTeAj9FgGb7{W_Bx{=j!1cN&I(66HiM}w=Q z2faF;6Uved@4?gIBhXONv5ep97(l;*mU0$drggh$jomV>CCLm0=cnCe!?S&Jp+B?Y zmNJHCW$eK6jKFqoWDF;tDVBoGO(yUR$G4fp8D?+B2r|KfmGO{CM-z%tpA_*>s zLC@=WAAivCyg>Id#uPM_{W`l9L}oFc4*#f{wj> zWf>`WVK1&-szN$}6P8~0p0#>*l8o#Qdm+Cwz@KHw7@n>4Dyz5vyp7)jAV0(2dv~-WcUPhLBU~Y zsF7mjt5qY_8O|)(aAq-vGwV2)qC<4kn0PtBj?s`>|XxA8#xC)&B)&tc;t%}$4Ne|HJFTCFIN^L@Utckm28Lp#3Y>w1jZ+EBScftZFC z4Z{l1@e{O1NGP0Dp0f)?s9}~~XrvsDP!q`d1)PHdR+`Y&_NLUy2`N zEOs?@eig}Zk>^+|C;aqvwBDtdZAcd3t6CfJXwI3CL7x8y$GTaMZM<3JgKb4)}DpUMEH z340rX@8plaKAhs}G|ux@m)ZVjxi|St1Q^4gFpfVn>;A(1Uy%zDYewP`IyH1XLbY=E zC&VirK?@fIg|9~h4IGBNf(B0uG+(3r4jNnkhN@O>GoPdC0S?WqK9Cs*8AwV z+h0~1rNtDLP7|ItSd&+_lCy!z_*ibLoS;fc&JIx-IV~+j9M|&d{J&(Dy{yR=C0*q5 zEVW-{rN6zUxsi!JzFkHyhI)b8x|aLsyu;*Jg&O+??d9^R3|T5e`8X={Gm?d`gl1m- E1^ze5SpWb4 diff --git a/target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class b/target/classes/me/tonsky/persistent_sorted_set/IPersistentSortedSet.class deleted file mode 100644 index c396ec60ec1859800d383d0c80afe75264cf7e90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1337 zcma)5U2oD*7=BJ+bTF{OfNgF%KhRZX?2U=hk|k!z5+ZYp-IX^>DJM%AP>v0S z`~}{6J1_hJ{wU*n3XT>E@#eJe^PcBHdF-lqtm27^rz)PQ zs4=YgJ=ft3Cwi-EU)x5{9$XkF?Jjo$a}hEM+@5jh^@p~w15cPQTTajGjszdGn-;$^ z8M2mpF|Y%I85;W8M}A|5$5?enqY=6e7D#2pB+GaIm1S~1=|itWsc0>Cz(0-pZ7$C2 zc8`$VNOH;4l+UjiDtfXvEkbuy|B~!4Sb^xDKuloTX z1mhbP4_?b{%Hsw0^2j2WM*%s8H+^md-oU@SF@|@Xb3c?l_jzC>Nip1?T}_5^Y~?0( zFl@x&X(0^jG0!+phGGnrn@hzxcdN~?Nkb2#&n&E&Vf3C>kU|D3R_U>=rfKJq1H&3w z3i4!A(NmlLvbaYcjr@DWPSIDZ@4u?wf-&4eN#h4WR4<(w`huj!VnwnFOUbEuay~*X zVVxxYMV8W9ov|g6HM(A2N=k@WB2o#JNu(y#s8FQZ_BWK{(Y~V?0VYaHr0@!=#8jbF nj#X@s;3l>rva)rx9VxpXEkeppfdtuVTvnNM=mEA#P{Gb$+-o#g diff --git a/target/classes/me/tonsky/persistent_sorted_set/ISeek.class b/target/classes/me/tonsky/persistent_sorted_set/ISeek.class deleted file mode 100644 index 5d3825ba52d7defd32c777a104f06caa30ecdc99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 405 zcmZutO;3YB5PeIrSc|q+;~y}_uSAX>hzEi;F+njD&kb!S21?nwEGGUd<4q6#0DqKm zN>3Ut^JZt>?7TPMKc8O!F0tQ%jqNU4*s;*F&=)XEqPf7;NsuKkiIXSAlM_xdnre4l zsdSn diff --git a/target/classes/me/tonsky/persistent_sorted_set/JavaIter.class b/target/classes/me/tonsky/persistent_sorted_set/JavaIter.class deleted file mode 100644 index 608e829f303151046a19f67756409ed83f02cf8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 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 F{{xU(k8%J2 diff --git a/target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class b/target/classes/me/tonsky/persistent_sorted_set/PersistentSortedSet.class deleted file mode 100644 index 30f1d5e9d1bf2e75867cc425ee5c30f4f3b9cef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12865 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 Tg6r@H?lOY>p8tF4BY6K0Q%8wf diff --git a/target/classes/me/tonsky/persistent_sorted_set/Settings$1.class b/target/classes/me/tonsky/persistent_sorted_set/Settings$1.class deleted file mode 100644 index ff1d90c221ed23773b63bbfedb4b264db42114e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 795 zcmb7CTWi!n6#iy6O?G2yqgK1tORL7cR54f(9~42#u9kLpp;_!hA4)bEyJ@pCOJ=sx z7ypjWDu{^m2l%7JleDxif)$4Io$p*{=A2)@e;fmNjz>0bV4;c%YE{y1=5*1*t(@G> z#T^TGEi5yvdclW8N26{y@y6WKQmBI?Z^AQ`D9wf5RWj2&-c?+CTm1dtXu=svJL?RU zmr&*tq22A?gkqXfiD3*?t;|J-;V| zX*B92JdN8~CNl@EJZuGZhLth*Pu}SN^^JaTdK>R4gR?FK&(_jVDXtjY^A`Q#K94j* z^P=S#8vhDC`(5)eOs9Fn$IaHx<^|JUFqpw$tKWOW(E5)gOJ;E*!n7D7P1OkcodLmi z`}KyqObb+{g<@_k|L1FuvwIE!zKL+%x#N2-7^7$jG)k~xJGDDDd2ix Ona2{LUEG%xxAF%s;KMTj diff --git a/target/classes/me/tonsky/persistent_sorted_set/Settings.class b/target/classes/me/tonsky/persistent_sorted_set/Settings.class deleted file mode 100644 index baf639b077ea6200a7269f685577dfcbbde62bf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3111 zcmb7GYf}?f7=BI?*btVxfRc(@BZhEEwN@>lMG%w<+7i5=ZRwI6VIkS1yBn2O`#Y*L zoqlU)I{jd6g&CQt?U#0@KdS9>c0&?@sR*-Y&z^lR&-=WW&ENmL_zl1rd>z3-T&#nR z3AIeBQ!hVK=O3%xY#q9hico7#!}SQ- za6`k*2-?;CRx7=I5VQ5MHq5-MO`b6zHAK$)uaNoYl2tLE4V#xYBYLoV@MI%z`7`Ld*}pEJ|R3*K!kpU+CeqE=f>fw5haPA-~`v6z(^ z!niF^J7QXjKEp7ZrcFy;E#?-bJ*N-|1moW8_>{U0ya50u z-;i>R^y-XJ@Qi6-K&?5`8rx1bosjOP9Z9H@4+@5rnU>&GLOKWH zw_mTHlCH~YaG01#CEtuFXRJ!5S~8Vs($f?7iOw3Qd&CWw=F~ zN&_QvLpx>8$LHpD*KM$+k*RVT9!Y0QB@s#Gi*{O0nkrWfTc`(B;R)1Vm5w_jRW=RClq_?ZIlMI!e(p9mocJ1yAWzMZAx5U_0d$Z>pSm1D=>O(lKqBDOWPbl3R2% z+*eutQXp1QF|B*~RhjhN_~H1Ugf$HhIg6+B`PE`U;G1faN=kkOUFO8fN+;$^4p%`e zVXr!QMN#E;7x6n)^1Do!l8JF6n@yRn9MTQ3I>5ff21FwI9Dzjta|9Cu&k_2W=lnQw?LZCJ zdP?f3<0rk5|7~dUEcKTx9Yv5kKuZM025KtG5xM|cQA-u&dZ0={z0nfU-4r^g(CH}* zmK0nX3R2;i!W#V+T1x;UqW^nn&rmy41zwcE2R!gSgbW-Y_%4JI^@wWWPm*>L)kUff zk*FA{N>Zu?fqy9w)SwHEzgVasZ~`Yw2o4c}ALFkGgdYAx4WFP|@3@ESyJg{nd{V+S z2?}o)P4LyJvS_p<8f*@47gWK&htphDx~gpkpKf!nQ1Y8l5*^}Qx$mU=(4S~gw{>Wb zp@n`xD5(7S{P@^Ud-2yG&_1g4&ftBn%H;|)FM*@a5PpKz#5(F8qka>S`3=;+^bVV& zq3!_Hom$HXYn_s^EezupijKftM8Y{rf#K!BV=!PMf0tE6l}=hocsu zm((!HO3gmXql~3 z9()#XtGgf%4dz)NL2uJQ5QX2yX_a9%r0(gWC8x=GxETE}n-9`&HOx!f# z3RF~-A4w%p-|rnq?<0R0rAPj5|5(QAG1J-MVWHC^V6c;zxyG4x{Z3KBMgI%|73f>wRQtX$o%8A?RU&kwst&*KiMYDhMpKs%)^z kc;@pVUFumHzuDbz$}Rt)&g?Evr)kpUI@ZV(RNe9VPrmG`fB*mh diff --git a/target/stale/leiningen.core.classpath.extract-native-dependencies b/target/stale/leiningen.core.classpath.extract-native-dependencies deleted file mode 100644 index 9be2eed..0000000 --- a/target/stale/leiningen.core.classpath.extract-native-dependencies +++ /dev/null @@ -1 +0,0 @@ -[{:dependencies {com.cognitect/transit-java {:vsn "1.0.362", :native-prefix nil}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil}, org.nrepl/incomplete {:vsn "0.1.0", :native-prefix nil}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil}, com.googlecode.json-simple/json-simple {:vsn "1.1.1", :native-prefix nil}, org.clojure/google-closure-library {:vsn "0.0-20211011-0726fdeb", :native-prefix nil}, org.clojure/clojurescript {:vsn "1.11.60", :native-prefix nil}, com.fasterxml.jackson.core/jackson-core {:vsn "2.8.7", :native-prefix nil}, org.clojure/google-closure-library-third-party {:vsn "0.0-20211011-0726fdeb", :native-prefix nil}, org.javassist/javassist {:vsn "3.18.1-GA", :native-prefix nil}, org.msgpack/msgpack {:vsn "0.6.12", :native-prefix nil}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil}, nrepl {:vsn "0.8.3", :native-prefix nil}, com.google.javascript/closure-compiler-unshaded {:vsn "v20220502", :native-prefix nil}, javax.xml.bind/jaxb-api {:vsn "2.3.0", :native-prefix nil}}, :native-path "target/native"} {:native-path "target/native", :dependencies {com.cognitect/transit-java {:vsn "1.0.362", :native-prefix nil, :native? false}, org.clojure/clojure {:vsn "1.11.1", :native-prefix nil, :native? false}, org.nrepl/incomplete {:vsn "0.1.0", :native-prefix nil, :native? false}, org.clojure/core.specs.alpha {:vsn "0.2.62", :native-prefix nil, :native? false}, org.clojure/spec.alpha {:vsn "0.3.218", :native-prefix nil, :native? false}, com.googlecode.json-simple/json-simple {:vsn "1.1.1", :native-prefix nil, :native? false}, org.clojure/google-closure-library {:vsn "0.0-20211011-0726fdeb", :native-prefix nil, :native? false}, org.clojure/clojurescript {:vsn "1.11.60", :native-prefix nil, :native? false}, com.fasterxml.jackson.core/jackson-core {:vsn "2.8.7", :native-prefix nil, :native? false}, org.clojure/google-closure-library-third-party {:vsn "0.0-20211011-0726fdeb", :native-prefix nil, :native? false}, org.javassist/javassist {:vsn "3.18.1-GA", :native-prefix nil, :native? false}, org.msgpack/msgpack {:vsn "0.6.12", :native-prefix nil, :native? false}, org.clojure/tools.reader {:vsn "1.3.6", :native-prefix nil, :native? false}, nrepl {:vsn "0.8.3", :native-prefix nil, :native? false}, com.google.javascript/closure-compiler-unshaded {:vsn "v20220502", :native-prefix nil, :native? false}, javax.xml.bind/jaxb-api {:vsn "2.3.0", :native-prefix nil, :native? false}}}] \ No newline at end of file From 78fd0ee7be9733aa76225629ee41525e8cd2c1bd Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Mon, 18 Dec 2023 19:48:20 +0800 Subject: [PATCH 22/28] Add delete fn --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 7 +++++++ src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index e7044fa..e23d160 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -406,6 +406,8 @@ (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] + (when-let [unused-address (aget addresses idx)] + (protocol/delete storage unused-address)) (if (<= (arrays/alength new-children) max-len) ;; ok as is (arrays/array (new-node new-keys new-children new-addresses)) @@ -435,6 +437,11 @@ new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) new-children (splice children left-idx right-idx disjned) new-addresses (splice addresses left-idx right-idx (arrays/make-array (count disjned)))] + (when (> right-idx left-idx) + (let [unused-addresses (.slice (arrays/aclone addresses) left-idx right-idx)] + (doseq [unused-address unused-addresses] + (when unused-address + (protocol/delete storage unused-address))))) (rotate (new-node new-keys new-children new-addresses) root? left right)))))))) (deftype Leaf [keys] diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs index a3d5207..f5c1d49 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -3,4 +3,5 @@ (defprotocol IStorage (restore [this address]) (accessed [this address]) - (store [this node])) + (store [this node]) + (delete [this address])) From 2ef401b11b00a15d4906bd8fc66538b7d5d4e5d6 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 19 Dec 2023 20:45:50 +0800 Subject: [PATCH 23/28] perf: reuse node address as possible as we can --- .../me/tonsky/persistent_sorted_set.cljs | 138 +++++++++++------- .../persistent_sorted_set/protocol.cljs | 2 +- 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index e23d160..4396a72 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -5,7 +5,8 @@ (:refer-clojure :exclude [iter conj disj sorted-set sorted-set-by]) (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] - [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol]) + [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol] + [goog.object :as gobj]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -269,7 +270,7 @@ (defprotocol INode (node-lim-key [_]) (node-len [_]) - (node-merge [_ next]) + (node-merge [_ next storage]) (node-merge-n-split [_ next]) (node-lookup [_ cmp key storage]) (node-child [_ idx storage]) @@ -278,9 +279,9 @@ (defn- set-child! [children idx child] - (aset children idx child)) + (arrays/aset children idx child)) -(defn- rotate [node root? left right] +(defn- rotate [node root? left right storage] (cond ;; root never merges root? @@ -292,11 +293,11 @@ ;; left and this can be merged to one (and left (<= (node-len left) min-len)) - (return-array (node-merge left node) right) + (return-array (node-merge left node storage) right) ;; right and this can be merged to one (and right (<= (node-len right) min-len)) - (return-array left (node-merge node right)) + (return-array left (node-merge node right storage)) ;; left has fewer nodes, redestribute with it (and left (or (nil? right) @@ -312,25 +313,36 @@ (defprotocol IStore (store-aux [this storage])) +(defn- node-addresses->array + [^js children] + (let [children-addresses (map #(gobj/get % "address") children)] + (arrays/into-array children-addresses))) + (defn- ensure-addresses! [^Node node size] (when (empty? (.-addresses node)) - (set! (.-addresses node) (arrays/make-array size)))) + (let [addresses (if (seq (.-children node)) + (node-addresses->array (.-children node)) + (arrays/make-array size))] + (set! (.-addresses node) addresses)))) (defn- set-address! [addresses idx address] - (aset addresses idx address)) + (arrays/aset addresses idx address)) (declare Node) (defn new-node - [keys children addresses] + [keys children addresses & {:keys [address dirty?] + :or {dirty? true}}] (let [addresses (if (nil? addresses) - (arrays/make-array (arrays/alength keys)) + (if (seq children) + (node-addresses->array children) + (arrays/make-array (arrays/alength keys))) addresses)] - (Node. keys children addresses))) + (Node. keys children addresses address dirty?))) -(deftype Node [keys children ^:mutable addresses] +(deftype Node [keys children ^:mutable addresses ^:mutable address dirty?] IStore (store-aux [this ^IStorage storage] (ensure-addresses! this (count children)) @@ -338,14 +350,18 @@ ;; Children first (dorun (map-indexed - (fn [idx address] - (when (nil? address) - (assert (not (nil? children))) - (assert (not (nil? (aget children idx)))) - (let [address (store-aux (aget children idx) storage)] - (set-address! addresses idx address)))) + (fn [idx addr] + (let [^object child (aget children idx)] + (when (and child (or (nil? addr) (.-dirty? child))) + (assert (not (nil? children))) + (assert (not (nil? child))) + (let [child-address (store-aux child storage)] + (set-address! addresses idx child-address))))) addresses)) - (protocol/store storage this)) + + (let [new-address (protocol/store storage this address)] + (set! address new-address) + new-address)) INode (node-lim-key [_] @@ -354,15 +370,18 @@ (node-len [_] (arrays/alength keys)) - (node-merge [this ^Node next] + (node-merge [this ^Node next ^IStorage storage] (assert (and (= (count keys) (count children)) (= (count (.-keys next)) (count (.-children next))))) (ensure-addresses! this (count children)) (ensure-addresses! next (count (.-children next))) + (when-let [next-address (.-address next)] + (protocol/delete storage next-address)) (new-node (arrays/aconcat keys (.-keys next)) (arrays/aconcat children (.-children next)) - (arrays/aconcat addresses (.-addresses next)))) + (arrays/aconcat addresses (.-addresses next)) + {:address address})) (node-merge-n-split [this ^Node next] (ensure-addresses! this (count children)) @@ -372,17 +391,19 @@ as (merge-n-split addresses (.-addresses next))] (return-array (new-node (arrays/aget ks 0) (arrays/aget ps 0) - (arrays/aget as 0)) + (arrays/aget as 0) + {:address address}) (new-node (arrays/aget ks 1) (arrays/aget ps 1) - (arrays/aget as 1))))) + (arrays/aget as 1) + {:address (.-address next)})))) (node-child [_this idx ^IStorage storage] (when-not (= -1 idx) ;; TODO: Remove when the implementation is stable (assert (or (and (seq children) (arrays/aget children idx)) ; child exists (and (seq addresses) (arrays/aget addresses idx))) - (str "Neither child or address exists" {:keys keys :addresses addresses :idx idx :children children})) + (str "Neither child or address exists" {:address address :keys keys :addresses addresses :idx idx :children children})) (let [child (arrays/aget children idx) address (when addresses (arrays/aget addresses idx))] (if-not child @@ -405,18 +426,17 @@ (when nodes (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) - new-addresses (splice addresses idx (inc idx) (arrays/make-array (count nodes)))] - (when-let [unused-address (aget addresses idx)] - (protocol/delete storage unused-address)) + new-addresses (splice addresses idx (inc idx) (node-addresses->array nodes))] (if (<= (arrays/alength new-children) max-len) ;; ok as is - (arrays/array (new-node new-keys new-children new-addresses)) + (arrays/array (new-node new-keys new-children new-addresses {:address address})) ;; gotta split it up (let [middle (arrays/half (arrays/alength new-children))] (arrays/array (new-node (.slice new-keys 0 middle) (.slice new-children 0 middle) - (.slice new-addresses 0 middle)) + (.slice new-addresses 0 middle) + {:address address}) (new-node (.slice new-keys middle) (.slice new-children middle) (.slice new-addresses middle))))))))) @@ -436,18 +456,30 @@ right-idx (if right-child (+ 2 idx) (+ 1 idx)) new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) new-children (splice children left-idx right-idx disjned) - new-addresses (splice addresses left-idx right-idx (arrays/make-array (count disjned)))] + new-addresses (splice addresses left-idx right-idx (node-addresses->array disjned))] (when (> right-idx left-idx) - (let [unused-addresses (.slice (arrays/aclone addresses) left-idx right-idx)] + (let [unused-addresses (.slice addresses left-idx right-idx) + new-addresses-set (set (remove nil? new-addresses))] (doseq [unused-address unused-addresses] - (when unused-address + (when (and unused-address (not (contains? new-addresses-set unused-address))) (protocol/delete storage unused-address))))) - (rotate (new-node new-keys new-children new-addresses) root? left right)))))))) + (rotate (new-node new-keys new-children new-addresses {:address address}) + root? left right storage)))))))) + +(declare Leaf) +(defn- new-leaf + [keys & {:keys [address dirty?] + :or {dirty? true}}] + (Leaf. keys address dirty?)) -(deftype Leaf [keys] +(deftype Leaf [keys ^:mutable address dirty?] IStore (store-aux [this storage] - (protocol/store storage this)) + (if (or dirty? (nil? address)) + (let [new-address (protocol/store storage this address)] + (set! address new-address) + new-address) + address)) INode (node-lim-key [_] @@ -458,13 +490,15 @@ (node-len [_] (arrays/alength keys)) - (node-merge [_ next] - (Leaf. (arrays/aconcat keys (.-keys next)))) + (node-merge [_ ^Object next storage] + (when-let [next-address (.-address next)] + (protocol/delete storage next-address)) + (new-leaf (arrays/aconcat keys (.-keys next)) {:address address})) - (node-merge-n-split [_ next] + (node-merge-n-split [_ ^Leaf next] (let [ks (merge-n-split keys (.-keys next))] - (return-array (Leaf. (arrays/aget ks 0)) - (Leaf. (arrays/aget ks 1))))) + (return-array (new-leaf (arrays/aget ks 0) {:address address}) + (new-leaf (arrays/aget ks 1) {:address (.-address next)})))) (node-child [_this idx _storage] (arrays/aget keys idx)) @@ -489,22 +523,22 @@ (if (> idx middle) ;; new key goes to the second half (arrays/array - (Leaf. (.slice keys 0 middle)) - (Leaf. (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) + (new-leaf (.slice keys 0 middle) {:address address}) + (new-leaf (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) ;; new key goes to the first half (arrays/array - (Leaf. (cut-n-splice keys 0 middle idx idx (arrays/array key))) - (Leaf. (.slice keys middle keys-l))))) + (new-leaf (cut-n-splice keys 0 middle idx idx (arrays/array key)) {:address address}) + (new-leaf (.slice keys middle keys-l))))) ;; ok as is :else - (arrays/array (Leaf. (splice keys idx idx (arrays/array key))))))) + (arrays/array (new-leaf (splice keys idx idx (arrays/array key)) {:address address}))))) (node-disj [_ cmp key root? left right storage] (let [idx (lookup-exact cmp keys key)] (when-not (== -1 idx) ;; key is here (let [new-keys (splice keys idx (inc idx) (arrays/array))] - (rotate (Leaf. new-keys) root? left right)))))) + (rotate (new-leaf new-keys {:address address}) root? left right storage)))))) ;; BTSet @@ -534,7 +568,7 @@ (-meta [_] meta) IEmptyableCollection - (-empty [_] (BTSet. storage (Leaf. (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) + (-empty [_] (BTSet. storage (new-leaf (arrays/array)) 0 0 comparator meta uninitialized-hash uninitialized-address)) IEquiv (-equiv [this other] @@ -1142,7 +1176,7 @@ :else (alter-btset set (new-node (arrays/amap node-lim-key roots) roots - (arrays/make-array (count roots))) + (node-addresses->array roots)) (inc (.-shift set)) (inc (.-cnt set)))))) @@ -1210,12 +1244,12 @@ ([cmp arr _len opts] (let [leaves (->> arr (arr-partition-approx min-len max-len) - (arr-map-inplace #(Leaf. %))) + (arr-map-inplace new-leaf)) storage (:storage opts)] (loop [current-level leaves shift 0] (case (count current-level) - 0 (BTSet. storage (Leaf. (arrays/array)) 0 0 cmp nil + 0 (BTSet. storage (new-leaf (arrays/array)) 0 0 cmp nil uninitialized-hash uninitialized-address) 1 (BTSet. storage (first current-level) shift (arrays/alength arr) cmp nil uninitialized-hash uninitialized-address) @@ -1234,11 +1268,11 @@ (defn sorted-set* "Create a set with custom comparator, metadata and settings" [opts] - (BTSet. (:storage opts) (Leaf. (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) + (BTSet. (:storage opts) (new-leaf (arrays/array)) 0 0 (or (:cmp opts) compare) (:meta opts) uninitialized-hash uninitialized-address)) (defn sorted-set-by - ([cmp] (BTSet. nil (Leaf. (arrays/array)) 0 0 cmp nil + ([cmp] (BTSet. nil (new-leaf (arrays/array)) 0 0 cmp nil uninitialized-hash uninitialized-address)) ([cmp & keys] (from-sequential cmp keys))) diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs index f5c1d49..16c5133 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -3,5 +3,5 @@ (defprotocol IStorage (restore [this address]) (accessed [this address]) - (store [this node]) + (store [this node address]) (delete [this address])) From efd90bd46982da61f8f46faad4f3df02094c3228 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 19 Dec 2023 21:03:47 +0800 Subject: [PATCH 24/28] fix: mutable dirty? to avoid saving nodes --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 4396a72..29e7571 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -342,7 +342,7 @@ addresses)] (Node. keys children addresses address dirty?))) -(deftype Node [keys children ^:mutable addresses ^:mutable address dirty?] +(deftype Node [keys children ^:mutable addresses ^:mutable address ^:mutable dirty?] IStore (store-aux [this ^IStorage storage] (ensure-addresses! this (count children)) @@ -360,6 +360,7 @@ addresses)) (let [new-address (protocol/store storage this address)] + (set! dirty? false) (set! address new-address) new-address)) @@ -472,11 +473,12 @@ :or {dirty? true}}] (Leaf. keys address dirty?)) -(deftype Leaf [keys ^:mutable address dirty?] +(deftype Leaf [keys ^:mutable address ^:mutable dirty?] IStore (store-aux [this storage] (if (or dirty? (nil? address)) (let [new-address (protocol/store storage this address)] + (set! dirty? false) (set! address new-address) new-address) address)) From 9315caafaedaeffe8650ac8037afbd6f89fccb29 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Dec 2023 18:52:42 +0800 Subject: [PATCH 25/28] fix: safe delete for unused addresses --- src-clojure/me/tonsky/persistent_sorted_set.cljs | 12 +++++------- .../me/tonsky/persistent_sorted_set/protocol.cljs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 29e7571..41aaf6d 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -378,7 +378,7 @@ (ensure-addresses! this (count children)) (ensure-addresses! next (count (.-children next))) (when-let [next-address (.-address next)] - (protocol/delete storage next-address)) + (protocol/delete storage [next-address] nil)) (new-node (arrays/aconcat keys (.-keys next)) (arrays/aconcat children (.-children next)) (arrays/aconcat addresses (.-addresses next)) @@ -459,11 +459,9 @@ new-children (splice children left-idx right-idx disjned) new-addresses (splice addresses left-idx right-idx (node-addresses->array disjned))] (when (> right-idx left-idx) - (let [unused-addresses (.slice addresses left-idx right-idx) - new-addresses-set (set (remove nil? new-addresses))] - (doseq [unused-address unused-addresses] - (when (and unused-address (not (contains? new-addresses-set unused-address))) - (protocol/delete storage unused-address))))) + (let [unused-addresses (.slice addresses left-idx right-idx)] + (when storage + (protocol/delete storage unused-addresses new-addresses)))) (rotate (new-node new-keys new-children new-addresses {:address address}) root? left right storage)))))))) @@ -494,7 +492,7 @@ (node-merge [_ ^Object next storage] (when-let [next-address (.-address next)] - (protocol/delete storage next-address)) + (protocol/delete storage [next-address] nil)) (new-leaf (arrays/aconcat keys (.-keys next)) {:address address})) (node-merge-n-split [_ ^Leaf next] diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs index 16c5133..ca6cf17 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -4,4 +4,4 @@ (restore [this address]) (accessed [this address]) (store [this node address]) - (delete [this address])) + (delete [this unused-addresses kept-addresses])) From 081320b9e92ae7ecf4f26fc5547e9c1cf8848897 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 21 Dec 2023 20:26:21 +0800 Subject: [PATCH 26/28] fix: release bug --- .../me/tonsky/persistent_sorted_set.cljs | 102 +++++++++--------- .../persistent_sorted_set/protocol.cljs | 2 +- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 41aaf6d..1aa2c24 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -6,7 +6,8 @@ (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol] - [goog.object :as gobj]) + [goog.object :as gobj] + [clojure.set :as set]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -315,16 +316,16 @@ (defn- node-addresses->array [^js children] - (let [children-addresses (map #(gobj/get % "address") children)] + (let [children-addresses (map #(gobj/get % "_address") children)] (arrays/into-array children-addresses))) (defn- ensure-addresses! [^Node node size] - (when (empty? (.-addresses node)) + (when (empty? (.-_addresses node)) (let [addresses (if (seq (.-children node)) (node-addresses->array (.-children node)) (arrays/make-array size))] - (set! (.-addresses node) addresses)))) + (set! (.-_addresses node) addresses)))) (defn- set-address! [addresses idx address] @@ -333,16 +334,16 @@ (declare Node) (defn new-node - [keys children addresses & {:keys [address dirty?] - :or {dirty? true}}] + [keys children addresses & {:keys [address dirty] + :or {dirty true}}] (let [addresses (if (nil? addresses) (if (seq children) (node-addresses->array children) (arrays/make-array (arrays/alength keys))) addresses)] - (Node. keys children addresses address dirty?))) + (Node. keys children addresses address dirty))) -(deftype Node [keys children ^:mutable addresses ^:mutable address ^:mutable dirty?] +(deftype Node [keys children ^:mutable _addresses ^:mutable _address ^:mutable _dirty] IStore (store-aux [this ^IStorage storage] (ensure-addresses! this (count children)) @@ -352,16 +353,16 @@ (map-indexed (fn [idx addr] (let [^object child (aget children idx)] - (when (and child (or (nil? addr) (.-dirty? child))) + (when (and child (or (nil? addr) (.-_dirty child))) (assert (not (nil? children))) (assert (not (nil? child))) (let [child-address (store-aux child storage)] - (set-address! addresses idx child-address))))) - addresses)) + (set-address! _addresses idx child-address))))) + _addresses)) - (let [new-address (protocol/store storage this address)] - (set! dirty? false) - (set! address new-address) + (let [new-address (protocol/store storage this _address)] + (set! _dirty false) + (set! _address new-address) new-address)) INode @@ -377,36 +378,36 @@ (count (.-children next))))) (ensure-addresses! this (count children)) (ensure-addresses! next (count (.-children next))) - (when-let [next-address (.-address next)] - (protocol/delete storage [next-address] nil)) + (when-let [next-address (.-_address next)] + (protocol/delete storage [next-address])) (new-node (arrays/aconcat keys (.-keys next)) (arrays/aconcat children (.-children next)) - (arrays/aconcat addresses (.-addresses next)) - {:address address})) + (arrays/aconcat _addresses (.-_addresses next)) + {:address _address})) (node-merge-n-split [this ^Node next] (ensure-addresses! this (count children)) (ensure-addresses! next (count (.-children next))) (let [ks (merge-n-split keys (.-keys next)) ps (merge-n-split children (.-children next)) - as (merge-n-split addresses (.-addresses next))] + as (merge-n-split _addresses (.-_addresses next))] (return-array (new-node (arrays/aget ks 0) (arrays/aget ps 0) (arrays/aget as 0) - {:address address}) + {:address _address}) (new-node (arrays/aget ks 1) (arrays/aget ps 1) (arrays/aget as 1) - {:address (.-address next)})))) + {:address (.-_address next)})))) (node-child [_this idx ^IStorage storage] (when-not (= -1 idx) ;; TODO: Remove when the implementation is stable (assert (or (and (seq children) (arrays/aget children idx)) ; child exists - (and (seq addresses) (arrays/aget addresses idx))) - (str "Neither child or address exists" {:address address :keys keys :addresses addresses :idx idx :children children})) + (and (seq _addresses) (arrays/aget _addresses idx))) + (str "Neither child or address exists" {:address _address :keys keys :addresses _addresses :idx idx :children children})) (let [child (arrays/aget children idx) - address (when addresses (arrays/aget addresses idx))] + address (when _addresses (arrays/aget _addresses idx))] (if-not child (let [child (protocol/restore storage address)] (set-child! children idx child)) @@ -427,17 +428,17 @@ (when nodes (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) - new-addresses (splice addresses idx (inc idx) (node-addresses->array nodes))] + new-addresses (splice _addresses idx (inc idx) (node-addresses->array nodes))] (if (<= (arrays/alength new-children) max-len) ;; ok as is - (arrays/array (new-node new-keys new-children new-addresses {:address address})) + (arrays/array (new-node new-keys new-children new-addresses {:address _address})) ;; gotta split it up (let [middle (arrays/half (arrays/alength new-children))] (arrays/array (new-node (.slice new-keys 0 middle) (.slice new-children 0 middle) (.slice new-addresses 0 middle) - {:address address}) + {:address _address}) (new-node (.slice new-keys middle) (.slice new-children middle) (.slice new-addresses middle))))))))) @@ -457,29 +458,30 @@ right-idx (if right-child (+ 2 idx) (+ 1 idx)) new-keys (check-n-splice cmp keys left-idx right-idx (arrays/amap node-lim-key disjned)) new-children (splice children left-idx right-idx disjned) - new-addresses (splice addresses left-idx right-idx (node-addresses->array disjned))] + new-addresses (splice _addresses left-idx right-idx (node-addresses->array disjned))] (when (> right-idx left-idx) - (let [unused-addresses (.slice addresses left-idx right-idx)] - (when storage - (protocol/delete storage unused-addresses new-addresses)))) - (rotate (new-node new-keys new-children new-addresses {:address address}) + (let [cut-addresses (.slice _addresses left-idx right-idx) + removed (set/difference (set (remove nil? cut-addresses)) (set (remove nil? new-addresses)))] + (when (and storage (seq removed)) + (protocol/delete storage removed)))) + (rotate (new-node new-keys new-children new-addresses {:address _address}) root? left right storage)))))))) (declare Leaf) (defn- new-leaf - [keys & {:keys [address dirty?] - :or {dirty? true}}] - (Leaf. keys address dirty?)) + [keys & {:keys [address dirty] + :or {dirty true}}] + (Leaf. keys address dirty)) -(deftype Leaf [keys ^:mutable address ^:mutable dirty?] +(deftype Leaf [keys ^:mutable _address ^:mutable _dirty] IStore (store-aux [this storage] - (if (or dirty? (nil? address)) - (let [new-address (protocol/store storage this address)] - (set! dirty? false) - (set! address new-address) + (if (or _dirty (nil? _address)) + (let [new-address (protocol/store storage this _address)] + (set! _dirty false) + (set! _address new-address) new-address) - address)) + _address)) INode (node-lim-key [_] @@ -491,14 +493,14 @@ (arrays/alength keys)) (node-merge [_ ^Object next storage] - (when-let [next-address (.-address next)] - (protocol/delete storage [next-address] nil)) - (new-leaf (arrays/aconcat keys (.-keys next)) {:address address})) + (when-let [next-address (.-_address next)] + (protocol/delete storage [next-address])) + (new-leaf (arrays/aconcat keys (.-keys next)) {:address _address})) (node-merge-n-split [_ ^Leaf next] (let [ks (merge-n-split keys (.-keys next))] - (return-array (new-leaf (arrays/aget ks 0) {:address address}) - (new-leaf (arrays/aget ks 1) {:address (.-address next)})))) + (return-array (new-leaf (arrays/aget ks 0) {:address _address}) + (new-leaf (arrays/aget ks 1) {:address (.-_address next)})))) (node-child [_this idx _storage] (arrays/aget keys idx)) @@ -523,22 +525,22 @@ (if (> idx middle) ;; new key goes to the second half (arrays/array - (new-leaf (.slice keys 0 middle) {:address address}) + (new-leaf (.slice keys 0 middle) {:address _address}) (new-leaf (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) ;; new key goes to the first half (arrays/array - (new-leaf (cut-n-splice keys 0 middle idx idx (arrays/array key)) {:address address}) + (new-leaf (cut-n-splice keys 0 middle idx idx (arrays/array key)) {:address _address}) (new-leaf (.slice keys middle keys-l))))) ;; ok as is :else - (arrays/array (new-leaf (splice keys idx idx (arrays/array key)) {:address address}))))) + (arrays/array (new-leaf (splice keys idx idx (arrays/array key)) {:address _address}))))) (node-disj [_ cmp key root? left right storage] (let [idx (lookup-exact cmp keys key)] (when-not (== -1 idx) ;; key is here (let [new-keys (splice keys idx (inc idx) (arrays/array))] - (rotate (new-leaf new-keys {:address address}) root? left right storage)))))) + (rotate (new-leaf new-keys {:address _address}) root? left right storage)))))) ;; BTSet diff --git a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs index ca6cf17..954a9f1 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set/protocol.cljs @@ -4,4 +4,4 @@ (restore [this address]) (accessed [this address]) (store [this node address]) - (delete [this unused-addresses kept-addresses])) + (delete [this unused-addresses])) From 85f7bb3378a8528cd2732fb32cb436312713c7dd Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 4 Jan 2024 20:15:04 +0800 Subject: [PATCH 27/28] Update new-node and new-leaf to be multi-arity fns --- .../me/tonsky/persistent_sorted_set.cljs | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 1aa2c24..4fb4921 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -6,7 +6,6 @@ (:require [me.tonsky.persistent-sorted-set.arrays :as arrays] [me.tonsky.persistent-sorted-set.protocol :refer [IStorage] :as protocol] - [goog.object :as gobj] [clojure.set :as set]) (:require-macros [me.tonsky.persistent-sorted-set.arrays :as arrays])) @@ -316,7 +315,7 @@ (defn- node-addresses->array [^js children] - (let [children-addresses (map #(gobj/get % "_address") children)] + (let [children-addresses (map #(.-_address %) children)] (arrays/into-array children-addresses))) (defn- ensure-addresses! @@ -334,14 +333,17 @@ (declare Node) (defn new-node - [keys children addresses & {:keys [address dirty] - :or {dirty true}}] - (let [addresses (if (nil? addresses) - (if (seq children) - (node-addresses->array children) - (arrays/make-array (arrays/alength keys))) - addresses)] - (Node. keys children addresses address dirty))) + ([keys children addresses] + (new-node keys children addresses nil)) + ([keys children addresses address] + (new-node keys children addresses address true)) + ([keys children addresses address dirty?] + (let [addresses (if (nil? addresses) + (if (seq children) + (node-addresses->array children) + (arrays/make-array (arrays/alength keys))) + addresses)] + (Node. keys children addresses address dirty?)))) (deftype Node [keys children ^:mutable _addresses ^:mutable _address ^:mutable _dirty] IStore @@ -383,7 +385,7 @@ (new-node (arrays/aconcat keys (.-keys next)) (arrays/aconcat children (.-children next)) (arrays/aconcat _addresses (.-_addresses next)) - {:address _address})) + _address)) (node-merge-n-split [this ^Node next] (ensure-addresses! this (count children)) @@ -394,7 +396,7 @@ (return-array (new-node (arrays/aget ks 0) (arrays/aget ps 0) (arrays/aget as 0) - {:address _address}) + _address) (new-node (arrays/aget ks 1) (arrays/aget ps 1) (arrays/aget as 1) @@ -431,14 +433,14 @@ new-addresses (splice _addresses idx (inc idx) (node-addresses->array nodes))] (if (<= (arrays/alength new-children) max-len) ;; ok as is - (arrays/array (new-node new-keys new-children new-addresses {:address _address})) + (arrays/array (new-node new-keys new-children new-addresses _address)) ;; gotta split it up (let [middle (arrays/half (arrays/alength new-children))] (arrays/array (new-node (.slice new-keys 0 middle) (.slice new-children 0 middle) (.slice new-addresses 0 middle) - {:address _address}) + _address) (new-node (.slice new-keys middle) (.slice new-children middle) (.slice new-addresses middle))))))))) @@ -464,14 +466,17 @@ removed (set/difference (set (remove nil? cut-addresses)) (set (remove nil? new-addresses)))] (when (and storage (seq removed)) (protocol/delete storage removed)))) - (rotate (new-node new-keys new-children new-addresses {:address _address}) + (rotate (new-node new-keys new-children new-addresses _address) root? left right storage)))))))) (declare Leaf) (defn- new-leaf - [keys & {:keys [address dirty] - :or {dirty true}}] - (Leaf. keys address dirty)) + ([keys] + (new-leaf keys nil)) + ([keys address] + (new-leaf keys address true)) + ([keys address dirty] + (Leaf. keys address dirty))) (deftype Leaf [keys ^:mutable _address ^:mutable _dirty] IStore @@ -495,12 +500,12 @@ (node-merge [_ ^Object next storage] (when-let [next-address (.-_address next)] (protocol/delete storage [next-address])) - (new-leaf (arrays/aconcat keys (.-keys next)) {:address _address})) + (new-leaf (arrays/aconcat keys (.-keys next)) _address)) (node-merge-n-split [_ ^Leaf next] (let [ks (merge-n-split keys (.-keys next))] - (return-array (new-leaf (arrays/aget ks 0) {:address _address}) - (new-leaf (arrays/aget ks 1) {:address (.-_address next)})))) + (return-array (new-leaf (arrays/aget ks 0) _address) + (new-leaf (arrays/aget ks 1) (.-_address next))))) (node-child [_this idx _storage] (arrays/aget keys idx)) @@ -525,22 +530,22 @@ (if (> idx middle) ;; new key goes to the second half (arrays/array - (new-leaf (.slice keys 0 middle) {:address _address}) + (new-leaf (.slice keys 0 middle) _address) (new-leaf (cut-n-splice keys middle keys-l idx idx (arrays/array key)))) ;; new key goes to the first half (arrays/array - (new-leaf (cut-n-splice keys 0 middle idx idx (arrays/array key)) {:address _address}) + (new-leaf (cut-n-splice keys 0 middle idx idx (arrays/array key)) _address) (new-leaf (.slice keys middle keys-l))))) ;; ok as is :else - (arrays/array (new-leaf (splice keys idx idx (arrays/array key)) {:address _address}))))) + (arrays/array (new-leaf (splice keys idx idx (arrays/array key)) _address))))) (node-disj [_ cmp key root? left right storage] (let [idx (lookup-exact cmp keys key)] (when-not (== -1 idx) ;; key is here (let [new-keys (splice keys idx (inc idx) (arrays/array))] - (rotate (new-leaf new-keys {:address _address}) root? left right storage)))))) + (rotate (new-leaf new-keys _address) root? left right storage)))))) ;; BTSet From d1c8ac4feb061bc8af12654b1199f63b72d6e41c Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Thu, 22 Aug 2024 16:52:05 +0800 Subject: [PATCH 28/28] fix: node could be missing --- .../me/tonsky/persistent_sorted_set.cljs | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src-clojure/me/tonsky/persistent_sorted_set.cljs b/src-clojure/me/tonsky/persistent_sorted_set.cljs index 4fb4921..51652d9 100644 --- a/src-clojure/me/tonsky/persistent_sorted_set.cljs +++ b/src-clojure/me/tonsky/persistent_sorted_set.cljs @@ -400,7 +400,7 @@ (new-node (arrays/aget ks 1) (arrays/aget ps 1) (arrays/aget as 1) - {:address (.-_address next)})))) + (.-_address next))))) (node-child [_this idx ^IStorage storage] (when-not (= -1 idx) @@ -426,7 +426,7 @@ (ensure-addresses! this (count children)) (let [idx (binary-search-l cmp keys (- (arrays/alength keys) 2) key) child (node-child this idx storage) - nodes (node-conj child cmp key storage)] + nodes (when child (node-conj child cmp key storage))] (when nodes (let [new-keys (check-n-splice cmp keys idx (inc idx) (arrays/amap node-lim-key nodes)) new-children (splice children idx (inc idx) nodes) @@ -470,7 +470,7 @@ root? left right storage)))))))) (declare Leaf) -(defn- new-leaf +(defn new-leaf ([keys] (new-leaf keys nil)) ([keys address] @@ -719,7 +719,8 @@ (path-set path level last-idx) (dec level))) ;; leaf - (path-set path 0 (dec (arrays/alength (.-keys node))))))) + (when node + (path-set path 0 (dec (arrays/alength (.-keys node)))))))) (defn- next-path "Returns path representing next item after `path` in natural traversal order. @@ -1056,19 +1057,20 @@ (loop [node (-ensure-root-node set) path empty-path level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (dec keys-l) key)] - (if (== keys-l idx) - nil - (path-set path 0 idx))) - (let [keys (.-keys node) - idx (binary-search-l comparator keys (- keys-l 2) key)] - (recur - (child node idx (.-storage set)) - (path-set path level idx) - (dec level)))))))) + (when node + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (dec keys-l) key)] + (if (== keys-l idx) + nil + (path-set path 0 idx))) + (let [keys (.-keys node) + idx (binary-search-l comparator keys (- keys-l 2) key)] + (recur + (child node idx (.-storage set)) + (path-set path level idx) + (dec level))))))))) (defn- -rseek* "Returns path to the first element that is > key. @@ -1080,19 +1082,20 @@ (loop [node (-ensure-root-node set) path empty-path level (.-shift set)] - (let [keys-l (node-len node)] - (if (== 0 level) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (dec keys-l) key) - res (path-set path 0 idx)] - res) - (let [keys (.-keys node) - idx (binary-search-r comparator keys (- keys-l 2) key) - res (path-set path level idx)] - (recur - (child node idx (.-storage set)) - res - (dec level)))))))) + (when node + (let [keys-l (node-len node)] + (if (== 0 level) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (dec keys-l) key) + res (path-set path 0 idx)] + res) + (let [keys (.-keys node) + idx (binary-search-r comparator keys (- keys-l 2) key) + res (path-set path level idx)] + (recur + (child node idx (.-storage set)) + res + (dec level))))))))) (defn -slice [^BTSet set key-from key-to comparator] (when-some [path (-seek* set key-from comparator)]