├── .github └── workflows │ └── clojure.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── doc └── api │ ├── com.dean.interval-tree.tree.html │ ├── com.dean.interval-tree.tree.interval-map.html │ ├── com.dean.interval-tree.tree.interval-set.html │ ├── com.dean.interval-tree.tree.interval.html │ ├── com.dean.interval-tree.tree.node.html │ ├── com.dean.interval-tree.tree.order.html │ ├── com.dean.interval-tree.tree.ordered-map.html │ ├── com.dean.interval-tree.tree.ordered-set.html │ ├── com.dean.interval-tree.tree.protocol.html │ ├── com.dean.interval-tree.tree.root.html │ ├── com.dean.interval-tree.tree.tree.html │ ├── css │ ├── default.css │ └── highlight.css │ ├── index.html │ └── js │ ├── highlight.min.js │ ├── jquery.min.js │ └── page_effects.js ├── project.clj ├── src └── com │ └── dean │ └── interval_tree │ ├── core.clj │ └── tree │ ├── interval.clj │ ├── interval_map.clj │ ├── interval_set.clj │ ├── node.clj │ ├── order.clj │ ├── ordered_map.clj │ ├── ordered_set.clj │ ├── protocol.clj │ ├── root.clj │ └── tree.clj └── test └── com └── dean └── interval_tree ├── interval_map_test.clj ├── interval_set_test.clj ├── interval_test.clj ├── ordered_map_test.clj ├── ordered_set_test.clj └── tree_test.clj /.github/workflows/clojure.yml: -------------------------------------------------------------------------------- 1 | name: Clojure CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install dependencies 17 | run: lein deps 18 | - name: Run tests 19 | run: lein test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | *~ 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | a) in the case of the initial Contributor, the initial code and 11 | documentation distributed under this Agreement, and 12 | b) in the case of each subsequent Contributor: 13 | i) changes to the Program, and 14 | ii) additions to the Program; 15 | 16 | where such changes and/or additions to the Program originate from and are 17 | distributed by that particular Contributor. A Contribution 'originates' from a 18 | Contributor if it was added to the Program by such Contributor itself or 19 | anyone acting on such Contributor's behalf. Contributions do not include 20 | additions to the Program which: (i) are separate modules of software 21 | distributed in conjunction with the Program under their own license agreement, 22 | and (ii) are not derivative works of the Program. 23 | "Contributor" means any person or entity that distributes the Program. 24 | 25 | "Licensed Patents" mean patent claims licensable by a Contributor which are 26 | necessarily infringed by the use or sale of its Contribution alone or when 27 | combined with the Program. 28 | 29 | "Program" means the Contributions distributed in accordance with this 30 | Agreement. 31 | 32 | "Recipient" means anyone who receives the Program under this Agreement, 33 | including all Contributors. 34 | 35 | 2. GRANT OF RIGHTS 36 | 37 | a) Subject to the terms of this Agreement, each Contributor hereby grants 38 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 39 | reproduce, prepare derivative works of, publicly display, publicly 40 | perform, distribute and sublicense the Contribution of such Contributor, 41 | if any, and such derivative works, in source code and object code form. 42 | 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | 54 | c) Recipient understands that although each Contributor grants the 55 | licenses to its Contributions set forth herein, no assurances are 56 | provided by any Contributor that the Program does not infringe the patent 57 | or other intellectual property rights of any other entity. Each 58 | Contributor disclaims any liability to Recipient for claims brought by 59 | any other entity based on infringement of intellectual property rights or 60 | otherwise. As a condition to exercising the rights and licenses granted 61 | hereunder, each Recipient hereby assumes sole responsibility to secure 62 | any other intellectual property rights needed, if any. For example, if a 63 | third party patent license is required to allow Recipient to distribute 64 | the Program, it is Recipient's responsibility to acquire that license 65 | before distributing the Program. 66 | 67 | d) Each Contributor represents that to its knowledge it has sufficient 68 | copyright rights in its Contribution, if any, to grant the copyright 69 | license set forth in this Agreement. 70 | 71 | 3. REQUIREMENTS 72 | A Contributor may choose to distribute the Program in object code form under 73 | its own license agreement, provided that: 74 | 75 | a) it complies with the terms and conditions of this Agreement; and 76 | 77 | b) its license agreement: 78 | i) effectively disclaims on behalf of all Contributors all 79 | warranties and conditions, express and implied, including warranties 80 | or conditions of title and non-infringement, and implied warranties 81 | or conditions of merchantability and fitness for a particular 82 | purpose; 83 | ii) effectively excludes on behalf of all Contributors all liability 84 | for damages, including direct, indirect, special, incidental and 85 | consequential damages, such as lost profits; 86 | iii) states that any provisions which differ from this Agreement are 87 | offered by that Contributor alone and not by any other party; and 88 | iv) states that source code for the Program is available from such 89 | Contributor, and informs licensees how to obtain it in a reasonable 90 | manner on or through a medium customarily used for software 91 | exchange. 92 | 93 | When the Program is made available in source code form: 94 | 95 | a) it must be made available under this Agreement; and 96 | 97 | b) a copy of this Agreement must be included with each copy of the 98 | Program. 99 | Contributors may not remove or alter any copyright notices contained within 100 | the Program. 101 | 102 | Each Contributor must identify itself as the originator of its Contribution, 103 | if any, in a manner that reasonably allows subsequent Recipients to identify 104 | the originator of the Contribution. 105 | 106 | 4. COMMERCIAL DISTRIBUTION 107 | Commercial distributors of software may accept certain responsibilities with 108 | respect to end users, business partners and the like. While this license is 109 | intended to facilitate the commercial use of the Program, the Contributor who 110 | includes the Program in a commercial product offering should do so in a manner 111 | which does not create potential liability for other Contributors. Therefore, 112 | if a Contributor includes the Program in a commercial product offering, such 113 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 114 | every other Contributor ("Indemnified Contributor") against any losses, 115 | damages and costs (collectively "Losses") arising from claims, lawsuits and 116 | other legal actions brought by a third party against the Indemnified 117 | Contributor to the extent caused by the acts or omissions of such Commercial 118 | Contributor in connection with its distribution of the Program in a commercial 119 | product offering. The obligations in this section do not apply to any claims 120 | or Losses relating to any actual or alleged intellectual property 121 | infringement. In order to qualify, an Indemnified Contributor must: a) 122 | promptly notify the Commercial Contributor in writing of such claim, and b) 123 | allow the Commercial Contributor to control, and cooperate with the Commercial 124 | Contributor in, the defense and any related settlement negotiations. The 125 | Indemnified Contributor may participate in any such claim at its own expense. 126 | 127 | For example, a Contributor might include the Program in a commercial product 128 | offering, Product X. That Contributor is then a Commercial Contributor. If 129 | that Commercial Contributor then makes performance claims, or offers 130 | warranties related to Product X, those performance claims and warranties are 131 | such Commercial Contributor's responsibility alone. Under this section, the 132 | Commercial Contributor would have to defend claims against the other 133 | Contributors related to those performance claims and warranties, and if a 134 | court requires any other Contributor to pay any damages as a result, the 135 | Commercial Contributor must pay those damages. 136 | 137 | 5. NO WARRANTY 138 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 139 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 140 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 141 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 142 | Recipient is solely responsible for determining the appropriateness of using 143 | and distributing the Program and assumes all risks associated with its 144 | exercise of rights under this Agreement , including but not limited to the 145 | risks and costs of program errors, compliance with applicable laws, damage to 146 | or loss of data, programs or equipment, and unavailability or interruption of 147 | operations. 148 | 149 | 6. DISCLAIMER OF LIABILITY 150 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 151 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 152 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 153 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 154 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 155 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 156 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 157 | OF SUCH DAMAGES. 158 | 159 | 7. GENERAL 160 | 161 | If any provision of this Agreement is invalid or unenforceable under 162 | applicable law, it shall not affect the validity or enforceability of the 163 | remainder of the terms of this Agreement, and without further action by the 164 | parties hereto, such provision shall be reformed to the minimum extent 165 | necessary to make such provision valid and enforceable. 166 | 167 | If Recipient institutes patent litigation against any entity (including a 168 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 169 | (excluding combinations of the Program with other software or hardware) 170 | infringes such Recipient's patent(s), then such Recipient's rights granted 171 | under Section 2(b) shall terminate as of the date such litigation is filed. 172 | 173 | All Recipient's rights under this Agreement shall terminate if it fails to 174 | comply with any of the material terms or conditions of this Agreement and does 175 | not cure such failure in a reasonable period of time after becoming aware of 176 | such noncompliance. If all Recipient's rights under this Agreement terminate, 177 | Recipient agrees to cease use and distribution of the Program as soon as 178 | reasonably practicable. However, Recipient's obligations under this Agreement 179 | and any licenses granted by Recipient relating to the Program shall continue 180 | and survive. 181 | 182 | Everyone is permitted to copy and distribute copies of this Agreement, but in 183 | order to avoid inconsistency the Agreement is copyrighted and may only be 184 | modified in the following manner. The Agreement Steward reserves the right to 185 | publish new versions (including revisions) of this Agreement from time to 186 | time. No one other than the Agreement Steward has the right to modify this 187 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 188 | Eclipse Foundation may assign the responsibility to serve as the Agreement 189 | Steward to a suitable separate entity. Each new version of the Agreement will 190 | be given a distinguishing version number. The Program (including 191 | Contributions) may always be distributed subject to the version of the 192 | Agreement under which it was received. In addition, after a new version of the 193 | Agreement is published, Contributor may elect to distribute the Program 194 | (including its Contributions) under the new version. Except as expressly 195 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 196 | licenses to the intellectual property of any Contributor under this Agreement, 197 | whether expressly, by implication, estoppel or otherwise. All rights in the 198 | Program not expressly granted under this Agreement are reserved. 199 | 200 | This Agreement is governed by the laws of the State of New York and the 201 | intellectual property laws of the United States of America. No party to this 202 | Agreement will bring a legal action under this Agreement more than one year 203 | after the cause of action arose. Each party waives its rights to a jury trial 204 | in any resulting litigation. 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # com.dean.interval-tree 2 | 3 | This library provides a collection of data structures implemented using a 4 | modular, extensible, foldable, weight balanced persistent binary tree: 5 | ordered-sets, ordered-maps, interval-sets, and interval-maps. 6 | 7 | ![tests](https://github.com/dco-dev/interval-tree/actions/workflows/clojure.yml/badge.svg) 8 | [![Clojars Project](https://img.shields.io/clojars/v/com.dean/interval-tree.svg)](https://clojars.org/com.dean/interval-tree) 9 | 10 | ### Usage 11 | 12 | To install, add the following dependency to your project or build file: 13 | 14 | ``` 15 | [com.dean/interval-tree "0.1.2"] 16 | ``` 17 | 18 | #### Public API 19 | 20 | The public api resides in the top-level `com.dean.interval-tree.core` namespace: 21 | 22 | ```clj 23 | (require '[com.dean.interval-tree.core :as dean]) 24 | ``` 25 | 26 | The basic operation of this library is as a drop-in replacement for 27 | `clojure.core/sorted-set` and `clojure.core/sorted-map`. 28 | 29 | #### Constructors 30 | 31 | * `(dean/ordered-set coll)` 32 | * `(dean/ordered-set-by pred coll)` 33 | * `(dean/ordered-map coll)` 34 | * `(dean/ordered-map-by pred coll)` 35 | * `(dean/interval-set coll)` 36 | * `(dean/interval-map coll)` 37 | 38 | ### Topics 39 | 40 | #### What is an Interval Map? 41 | 42 | Imagine you'd like to associate values with members of a set of 43 | intervals over some continuous domain such as time or real numbers. 44 | An example of this is shown below. An interval map answers the question, 45 | which intervals overlap at some point on the domain. At 3.14159, in this 46 | case, would be `x4` and `x7`. The interval map is sparse itself, of 47 | course, and would only need to contain the 8 constituent intervals. 48 | 49 | ``` 50 | x8: +-----+ 51 | x7: +-----------------------------------+ 52 | x6: + 53 | x5: +-----------+ 54 | x4: +-----------------------------+ 55 | x3: +-----+ 56 | x2: +-----------------+ 57 | x1: +-----------+ 58 | 59 | 0=====1=====2=====3=====4=====5=====6=====7=====8=====9 60 | ``` 61 | 62 | This corresponds to the following example code: 63 | 64 | ```clj 65 | 66 | (def x (dean/interval-map {[1 3] :x1 67 | [4 7] :x2 68 | [8 9] :x3 69 | [0 5] :x4 70 | [6 8] :x5 71 | [9 9] :x6 72 | [3 9] :x7 73 | [4 5] :x8})) 74 | 75 | (x 3.141592654) ;; => [:x4 :x7] 76 | (x [5 5]) ;; => [:x4 :x7 :x8 :x2] 77 | 78 | (get x 9) ;; => [:x7 :x3 :x6] 79 | (get x 9.00001) ;; => nil 80 | (get x [1 4]) ;; => [:x4 :x1 :x7 :x8 :x2] 81 | 82 | ``` 83 | 84 | #### Efficient Set Operations 85 | 86 | This library implements a diverse collection of efficent set operations 87 | on foldably parallel ordered sets: 88 | 89 | ``` 90 | (def foo (shuffle (range 500000))) 91 | (def bar (shuffle (range 1000000))) 92 | 93 | (def s0 (shuffle (range 0 1000000 2))) 94 | (def s1 (shuffle (range 0 1000000 3))) 95 | 96 | ;; 97 | ;;; dean/ordered-set 98 | ;; 99 | 100 | (time (def x (ordered-set foo))) ;; 500K: "Elapsed time: 564.248517 msecs" 101 | (time (def y (ordered-set bar))) ;; 1M: "Elapsed time: 1187.734211 msecs" 102 | 103 | (time (def s (dean/intersection 104 | (ordered-set s0) 105 | (ordered-set s1)))) ;; 833K: "Elapsed time: 1242.961445 msecs" 106 | 107 | (time (r/fold + + y)) ;; 1M: "Elapsed time: 54.363545 msecs" 108 | 109 | 110 | ;; 111 | ;;; clojure.core/sorted-set 112 | ;; 113 | 114 | (time (def v (into (sorted-set) foo))) ;; 500K: "Elapsed time: 839.188189 msecs" 115 | (time (def w (into (sorted-set) bar))) ;; 1M: "Elapsed time: 1974.798286 msecs" 116 | 117 | (time (def s (clojure.set/intersection 118 | (into (sorted-set) s0) 119 | (into (sorted-set) s1)))) ;; 833K: "Elapsed time: 1589.786106 msecs" 120 | 121 | (time (r/fold + + w)) ;; 1M: "Elapsed time: 167.916539 msecs" 122 | ``` 123 | 124 | ### Testing 125 | 126 | Testing is accomplished with the standard `lein test` 127 | ``` 128 | $ time lein test 129 | 130 | lein test com.dean.interval-tree.interval-map-test 131 | 132 | lein test com.dean.interval-tree.interval-set-test 133 | 134 | lein test com.dean.interval-tree.interval-test 135 | 136 | lein test com.dean.interval-tree.ordered-map-test 137 | 138 | lein test com.dean.interval-tree.ordered-set-test 139 | 140 | lein test com.dean.interval-tree.tree-test 141 | 142 | Ran 30 tests containing 98214 assertions. 143 | 0 failures, 0 errors. 144 | 145 | real 5m34.487s 146 | user 10m21.397s 147 | sys 0m5.047s 148 | ``` 149 | 150 | ### Modularity 151 | 152 | This data structure library is designed around the following concepts of 153 | modularity and extensibility. 154 | 155 | #### Clojure/Java Interfaces 156 | 157 | The top level collections are built on the standard Clojure/Java 158 | interfaces, so, for example, working with an `ordered-set` is 159 | identical to working with Clojure's `sorted-set`, using all of the same 160 | standard collection functions, for the 99% case: meta, nth, seq, rseq, 161 | assoc(-in), get(-in), invoke, compare, to-array, empty, .indexOf, 162 | .lastIndexof, size, iterator-seq, first, last, =, count, empty, 163 | contains, conj. disj, cons, fold, and many old friends will just 164 | work, using an efficient implementation that takes full advantage of the 165 | capabilities of our underlying tree index. 166 | 167 | #### PExtensibleset 168 | 169 | An exception to the above is due to the fact that `clojure.set` does not 170 | provide interfaces for extensible sets. So, we provide our own 171 | intersection, union, difference, subset, and superset. These operators 172 | work most efficiently on com.dean.interval-tree collections and provide 173 | support for backward interoperability with clojure (or possibly other) 174 | set datatypes. 175 | 176 | #### Root Container 177 | 178 | The individual collection types (ordered-set, ordered-map, interval-set, 179 | interval-map} are defined by their individual Class (clojure 180 | `deftype`) of top level container that holds the root of an 181 | indexed tree. This container describes the behavior of the underlying 182 | tree data structure along several architectural dimensions. 183 | 184 | ##### INodeCollection 185 | 186 | 187 | The fundamental collection of nodes provides an interface to node 188 | allocation machinery and to the root contained node. A variant 189 | based on persistent (on-disk) storage, for example, has been built 190 | with customizations at this layer. 191 | 192 | ##### IBalancedCollection 193 | 194 | For functional balanced trees, provides an interface to the `stitch` 195 | function that returns a new, properly balanced tree containing one newly 196 | allocated node adjoined. The provided algorithm is 197 | [weight balanced](https://en.wikipedia.org/wiki/Weight-balanced_tree) 198 | however others may be used. We've experimented with red-black trees, 199 | in particular, as variants at this layer. 200 | 201 | ##### IOrderedCollection 202 | 203 | Ordered collections define a comparator and predicates to determine the 204 | underlying algorithmic compatibility of other collections. Interval 205 | Collections are a special type of OrderedCollection. 206 | 207 | #### Tree 208 | 209 | The heart of the library is our [persistent tree](https://github.com/dco-dev/interval-tree/blob/master/src/com/dean/interval_tree/tree/tree.clj). 210 | 211 | The code is well documented and explains in more detail the efficiencies 212 | of the internal collection operators. 213 | 214 | This species of binary tree supports representations of sets, maps, 215 | and vectors. In addition to indexed key and range query, it 216 | supports the `nth` operation to return nth node from the beginning of 217 | the ordered tree, and `node-rank` to return the rank (sequential 218 | position) of a given key within the ordered tree, both in logarithmic 219 | time. 220 | 221 | The axes of exstensibility of the tree implemntation 222 | (`*compare*`,`*n-join*`, `*t-join*`) correspond to the interfaces 223 | described above. 224 | 225 | ### Inspiration 226 | 227 | This implementation of a weight-balanced binary interval-tree data 228 | structure was inspired by the following: 229 | 230 | - Adams (1992) 231 | 'Implementing Sets Efficiently in a Functional Language' 232 | Technical Report CSTR 92-10, University of Southampton. 233 | 234 | 235 | - Hirai and Yamamoto (2011) 236 | 'Balancing Weight-Balanced Trees' 237 | Journal of Functional Programming / 21 (3): 238 | Pages 287-307 239 | 240 | 241 | - Oleg Kiselyov 242 | 'Towards the best collection API, A design of the overall optimal 243 | collection traversal interface' 244 | 245 | 246 | - Nievergelt and Reingold (1972) 247 | 'Binary Search Trees of Bounded Balance' 248 | STOC '72 Proceedings 249 | 4th Annual ACM symposium on Theory of Computing 250 | Pages 137-142 251 | 252 | - Driscoll, Sarnak, Sleator, and Tarjan (1989) 253 | 'Making Data Structures Persistent' 254 | Journal of Computer and System Sciences Volume 38 Issue 1, February 1989 255 | 18th Annual ACM Symposium on Theory of Computing 256 | Pages 86-124 257 | 258 | - MIT Scheme weight balanced tree as reimplemented by Yoichi Hirai 259 | and Kazuhiko Yamamoto using the revised non-variant algorithm recommended 260 | integer balance parameters from (Hirai/Yamomoto 2011). 261 | 262 | - Wikipedia 263 | 'Interval Tree' 264 | 265 | 266 | - Wikipedia 267 | 'Weight Balanced Tree' 268 | 269 | 270 | - Andrew Baine, Rahul Jaine (2007) 271 | 'Purely Functional Data Structures in Common Lisp' 272 | Google Summer of Code 2007 273 | 274 | 275 | 276 | - Scott L. Burson 277 | 'Functional Set-Theoretic Collections for Common Lisp' 278 | 279 | 280 | ### License 281 | 282 | The use and distribution terms for this software are covered by the [Eclipse Public License 1.0](http://opensource.org/licenses/eclipse-1.0.php), which can be found in the file LICENSE.txt at the root of this distribution. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software. 283 | -------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree documentation

com.dean.interval-tree.tree

difference

intersection

interval-map

(interval-map)(interval-map coll)

interval-set

(interval-set)(interval-set coll)

ordered-map

(ordered-map)(ordered-map coll)(ordered-map compare-fn coll)

ordered-map-by

(ordered-map-by pred coll)

ordered-set

(ordered-set)(ordered-set coll)

ordered-set-by

(ordered-set-by pred coll)

subset

superset

union

-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.interval-map.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.interval-map documentation

com.dean.interval-tree.tree.interval-map

with-interval-map

macro

(with-interval-map x & body)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.interval-set.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.interval-set documentation

com.dean.interval-tree.tree.interval-set

with-interval-set

macro

(with-interval-set x & body)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.interval.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.interval documentation

com.dean.interval-tree.tree.interval

includes?

(includes? i0 i1)
Inclusive intervals?    [==========]
 4 | [====]

intersects?

(intersects? i0 i1)
returns true if there is any common point between intervals i0 and i1
 5 | 

ordered-pair

(ordered-pair x y)(ordered-pair x)
Ensure a normalized interval pair.
 6 | 

ordered-pair?

(ordered-pair? x)
valid interval pair?
 7 | 

overlaps?

(overlaps? i0 i1)
Overlapping intervals?   [=========]
 8 | [=========]

PInterval

protocol

an interval is represented as an ordered pair of endpoints
 9 | 

members

a

(a _)
interval start coordinate
10 | 

b

(b _)
interval end coordinate
11 | 
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.node.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.node documentation

com.dean.interval-tree.tree.node

-k

(-k n)

-kv

(-kv n)

-l

(-l n)

-r

(-r n)

-v

(-v n)

-x

(-x n)

-z

(-z n)

leaf

(leaf)

leaf?

(leaf? x)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.order.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.order documentation

com.dean.interval-tree.tree.order

*compare*

dynamic

<=

(<= x)(<= x y)(<= x y & more)

>=

(>= x)(>= x y)(>= x y & more)

compare

(compare x y)

compare-by

(compare-by pred)
Given a predicate that defines a total order over some domain,
4 | return a three-way comparator built from it.

compare<

(compare< x y)

compare<=

(compare<= x y)

compare=

(compare= x y)

compare>

(compare> x y)

compare>=

(compare>= x y)

max

(max x & args)

normal-compare

(normal-compare x y)

normalize

(normalize x)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.ordered-map.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.ordered-map documentation

com.dean.interval-tree.tree.ordered-map

with-ordered-map

macro

(with-ordered-map x & body)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.ordered-set.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.ordered-set documentation

com.dean.interval-tree.tree.ordered-set

with-ordered-set

macro

(with-ordered-set x & body)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.protocol.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.protocol documentation

com.dean.interval-tree.tree.protocol

PExtensibleSet

protocol

members

difference

(difference this that)

intersection

(intersection this that)

subset

(subset this that)

superset

(superset this that)

union

(union this that)
-------------------------------------------------------------------------------- /doc/api/com.dean.interval-tree.tree.root.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean.interval-tree.tree.root documentation

com.dean.interval-tree.tree.root

-------------------------------------------------------------------------------- /doc/api/css/default.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | font-size: 15px; 4 | } 5 | 6 | pre, code { 7 | font-family: Monaco, DejaVu Sans Mono, Consolas, monospace; 8 | font-size: 9pt; 9 | margin: 15px 0; 10 | } 11 | 12 | h1 { 13 | font-weight: normal; 14 | font-size: 29px; 15 | margin: 10px 0 2px 0; 16 | padding: 0; 17 | } 18 | 19 | h2 { 20 | font-weight: normal; 21 | font-size: 25px; 22 | } 23 | 24 | h5.license { 25 | margin: 9px 0 22px 0; 26 | color: #555; 27 | font-weight: normal; 28 | font-size: 12px; 29 | font-style: italic; 30 | } 31 | 32 | .document h1, .namespace-index h1 { 33 | font-size: 32px; 34 | margin-top: 12px; 35 | } 36 | 37 | #header, #content, .sidebar { 38 | position: fixed; 39 | } 40 | 41 | #header { 42 | top: 0; 43 | left: 0; 44 | right: 0; 45 | height: 22px; 46 | color: #f5f5f5; 47 | padding: 5px 7px; 48 | } 49 | 50 | #content { 51 | top: 32px; 52 | right: 0; 53 | bottom: 0; 54 | overflow: auto; 55 | background: #fff; 56 | color: #333; 57 | padding: 0 18px; 58 | } 59 | 60 | .sidebar { 61 | position: fixed; 62 | top: 32px; 63 | bottom: 0; 64 | overflow: auto; 65 | } 66 | 67 | .sidebar.primary { 68 | background: #e2e2e2; 69 | border-right: solid 1px #cccccc; 70 | left: 0; 71 | width: 250px; 72 | } 73 | 74 | .sidebar.secondary { 75 | background: #f2f2f2; 76 | border-right: solid 1px #d7d7d7; 77 | left: 251px; 78 | width: 200px; 79 | } 80 | 81 | #content.namespace-index, #content.document { 82 | left: 251px; 83 | } 84 | 85 | #content.namespace-docs { 86 | left: 452px; 87 | } 88 | 89 | #content.document { 90 | padding-bottom: 10%; 91 | } 92 | 93 | #header { 94 | background: #3f3f3f; 95 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.4); 96 | z-index: 100; 97 | } 98 | 99 | #header h1 { 100 | margin: 0; 101 | padding: 0; 102 | font-size: 18px; 103 | font-weight: lighter; 104 | text-shadow: -1px -1px 0px #333; 105 | } 106 | 107 | #header h1 .project-version { 108 | font-weight: normal; 109 | } 110 | 111 | .project-version { 112 | padding-left: 0.15em; 113 | } 114 | 115 | #header a, .sidebar a { 116 | display: block; 117 | text-decoration: none; 118 | } 119 | 120 | #header a { 121 | color: #f5f5f5; 122 | } 123 | 124 | .sidebar a { 125 | color: #333; 126 | } 127 | 128 | #header h2 { 129 | float: right; 130 | font-size: 9pt; 131 | font-weight: normal; 132 | margin: 4px 3px; 133 | padding: 0; 134 | color: #bbb; 135 | } 136 | 137 | #header h2 a { 138 | display: inline; 139 | } 140 | 141 | .sidebar h3 { 142 | margin: 0; 143 | padding: 10px 13px 0 13px; 144 | font-size: 19px; 145 | font-weight: lighter; 146 | } 147 | 148 | .sidebar h3 a { 149 | color: #444; 150 | } 151 | 152 | .sidebar h3.no-link { 153 | color: #636363; 154 | } 155 | 156 | .sidebar ul { 157 | padding: 7px 0 6px 0; 158 | margin: 0; 159 | } 160 | 161 | .sidebar ul.index-link { 162 | padding-bottom: 4px; 163 | } 164 | 165 | .sidebar li { 166 | display: block; 167 | vertical-align: middle; 168 | } 169 | 170 | .sidebar li a, .sidebar li .no-link { 171 | border-left: 3px solid transparent; 172 | padding: 0 10px; 173 | white-space: nowrap; 174 | } 175 | 176 | .sidebar li .no-link { 177 | display: block; 178 | color: #777; 179 | font-style: italic; 180 | } 181 | 182 | .sidebar li .inner { 183 | display: inline-block; 184 | padding-top: 7px; 185 | height: 24px; 186 | } 187 | 188 | .sidebar li a, .sidebar li .tree { 189 | height: 31px; 190 | } 191 | 192 | .depth-1 .inner { padding-left: 2px; } 193 | .depth-2 .inner { padding-left: 6px; } 194 | .depth-3 .inner { padding-left: 20px; } 195 | .depth-4 .inner { padding-left: 34px; } 196 | .depth-5 .inner { padding-left: 48px; } 197 | .depth-6 .inner { padding-left: 62px; } 198 | 199 | .sidebar li .tree { 200 | display: block; 201 | float: left; 202 | position: relative; 203 | top: -10px; 204 | margin: 0 4px 0 0; 205 | padding: 0; 206 | } 207 | 208 | .sidebar li.depth-1 .tree { 209 | display: none; 210 | } 211 | 212 | .sidebar li .tree .top, .sidebar li .tree .bottom { 213 | display: block; 214 | margin: 0; 215 | padding: 0; 216 | width: 7px; 217 | } 218 | 219 | .sidebar li .tree .top { 220 | border-left: 1px solid #aaa; 221 | border-bottom: 1px solid #aaa; 222 | height: 19px; 223 | } 224 | 225 | .sidebar li .tree .bottom { 226 | height: 22px; 227 | } 228 | 229 | .sidebar li.branch .tree .bottom { 230 | border-left: 1px solid #aaa; 231 | } 232 | 233 | .sidebar.primary li.current a { 234 | border-left: 3px solid #a33; 235 | color: #a33; 236 | } 237 | 238 | .sidebar.secondary li.current a { 239 | border-left: 3px solid #33a; 240 | color: #33a; 241 | } 242 | 243 | .namespace-index h2 { 244 | margin: 30px 0 0 0; 245 | } 246 | 247 | .namespace-index h3 { 248 | font-size: 16px; 249 | font-weight: bold; 250 | margin-bottom: 0; 251 | } 252 | 253 | .namespace-index .topics { 254 | padding-left: 30px; 255 | margin: 11px 0 0 0; 256 | } 257 | 258 | .namespace-index .topics li { 259 | padding: 5px 0; 260 | } 261 | 262 | .namespace-docs h3 { 263 | font-size: 18px; 264 | font-weight: bold; 265 | } 266 | 267 | .public h3 { 268 | margin: 0; 269 | float: left; 270 | } 271 | 272 | .usage { 273 | clear: both; 274 | } 275 | 276 | .public { 277 | margin: 0; 278 | border-top: 1px solid #e0e0e0; 279 | padding-top: 14px; 280 | padding-bottom: 6px; 281 | } 282 | 283 | .public:last-child { 284 | margin-bottom: 20%; 285 | } 286 | 287 | .members .public:last-child { 288 | margin-bottom: 0; 289 | } 290 | 291 | .members { 292 | margin: 15px 0; 293 | } 294 | 295 | .members h4 { 296 | color: #555; 297 | font-weight: normal; 298 | font-variant: small-caps; 299 | margin: 0 0 5px 0; 300 | } 301 | 302 | .members .inner { 303 | padding-top: 5px; 304 | padding-left: 12px; 305 | margin-top: 2px; 306 | margin-left: 7px; 307 | border-left: 1px solid #bbb; 308 | } 309 | 310 | #content .members .inner h3 { 311 | font-size: 12pt; 312 | } 313 | 314 | .members .public { 315 | border-top: none; 316 | margin-top: 0; 317 | padding-top: 6px; 318 | padding-bottom: 0; 319 | } 320 | 321 | .members .public:first-child { 322 | padding-top: 0; 323 | } 324 | 325 | h4.type, 326 | h4.dynamic, 327 | h4.added, 328 | h4.deprecated { 329 | float: left; 330 | margin: 3px 10px 15px 0; 331 | font-size: 15px; 332 | font-weight: bold; 333 | font-variant: small-caps; 334 | } 335 | 336 | .public h4.type, 337 | .public h4.dynamic, 338 | .public h4.added, 339 | .public h4.deprecated { 340 | font-size: 13px; 341 | font-weight: bold; 342 | margin: 3px 0 0 10px; 343 | } 344 | 345 | .members h4.type, 346 | .members h4.added, 347 | .members h4.deprecated { 348 | margin-top: 1px; 349 | } 350 | 351 | h4.type { 352 | color: #717171; 353 | } 354 | 355 | h4.dynamic { 356 | color: #9933aa; 357 | } 358 | 359 | h4.added { 360 | color: #508820; 361 | } 362 | 363 | h4.deprecated { 364 | color: #880000; 365 | } 366 | 367 | .namespace { 368 | margin-bottom: 30px; 369 | } 370 | 371 | .namespace:last-child { 372 | margin-bottom: 10%; 373 | } 374 | 375 | .index { 376 | padding: 0; 377 | font-size: 80%; 378 | margin: 15px 0; 379 | line-height: 16px; 380 | } 381 | 382 | .index * { 383 | display: inline; 384 | } 385 | 386 | .index p { 387 | padding-right: 3px; 388 | } 389 | 390 | .index li { 391 | padding-right: 5px; 392 | } 393 | 394 | .index ul { 395 | padding-left: 0; 396 | } 397 | 398 | .type-sig { 399 | clear: both; 400 | color: #088; 401 | } 402 | 403 | .type-sig pre { 404 | padding-top: 10px; 405 | margin: 0; 406 | } 407 | 408 | .usage code { 409 | display: block; 410 | color: #008; 411 | margin: 2px 0; 412 | } 413 | 414 | .usage code:first-child { 415 | padding-top: 10px; 416 | } 417 | 418 | p { 419 | margin: 15px 0; 420 | } 421 | 422 | .public p:first-child, .public pre.plaintext { 423 | margin-top: 12px; 424 | } 425 | 426 | .doc { 427 | margin: 0 0 26px 0; 428 | clear: both; 429 | } 430 | 431 | .public .doc { 432 | margin: 0; 433 | } 434 | 435 | .namespace-index .doc { 436 | margin-bottom: 20px; 437 | } 438 | 439 | .namespace-index .namespace .doc { 440 | margin-bottom: 10px; 441 | } 442 | 443 | .markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td { 444 | line-height: 22px; 445 | } 446 | 447 | .markdown li { 448 | padding: 2px 0; 449 | } 450 | 451 | .markdown h2 { 452 | font-weight: normal; 453 | font-size: 25px; 454 | margin: 30px 0 10px 0; 455 | } 456 | 457 | .markdown h3 { 458 | font-weight: normal; 459 | font-size: 20px; 460 | margin: 30px 0 0 0; 461 | } 462 | 463 | .markdown h4 { 464 | font-size: 15px; 465 | margin: 22px 0 -4px 0; 466 | } 467 | 468 | .doc, .public, .namespace .index { 469 | max-width: 680px; 470 | overflow-x: visible; 471 | } 472 | 473 | .markdown pre > code { 474 | display: block; 475 | padding: 10px; 476 | } 477 | 478 | .markdown pre > code, .src-link a { 479 | border: 1px solid #e4e4e4; 480 | border-radius: 2px; 481 | } 482 | 483 | .markdown code:not(.hljs), .src-link a { 484 | background: #f6f6f6; 485 | } 486 | 487 | pre.deps { 488 | display: inline-block; 489 | margin: 0 10px; 490 | border: 1px solid #e4e4e4; 491 | border-radius: 2px; 492 | padding: 10px; 493 | background-color: #f6f6f6; 494 | } 495 | 496 | .markdown hr { 497 | border-style: solid; 498 | border-top: none; 499 | color: #ccc; 500 | } 501 | 502 | .doc ul, .doc ol { 503 | padding-left: 30px; 504 | } 505 | 506 | .doc table { 507 | border-collapse: collapse; 508 | margin: 0 10px; 509 | } 510 | 511 | .doc table td, .doc table th { 512 | border: 1px solid #dddddd; 513 | padding: 4px 6px; 514 | } 515 | 516 | .doc table th { 517 | background: #f2f2f2; 518 | } 519 | 520 | .doc dl { 521 | margin: 0 10px 20px 10px; 522 | } 523 | 524 | .doc dl dt { 525 | font-weight: bold; 526 | margin: 0; 527 | padding: 3px 0; 528 | border-bottom: 1px solid #ddd; 529 | } 530 | 531 | .doc dl dd { 532 | padding: 5px 0; 533 | margin: 0 0 5px 10px; 534 | } 535 | 536 | .doc abbr { 537 | border-bottom: 1px dotted #333; 538 | font-variant: none; 539 | cursor: help; 540 | } 541 | 542 | .src-link { 543 | margin-bottom: 15px; 544 | } 545 | 546 | .src-link a { 547 | font-size: 70%; 548 | padding: 1px 4px; 549 | text-decoration: none; 550 | color: #5555bb; 551 | } 552 | -------------------------------------------------------------------------------- /doc/api/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | github.com style (c) Vasily Polovnyov 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | color: #333; 10 | background: #f8f8f8; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #998; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-keyword, 20 | .hljs-selector-tag, 21 | .hljs-subst { 22 | color: #333; 23 | font-weight: bold; 24 | } 25 | 26 | .hljs-number, 27 | .hljs-literal, 28 | .hljs-variable, 29 | .hljs-template-variable, 30 | .hljs-tag .hljs-attr { 31 | color: #008080; 32 | } 33 | 34 | .hljs-string, 35 | .hljs-doctag { 36 | color: #d14; 37 | } 38 | 39 | .hljs-title, 40 | .hljs-section, 41 | .hljs-selector-id { 42 | color: #900; 43 | font-weight: bold; 44 | } 45 | 46 | .hljs-subst { 47 | font-weight: normal; 48 | } 49 | 50 | .hljs-type, 51 | .hljs-class .hljs-title { 52 | color: #458; 53 | font-weight: bold; 54 | } 55 | 56 | .hljs-tag, 57 | .hljs-name, 58 | .hljs-attribute { 59 | color: #000080; 60 | font-weight: normal; 61 | } 62 | 63 | .hljs-regexp, 64 | .hljs-link { 65 | color: #009926; 66 | } 67 | 68 | .hljs-symbol, 69 | .hljs-bullet { 70 | color: #990073; 71 | } 72 | 73 | .hljs-built_in, 74 | .hljs-builtin-name { 75 | color: #0086b3; 76 | } 77 | 78 | .hljs-meta { 79 | color: #999; 80 | font-weight: bold; 81 | } 82 | 83 | .hljs-deletion { 84 | background: #fdd; 85 | } 86 | 87 | .hljs-addition { 88 | background: #dfd; 89 | } 90 | 91 | .hljs-emphasis { 92 | font-style: italic; 93 | } 94 | 95 | .hljs-strong { 96 | font-weight: bold; 97 | } 98 | -------------------------------------------------------------------------------- /doc/api/index.html: -------------------------------------------------------------------------------- 1 | 3 | com.dean/interval-tree 0.1.0

com.dean/interval-tree 0.1.0

Released under the Eclipse Public License

Modular, Extensible, Foldable Weight-Balanced Tree.

Installation

To install, add the following dependency to your project or build file:

[com.dean/interval-tree "0.1.0"]

Namespaces

com.dean.interval-tree.tree.node

Public variables and functions:

com.dean.interval-tree.tree.root

Public variables and functions:

    -------------------------------------------------------------------------------- /doc/api/js/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.6.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return R(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||R(i))return i}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):E(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"===e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var a=r?"":y.classPrefix,i='',i+n+o}function p(){var e,t,r,a;if(!E.k)return n(B);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)a+=n(B.substr(t,r.index-t)),e=g(E,r),e?(M+=e[1],a+=h(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return a+n(B.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=""}function v(e){k+=e.cN?h(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?B+=n:(a.rE||a.eE||(B+=n),b(),a.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,E=i||N,L={},k="";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var B="",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substr(O,I.index-O),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},a=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&"\n"===e?"
    ":y.tabReplace?n.replace(/\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n,t,r,o,s,p=i(e);a(p)||(y.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");w.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||"").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
    ",y={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},I={"&":"&","<":"<",">":">"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("clojure",function(e){var t={"builtin-name":"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"},r="a-zA-Z_\\-!.?+*=<>&#'",n="["+r+"]["+r+"0-9/;:]*",a="[-+]?\\d+(\\.\\d+)?",o={b:n,r:0},s={cN:"number",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(";","$",{r:0}),d={cN:"literal",b:/\b(true|false|nil)\b/},l={b:"[\\[\\{]",e:"[\\]\\}]"},m={cN:"comment",b:"\\^"+n},p=e.C("\\^\\{","\\}"),u={cN:"symbol",b:"[:]{1,2}"+n},f={b:"\\(",e:"\\)"},h={eW:!0,r:0},y={k:t,l:n,cN:"name",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C("comment",""),y,h],h.c=b,l.c=b,{aliases:["clj"],i:/\S/,c:[f,i,m,p,c,u,l,s,d]}});hljs.registerLanguage("clojure-repl",function(e){return{c:[{cN:"meta",b:/^([\w.-]+|\s*#_)=>/,starts:{e:/$/,sL:"clojure"}}]}}); -------------------------------------------------------------------------------- /doc/api/js/page_effects.js: -------------------------------------------------------------------------------- 1 | function visibleInParent(element) { 2 | var position = $(element).position().top 3 | return position > -50 && position < ($(element).offsetParent().height() - 50) 4 | } 5 | 6 | function hasFragment(link, fragment) { 7 | return $(link).attr("href").indexOf("#" + fragment) != -1 8 | } 9 | 10 | function findLinkByFragment(elements, fragment) { 11 | return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first() 12 | } 13 | 14 | function scrollToCurrentVarLink(elements) { 15 | var elements = $(elements); 16 | var parent = elements.offsetParent(); 17 | 18 | if (elements.length == 0) return; 19 | 20 | var top = elements.first().position().top; 21 | var bottom = elements.last().position().top + elements.last().height(); 22 | 23 | if (top >= 0 && bottom <= parent.height()) return; 24 | 25 | if (top < 0) { 26 | parent.scrollTop(parent.scrollTop() + top); 27 | } 28 | else if (bottom > parent.height()) { 29 | parent.scrollTop(parent.scrollTop() + bottom - parent.height()); 30 | } 31 | } 32 | 33 | function setCurrentVarLink() { 34 | $('.secondary a').parent().removeClass('current') 35 | $('.anchor'). 36 | filter(function(index) { return visibleInParent(this) }). 37 | each(function(index, element) { 38 | findLinkByFragment(".secondary a", element.id). 39 | parent(). 40 | addClass('current') 41 | }); 42 | scrollToCurrentVarLink('.secondary .current'); 43 | } 44 | 45 | var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }()) 46 | 47 | function scrollPositionId(element) { 48 | var directory = window.location.href.replace(/[^\/]+\.html$/, '') 49 | return 'scroll::' + $(element).attr('id') + '::' + directory 50 | } 51 | 52 | function storeScrollPosition(element) { 53 | if (!hasStorage) return; 54 | localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft()) 55 | localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop()) 56 | } 57 | 58 | function recallScrollPosition(element) { 59 | if (!hasStorage) return; 60 | $(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x")) 61 | $(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y")) 62 | } 63 | 64 | function persistScrollPosition(element) { 65 | recallScrollPosition(element) 66 | $(element).scroll(function() { storeScrollPosition(element) }) 67 | } 68 | 69 | function sidebarContentWidth(element) { 70 | var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() }) 71 | return Math.max.apply(Math, widths) 72 | } 73 | 74 | function calculateSize(width, snap, margin, minimum) { 75 | if (width == 0) { 76 | return 0 77 | } 78 | else { 79 | return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2)) 80 | } 81 | } 82 | 83 | function resizeSidebars() { 84 | var primaryWidth = sidebarContentWidth('.primary') 85 | var secondaryWidth = 0 86 | 87 | if ($('.secondary').length != 0) { 88 | secondaryWidth = sidebarContentWidth('.secondary') 89 | } 90 | 91 | // snap to grid 92 | primaryWidth = calculateSize(primaryWidth, 32, 13, 160) 93 | secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160) 94 | 95 | $('.primary').css('width', primaryWidth) 96 | $('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1) 97 | 98 | if (secondaryWidth > 0) { 99 | $('#content').css('left', primaryWidth + secondaryWidth + 2) 100 | } 101 | else { 102 | $('#content').css('left', primaryWidth + 1) 103 | } 104 | } 105 | 106 | $(window).ready(resizeSidebars) 107 | $(window).ready(setCurrentVarLink) 108 | $(window).ready(function() { persistScrollPosition('.primary')}) 109 | $(window).ready(function() { 110 | $('#content').scroll(setCurrentVarLink) 111 | $(window).resize(setCurrentVarLink) 112 | }) 113 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject com.dean/interval-tree "0.1.2" 2 | :description "Modular, Extensible, Foldable Weight-Balanced Tree" 3 | :url "http://github.com/dco-dev/interval-tree" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | 7 | :dependencies [[org.clojure/clojure "1.8.0"] 8 | [org.clojure/math.combinatorics "0.1.4"]] 9 | 10 | :plugins [[lein-asciidoctor "0.1.14"] 11 | [lein-codox "0.10.7"]] 12 | 13 | :signing {:gpg-key "3A2F2AA9"} 14 | 15 | :deploy-repositories [["clojars" {:url "https://clojars.org/repo" 16 | :username :env/clojars_user 17 | :password :env/clojars_pass 18 | :sign-releases false}]] 19 | 20 | :codox {:output-path "doc/api" 21 | :src-dir-uri "https://github.com/dco-dev/interval-tree/blob/master/" 22 | :src-linenum-anchor-prefix "L" 23 | :project {:name "com.dean/interval-tree"}} 24 | 25 | :asciidoctor {:sources ["doc/*.adoc"] 26 | :to-dir "doc/html" 27 | :toc :left 28 | :doctype :article 29 | :format :html5 30 | :extract-css true 31 | :source-highlight true}) 32 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/core.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.core 2 | (:require [clojure.core.reducers :as r] 3 | [com.dean.interval-tree.tree.interval :as interval] 4 | [com.dean.interval-tree.tree.interval-map :refer [->IntervalMap]] 5 | [com.dean.interval-tree.tree.interval-set :refer [->IntervalSet]] 6 | [com.dean.interval-tree.tree.node :as node] 7 | [com.dean.interval-tree.tree.order :as order] 8 | [com.dean.interval-tree.tree.protocol :as proto] 9 | [com.dean.interval-tree.tree.ordered-map :refer [->OrderedMap]] 10 | [com.dean.interval-tree.tree.ordered-set :refer [->OrderedSet]] 11 | [com.dean.interval-tree.tree.tree :as tree])) 12 | 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | ;; Set Algebra 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | (def intersection proto/intersection) 18 | (def union proto/union) 19 | (def difference proto/difference) 20 | (def subset proto/subset) 21 | (def superset proto/superset) 22 | 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | ;; Ordered Set 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | 27 | ;; TODO: allow high speed construction AND custom compare-fn 28 | ;; TODO: refactor 29 | 30 | ;; NOTE: subject to change! 31 | ;; experimentally determined to be in the ballpark, given the current 32 | ;; performance characteristics upstream 33 | 34 | (def ^:private +chunk-size+ 2048) 35 | 36 | (defn- ordered-set* [compare-fn coll] 37 | (binding [order/*compare* compare-fn] 38 | (->OrderedSet 39 | (r/fold +chunk-size+ 40 | (fn 41 | ([] (node/leaf)) 42 | ([n0 n1] (tree/node-set-union n0 n1))) tree/node-add coll) 43 | compare-fn nil nil {}))) 44 | 45 | (defn ordered-set 46 | ([] 47 | (ordered-set* order/normal-compare nil)) 48 | ([coll] 49 | (ordered-set* order/normal-compare coll))) 50 | 51 | (defn ordered-set-by [pred coll] 52 | (-> pred order/compare-by (ordered-set* (seq coll)))) 53 | 54 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 55 | ;; Ordered Map 56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 57 | 58 | (defn ordered-map 59 | ([] 60 | (ordered-map order/normal-compare nil)) 61 | ([coll] 62 | (ordered-map order/normal-compare coll)) 63 | ([compare-fn coll] 64 | (binding [order/*compare* compare-fn] 65 | (->OrderedMap (reduce (fn [n [k v]] (tree/node-add n k v)) (node/leaf) coll) 66 | compare-fn nil nil {})))) 67 | 68 | (defn ordered-map-by [pred coll] 69 | (-> pred order/compare-by (ordered-map coll))) 70 | 71 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72 | ;; Interval Map 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | 75 | (defn interval-map 76 | ([] 77 | (interval-map nil)) 78 | ([coll] 79 | (binding [tree/*t-join* tree/node-create-weight-balanced-interval 80 | order/*compare* order/normal-compare] 81 | (->IntervalMap (reduce (fn [n [k v]] (tree/node-add n k v)) (node/leaf) coll) 82 | order/*compare* tree/*t-join* nil {})))) 83 | 84 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 85 | ;; Interval Set 86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 87 | 88 | (defn interval-set 89 | ([] 90 | (interval-set nil)) 91 | ([coll] 92 | (binding [tree/*t-join* tree/node-create-weight-balanced-interval 93 | order/*compare* order/normal-compare] 94 | (->IntervalSet (reduce #(tree/node-add %1 (interval/ordered-pair %2)) (node/leaf) coll) 95 | order/*compare* tree/*t-join* nil {})))) 96 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/interval.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.interval 2 | (:require [com.dean.interval-tree.tree.order :as order]) 3 | (:import [clojure.lang MapEntry PersistentVector])) 4 | 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | ;; Representation 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | 9 | (defprotocol PInterval 10 | "an interval is represented as an ordered pair of endpoints" 11 | (a [_] "interval start coordinate") 12 | (b [_] "interval end coordinate")) 13 | 14 | (extend-protocol PInterval 15 | MapEntry 16 | (a [this] (.key this)) 17 | (b [this] (.val this)) 18 | PersistentVector 19 | (a [this] (this 0)) 20 | (b [this] (this 1))) 21 | 22 | (defn ordered-pair? 23 | "valid interval pair?" 24 | [x] 25 | (and (or (instance? MapEntry x) (instance? PersistentVector x) (satisfies? PInterval x)) 26 | (order/compare<= (a x) (b x)))) 27 | 28 | (defn ordered-pair 29 | "Ensure a normalized interval pair." 30 | ([x y] {:pre [(order/compare<= x y)]} 31 | (MapEntry. x y)) 32 | ([x] 33 | (cond 34 | (ordered-pair? x) x 35 | (sequential? x) (throw (AssertionError. (str "Non ordered sequential pair " x))) 36 | true (MapEntry. x x)))) 37 | 38 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 39 | ;; Semantics 40 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 41 | 42 | (defn overlaps? 43 | "Overlapping intervals? [=========] 44 | [=========]" 45 | [i0 i1] 46 | (or (order/<= (a i1) (b i0) (b i1)) 47 | (order/<= (a i1) (a i0) (b i1)))) 48 | 49 | (defn includes? 50 | "Inclusive intervals? [==========] 51 | [====]" 52 | [i0 i1] 53 | (order/<= (a i0) (a i1) (b i1) (b i0))) 54 | 55 | (defn intersects? 56 | "returns true if there is any common point between intervals i0 and i1" 57 | [i0 i1] 58 | (or (overlaps? i0 i1) 59 | (includes? i0 i1) 60 | (includes? i1 i0))) 61 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/interval_map.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.interval-map 2 | (:require [clojure.core.reducers :as r :refer [coll-fold]] 3 | [com.dean.interval-tree.tree.interval :as interval] 4 | [com.dean.interval-tree.tree.node :as node] 5 | [com.dean.interval-tree.tree.root] 6 | [com.dean.interval-tree.tree.order :as order] 7 | [com.dean.interval-tree.tree.tree :as tree]) 8 | (:import [clojure.lang RT] 9 | [com.dean.interval_tree.tree.root INodeCollection 10 | IBalancedCollection 11 | IOrderedCollection 12 | IIntervalCollection])) 13 | 14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 15 | ;; Dynamic Environment 16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 17 | 18 | (defmacro with-interval-map [x & body] 19 | `(binding [order/*compare* (.getCmp ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.IOrderedCollection})) 20 | tree/*t-join* (.getAllocator ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.INodeCollection}))] 21 | ~@body)) 22 | 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | ;; Interval Map 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | 27 | (deftype IntervalMap [root cmp alloc stitch _meta] 28 | 29 | INodeCollection 30 | (getAllocator [_] 31 | alloc) 32 | (getRoot [_] 33 | root) 34 | 35 | IOrderedCollection 36 | (getCmp [_] 37 | cmp) 38 | (isCompatible [_ o] 39 | (and (instance? IntervalMap o) (= cmp (.getCmp o)) (= stitch (.getStitch o)))) 40 | (isSimilar [_ o] 41 | (map? o)) 42 | 43 | IBalancedCollection 44 | (getStitch [_] 45 | stitch) 46 | 47 | IIntervalCollection 48 | 49 | clojure.lang.IMeta 50 | (meta [_] 51 | _meta) 52 | 53 | clojure.lang.IObj 54 | (withMeta [_ m] 55 | (IntervalMap. root cmp alloc stitch m)) 56 | 57 | clojure.lang.Indexed 58 | (nth [this i] 59 | (with-interval-map this 60 | (node/-kv (tree/node-nth root i)))) 61 | 62 | clojure.lang.MapEquivalence 63 | 64 | clojure.lang.Seqable 65 | (seq [this] 66 | (with-interval-map this 67 | (map node/-kv (tree/node-seq root)))) 68 | 69 | clojure.lang.Reversible 70 | (rseq [this] 71 | (with-interval-map this 72 | (map node/-kv (tree/node-seq-reverse root)))) 73 | 74 | clojure.lang.ILookup 75 | (valAt [this k not-found] 76 | (with-interval-map this 77 | (if-let [found (tree/node-find-intervals root k)] 78 | (map node/-v found) 79 | not-found))) 80 | (valAt [this k] 81 | (.valAt this k nil)) 82 | 83 | clojure.lang.IFn 84 | (invoke [this k not-found] 85 | (.valAt this k not-found)) 86 | (invoke [this k] 87 | (.valAt this k)) 88 | (applyTo [this args] 89 | (let [n (RT/boundedLength args 2)] 90 | (case n 91 | 0 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName)))) 92 | 1 (.invoke this (first args)) 93 | 2 (.invoke this (first args) (second args)) 94 | 3 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName))))))) 95 | 96 | java.lang.Comparable 97 | (compareTo [this o] 98 | (with-interval-map this 99 | (cond 100 | (identical? this o) 0 101 | (.isCompatible this o) (tree/node-map-compare root (.getRoot o)) 102 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 103 | 104 | clojure.lang.Counted 105 | (count [this] 106 | (tree/node-size root)) 107 | 108 | clojure.lang.Associative 109 | (containsKey [this k] 110 | (with-interval-map this 111 | (not (empty? (tree/node-find-intervals root k))))) 112 | (entryAt [this k] 113 | (with-interval-map this 114 | (some->> k (tree/node-find-intervals root) (map node/-kv)))) 115 | (assoc [this k v] 116 | (with-interval-map this 117 | (IntervalMap. (tree/node-add root (interval/ordered-pair k) v) cmp alloc stitch _meta))) 118 | (empty [this] 119 | (IntervalMap. (node/leaf) cmp alloc stitch {})) 120 | 121 | java.util.Map 122 | (get [this k] 123 | (.valAt this k)) 124 | (isEmpty [_] 125 | (node/leaf? root)) 126 | (size [_] 127 | (tree/node-size root)) 128 | (keySet [this] 129 | (with-interval-map this 130 | (set (tree/node-vec root :accessor :k)))) 131 | (put [_ _ _] 132 | (throw (UnsupportedOperationException.))) 133 | (putAll [_ _] 134 | (throw (UnsupportedOperationException.))) 135 | (clear [_] 136 | (throw (UnsupportedOperationException.))) 137 | (values [this] 138 | (with-interval-map this 139 | (tree/node-vec root :accessor :v))) 140 | (entrySet [this] 141 | (with-interval-map this 142 | (set (tree/node-vec root :accessor :kv)))) 143 | (iterator [this] 144 | (clojure.lang.SeqIterator. (seq this))) 145 | 146 | clojure.lang.IPersistentCollection 147 | (equiv [this o] 148 | (with-interval-map this 149 | (cond 150 | (identical? this o) 0 151 | (.isCompatible this o) (and (= (.count this) (.count o)) 152 | (zero? (tree/node-map-compare root (.getRoot o)))) 153 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 154 | 155 | (cons [this o] 156 | (.assoc this (nth o 0) (nth o 1))) 157 | 158 | clojure.lang.IPersistentMap 159 | (assocEx [this k v] 160 | (if (contains? this k) 161 | (throw (RuntimeException. "Key or value already present")) 162 | (assoc this k v))) 163 | (without [this k] 164 | (with-interval-map this 165 | (IntervalMap. (tree/node-remove root k) cmp alloc stitch _meta)))) 166 | 167 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 168 | ;; Literal Representation 169 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 170 | 171 | (defmethod print-method IntervalMap [m w] 172 | ((get (methods print-method) clojure.lang.IPersistentMap) m w)) 173 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/interval_set.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.interval-set 2 | (:require [clojure.core.reducers :as r :refer [coll-fold]] 3 | [clojure.set] 4 | [com.dean.interval-tree.tree.interval :as interval] 5 | [com.dean.interval-tree.tree.node :as node] 6 | [com.dean.interval-tree.tree.order :as order] 7 | [com.dean.interval-tree.tree.protocol :as proto] 8 | [com.dean.interval-tree.tree.root] 9 | [com.dean.interval-tree.tree.tree :as tree]) 10 | (:import [clojure.lang RT] 11 | [com.dean.interval_tree.tree.protocol PExtensibleSet] 12 | [com.dean.interval_tree.tree.root INodeCollection 13 | IBalancedCollection 14 | IOrderedCollection 15 | IIntervalCollection])) 16 | 17 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 18 | ;; Dynamic Environment 19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 20 | 21 | (defmacro with-interval-set [x & body] 22 | `(binding [order/*compare* (.getCmp ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.IOrderedCollection})) 23 | tree/*t-join* (.getAllocator ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.INodeCollection}))] 24 | ~@body)) 25 | 26 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 27 | ;; Interval Set 28 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 29 | 30 | (deftype IntervalSet [root cmp alloc stitch _meta] 31 | 32 | INodeCollection 33 | (getAllocator [_] 34 | alloc) 35 | (getRoot [_] 36 | root) 37 | 38 | IOrderedCollection 39 | (getCmp [_] 40 | cmp) 41 | (isCompatible [_ o] 42 | (and (instance? IntervalSet o) (= cmp (.getCmp o)))) 43 | (isSimilar [_ _] 44 | false) 45 | 46 | IBalancedCollection 47 | (getStitch [_] 48 | stitch) 49 | 50 | IIntervalCollection 51 | 52 | ;; TODO: how should these work for interval-set? 53 | PExtensibleSet 54 | (intersection [this that] 55 | (with-interval-set this 56 | (cond 57 | (identical? this that) this 58 | (.isCompatible this that) (IntervalSet. (tree/node-set-intersection root (.getRoot that)) 59 | cmp alloc stitch {}) 60 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 61 | (union [this that] 62 | (with-interval-set this 63 | (cond 64 | (identical? this that) this 65 | (.isCompatible this that) (IntervalSet. (tree/node-set-union root (.getRoot that)) 66 | cmp alloc stitch {}) 67 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 68 | (difference [this that] 69 | (with-interval-set this 70 | (cond 71 | (identical? this that) (.empty this) 72 | (.isCompatible this that) (IntervalSet. (tree/node-set-difference root (.getRoot that)) 73 | cmp alloc stitch {}) 74 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 75 | (subset [this that] 76 | (with-interval-set this 77 | (cond 78 | (identical? this that) true 79 | (.isCompatible this that) (tree/node-subset? (.getRoot that) root) ;; Grr. reverse args of tree/subset 80 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 81 | (superset [this that] 82 | (with-interval-set this 83 | (cond 84 | (identical? this that) true 85 | (.isCompatible this that) (tree/node-subset? root (.getRoot that)) ;; Grr. reverse args of tree/subset 86 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 87 | 88 | clojure.lang.IMeta 89 | (meta [_] 90 | _meta) 91 | 92 | clojure.lang.IObj 93 | (withMeta [_ m] 94 | (IntervalSet. root cmp alloc stitch m)) 95 | 96 | clojure.lang.Indexed 97 | (nth [this i] 98 | (with-interval-set this 99 | (node/-k (tree/node-nth root i)))) 100 | 101 | clojure.lang.Seqable 102 | (seq [this] 103 | (with-interval-set this 104 | (map node/-k (tree/node-seq root)))) 105 | 106 | clojure.lang.Reversible 107 | (rseq [this] 108 | (with-interval-set this 109 | (map node/-k (tree/node-seq-reverse root)))) 110 | 111 | clojure.lang.ILookup 112 | (valAt [this k not-found] 113 | (with-interval-set this 114 | (if-let [found (seq (tree/node-find-intervals root k))] 115 | (map node/-k found) 116 | not-found))) 117 | (valAt [this k] 118 | (.valAt this k nil)) 119 | 120 | clojure.lang.IFn 121 | (invoke [this k not-found] 122 | (.valAt this k not-found)) 123 | (invoke [this k] 124 | (.valAt this k)) 125 | (applyTo [this args] 126 | (let [n (RT/boundedLength args 2)] 127 | (case n 128 | 0 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName)))) 129 | 1 (.invoke this (first args)) 130 | 2 (.invoke this (first args) (second args)) 131 | 3 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName))))))) 132 | 133 | java.lang.Comparable 134 | (compareTo [this o] 135 | (with-interval-set this 136 | (cond 137 | (identical? this o) 0 138 | (.isCompatible this o) (tree/node-set-compare root (.getRoot o)) 139 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 140 | 141 | java.util.Collection 142 | (toArray [this] 143 | (with-interval-set this 144 | (object-array (tree/node-vec root :accessor :k)))) ; better constructor not a priority 145 | (isEmpty [_] 146 | (node/leaf? root)) 147 | (add [_ _] 148 | (throw (UnsupportedOperationException.))) 149 | (addAll [_ _] 150 | (throw (UnsupportedOperationException.))) 151 | (removeAll [_ _] 152 | (throw (UnsupportedOperationException.))) 153 | (retainAll [_ _] 154 | (throw (UnsupportedOperationException.))) 155 | 156 | java.util.Set 157 | (size [_] 158 | (tree/node-size root)) 159 | (iterator [this] 160 | (clojure.lang.SeqIterator. (seq this))) 161 | (containsAll [this s] ;; TODO: is this how an interval-set should work? 162 | (with-interval-set this 163 | (cond 164 | (identical? this s) true 165 | (coll? s) (every? #(.contains this %) s) 166 | true (throw (ex-info "unsupported comparison: " {:this this :s s}))))) 167 | 168 | java.util.SortedSet 169 | (comparator [_] 170 | cmp) 171 | (first [this] 172 | (with-interval-set this 173 | (node/-k (tree/node-least root)))) 174 | (last [this] 175 | (with-interval-set this 176 | (node/-k (tree/node-greatest root)))) 177 | 178 | clojure.lang.IPersistentSet 179 | (equiv [this o] 180 | (with-interval-set this 181 | (cond 182 | (identical? this o) true 183 | (.isCompatible this o) (and (= (.count this) (.count o)) 184 | (zero? (tree/node-set-compare root (.getRoot o)))) 185 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 186 | (count [_] 187 | (tree/node-size root)) 188 | (empty [_] 189 | (IntervalSet. (node/leaf) cmp alloc stitch {})) 190 | (contains [this k] 191 | (with-interval-set this 192 | (some? (seq (tree/node-find-intervals this (interval/ordered-pair k)))))) 193 | (disjoin [this k] 194 | (with-interval-set this 195 | (IntervalSet. (tree/node-remove root (interval/ordered-pair k)) cmp alloc stitch _meta))) 196 | (cons [this k] 197 | (with-interval-set this 198 | (IntervalSet. (tree/node-add root (interval/ordered-pair k)) cmp alloc stitch _meta))) 199 | 200 | clojure.core.reducers.CollFold 201 | (coll-fold [this n combinef reducef] 202 | (with-interval-set this 203 | (tree/node-chunked-fold n root combinef 204 | (fn [acc node] (reducef acc (node/-k node))))))) 205 | 206 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 207 | ;; Literal Representation 208 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 209 | 210 | (defmethod print-method IntervalSet [s w] 211 | ((get (methods print-method) clojure.lang.IPersistentSet) s w)) 212 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/node.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.node 2 | (:import [clojure.lang MapEntry])) 3 | 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | ;; Leaf Representation 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | ;; It can sometimes be the case that "leaf" nodes aren't a static value, 9 | ;; but computed/generated/populated in some way. so i usually make `leaf` 10 | ;; a function rather than value just as a matter of practice in order to 11 | ;; have a complete abstraction layer between node and tree layers. 12 | 13 | (definline leaf [] 14 | nil) 15 | 16 | (definline leaf? [x] 17 | `(identical? ~x nil)) 18 | 19 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 20 | ;; Node Capability 21 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 | 23 | ;; TODO: this exists to work around spurious build warnings during clojurescript 24 | ;; build phase of enclosing project 25 | 26 | (defmacro ^:private definterface-once [iname & args] 27 | (when-not (resolve iname) 28 | `(definterface ~iname ~@args))) 29 | 30 | (definterface-once INode 31 | (k [] "key: any value") 32 | (v [] "value: any value") 33 | (l [] "left-child: a Node or Leaf") 34 | (r [] "right-child: a Node or Leaf") 35 | (kv [] "key-val: a pair containing both key and value")) 36 | 37 | (definterface-once IBalancedNode 38 | (^long x [] "balance-metric: an integer value")) 39 | 40 | (definterface-once IAugmentedNode 41 | (z [] "auxiliary constituent(s) for extended tree algorithms")) 42 | 43 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 44 | ;; Storage Model 45 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 46 | 47 | (deftype SimpleNode [k v l r ^long x] 48 | IBalancedNode 49 | (x [_] x) 50 | INode 51 | (k [_] k) 52 | (v [_] v) 53 | (l [_] l) 54 | (r [_] r) 55 | (kv [_] (MapEntry. k v))) 56 | 57 | (deftype IntervalNode [k v l r ^long x z] 58 | IBalancedNode 59 | (x [_] x) 60 | IAugmentedNode 61 | (z [_] z) ;; max node child interval span 62 | INode 63 | (k [_] k) 64 | (v [_] v) 65 | (l [_] l) 66 | (r [_] r) 67 | (kv [_] (MapEntry. k v))) 68 | 69 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 70 | ;; Constitutent Accessors 71 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72 | 73 | ;; @gunnarson style 74 | 75 | (definline -k [n] `(.k ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.INode}))) 76 | (definline -v [n] `(.v ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.INode}))) 77 | (definline -l [n] `(.l ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.INode}))) 78 | (definline -r [n] `(.r ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.INode}))) 79 | (definline -x [n] `(.x ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.IBalancedNode}))) 80 | (definline -z [n] `(.z ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.IAugmentedNode}))) 81 | (definline -kv [n] `(.kv ~(with-meta n {:tag 'com.dean.interval_tree.tree.node.INode}))) 82 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/order.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.order 2 | (:refer-clojure :exclude [compare <= >= max])) 3 | 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | ;; Comparator 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | ;; TODO: note about fanciness 9 | 10 | (defn normalize ^long [^long x] 11 | (if (zero? x) 12 | x 13 | (bit-or 1 14 | (bit-shift-right x 63)))) 15 | 16 | (defn compare-by 17 | "Given a predicate that defines a total order over some domain, 18 | return a three-way comparator built from it." 19 | [pred] 20 | (fn [x y] 21 | (cond 22 | (pred x y) -1 23 | (pred y x) +1 24 | true 0))) 25 | 26 | (defn normal-compare ^long [x y] 27 | (normalize (clojure.core/compare x y))) 28 | 29 | (def ^:dynamic *compare* normal-compare) 30 | 31 | (defn compare ^long [x y] 32 | (*compare* x y)) 33 | 34 | (defn compare< [x y] 35 | (neg? (compare x y))) 36 | 37 | (defn compare<= [x y] 38 | (not (pos? (compare x y)))) 39 | 40 | (defn compare> [x y] 41 | (pos? (compare x y))) 42 | 43 | (defn compare>= [x y] 44 | (not (neg? (compare x y)))) 45 | 46 | (defn compare= [x y] 47 | (zero? (compare x y))) 48 | 49 | (defn max [x & args] 50 | (reduce #(if (compare> %1 %2) %1 %2) x args)) 51 | 52 | (defn <= 53 | ([x] true) 54 | ([x y] (compare<= x y)) 55 | ([x y & more] 56 | (if (compare<= x y) 57 | (if (next more) 58 | (recur y (first more) (next more)) 59 | (compare<= y (first more))) 60 | false))) 61 | 62 | (defn >= 63 | ([x] true) 64 | ([x y] (compare>= x y)) 65 | ([x y & more] 66 | (if (compare>= x y) 67 | (if (next more) 68 | (recur y (first more) (next more)) 69 | (compare>= y (first more))) 70 | false))) 71 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/ordered_map.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.ordered-map 2 | (:require [clojure.core.reducers :as r :refer [coll-fold]] 3 | [com.dean.interval-tree.tree.node :as node] 4 | [com.dean.interval-tree.tree.protocol :as proto] 5 | [com.dean.interval-tree.tree.root] 6 | [com.dean.interval-tree.tree.tree :as tree] 7 | [com.dean.interval-tree.tree.order :as order]) 8 | (:import [clojure.lang RT] 9 | [com.dean.interval_tree.tree.root INodeCollection 10 | IBalancedCollection 11 | IOrderedCollection])) 12 | 13 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 14 | ;; Dynamic Environment 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | (defmacro with-ordered-map [x & body] 18 | `(binding [order/*compare* (.getCmp ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.IOrderedCollection}))] 19 | ~@body)) 20 | 21 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 | ;; Ordered Map 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | 25 | (deftype OrderedMap [root cmp alloc stitch _meta] 26 | 27 | INodeCollection 28 | (getAllocator [_] 29 | alloc) 30 | (getRoot [_] 31 | root) 32 | 33 | IOrderedCollection 34 | (getCmp [_] 35 | cmp) 36 | (isCompatible [_ o] 37 | (and (instance? OrderedMap o) (= cmp (.getCmp o)) (= stitch (.getStitch o)))) 38 | (isSimilar [_ o] 39 | (map? o)) 40 | 41 | IBalancedCollection 42 | (getStitch [_] 43 | stitch) 44 | 45 | clojure.lang.IMeta 46 | (meta [_] 47 | _meta) 48 | 49 | clojure.lang.IObj 50 | (withMeta [_ m] 51 | (OrderedMap. root cmp alloc stitch m)) 52 | 53 | clojure.lang.Indexed 54 | (nth [this i] 55 | (with-ordered-map this 56 | (node/-kv (tree/node-nth root i)))) 57 | 58 | clojure.lang.MapEquivalence 59 | 60 | clojure.lang.Seqable 61 | (seq [this] 62 | (with-ordered-map this 63 | (map node/-kv (tree/node-seq root)))) 64 | 65 | clojure.lang.Reversible 66 | (rseq [this] 67 | (with-ordered-map this 68 | (map node/-kv (tree/node-seq-reverse root)))) 69 | 70 | clojure.lang.ILookup 71 | (valAt [this k not-found] 72 | (with-ordered-map this 73 | (if-let [found (tree/node-find root k)] 74 | (node/-v found) 75 | not-found))) 76 | (valAt [this k] 77 | (.valAt this k nil)) 78 | 79 | clojure.lang.IFn 80 | (invoke [this k not-found] 81 | (.valAt this k not-found)) 82 | (invoke [this k] 83 | (.valAt this k)) 84 | (applyTo [this args] 85 | (let [n (RT/boundedLength args 2)] 86 | (case n 87 | 0 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName)))) 88 | 1 (.invoke this (first args)) 89 | 2 (.invoke this (first args) (second args)) 90 | 3 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName))))))) 91 | 92 | java.lang.Comparable 93 | (compareTo [this o] 94 | (with-ordered-map this 95 | (cond 96 | (identical? this o) 0 97 | (.isCompatible this o) (tree/node-map-compare root (.getRoot o)) 98 | (.isSimilar this o) (.compareTo (into (empty o) this) o) 99 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 100 | 101 | clojure.lang.Counted 102 | (count [this] 103 | (tree/node-size root)) 104 | 105 | clojure.lang.Associative 106 | (containsKey [this k] 107 | (with-ordered-map this 108 | (some? (tree/node-find root k)))) 109 | (entryAt [this k] 110 | (with-ordered-map this 111 | (some-> root (tree/node-find k) node/-kv))) 112 | (assoc [this k v] 113 | (with-ordered-map this 114 | (OrderedMap. (tree/node-add root k v) cmp alloc stitch _meta))) 115 | (empty [this] 116 | (OrderedMap. (node/leaf) cmp alloc stitch {})) 117 | 118 | java.util.Map 119 | (get [this k] 120 | (.valAt this k)) 121 | (isEmpty [_] 122 | (node/leaf? root)) 123 | (size [_] 124 | (tree/node-size root)) 125 | (keySet [this] 126 | (with-ordered-map this 127 | (set (tree/node-vec root :accessor :k)))) 128 | (put [_ _ _] 129 | (throw (UnsupportedOperationException.))) 130 | (putAll [_ _] 131 | (throw (UnsupportedOperationException.))) 132 | (clear [_] 133 | (throw (UnsupportedOperationException.))) 134 | (values [this] 135 | (with-ordered-map this 136 | (tree/node-vec root :accessor :v))) 137 | (entrySet [this] 138 | (with-ordered-map this 139 | (set (tree/node-vec root :accessor :kv)))) 140 | (iterator [this] 141 | (clojure.lang.SeqIterator. (seq this))) 142 | 143 | clojure.lang.IPersistentCollection 144 | (equiv [this o] 145 | (with-ordered-map this 146 | (cond 147 | (identical? this o) 0 148 | (.isCompatible this o) (and (= (.count this) (.count o)) 149 | (zero? (tree/node-map-compare root (.getRoot o)))) 150 | (map? o) (.equiv (into (empty o) (tree/node-vec root :accessor :kv)) o) 151 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 152 | 153 | (cons [this o] 154 | (.assoc this (nth o 0) (nth o 1))) 155 | 156 | clojure.lang.IPersistentMap 157 | (assocEx [this k v] ;; TODO: use `tree/node-add-if` 158 | (if (contains? this k) 159 | (throw (Exception. "Key or value already present")) 160 | (assoc this k v))) 161 | (without [this k] 162 | (with-ordered-map this 163 | (OrderedMap. (tree/node-remove root k) cmp alloc stitch _meta)))) 164 | 165 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 166 | ;; Literal Representation 167 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 168 | 169 | (defmethod print-method OrderedMap [m w] 170 | ((get (methods print-method) clojure.lang.IPersistentMap) m w)) 171 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/ordered_set.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.ordered-set 2 | (:require [clojure.core.reducers :as r :refer [coll-fold]] 3 | [clojure.set] 4 | [com.dean.interval-tree.tree.node :as node] 5 | [com.dean.interval-tree.tree.order :as order] 6 | [com.dean.interval-tree.tree.protocol :as proto] 7 | [com.dean.interval-tree.tree.root] 8 | [com.dean.interval-tree.tree.tree :as tree]) 9 | (:import [clojure.lang RT] 10 | [com.dean.interval_tree.tree.protocol PExtensibleSet] 11 | [com.dean.interval_tree.tree.root INodeCollection 12 | IBalancedCollection 13 | IOrderedCollection])) 14 | 15 | ;; TODO: 16 | ;; - clojure.lang.Sorted 17 | ;; - ISeq .seqFrom 18 | 19 | ;; - IReduce, IReduceKV, 20 | ;; - IMapIterable: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/PersistentHashMap.java 21 | ;; - Collection Check: https://github.com/ztellman/collection-check/blob/master/src/collection_check/core.cljc 22 | 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | ;; Dynamic Environment 25 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 26 | 27 | (defmacro with-ordered-set [x & body] 28 | `(binding [order/*compare* (.getCmp ~(with-meta x {:tag 'com.dean.interval_tree.tree.root.IOrderedCollection}))] 29 | ~@body)) 30 | 31 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 | ;; Ordered Set 33 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 34 | 35 | (deftype OrderedSet [root cmp alloc stitch _meta] 36 | 37 | INodeCollection 38 | (getAllocator [_] 39 | alloc) 40 | (getRoot [_] 41 | root) 42 | 43 | IOrderedCollection 44 | (getCmp [_] 45 | cmp) 46 | (isCompatible [_ o] 47 | (and (instance? OrderedSet o) (= cmp (.getCmp ^OrderedSet o)) (= stitch (.getStitch ^OrderedSet o)))) 48 | (isSimilar [_ o] 49 | (set? o)) 50 | 51 | IBalancedCollection 52 | (getStitch [_] 53 | stitch) 54 | 55 | PExtensibleSet 56 | (intersection [this that] 57 | (with-ordered-set this 58 | (cond 59 | (identical? this that) this 60 | (.isCompatible this that) (new OrderedSet (tree/node-set-intersection root (.getRoot ^OrderedSet that)) 61 | cmp alloc stitch {}) 62 | (.isSimilar this that) (clojure.set/intersection (into #{} this) that) 63 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 64 | (union [this that] 65 | (with-ordered-set this 66 | (cond 67 | (identical? this that) this 68 | (.isCompatible this that) (new OrderedSet (tree/node-set-union root (.getRoot ^OrderedSet that)) 69 | cmp alloc stitch {}) 70 | (.isSimilar this that) (clojure.set/union (into #{} this) that) 71 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 72 | (difference [this that] 73 | (with-ordered-set this 74 | (cond 75 | (identical? this that) (.empty this) 76 | (.isCompatible this that) (new OrderedSet (tree/node-set-difference root (.getRoot ^OrderedSet that)) 77 | cmp alloc stitch{}) 78 | (.isSimilar this that) (clojure.set/difference (into #{} this) that) 79 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 80 | (subset [this that] 81 | (with-ordered-set this 82 | (cond 83 | (identical? this that) true 84 | (.isCompatible this that) (tree/node-subset? (.getRoot ^OrderedSet that) root) ;; Grr. reverse args of tree/subset 85 | (.isSimilar this that) (clojure.set/subset? (into #{} this) that) 86 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 87 | (superset [this that] 88 | (with-ordered-set this 89 | (cond 90 | (identical? this that) true 91 | (.isCompatible this that) (tree/node-subset? root (.getRoot ^OrderedSet that)) ;; Grr. reverse args of tree/subset 92 | (.isSimilar this that) (clojure.set/subset? that (into #{} this)) 93 | true (throw (ex-info "unsupported set operands: " {:this this :that that}))))) 94 | 95 | clojure.lang.IMeta 96 | (meta [_] 97 | _meta) 98 | 99 | clojure.lang.IObj 100 | (withMeta [_ m] 101 | (new OrderedSet root cmp alloc stitch m)) 102 | 103 | clojure.lang.Indexed 104 | (nth [this i] 105 | (with-ordered-set this 106 | (node/-k (tree/node-nth root i)))) 107 | 108 | clojure.lang.Seqable 109 | (seq [this] 110 | (with-ordered-set this 111 | (map node/-k (tree/node-seq root)))) 112 | 113 | clojure.lang.Reversible 114 | (rseq [this] 115 | (with-ordered-set this 116 | (map node/-k (tree/node-seq-reverse root)))) 117 | 118 | clojure.lang.ILookup 119 | (valAt [this k not-found] 120 | (with-ordered-set this 121 | (if-let [found (tree/node-find root k)] 122 | (node/-k found) 123 | not-found))) 124 | (valAt [this k] 125 | (.valAt this k nil)) 126 | 127 | clojure.lang.IFn 128 | (invoke [this k not-found] 129 | (.valAt this k not-found)) 130 | (invoke [this k] 131 | (.valAt this k)) 132 | (applyTo [this args] 133 | (let [n (RT/boundedLength args 2)] 134 | (case n 135 | 0 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName)))) 136 | 1 (.invoke this (first args)) 137 | 2 (.invoke this (first args) (second args)) 138 | 3 (throw (clojure.lang.ArityException. n (.. this (getClass) (getSimpleName))))))) 139 | 140 | java.lang.Comparable 141 | (compareTo [this o] 142 | (with-ordered-set this 143 | (cond 144 | (identical? this o) 0 145 | (.isCompatible this o) (tree/node-set-compare root (.getRoot ^OrderedSet o)) 146 | (.isSimilar this o) (.compareTo ^Comparable (into (empty o) this) o) 147 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 148 | 149 | java.util.Collection 150 | (toArray [this] 151 | (with-ordered-set this 152 | (object-array (tree/node-vec root :accessor :k)))) ; better constructor not a priority 153 | (isEmpty [_] 154 | (node/leaf? root)) 155 | (add [_ _] 156 | (throw (UnsupportedOperationException.))) 157 | (addAll [_ _] 158 | (throw (UnsupportedOperationException.))) 159 | (removeAll [_ _] 160 | (throw (UnsupportedOperationException.))) 161 | (retainAll [_ _] 162 | (throw (UnsupportedOperationException.))) 163 | 164 | java.util.List 165 | (indexOf [this x] 166 | (with-ordered-set this 167 | (tree/node-rank root x))) 168 | (lastIndexOf [this x] 169 | (.indexOf this x)) 170 | 171 | java.util.Set 172 | (size [_] 173 | (tree/node-size root)) 174 | (iterator [this] 175 | (clojure.lang.SeqIterator. (seq this))) 176 | (containsAll [this s] 177 | (with-ordered-set this 178 | (cond 179 | (identical? this s) true 180 | (.isCompatible this s) (tree/node-subset? root (.getRoot ^OrderedSet s)) 181 | (coll? s) (every? #(.contains this %) s) 182 | true (throw (ex-info "unsupported comparison: " {:this this :s s}))))) 183 | 184 | java.util.SortedSet 185 | (comparator [_] 186 | cmp) 187 | (first [this] 188 | (with-ordered-set this 189 | (node/-k (tree/node-least root)))) 190 | (last [this] 191 | (with-ordered-set this 192 | (node/-k (tree/node-greatest root)))) 193 | (headSet [this x] 194 | (with-ordered-set this 195 | (new OrderedSet (tree/node-split-lesser root x) cmp alloc stitch {}))) 196 | (tailSet [this x] 197 | (with-ordered-set this 198 | (new OrderedSet (tree/node-split-greater root x) cmp alloc stitch {}))) 199 | (subSet [this from to] 200 | (with-ordered-set this 201 | (let [left (tree/node-split-greater root from) 202 | right (tree/node-split-lesser root to) 203 | result (tree/node-set-intersection left right)] 204 | (new OrderedSet result cmp alloc stitch {})))) 205 | 206 | java.util.NavigableSet 207 | (ceiling [this x] 208 | (with-ordered-set this 209 | (let [[_ x' r] (tree/node-split root x)] 210 | (if (some? x') 211 | (first x') 212 | (some-> (tree/node-least r) node/-k))))) 213 | (floor [this x] 214 | (with-ordered-set this 215 | (let [[l x' _] (tree/node-split root x)] 216 | (if (some? x') 217 | (first x') 218 | (some-> (tree/node-greatest l) node/-k))))) 219 | 220 | clojure.lang.IPersistentSet 221 | (equiv [this o] 222 | (with-ordered-set this 223 | (cond 224 | (identical? this o) true 225 | (not= (tree/node-size root) (.count ^clojure.lang.Counted o)) false 226 | (.isCompatible this o) (zero? (tree/node-set-compare root (.getRoot ^OrderedSet o))) 227 | (.isSimilar this o) (.equiv ^clojure.lang.IPersistentSet (into (empty o) this) o) 228 | true (throw (ex-info "unsupported comparison: " {:this this :o o}))))) 229 | (count [_] 230 | (tree/node-size root)) 231 | (empty [_] 232 | (new OrderedSet (node/leaf) cmp alloc stitch {})) 233 | (contains [this k] 234 | (with-ordered-set this 235 | (if (tree/node-find root k) true false))) 236 | (disjoin [this k] 237 | (with-ordered-set this 238 | (new OrderedSet (tree/node-remove root k) cmp alloc stitch _meta))) 239 | (cons [this k] 240 | (with-ordered-set this 241 | (new OrderedSet (tree/node-add root k) cmp alloc stitch _meta))) 242 | 243 | clojure.core.reducers.CollFold 244 | (coll-fold [this n combinef reducef] 245 | (with-ordered-set this 246 | (tree/node-chunked-fold n root combinef 247 | (fn [acc node] (reducef acc (node/-k node))))))) 248 | 249 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 250 | ;; Literal Representation 251 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 252 | 253 | (defmethod print-method OrderedSet [s w] 254 | ((get (methods print-method) clojure.lang.IPersistentSet) s w)) 255 | 256 | 257 | 258 | (comment 259 | 260 | ;; W00T! 261 | 262 | (def foo (shuffle (range 500000))) 263 | (def bar (shuffle (range 1000000))) 264 | 265 | (def s0 (shuffle (range 0 1000000 2))) 266 | (def s1 (shuffle (range 0 1000000 3))) 267 | 268 | ;; Home Team: 269 | 270 | (time (def x (ordered-set foo))) ;; 500K: "Elapsed time: 564.248517 msecs" 271 | (time (def y (ordered-set bar))) ;; 1M: "Elapsed time: 1187.734211 msecs" 272 | (time (def s (proto/intersection 273 | (ordered-set s0) 274 | (ordered-set s1)))) ;; 833K: "Elapsed time: 1242.961445 msecs" 275 | (time (r/fold + + y)) ;; 1M: "Elapsed time: 54.363545 msecs" 276 | 277 | ;; Visitors: 278 | 279 | (time (def v (into (sorted-set) foo))) ;; 500K: "Elapsed time: 839.188189 msecs" 280 | (time (def w (into (sorted-set) bar))) ;; 1M: "Elapsed time: 1974.798286 msecs" 281 | (time (def s (clojure.set/intersection 282 | (into (sorted-set) s0) 283 | (into (sorted-set) s1)))) ;; 833K: "Elapsed time: 1589.786106 msecs" 284 | (time (r/fold + + w)) ;; 1M: "Elapsed time: 167.916539 msecs" 285 | 286 | 287 | (require '[criterium.core]) 288 | 289 | (criterium.core/bench (def x (ordered-set foo))) 290 | 291 | ;; Evaluation count : 120 in 60 samples of 2 calls. 292 | ;; Execution time mean : 612.435645 ms 293 | ;; Execution time std-deviation : 60.421726 ms 294 | ;; Execution time lower quantile : 565.022632 ms ( 2.5%) 295 | ;; Execution time upper quantile : 771.090227 ms (97.5%) 296 | ;; Overhead used : 1.708588 ns 297 | ;; 298 | ;; Found 11 outliers in 60 samples (18.3333 %) 299 | ;; low-severe 1 (1.6667 %) 300 | ;; low-mild 10 (16.6667 %) 301 | ;; Variance from outliers : 68.6890 % Variance is severely inflated by outliers 302 | 303 | (criterium.core/bench (def v (into (sorted-set) foo))) 304 | 305 | ;; Evaluation count : 120 in 60 samples of 2 calls. 306 | ;; Execution time mean : 819.376840 ms 307 | ;; Execution time std-deviation : 29.835432 ms 308 | ;; Execution time lower quantile : 789.678093 ms ( 2.5%) 309 | ;; Execution time upper quantile : 907.561055 ms (97.5%) 310 | ;; Overhead used : 1.708588 ns 311 | ;; 312 | ;; Found 5 outliers in 60 samples (8.3333 %) 313 | ;; low-severe 3 (5.0000 %) 314 | ;; low-mild 2 (3.3333 %) 315 | ;; Variance from outliers : 22.2640 % Variance is moderately inflated by outliers 316 | 317 | ;;; 318 | ;; clojure.data.avl 319 | 320 | (require '[clojure.data.avl :as avl]) 321 | 322 | (time (def z (into (avl/sorted-set) foo))) ;; 500K: "Elapsed time: 586.862601 msecs" 323 | (time (def z (into (avl/sorted-set) bar))) ;; 1M: "Elapsed time: 1399.241718 msecs" 324 | 325 | (criterium.core/bench (def z (into (avl/sorted-set) foo))) 326 | 327 | ;; Evaluation count : 120 in 60 samples of 2 calls. 328 | ;; Execution time mean : 606.249611 ms 329 | ;; Execution time std-deviation : 16.864172 ms 330 | ;; Execution time lower quantile : 560.393078 ms ( 2.5%) 331 | ;; Execution time upper quantile : 631.176588 ms (97.5%) 332 | ;; Overhead used : 1.710404 ns 333 | ;; 334 | ;; Found 4 outliers in 60 samples (6.6667 %) 335 | ;; low-severe 3 (5.0000 %) 336 | ;; low-mild 1 (1.6667 %) 337 | ;; Variance from outliers : 14.2428 % Variance is moderately inflated by outliers 338 | 339 | (time (r/fold + + z)) 340 | 341 | ) 342 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/protocol.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.protocol 2 | (:require [clojure.set :as set])) 3 | 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | ;; Set Protocol 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | 8 | (defprotocol PExtensibleSet 9 | (intersection [this that]) 10 | (union [this that]) 11 | (difference [this that]) 12 | (subset [this that]) 13 | (superset [this that])) 14 | 15 | (extend-type clojure.lang.PersistentHashSet 16 | PExtensibleSet 17 | (intersection [this that] 18 | (set/intersection this that)) 19 | (union [this that] 20 | (set/union this that)) 21 | (difference [this that] 22 | (set/difference this that)) 23 | (subset [this that] 24 | (set/subset? this that)) 25 | (superset [this that] 26 | (set/subset? that this))) 27 | 28 | (extend-type clojure.lang.PersistentTreeSet 29 | PExtensibleSet 30 | (intersection [this that] 31 | (set/intersection this that)) 32 | (union [this that] 33 | (set/union this that)) 34 | (difference [this that] 35 | (set/difference this that)) 36 | (subset [this that] 37 | (set/subset? this that)) 38 | (superset [this that] 39 | (set/subset? that this))) 40 | -------------------------------------------------------------------------------- /src/com/dean/interval_tree/tree/root.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree.root) 2 | 3 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 4 | ;; Root Container 5 | ;; 6 | ;; The Root Container implements the external interface that encapsulates 7 | ;; the internal algorithm/structure of a rooted node collection (tree) 8 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | 10 | (definterface INodeCollection 11 | (getAllocator [] "Return a fuction that allocates a new node of this colletion") 12 | (getRoot [] "Return a 'root' node as appropriate for the given collection")) 13 | 14 | (definterface IBalancedCollection 15 | (getStitch [] "Return a 'balanced constructor' that joins an allocated node 16 | as appropriate to this collection. Various balancing algorithms 17 | are functions of the implementation of stitch.")) 18 | 19 | (definterface IOrderedCollection 20 | (getCmp [] "Return a three-way comparator that defintes a total order 21 | over all items in the collection") 22 | (isCompatible [other] "Return `true` if `other` collection is algoritmically compatible") 23 | (isSimilar [other] "Return `true` if `other` is algorithmically identical")) 24 | 25 | (definterface IIntervalCollection) 26 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/interval_map_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.interval-map-test 2 | (:require [clojure.test :refer :all] 3 | [com.dean.interval-tree.core :refer [interval-map]])) 4 | 5 | 6 | ;; x8: +-----+ 7 | ;; x7: +-----------------------------------+ 8 | ;; x6: + 9 | ;; x5: +-----------+ 10 | ;; x4: +-----------------------------+ 11 | ;; x3: +-----+ 12 | ;; x2: +-----------------+ 13 | ;; x1: +-----------+ 14 | ;; 15 | ;; 0=====1=====2=====3=====4=====5=====6=====7=====8=====9 16 | 17 | (deftest interval-query-check 18 | (let [x (interval-map {[1 3] :x1 19 | [4 7] :x2 20 | [8 9] :x3 21 | [0 5] :x4 22 | [6 8] :x5 23 | [9 9] :x6 24 | [3 9] :x7 25 | [4 5] :x8})] 26 | (testing "pointwise query" ;; note: order returned determined by interval start 27 | (is (empty? (x -1.00000000))) 28 | (is (empty? (x -0.00000001))) 29 | (is (= [:x4] (x 0.00000000))) 30 | (is (= [:x4] (x 0.00000000001))) 31 | (is (= [:x4] (x 0.99999999999))) 32 | (is (= [:x4 :x1] (x 1))) 33 | (is (= [:x4 :x1] (x 1.8675309))) 34 | (is (= [:x4 :x1] (x 2.0))) 35 | (is (= [:x4 :x1] (x 2.99999999))) 36 | (is (= [:x4 :x1 :x7] (x 3))) 37 | (is (= [:x4 :x7] (x 3.00000001))) 38 | (is (= [:x4 :x7] (x 3.99999999))) 39 | (is (= [:x4 :x7 :x8 :x2] (x 4))) 40 | (is (= [:x4 :x7 :x8 :x2] (x 5))) 41 | (is (= [:x7 :x2] (x 5.00000001))) 42 | (is (= [:x7 :x2 :x5] (x 6))) 43 | (is (= [:x7 :x2 :x5] (x 7))) 44 | (is (= [:x7 :x5] (x 7.00000001))) 45 | (is (= [:x7 :x5] (x 7.99999999))) 46 | (is (= [:x7 :x5 :x3] (x 8))) 47 | (is (= [:x7 :x3] (x 8.00000001))) 48 | (is (= [:x7 :x3] (x 8.99999999))) 49 | (is (= [:x7 :x3 :x6] (x 9))) 50 | (is (empty? (x 9.00000001)))) 51 | (testing "interval query" 52 | (is (= [:x4] (x [0 0]))) 53 | (is (= [:x4] (x [0 0.5]))) 54 | (is (= [:x4 :x1] (x [0 1.0]))) 55 | (is (= [:x4 :x1] (x [0 1.5]))) 56 | (is (= [:x4 :x1] (x [0 2.0]))) 57 | (is (= [:x4 :x1] (x [0 2.5]))) 58 | (is (= [:x4 :x1 :x7] (x [0 3.0]))) 59 | (is (= [:x4 :x1 :x7] (x [0 3.5]))) 60 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [0 4.0]))) 61 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [0 4.5]))) 62 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [0 5.0]))) 63 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [0 5.5]))) 64 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [0 6.0]))) 65 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [0 6.5]))) 66 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [0 7.0]))) 67 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [0 7.5]))) 68 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [0 8.0]))) 69 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [0 8.5]))) 70 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [0 9.0]))) 71 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [0 9.5]))) 72 | (is (= [:x4 :x1] (x [1 1]))) 73 | (is (= [:x4 :x1] (x [1 1.5]))) 74 | (is (= [:x4 :x1] (x [1 2.0]))) 75 | (is (= [:x4 :x1] (x [1 2.5]))) 76 | (is (= [:x4 :x1 :x7] (x [1 3.0]))) 77 | (is (= [:x4 :x1 :x7] (x [1 3.5]))) 78 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [1 4.0]))) 79 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [1 4.5]))) 80 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [1 5.0]))) 81 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [1 5.5]))) 82 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [1 6.0]))) 83 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [1 6.5]))) 84 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [1 7.0]))) 85 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [1 7.5]))) 86 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [1 8.0]))) 87 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [1 8.5]))) 88 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [1 9.0]))) 89 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [1 9.5]))) 90 | (is (= [:x4 :x1] (x [2 2]))) 91 | (is (= [:x4 :x1] (x [2 2.5]))) 92 | (is (= [:x4 :x1 :x7] (x [2 3.0]))) 93 | (is (= [:x4 :x1 :x7] (x [2 3.5]))) 94 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [2 4.0]))) 95 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [2 4.5]))) 96 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [2 5.0]))) 97 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [2 5.5]))) 98 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [2 6.0]))) 99 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [2 6.5]))) 100 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [2 7.0]))) 101 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [2 7.5]))) 102 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [2 8.0]))) 103 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [2 8.5]))) 104 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [2 9.0]))) 105 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [2 9.5]))) 106 | (is (= [:x4 :x1 :x7] (x [3 3]))) 107 | (is (= [:x4 :x1 :x7] (x [3 3.5]))) 108 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [3 4.0]))) 109 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [3 4.5]))) 110 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [3 5.0]))) 111 | (is (= [:x4 :x1 :x7 :x8 :x2] (x [3 5.5]))) 112 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [3 6.0]))) 113 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [3 6.5]))) 114 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [3 7.0]))) 115 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5] (x [3 7.5]))) 116 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [3 8.0]))) 117 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3] (x [3 8.5]))) 118 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [3 9.0]))) 119 | (is (= [:x4 :x1 :x7 :x8 :x2 :x5 :x3 :x6] (x [3 9.5]))) 120 | (is (= [:x4 :x7 :x8 :x2] (x [4 4]))) 121 | (is (= [:x4 :x7 :x8 :x2] (x [4 4.5]))) 122 | (is (= [:x4 :x7 :x8 :x2] (x [4 5.0]))) 123 | (is (= [:x4 :x7 :x8 :x2] (x [4 5.5]))) 124 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [4 6.0]))) 125 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [4 6.5]))) 126 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [4 7.0]))) 127 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [4 7.5]))) 128 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3] (x [4 8.0]))) 129 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3] (x [4 8.5]))) 130 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3 :x6] (x [4 9.0]))) 131 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3 :x6] (x [4 9.5]))) 132 | (is (= [:x4 :x7 :x8 :x2] (x [5 5]))) 133 | (is (= [:x4 :x7 :x8 :x2] (x [5 5.5]))) 134 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [5 6.0]))) 135 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [5 6.5]))) 136 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [5 7.0]))) 137 | (is (= [:x4 :x7 :x8 :x2 :x5] (x [5 7.5]))) 138 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3] (x [5 8.0]))) 139 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3] (x [5 8.5]))) 140 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3 :x6] (x [5 9.0]))) 141 | (is (= [:x4 :x7 :x8 :x2 :x5 :x3 :x6] (x [5 9.5]))) 142 | (is (= [:x7 :x2 :x5] (x [6 6]))) 143 | (is (= [:x7 :x2 :x5] (x [6 6.5]))) 144 | (is (= [:x7 :x2 :x5] (x [6 7.0]))) 145 | (is (= [:x7 :x2 :x5] (x [6 7.5]))) 146 | (is (= [:x7 :x2 :x5 :x3] (x [6 8.0]))) 147 | (is (= [:x7 :x2 :x5 :x3] (x [6 8.5]))) 148 | (is (= [:x7 :x2 :x5 :x3 :x6] (x [6 9.0]))) 149 | (is (= [:x7 :x2 :x5 :x3 :x6] (x [6 9.5]))) 150 | (is (= [:x7 :x2 :x5] (x [7 7]))) 151 | (is (= [:x7 :x2 :x5] (x [7 7.5]))) 152 | (is (= [:x7 :x2 :x5 :x3] (x [7 8.0]))) 153 | (is (= [:x7 :x2 :x5 :x3] (x [7 8.5]))) 154 | (is (= [:x7 :x2 :x5 :x3 :x6] (x [7 9.0]))) 155 | (is (= [:x7 :x2 :x5 :x3 :x6] (x [7 9.5]))) 156 | (is (= [:x7 :x5 :x3] (x [8 8]))) 157 | (is (= [:x7 :x5 :x3] (x [8 8.5]))) 158 | (is (= [:x7 :x5 :x3 :x6] (x [8 9.0]))) 159 | (is (= [:x7 :x5 :x3 :x6] (x [8 9.5]))) 160 | (is (= [:x7 :x3 :x6] (x [9 9]))) 161 | (is (= [:x7 :x3 :x6] (x [9 9.5])))))) 162 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/interval_set_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.interval-set-test 2 | (:require [clojure.test :refer :all] 3 | [com.dean.interval-tree.core :refer [interval-set]])) 4 | 5 | ;; TODO: more 6 | 7 | (deftest smoke-check 8 | (let [x (interval-set [[1 3] [2 4] [5 9] [3 6]])] 9 | (is (= [[1 3] [2 4] [3 6] [5 9]] (seq x))) 10 | (is (= nil (x 0))) 11 | (is (= [[1 3]] (x 1))) 12 | (is (= [[1 3]] (x 1.99999))) 13 | (is (= [[1 3] [2 4]] (x [1 2]))) 14 | (is (= [[1 3] [2 4] [3 6]] (x [1 3]))) 15 | (is (= [[5 9]] (x 7)))) 16 | (let [y (interval-set (range 5))] 17 | (is (= [[0 0] [1 1] [2 2] [3 3] [4 4]] (seq y))) 18 | (is (= [[0 0] [1 1] [2 2] [3 3]] (y [0 3.1415926]))) 19 | (is (= [[1 1] [2 2]] (y [1 2.5]))) 20 | (is (= nil (y 1.5))) 21 | (is (= [[1 1]] (y 1))) 22 | (is (= [[2 2]] (y 2))))) 23 | 24 | (defn random-segments [n] 25 | (->> n (* 2) range shuffle (partition-all 2) (map sort) (map vec))) 26 | 27 | (defn random-interval-set [n] 28 | (-> n random-segments interval-set)) 29 | 30 | (deftest interval-set-lookup-check 31 | (doseq [size [10 100 1000 10000 100000]] 32 | (let [greatest (-> size (* 2) dec) 33 | x (random-interval-set size)] 34 | (is (= size (count x))) 35 | (is (= 0 (count (x -1)))) 36 | (is (= 0 (count (x (inc greatest))))) 37 | (is (= 1 (count (x 0)))) 38 | (is (= 1 (count (x 0.5)))) 39 | (is (= 1 (count (x greatest)))) 40 | (is (= 1 (count (x (- greatest 0.5))))) 41 | (is (= (seq x) (x [0 greatest]))) 42 | (is (= size (count (x [0 greatest]))))))) 43 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/interval_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.interval-test 2 | (:require [clojure.test :refer :all] 3 | [com.dean.interval-tree.tree.interval :as interval :refer :all]) 4 | (:import [clojure.lang MapEntry])) 5 | 6 | (deftest pair-check 7 | (is (ordered-pair? (MapEntry. 0 1))) 8 | (is (ordered-pair? (vector 0 1))) 9 | (is (ordered-pair? (ordered-pair 0 1))) 10 | (is (ordered-pair? (ordered-pair [0 1]))) 11 | (is (thrown? java.lang.AssertionError (ordered-pair 1 0))) 12 | (is (thrown? java.lang.AssertionError (ordered-pair [1 0]))) 13 | (is (not (ordered-pair? (MapEntry. 1 0)))) 14 | (is (not (ordered-pair? [1 0]))) 15 | (is (not (ordered-pair? :foo)))) 16 | 17 | (deftest intersection-check 18 | (is (overlaps? [1 3] [2 4])) 19 | (is (overlaps? [2 4] [1 3])) 20 | (is (overlaps? [1 3] [3 4])) 21 | (is (not (overlaps? [1 2] [3 4]))) 22 | (is (not (includes? [0 1] [0 2]))) 23 | (is (not (includes? [0 1] [1 2]))) 24 | (is (includes? [0 2] [0 1])) 25 | (is (intersects? [1 3] [2 4])) 26 | (is (intersects? [2 4] [1 3])) 27 | (is (intersects? [1 3] [3 4])) 28 | (is (not (intersects? [1 2] [3 4]))) 29 | (is (intersects? [0 1] [0 2])) 30 | (is (intersects? [0 1] [1 2])) 31 | (is (intersects? [0 2] [0 1]))) 32 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/ordered_map_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.ordered-map-test 2 | (:require [clojure.test :refer :all] 3 | [com.dean.interval-tree.core :refer [ordered-map ordered-map-by]]) 4 | (:import [java.util UUID])) 5 | 6 | 7 | ;; TODO: more 8 | 9 | (deftest smoke-check 10 | (is (= {} (ordered-map))) 11 | (is (map? (ordered-map))) ;; => true 12 | (is (= {:a 4, :b 5, :x 1, :y 2, :z 3} 13 | (ordered-map {:x 1 :y 2 :z 3 :a 4 :b 5}))) 14 | (is (= [[:a 4] [:b 5] [:x 1] [:y 2] [:z 3]] 15 | (seq (ordered-map {:x 1 :y 2 :z 3 :a 4 :b 5})))) 16 | (is (= {:a 4, :b 5, :x 1, :y 2, :z 3} 17 | (assoc (ordered-map) :x 1 :y 2 :z 3 :a 4 :b 5))) 18 | (is (= [[1 "a"] [2 "b"] [3 "c"] [4 "d"]] 19 | (seq (ordered-map [[2 "b"] [3 "c"] [1 "a"] [4 "d"]])))) 20 | (is (= {:a "a", :b "b", :c "c"} 21 | (-> (ordered-map) (assoc :b "b") (assoc :a "a") (assoc :c "c")))) 22 | (is (= {:b "b", :c "c"} 23 | (-> (ordered-map) (assoc :b "b") (assoc :a "a") (assoc :c "c") 24 | (dissoc :a)))) 25 | (is (= "c" ((ordered-map {:a "a", :b "b", :c "c", :d "d"}) :c))) 26 | (is (= ::not-found 27 | ((ordered-map {:a "a", :b "b", :c "c", :d "d"}) :z ::not-found)))) 28 | 29 | (defn random-entry [] 30 | (vector (UUID/randomUUID) (UUID/randomUUID))) 31 | 32 | (defn random-map 33 | ([size] 34 | (random-map (sorted-map) size)) 35 | ([this size] 36 | (into this (repeatedly size random-entry)))) 37 | 38 | (deftest map-equivalence-check 39 | (doseq [size [10 100 1000 10000 100000 500000]] 40 | (let [s (random-map size) 41 | t (random-map 1000) 42 | x (ordered-map s) 43 | [k v] (random-entry)] 44 | (is (= s x)) 45 | (is (= (count s) (count x))) 46 | (is (= (reverse s) (reverse x))) 47 | (is (= (seq s) (seq x))) 48 | (is (= (keys s) (keys x))) 49 | (is (= (vals s) (vals x))) 50 | (is (= (vals s) (map x (keys s)))) 51 | (is (= nil (x k))) 52 | (is (= ::nope (x k ::nope))) 53 | (is (= v ((assoc x k v) k))) 54 | (is (= (assoc s k v) (assoc x k v))) 55 | (is (= s (-> x (assoc k v) (dissoc k)))) 56 | (is (= (into s t) (into x t))) 57 | (is (= (into s t) (-> x (into t) (into t))))))) 58 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/ordered_set_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.ordered-set-test 2 | (:require [clojure.core.reducers :as r] 3 | [clojure.math.combinatorics :as combo] 4 | [clojure.set :as set] 5 | [clojure.test :refer :all] 6 | [com.dean.interval-tree.core :refer :all])) 7 | 8 | ;; TODO: more coverage 9 | 10 | (deftest simple-checks 11 | (let [x (ordered-set (shuffle (range 8))) 12 | y (ordered-set (shuffle (range 20))) 13 | z (ordered-set (shuffle (range 0 20 2)))] 14 | (is (= #{} (ordered-set))) 15 | (is (= #{} (disj (ordered-set) 1))) 16 | (is (= #{} (disj (ordered-set [1]) 1))) 17 | (is (set? (ordered-set))) 18 | (is (= #{0 1 2 3 4 5 6 7} (conj x 5))) 19 | (is (= #{0 1 2 3 4 5 6 7 9} (conj x 9))) 20 | (is (= #{-1 0 1 2 3 4 5 6 7} (conj x -1))) 21 | (is (= #{1 2 3 4 5 6 7} (disj x 0))) 22 | (is (= [9 0 1 2 3 4 5 6 7] (cons 9 x))) 23 | (is (= 0 (first x))) 24 | (is (= 7 (last x))) 25 | (doseq [i (range 20)] 26 | (is (= i (nth y i))) 27 | (is (= i (y i))) 28 | (is (= i (get y i))) 29 | (is (= ::nope (get y (+ 100 i) ::nope))) 30 | (is (= i (.ceiling y i))) 31 | (is (= i (.floor y i))) 32 | (is (= (if (even? i) i (dec i)) (.floor z i))) 33 | (is (= i (->> y (drop i) first)))) 34 | (is (= #{4 5 6} (.subSet x 3 7))))) 35 | 36 | (deftest set-algebra-checks 37 | (doseq [size [10 100 1000 10000 100000]] 38 | (let [x (ordered-set (rest (shuffle (range size)))) ;; rest randomizes among runs 39 | y (ordered-set (rest (shuffle (range (* 2 size))))) 40 | v (ordered-set (rest (shuffle (range 0 (* 2 size) 7)))) 41 | w (ordered-set (rest (shuffle (range 0 size 3)))) 42 | z (ordered-set (rest (shuffle (range 0 size 2)))) 43 | chk (fn [x y] 44 | (doseq [[theirs ours] [[set/intersection intersection] 45 | [set/union union] 46 | [set/difference difference] 47 | [set/subset? subset] 48 | [set/superset? superset]]] 49 | (is (= (theirs (set x) (set y)) (ours x y))) 50 | (is (= (theirs (set y) (set x)) (ours y x))) 51 | (is (= (theirs (set x) (set y)) (ours x (set y)))) 52 | (is (= (theirs (set y) (set x)) (ours y (set x))))))] 53 | (doseq [xy (combo/combinations [x y v w z] 2)] 54 | (apply chk xy))))) 55 | 56 | (deftest set-equivalence-checks 57 | (doseq [size [1 10 100 1000 10000 100000]] 58 | (is (= (range size) 59 | (seq (ordered-set (shuffle (range size)))))) 60 | (is (= (range size) 61 | (seq (ordered-set-by < (shuffle (range size)))))) 62 | (is (= (reverse (range size)) 63 | (seq (ordered-set-by > (shuffle (range size)))))) 64 | (is (not= (range (inc size)) 65 | (seq (ordered-set (shuffle (range size)))))) 66 | (is (= (range size) 67 | (ordered-set (shuffle (range size))))) 68 | (is (not= (range size) 69 | (ordered-set (shuffle (range (inc size)))))) 70 | (is (= (ordered-set (shuffle (range size))) 71 | (set (range size)))) 72 | (is (= (set (range size)) 73 | (ordered-set (shuffle (range size))))) 74 | (is (not= (set (range 100000)) 75 | (ordered-set (shuffle (range (inc size)))))) 76 | (is (= (ordered-set (shuffle (range size))) 77 | (ordered-set (shuffle (range size))))) 78 | (is (not= (ordered-set (shuffle (range 1 (inc size)))) 79 | (ordered-set (shuffle (range size))))))) 80 | 81 | (deftest sets-of-various-size-and-element-types 82 | (doseq [size [1 10 100 1000 10000 100000 250000 500000] 83 | f [identity str gensym 84 | #(java.util.Date. %) 85 | (fn [_] (java.util.UUID/randomUUID))]] 86 | (let [data (mapv f (shuffle (range size))) 87 | this (ordered-set data) 88 | that (apply sorted-set data) 89 | afew (take 1000 data)] 90 | (is (= that this)) 91 | (is (= that (into this afew))) 92 | (is (= (apply disj that afew) (apply disj this afew))) 93 | (is (= (seq this) (seq that))) 94 | (is (= (count this) (count that))) 95 | (is (every? #(= (nth this %) (->> that (drop %) first)) 96 | (take 10 (repeatedly #(rand-int size))))) 97 | (is (every? #(= (this %) (that %)) afew))))) 98 | 99 | (deftest foldable-reducible-collection-check 100 | (doseq [size [1 10 100 1000 10000 100000 250000 500000 1000000] 101 | chunk [1 10 100 1000]] 102 | (let [data (shuffle (range size)) 103 | sum (reduce + data) 104 | this (ordered-set data)] 105 | (is (= sum (r/fold chunk + + this))) 106 | (is (= sum (reduce + this)))))) 107 | -------------------------------------------------------------------------------- /test/com/dean/interval_tree/tree_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.dean.interval-tree.tree-test 2 | (:require [clojure.test :refer :all] 3 | [com.dean.interval-tree.tree.node :as node] 4 | [com.dean.interval-tree.tree.tree :as tree])) 5 | 6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 7 | ;; Fixtures 8 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | 10 | (defn- matches [n1 n2] 11 | (if (node/leaf? n1) 12 | (is (node/leaf? n2)) 13 | (do 14 | (is (= (node/-k n1) (node/-k n2))) 15 | (is (= (node/-v n1) (node/-v n2))) 16 | (is (= (node/-x n1) (node/-x n2))) 17 | (matches (node/-l n1) (node/-l n2)) 18 | (matches (node/-r n1) (node/-r n2))))) 19 | 20 | (def x1 (tree/node-singleton (gensym) true)) 21 | (def x3 (tree/node-create (gensym) true x1 x1)) 22 | (def x5 (tree/node-create (gensym) true x3 x1)) 23 | (def x7 (tree/node-create (gensym) true x3 x3)) 24 | (def x11 (tree/node-create (gensym) true x3 x7)) 25 | (def x15 (tree/node-create (gensym) true x7 x7)) 26 | (def x23 (tree/node-create (gensym) true x15 x7)) 27 | (def x27 (tree/node-create (gensym) true x15 x11)) 28 | (def x31 (tree/node-create (gensym) true x15 x15)) 29 | (def x39 (tree/node-create (gensym) true x15 x23)) 30 | (def x51 (tree/node-create (gensym) true x23 x27)) 31 | (def x63 (tree/node-create (gensym) true x31 x31)) 32 | (def x127 (tree/node-create (gensym) true x63 x63)) 33 | 34 | ;; TODO: consolidate 35 | 36 | (defn- make-integer-tree 37 | ([size] (reduce tree/node-add (node/leaf) (shuffle (range size)))) 38 | ([start end] (reduce tree/node-add (node/leaf) (shuffle (range start end)))) 39 | ([start end step] (reduce tree/node-add (node/leaf) (shuffle (range start end step))))) 40 | 41 | (defn- make-string-tree [size] 42 | (reduce tree/node-add (node/leaf) (map str (shuffle (range size))))) 43 | 44 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 45 | ;; Structural Tests 46 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 47 | 48 | (deftest tree-allocator-check 49 | (is (= 0 (tree/node-size (node/leaf)))) 50 | (is (= 1 (tree/node-weight (node/leaf)))) 51 | (is (= 1 (tree/node-size (tree/node-singleton :k :v)))) 52 | (is (= 2 (tree/node-weight (tree/node-singleton :k :v)))) 53 | (is (= 1 (tree/node-size (tree/node-create :k :v (node/leaf) (node/leaf))))) 54 | (is (= 2 (tree/node-weight (tree/node-create :k :v (node/leaf) (node/leaf))))) 55 | (is (= 1 (tree/node-size x1))) 56 | (is (= 2 (tree/node-weight x1))) 57 | (is (= 3 (tree/node-size x3))) 58 | (is (= 4 (tree/node-weight x3))) 59 | (is (= 7 (tree/node-size x7))) 60 | (is (= 8 (tree/node-weight x7))) 61 | (is (= 15 (tree/node-size x15))) 62 | (is (= 16 (tree/node-weight x15))) 63 | (is (= 31 (tree/node-size x31))) 64 | (is (= 32 (tree/node-weight x31))) 65 | (is (= 63 (tree/node-size x63))) 66 | (is (= 64 (tree/node-weight x63)))) 67 | 68 | ;; (1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 69 | ;; 65536 131072 262144 524288 1048576) 70 | 71 | (deftest tree-health-check 72 | (doseq [size (take 21 (iterate #(* % 2) 1))] 73 | (is (tree/node-healthy? (make-string-tree size))) ;; string/int tree are structurally 74 | (is (tree/node-healthy? (make-integer-tree size))))) ;; very different due to sort order 75 | 76 | (deftest rotation-check:single-left 77 | (let [node node/->SimpleNode] 78 | (matches (tree/rotate-single-left :AK :AV 79 | (node :XK :XV (node/leaf) (node/leaf) 1) 80 | (node :BK :BV (node :YK :YV (node/leaf) (node/leaf) 1) 81 | (node :ZK :XZ (node/leaf) (node/leaf) 1) 3)) 82 | (node :BK :BV 83 | (node :AK :AV (node :XK :XV (node/leaf) (node/leaf) 1) 84 | (node :YK :YV (node/leaf) (node/leaf) 1) 3) 85 | (node :ZK :XZ (node/leaf) (node/leaf) 1) 5)))) 86 | 87 | (deftest rotation-check:double-left 88 | (let [node node/->SimpleNode] 89 | (matches (tree/rotate-double-left :AK :AV 90 | (node :XK :XV (node/leaf) (node/leaf) 1) 91 | (node :CK :CV 92 | (node :BK :BV (node :Y1K :Y1V (node/leaf) (node/leaf) 1) 93 | (node :Y2K :Y2V (node/leaf) (node/leaf) 1) 3) 94 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 5)) 95 | (node :BK :BV 96 | (node :AK :AV (node :XK :XV (node/leaf) (node/leaf) 1) 97 | (node :Y1K :Y1V (node/leaf) (node/leaf) 1) 3) 98 | (node :CK :CV 99 | (node :Y2K :Y2V (node/leaf) (node/leaf) 1) 100 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 3) 7)))) 101 | 102 | (deftest rotation-check:single-right 103 | (let [node node/->SimpleNode] 104 | (matches (tree/rotate-single-right :BK :BV 105 | (node :AK :AV (node :XK :XV (node/leaf) (node/leaf) 1) 106 | (node :YK :YV (node/leaf) (node/leaf) 1) 3) 107 | (node :ZK :XZ (node/leaf) (node/leaf) 1)) 108 | (node :AK :AV 109 | (node :XK :XV (node/leaf) (node/leaf) 1) 110 | (node :BK :BV (node :YK :YV (node/leaf) (node/leaf) 1) 111 | (node :ZK :XZ (node/leaf) (node/leaf) 1) 3) 5)))) 112 | 113 | (deftest rotation-check:double-right 114 | (let [node node/->SimpleNode] 115 | (matches (tree/rotate-double-right :CK :CV 116 | (node :AK :AV (node :XK :XV (node/leaf) (node/leaf) 1) 117 | (node :BK :BV (node :Y1K :Y1V (node/leaf) (node/leaf) 1) 118 | (node :Y2K :Y2V (node/leaf) (node/leaf) 1) 3) 5) 119 | (node :ZK :ZV (node/leaf) (node/leaf) 1)) 120 | (node :BK :BV 121 | (node :AK :AV (node :XK :XV (node/leaf) (node/leaf) 1) 122 | (node :Y1K :Y1V (node/leaf) (node/leaf) 1) 3) 123 | (node :CK :CV (node :Y2K :Y2V (node/leaf) (node/leaf) 1) 124 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 3) 7)))) 125 | 126 | (deftest join-check:single-left 127 | (let [rot:1L (tree/node-stitch :root true x1 x7)] 128 | (is (= 9 (tree/node-size rot:1L))) 129 | (is (= :root (node/-k (node/-l rot:1L)))) 130 | (is (= 5 (tree/node-size (node/-l rot:1L)))) 131 | (is (= 3 (tree/node-size (node/-r rot:1L)))) 132 | (tree/kvlr [k v l r] rot:1L 133 | (is (= k (node/-k (tree/node-stitch k v l r))))))) 134 | 135 | (deftest join-check:single-right 136 | (let [rot:1R (tree/node-stitch :root true x7 x1)] 137 | (is (= 9 (tree/node-size rot:1R))) 138 | (is (= :root (node/-k (node/-r rot:1R)))) 139 | (is (= 5 (tree/node-size (node/-r rot:1R)))) 140 | (is (= 3 (tree/node-size (node/-l rot:1R)))) 141 | (tree/kvlr [k v l r] rot:1R 142 | (is (= k (node/-k (tree/node-stitch k v l r))))))) 143 | 144 | (deftest join-check:double-left 145 | (let [node node/->SimpleNode] 146 | (matches (tree/node-stitch :AK :AV 147 | (node :XK :XV (node/leaf) (node/leaf) 1) 148 | (node :CK :CV 149 | (node :BK :BV 150 | (node :Y1K :Y1V (node :Q1K :Q1V (node/leaf) (node/leaf) 1) (node/leaf) 2) 151 | (node :Y2K :Y2V (node :Q2K :Q2V (node/leaf) (node/leaf) 1) (node/leaf) 2) 5) 152 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 7)) 153 | (node :BK :BV 154 | (node :AK :AV 155 | (node :XK :XV (node/leaf) (node/leaf) 1) 156 | (node :Y1K :Y1V (node :Q1K :Q1V (node/leaf) (node/leaf) 1) (node/leaf) 2) 4) 157 | (node :CK :CV 158 | (node :Y2K :Y2V (node :Q2K :Q2V (node/leaf) (node/leaf) 1) (node/leaf) 2) 159 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 4) 9)))) 160 | 161 | (deftest join-check:double-right 162 | (let [node node/->SimpleNode] 163 | (matches (tree/node-stitch :CK :CV 164 | (node :AK :AV 165 | (node :XK :XV (node/leaf) (node/leaf) 1) 166 | (node :BK :BV 167 | (node :Y1K :Y1V (node :Q1K :Q1V (node/leaf) (node/leaf) 1) (node/leaf) 2) 168 | (node :Y2K :Y2V (node :Q2K :Q2V (node/leaf) (node/leaf) 1) (node/leaf) 2) 5) 7) 169 | (node :ZK :ZV (node/leaf) (node/leaf) 1)) 170 | (node :BK :BV 171 | (node :AK :AV 172 | (node :XK :XV (node/leaf) (node/leaf) 1) 173 | (node :Y1K :Y1V (node :Q1K :Q1V (node/leaf) (node/leaf) 1) (node/leaf) 2) 4) 174 | (node :CK :CV 175 | (node :Y2K :Y2V (node :Q2K :Q2V (node/leaf) (node/leaf) 1) (node/leaf) 2) 176 | (node :ZK :ZV (node/leaf) (node/leaf) 1) 4) 9)))) 177 | 178 | (deftest concat3-check 179 | (is 180 | (= \A 181 | (first 182 | (name 183 | (node/-k 184 | (tree/node-least 185 | (tree/node-concat3 186 | (gensym "A") true (node/leaf) x5))))))) 187 | (is 188 | (= \Z 189 | (first 190 | (name 191 | (node/-k 192 | (tree/node-greatest 193 | (tree/node-concat3 194 | (gensym "Z") true x5 (node/leaf)))))))) 195 | (is 196 | (= \A 197 | (first 198 | (name 199 | (node/-k 200 | (tree/node-least 201 | (tree/node-concat3 202 | (gensym "C") true 203 | (tree/node-singleton (gensym "A") true) x7)))))))) 204 | 205 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 206 | ;; Operational Tests 207 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 208 | 209 | ;; TODO: consolidate 210 | 211 | (defn- make-tree0 [n] 212 | (let [ks (shuffle (range n)) 213 | vs (map str ks) 214 | pairs (map vector ks vs)] 215 | (reduce #(tree/node-add %1 (first %2) (second %2)) (node/leaf) pairs))) 216 | 217 | (defn- make-tree1 [n] 218 | (let [ks (shuffle (range n)) 219 | vs (map str ks) 220 | pairs (map vector vs ks)] 221 | (reduce #(tree/node-add %1 (first %2) (second %2)) (node/leaf) pairs))) 222 | 223 | (deftest extrema-check 224 | (doseq [size [10 100 1000 10000 100000]] 225 | (let [tree (make-string-tree size)] 226 | (is (= "0" (-> tree tree/node-least node/-k))) 227 | (is (= (-> size dec str) (-> tree tree/node-greatest node/-k))) 228 | (is (= "1" (-> tree tree/node-remove-least tree/node-least node/-k))) 229 | (is (= (-> size dec dec str) 230 | (-> tree tree/node-remove-greatest tree/node-greatest node/-k)))))) 231 | 232 | (deftest node-seq-check 233 | (doseq [size [1 10 100 1000 10000 100000]] 234 | (let [tree (make-integer-tree size)] 235 | (is (= (sort < (range size)) (map node/-k (tree/node-seq tree)))) 236 | (is (= (sort > (range size)) (map node/-k (tree/node-seq-reverse tree))))))) 237 | 238 | ;; TODO: restructure 239 | 240 | (deftest association-check 241 | (letfn [(chk0 [n] 242 | (let [ks (range n) vs (map str ks) pairs (map vector ks vs)] 243 | (is (= pairs (tree/node-vec (make-tree0 n) :accessor :kv))))) 244 | (chk1 [n] 245 | (let [ks (range n) vs (map str ks) pairs (map vector vs ks)] 246 | (is (= (sort-by first pairs) (tree/node-vec (make-tree1 n) :accessor :kv)))))] 247 | (doseq [size [10 100 1000 10000 100000 500000]] 248 | (chk0 size) 249 | (chk1 size)))) 250 | 251 | (deftest node-find-check 252 | (doseq [size [1 10 100 1000 10000 100000]] 253 | (let [tree (make-string-tree size)] 254 | (dotimes [_ 5000] 255 | (let [i (-> size rand-int str)] 256 | (is (= i (-> tree (tree/node-find i) node/-v)))))))) 257 | 258 | (deftest node-rank-nth-check 259 | (doseq [size [1 10 100 1000 10000 100000]] 260 | (let [tree (make-integer-tree size)] 261 | (dotimes [_ 5000] 262 | (let [i (rand-int size)] 263 | (is (= i (node/-k (tree/node-nth tree i)))) 264 | (is (= i (tree/node-rank tree i)))))))) 265 | 266 | ;; NOTE: implicitly exercises node-split-nth, node-subseq 267 | 268 | (deftest node-reduction-check 269 | (doseq [size [1 10 100 1000 10000 100000]] 270 | (let [tree (make-integer-tree size) 271 | sum (reduce + (range size))] 272 | (is (= sum (reduce + (map node/-k (tree/node-seq tree))))) 273 | (dotimes [_ 1000] 274 | (is (= sum (tree/node-chunked-fold (inc (rand-int size)) 275 | tree + (fn ([acc x] (+ acc (node/-k x))))))))))) 276 | 277 | (deftest node-comparison-check 278 | (let [nums #(-> % (repeatedly (partial rand-int 1000000000))) 279 | tree #(->> % nums (reduce tree/node-add (node/leaf))) 280 | chk #(let [m0 (node/-k (tree/node-least %1)) 281 | m1 (node/-k (tree/node-least %2))] 282 | (is (zero? (tree/node-set-compare %1 %1))) 283 | (is (zero? (tree/node-set-compare %2 %2))) 284 | (cond 285 | (< m0 m1) (is (= -1 (tree/node-set-compare %1 %2))) 286 | (< m1 m0) (is (= 1 (tree/node-set-compare %1 %2))) 287 | true (recur (tree/node-remove-least %1) 288 | (tree/node-remove-least %2))))] 289 | (doseq [size [1 10 100 1000 10000 100000]] 290 | (chk (tree size) (tree size))))) 291 | --------------------------------------------------------------------------------