├── .github └── workflows │ ├── doc-build.yml │ ├── release.yml │ ├── snapshot.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── Changes.md ├── LICENSE ├── README.md ├── deps.edn ├── notes.txt ├── pom.xml └── src ├── main └── clojure │ └── clojure │ └── data │ └── finger_tree.clj └── test └── clojure └── clojure └── data └── finger_tree └── tests.clj /.github/workflows/doc-build.yml: -------------------------------------------------------------------------------- 1 | name: Build API Docs 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | call-doc-build-workflow: 8 | uses: clojure/build.ci/.github/workflows/doc-build.yml@master 9 | with: 10 | project: clojure/data.finger-tree 11 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release on demand 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseVersion: 7 | description: "Version to release" 8 | required: true 9 | snapshotVersion: 10 | description: "Snapshot version after release" 11 | required: true 12 | 13 | jobs: 14 | call-release: 15 | uses: clojure/build.ci/.github/workflows/release.yml@master 16 | with: 17 | releaseVersion: ${{ github.event.inputs.releaseVersion }} 18 | snapshotVersion: ${{ github.event.inputs.snapshotVersion }} 19 | secrets: inherit -------------------------------------------------------------------------------- /.github/workflows/snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Snapshot on demand 2 | 3 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | call-snapshot: 7 | uses: clojure/build.ci/.github/workflows/snapshot.yml@master 8 | secrets: inherit 9 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | call-test: 7 | uses: clojure/build.ci/.github/workflows/test.yml@master 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .cpcache/ 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | This is a [Clojure contrib] project. 2 | 3 | Under the Clojure contrib [guidelines], this project cannot accept 4 | pull requests. All patches must be submitted via [JIRA]. 5 | 6 | See [Contributing] on the Clojure website for 7 | more information on how to contribute. 8 | 9 | [Clojure contrib]: https://clojure.org/community/contrib_libs 10 | [Contributing]: https://clojure.org/community/contributing 11 | [JIRA]: https://clojure.atlassian.net/browse/DFINGER 12 | [guidelines]: https://clojure.org/community/contrib_howto 13 | -------------------------------------------------------------------------------- /Changes.md: -------------------------------------------------------------------------------- 1 | # Finger Tree change log 2 | 3 | ### Version 0.0.3 4 | 5 | Removed TBD toArray impls to fix reflection error with Java 11. 6 | 7 | ### Version 0.0.2 8 | 9 | Breaking changes: 10 | - Renamed `consl` to `conjl` (same argument order) 11 | - Removed `conjr` -- just use `conj` for appending on the right 12 | 13 | New: 14 | - Implemented equality 15 | - counted-sorted-set can be equal to both sets and sequentials 16 | - Implemented hashCode 17 | - Implemented support for meta and with-meta (IObj) 18 | 19 | ### Version 0.0.1 20 | 21 | This is essentially the version described in the [Clojure Conj talk][1] 22 | 23 | [1]: http://talk-finger-tree.heroku.com/ 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 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 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | 205 | 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Finger Trees 2 | 3 | Finger trees are a versatile family of fully persistent collections. This library includes everything you need to make your own, as well a few ready-to-use collection types: 4 | 5 | - **double-list** is a sequential collection that provides constant-time access to both the left and right ends. 6 | 7 | - **counted-double-list** provides all the features of double-list plus constant-time `count` and log-n `nth`. 8 | 9 | - **counted-sorted-set** is sorted set that also provides log-n `nth` 10 | 11 | There are examples of all these later in the README. 12 | 13 | # Finger Tree Quickstart 14 | 15 | ## [CLI/`deps.edn`](https://clojure.org/reference/deps_and_cli) dependency information: 16 | ```clojure 17 | org.clojure/data.finger-tree {:mvn/version "0.1.0"} 18 | ``` 19 | 20 | ## project.clj 21 | 22 | If you use leiningen or cake, add this to the `:dependencies` in your `project.clj`: 23 | 24 | [org.clojure/data.finger-tree "0.1.0"] 25 | 26 | ## pom.xml 27 | 28 | If you use maven, add this to the `` in your `pom.xml`: 29 | 30 | 31 | org.clojure 32 | data.finger-tree 33 | 0.1.0 34 | 35 | 36 | You'll need git and maven, then execute the following at a shell prompt to fetch finger trees and all its dependencies (including a recent snapshot of Clojure itself) and start a REPL: 37 | 38 | ## use/require 39 | 40 | Regardless of how you fetch the dependency, to use a finger-tree 41 | function in your project you'll need to add something like this to 42 | your `ns` declaration: 43 | 44 | (:use [clojure.data.finger-tree :only [double-list]]) 45 | 46 | # Talk 47 | 48 | Thanks to heroku for hosting the [slides for my Clojure Conj talk][1] about this library. The rather raw sources and enormous PDF of the slides are at [github][2]. 49 | 50 | # Examples 51 | 52 | The finger-tree lib actually includes several collections built on top 53 | of [Ralf Hinze and Ross Paterson's finger trees][3]. Here are some 54 | examples of each of them: 55 | 56 | ## double-list 57 | 58 | The double-list is a sequential collection that provides constant-time 59 | access to both the left and right ends: 60 | 61 | (def dl (double-list 4 5 6 7)) 62 | 63 | dl 64 | ;=> (4 5 6 7) 65 | 66 | [(first dl) (rest dl)] 67 | ;=> [4 (5 6 7)] 68 | 69 | (conjl dl 'x) 70 | ;=> (x 4 5 6 7) 71 | 72 | [(pop dl) (peek dl)] 73 | ;=> [(4 5 6) 7] 74 | 75 | (conj dl 'x) 76 | ;=> (4 5 6 7 x) 77 | 78 | ## counted-double-list 79 | 80 | This provides all the features of double-list plus constant-time 81 | `count` and log-n `nth`: 82 | 83 | (def cdl 84 | (apply counted-double-list '[a b c d e f g h i j k l m])) 85 | 86 | (nth cdl 5) 87 | ;=> f 88 | 89 | (assoc cdl 5 'XX) 90 | ;=> (a b c d e XX g h i j k l m) 91 | 92 | (def parts 93 | (let [[left _ right] (ft-split-at cdl 5)] 94 | {:left left, :right right})) 95 | 96 | parts 97 | ;=> {:left (a b c d e), :right (g h i j k l m)} 98 | 99 | (ft-concat (conj (:left parts) 'XX) (:right parts)) 100 | ;=> (a b c d e XX g h i j k l m) 101 | 102 | (ft-concat (:left parts) (:right parts)) 103 | ;=> (a b c d e g h i j k l m) 104 | ; ^-- missing f 105 | 106 | (ft-concat (into (:left parts) '[X Y Z]) (:right parts)) 107 | ;=> (a b c d e X Y Z g h i j k l m) 108 | 109 | ## counted-sorted-set 110 | 111 | This is like counted-double-list, but does not support `conjl`. Instead, `conj` is used to insert items in sorted order. 112 | 113 | (def css (apply counted-sorted-set 114 | '[m j i e d a f k b c f g h l])) 115 | css 116 | ;=> (a b c d e f g h i j k l m) 117 | 118 | (get css 'e) ; O(log(n)) 119 | ;=> e 120 | 121 | (get css 'ee) ; O(log(n)) 122 | ;=> nil 123 | 124 | (count css) ; O(1) 125 | ;=> 13 126 | 127 | (nth css 5) ; O(log(n)) 128 | ;=> f 129 | 130 | ## Build-your-own finger tree 131 | 132 | (def empty-cost-tree (finger-tree (meter :cost 0 +))) 133 | 134 | (def ct (conj empty-cost-tree 135 | {:id :h, :cost 5} {:id :i, :cost 1} 136 | {:id :j, :cost 2} {:id :k, :cost 3} 137 | {:id :l, :cost 4})) 138 | 139 | (measured ct) 140 | ;=> 15 141 | 142 | (next (split-tree ct #(> % 7))) 143 | ;=> ({:cost 2, :id :j} 144 | ({:cost 3, :id :k} {:cost 4, :id :l})) 145 | 146 | (next (split-tree (rest ct) #(> % 7))) 147 | ;=> ({:cost 4, :id :l} ()) 148 | 149 | [1]: http://talk-finger-tree.heroku.com/ 150 | [2]: http://github.com/Chouser/talk-finger-tree 151 | [3]: http://www.soi.city.ac.uk/~ross/papers/FingerTree.html 152 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src/main/clojure"]} 2 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | Queue: 2 | (time (count (reduce #(-> % (conjr %2) rest) (reduce consl (EmptyTree. nil) (range 1e4)) (range 1e6)))) 3 | 4 | letfn: "Elapsed time: 5043.853495 msecs" 5 | digit macro: "Elapsed time: 3411.159957 msecs" 6 | 32% better 7 | 8 | 9 | Split: 10 | (time (let [n 1e4, t (to-tree {:size [(constantly 1) + 0]} (range n))] (dotimes [i n] (split-tree t #(< i (:size %)))))) 11 | 12 | "Elapsed time: 4408.469664 msecs" 13 | loopless split digit: "Elapsed time: 4102.303245 msecs" 14 | later got this for the same code: "Elapsed time: 3976.687969 msecs" 15 | 16 | 17 | Concat: 18 | (time (let [t (to-tree nil (range 1e5))] (dotimes [_ 3e4] (ft-concat t t)))) 19 | "Elapsed time: 2402.644912 msecs" 20 | nodes without apply: "Elapsed time: 2004.185324 msecs" 21 | 22 | 23 | Using digit for node and always using a delayed measure cache: 24 | queue: 3302.095682 (3% better) 25 | split: 3096.94213 (22% better) 26 | concat: 1680.462405 (16% better) 27 | ...it's also less code. 28 | 29 | Removing the assert in 'deep' helps a bit too: 30 | queue: 2934.046655 31 | split: 3086.496488 32 | concat: 1631.712977 33 | 34 | Delay deep's measure: 35 | queue: 2156.064809 36 | split: 2471.035156 37 | concat: 1510.606604 38 | 39 | Slightly better 'split' only calling measure/reduce on middle tree when needed. 40 | split: 2347.711168 41 | 42 | Reify: 43 | queue: 2151.073871 44 | split: 2351.481345 45 | concat: 1699.367853 46 | 47 | defprotocol: 48 | queue: 6234.505356 49 | split: 3824.236948 50 | concat: 1958.039438 51 | 52 | protocol call site caching: 53 | queue: 5380.091688 54 | split: 3552.108167 55 | concat: 1679.4733 56 | 57 | protocol methods inside deftype: 58 | queue: 6115.087433 59 | split: 2851.264586 60 | concat: 1532.471135 61 | 62 | Note PersistentQueue does queue test in 363.281297 msecs 63 | (defn t [] (count (reduce #(-> % (conj %2) pop) (reduce conj clojure.lang.PersistentQueue/EMPTY (range 1e4)) (range 1e6)))) 64 | 65 | with Clojure 1.2.0: 66 | queue: 2645.928817 67 | split: 2374.810216 68 | concat: 1530.395627 69 | 70 | with record-based meters (+ some performance tweaks): 71 | queue: (time (count (reduce #(-> % (conjr %2) rest) (reduce consl (EmptyTree. nil) (range 1e4)) (range 1e6)))) 72 | split: (time (let [n 1e4, t (to-tree len-meter (range n))] (dotimes [i n] (split-tree t #(< i %))))) 73 | concat: (time (let [t (to-tree nil (range 1e5))] (dotimes [_ 3e4] (ft-concat t t)))) 74 | 75 | queue: 3334.090979 76 | split: 313.484652 77 | concat: 1763.906721 78 | 79 | Len-Meter instead of Integer: 80 | queue: (time (count (reduce #(-> % (conjr %2) rest) (reduce consl (EmptyTree. nil) (range 1e4)) (range 1e6)))) 81 | split: (time (let [n 1e4, t (to-tree len-meter (range n))] (dotimes [i n] (split-tree t #(< i (:len %)))))) 82 | concat: (time (let [t (to-tree nil (range 1e5))] (dotimes [_ 3e4] (ft-concat t t)))) 83 | split: 331.637379 84 | 85 | Test for nil op: 86 | queue: 2346.357413 87 | split: 352.238152 88 | concat 1518.418893 89 | 90 | split with len-string-meter: 40264.907112 91 | 92 | --- sorted set slowness: 93 | (do (def rands (let [r (java.util.Random. 42)] (take 10000 (repeatedly #(.nextInt r))))) nil) 94 | 95 | (defn s1 [] (first (reduce (fn [t i] (insert-where t #(when-let [r (:right %)] (< i r)) i)) (finger-tree right-meter) rands))) 96 | ; 914.896926 97 | 98 | (defn s2 [] (first (reduce conj (sorted-set) rands))) 99 | ; 55.687814 100 | 101 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | data.finger-tree 4 | 0.1.1-SNAPSHOT 5 | data.finger-tree 6 | Persistent collections based on 2-3 finger trees. 7 | 8 | 9 | 10 | Chouser 11 | chouser@n01se.net 12 | https://chouser.n01se.net 13 | -5 14 | 15 | 16 | 17 | 18 | org.clojure 19 | pom.contrib 20 | 1.3.0 21 | 22 | 23 | 24 | scm:git:git@github.com:clojure/data.finger-tree.git 25 | scm:git:git@github.com:clojure/data.finger-tree.git 26 | git@github.com:clojure/data.finger-tree.git 27 | HEAD 28 | 29 | 30 | 31 | 32 | 33 | sonatype-oss-snapshots 34 | https://oss.sonatype.org/content/repositories/snapshots 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/clojure/clojure/data/finger_tree.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns ^{:doc "Persistent collections based on 2-3 finger trees." 10 | :author "Chris Houser"} 11 | clojure.data.finger-tree 12 | (:import (clojure.lang Seqable Sequential ISeq IPersistentSet ILookup 13 | IPersistentStack IPersistentCollection Associative 14 | Sorted Reversible Indexed Counted IHashEq))) 15 | 16 | (comment ; TODO: 17 | 18 | - implement java.util.Collection 19 | - implement IMeta 20 | - implement IChunkedSeq? 21 | - replace copy/pasted code with macros 22 | - test dequeue complexity 23 | - confirm recursion is bounded, though perhaps O(log n) growth is slow enough 24 | - add simple lookup to Splittable? 25 | - add sorted map with index? 26 | ) 27 | 28 | ;(set! *warn-on-reflection* true) 29 | 30 | (defprotocol ConjL 31 | (conjl [s a] "Append a to the left-hand side of s")) 32 | 33 | (defprotocol ObjMeter 34 | "Object for annotating tree elements. idElem and op together form a Monoid." 35 | (measure [_ o] "Return the measured value of o (same type as idElem)") 36 | (idElem [_] "Return the identity element for this meter") 37 | (opfn [_] "Return an associative function of two args for combining measures")) 38 | 39 | (defprotocol Measured 40 | (measured [o] "Return the measured value of o") 41 | (getMeter [o] "Return the meter object for o")) 42 | 43 | (defprotocol Splittable 44 | (split [o pred acc] "Return [pre m post] where pre and post are trees")) 45 | 46 | (defprotocol SplitAt 47 | (ft-split-at [o k notfound] [o k] 48 | "Return [pre m post] where pre and post are trees")) 49 | 50 | (defprotocol Tree 51 | (app3 [t1 ts t2] "Append ts and (possibly deep) t2 to tree t1") 52 | (app3deep [t2 ts t1] "Append ts and t2 to deep tree t1") 53 | (measureMore [o] "Return the measure of o not including the leftmost item") 54 | (measurePop [o] "Return the measure of o not including the rightmost item")) 55 | 56 | (extend-type nil 57 | ObjMeter 58 | (measure [_ _] nil) 59 | (idElem [_] nil) 60 | (opfn [_] nil) 61 | Measured 62 | (measured [_] nil) 63 | (getMeter [_] nil)) 64 | 65 | (declare newEmptyTree newSingleTree newDeepTree digit deep) 66 | 67 | (defmacro ^:private defdigit [& items] 68 | (let [i (gensym "i_") 69 | p (gensym "p_") 70 | o (gensym "o_") 71 | typename (symbol (str "Digit" (count items))) 72 | this-items (map #(list (keyword %) o) items)] 73 | `(deftype ~typename [~@items ~'meter-obj ~'measure-ref] 74 | Seqable 75 | (seq [_] ~(reduce #(list `cons %2 %1) nil (reverse items))) 76 | Indexed 77 | (count [_] ~(count items)) ; not needed? 78 | (nth [_ ~i notfound#] 79 | (cond ~@(mapcat (fn [sym n] [`(== ~i (int ~n)) sym]) 80 | items 81 | (range (count items))) 82 | :else notfound#)) 83 | Sequential 84 | ISeq 85 | (first [_] ~(first items)) 86 | (more [_] ~(if (> (count items) 1) 87 | `(digit ~'meter-obj ~@(next items)) 88 | `(newEmptyTree ~'meter-obj))) 89 | (next [_] ~(when (> (count items) 1) 90 | `(digit ~'meter-obj ~@(next items)))) 91 | IPersistentStack 92 | (peek [_] ~(last items)) 93 | (pop [_] ~(if (> (count items) 1) 94 | `(digit ~'meter-obj ~@(drop-last items)) 95 | `(newEmptyTree ~'meter-obj))) 96 | IPersistentCollection 97 | (empty [_]) ; TBD ; not needed? 98 | (equiv [_ x#] false) ; TBD 99 | (cons [_ x#] (digit ~'meter-obj ~@items x#)) 100 | ConjL 101 | (conjl [_ x#] (digit ~'meter-obj x# ~@items)) 102 | Measured 103 | (measured [_] @~'measure-ref) 104 | (getMeter [_] ~'meter-obj) ; not needed? 105 | Splittable ; allow to fail if op is nil: 106 | (split [_ ~p ~i] 107 | ~(letfn [(step [ips [ix & ixs]] 108 | (if (empty? ixs) 109 | [(when ips `(digit ~'meter-obj ~@ips)) 110 | ix 111 | nil] 112 | `(let [~i ((opfn ~'meter-obj) 113 | ~i 114 | (measure ~'meter-obj ~ix))] 115 | (if (~p ~i) 116 | [~(when ips 117 | `(digit ~'meter-obj ~@ips)) 118 | ~ix 119 | (digit ~'meter-obj ~@ixs)] 120 | ~(step (concat ips [ix]) ixs)))))] 121 | (step nil items)))))) 122 | 123 | (defmacro ^:private make-digit [meter-obj & items] 124 | (let [typename (symbol (str "Digit" (count items)))] 125 | `(let [~'mobj ~meter-obj 126 | ~'op (opfn ~'mobj)] 127 | (new ~typename ~@items ~'mobj 128 | (when ~'op 129 | (delay ~(reduce #(list 'op %1 %2) 130 | (map #(list `measure 'mobj %) items)))))))) 131 | 132 | (defmacro meter [measure idElem op] 133 | `(reify ObjMeter 134 | (measure [_ a#] (~measure a#)) 135 | (idElem [_] ~idElem) 136 | (opfn [_] ~op))) 137 | 138 | (defdigit a) 139 | (defdigit a b) 140 | (defdigit a b c) 141 | (defdigit a b c d) 142 | 143 | ;; cannot be static because it calls protocol methods 144 | (defn digit 145 | ([meter-obj a] (make-digit meter-obj a)) 146 | ([meter-obj a b] (make-digit meter-obj a b)) 147 | ([meter-obj a b c] (make-digit meter-obj a b c)) 148 | ([meter-obj a b c d] (make-digit meter-obj a b c d))) 149 | 150 | (defn ^:static nodes [mfns xs] 151 | (let [v (vec xs), c (count v)] 152 | (seq 153 | (loop [i (int 0), nds []] 154 | (condp == (- c i) 155 | (int 2) (-> nds (conj (digit mfns (v i) (v (+ (int 1) i))))) 156 | (int 3) (-> nds (conj (digit mfns (v i) (v (+ (int 1) i)) 157 | (v (+ (int 2) i))))) 158 | (int 4) (-> nds (conj (digit mfns (v i) (v (+ (int 1) i)))) 159 | (conj (digit mfns (v (+ (int 2) i)) 160 | (v (+ (int 3) i))))) 161 | (recur (+ (int 3) i) 162 | (-> nds 163 | (conj (digit mfns (v i) (v (+ (int 1) i)) 164 | (v (+ (int 2) i))))))))))) 165 | 166 | (deftype EmptyTree [meter-obj] 167 | Seqable 168 | (seq [_] nil) 169 | Sequential 170 | ISeq 171 | (first [_] nil) 172 | (more [this] this) 173 | (next [_] nil) 174 | IPersistentStack 175 | (peek [_] nil) 176 | (pop [this] this) 177 | Reversible 178 | (rseq [_] nil) 179 | IPersistentCollection 180 | (count [_] 0) ; not needed? 181 | (empty [this] this) 182 | (equiv [_ x] false) ; TBD 183 | (cons [_ b] (newSingleTree meter-obj b)) 184 | ConjL 185 | (conjl [_ a] (newSingleTree meter-obj a)) 186 | Measured 187 | (measured [_] (idElem meter-obj)) 188 | (getMeter [_] meter-obj) ; not needed? 189 | ; Splittable 190 | ; (split [pred acc]) ; TBD -- not needed?? 191 | Tree 192 | (app3 [_ ts t2] (reduce conjl t2 (reverse ts))) 193 | (app3deep [_ ts t1] (reduce conj t1 ts)) 194 | (measureMore [_] (idElem meter-obj)) 195 | (measurePop [_] (idElem meter-obj))) 196 | 197 | (defn ^:static newEmptyTree [meter-obj] 198 | (EmptyTree. meter-obj)) 199 | 200 | (defn ^:static finger-meter [meter-obj] 201 | (when meter-obj 202 | (meter 203 | #(measured %) 204 | (idElem meter-obj) 205 | (opfn meter-obj)))) 206 | 207 | (deftype SingleTree [meter-obj x] 208 | Seqable 209 | (seq [this] this) 210 | Sequential 211 | ISeq 212 | (first [_] x) 213 | (more [_] (EmptyTree. meter-obj)) 214 | (next [_] nil) 215 | IPersistentStack 216 | (peek [_] x) 217 | (pop [_] (EmptyTree. meter-obj)) 218 | Reversible 219 | (rseq [_] (list x)) ; not 'this' because tree ops can't be reversed 220 | IPersistentCollection 221 | (count [_]) ; not needed? 222 | (empty [_] (EmptyTree. meter-obj)) ; not needed? 223 | (equiv [_ x] false) ; TBD 224 | (cons [_ b] (deep (digit meter-obj x) 225 | (EmptyTree. (finger-meter meter-obj)) 226 | (digit meter-obj b))) 227 | ConjL 228 | (conjl [_ a] (deep (digit meter-obj a) 229 | (EmptyTree. (finger-meter meter-obj)) 230 | (digit meter-obj x))) 231 | Measured 232 | (measured [_] (measure meter-obj x)) 233 | (getMeter [_] meter-obj) ; not needed? 234 | Splittable 235 | (split [this pred acc] (let [e (empty this)] [e x e])) 236 | Tree 237 | (app3 [this ts t2] (conjl (app3 (empty this) ts t2) x)) 238 | (app3deep [_ ts t1] (conj (reduce conj t1 ts) x)) 239 | (measureMore [_] (idElem meter-obj)) 240 | (measurePop [_] (idElem meter-obj))) 241 | 242 | (defn ^:static newSingleTree [meter-obj x] 243 | (SingleTree. meter-obj x)) 244 | 245 | (deftype DelayedTree [tree-ref mval] 246 | Seqable 247 | (seq [this] this) 248 | Sequential 249 | ISeq 250 | (first [_] (first @tree-ref)) 251 | (more [_] (rest @tree-ref)) 252 | (next [_] (next @tree-ref)) 253 | IPersistentStack 254 | (peek [_] (peek @tree-ref)) 255 | (pop [_] (pop @tree-ref)) 256 | Reversible 257 | (rseq [_] (rseq @tree-ref)) ; not this because tree ops can't be reversed 258 | IPersistentCollection 259 | (count [_]) ; not needed? 260 | (empty [_] (empty @tree-ref)) 261 | (equiv [_ x] false) ; TBD 262 | (cons [_ b] (conj @tree-ref b)) 263 | ConjL 264 | (conjl [_ a] (conjl @tree-ref a)) 265 | Measured 266 | (measured [_] mval) 267 | (getMeter [_] (getMeter @tree-ref)) ; not needed? 268 | Splittable 269 | (split [_ pred acc] (split @tree-ref pred acc)) 270 | Tree 271 | (app3 [_ ts t2] (app3 @tree-ref ts t2)) 272 | (app3deep [_ ts t1] (app3deep @tree-ref ts t1)) 273 | (measureMore [_] (measureMore @tree-ref)) 274 | (measurePop [_] (measurePop @tree-ref))) 275 | 276 | (defmacro ^:private delay-ft [tree-expr mval] 277 | `(DelayedTree. (delay ~tree-expr) ~mval)) 278 | ;`(let [v# ~mval] (assert v#) ~tree-expr)) 279 | ;`(delayed-ft (delay (do (print "\nforce ") ~tree-expr)) ~mval)) 280 | 281 | (defn ^:static to-tree [meter-obj coll] 282 | (reduce conj (EmptyTree. meter-obj) coll)) 283 | 284 | (defn deep-left [pre m suf] 285 | (cond 286 | (seq pre) (deep pre m suf) 287 | (empty? (first m)) (to-tree (getMeter suf) suf) 288 | :else (deep (first m) 289 | (delay-ft (rest m) (measureMore m)) 290 | suf))) 291 | 292 | (defn deep-right [pre m suf] 293 | (cond 294 | (seq suf) (deep pre m suf) 295 | (empty? (peek m)) (to-tree (getMeter pre) pre) 296 | :else (deep pre 297 | (delay-ft (pop m) (measurePop m)) 298 | (peek m)))) 299 | 300 | (defn ^:private measured3 [meter-obj pre m suf] 301 | (when-let [op (opfn meter-obj)] 302 | (op 303 | (op (measured pre) 304 | (measured m)) 305 | (measured suf)))) 306 | 307 | (defn deep [pre m suf] 308 | (let [meter-obj (getMeter pre) 309 | op (opfn meter-obj)] 310 | (newDeepTree meter-obj pre m suf 311 | (when op 312 | (delay (if (seq m) 313 | (measured3 meter-obj pre m suf) 314 | (op (measured pre) (measured suf)))))))) 315 | 316 | (deftype DeepTree [meter-obj pre mid suf mval] 317 | Seqable 318 | (seq [this] this) 319 | Sequential 320 | ISeq 321 | (first [_] (first pre)) 322 | (more [_] (deep-left (rest pre) mid suf)) 323 | (next [this] (seq (rest this))) 324 | IPersistentStack 325 | (peek [_] (peek suf)) 326 | (pop [_] (deep-right pre mid (pop suf))) 327 | Reversible 328 | (rseq [this] (lazy-seq (cons (peek this) (rseq (pop this))))) 329 | IPersistentCollection 330 | (count [_]) ; not needed? 331 | (empty [_] (newEmptyTree meter-obj)) 332 | (equiv [_ x] false) ; TBD 333 | (cons [_ a] (if (< (count suf) 4) 334 | (deep pre mid (conj suf a)) 335 | (let [[e d c b] suf 336 | n (digit meter-obj e d c)] 337 | (deep pre (conj mid n) (digit meter-obj b a))))) 338 | ConjL 339 | (conjl [_ a] (if (< (count pre) 4) 340 | (deep (conjl pre a) mid suf) 341 | (let [[b c d e] pre 342 | n (digit meter-obj c d e)] 343 | (deep (digit meter-obj a b) (conjl mid n) suf)))) 344 | Measured 345 | (measured [_] @mval) 346 | (getMeter [_] (getMeter pre)) ; not needed? 347 | Splittable ; allow to fail if op is nil: 348 | (split [_ pred acc] 349 | (let [op (opfn meter-obj) 350 | vpr (op acc (measured pre))] 351 | (if (pred vpr) 352 | (let [[sl sx sr] (split pre pred acc)] 353 | [(to-tree meter-obj sl) sx (deep-left sr mid suf)]) 354 | (let [vm (op vpr (measured mid))] 355 | (if (pred vm) 356 | (let [[ml xs mr] (split mid pred vpr) 357 | [sl sx sr] (split xs pred (op vpr (measured ml)))] 358 | [(deep-right pre ml sl) sx (deep-left sr mr suf)]) 359 | (let [[sl sx sr] (split suf pred vm)] 360 | [(deep-right pre mid sl) 361 | sx 362 | (to-tree meter-obj sr)])))))) 363 | Tree 364 | (app3 [this ts t2] (app3deep t2 ts this)) 365 | (app3deep [_ ts t1] 366 | (deep (.pre ^DeepTree t1) 367 | (app3 (.mid ^DeepTree t1) 368 | (nodes meter-obj (concat (.suf ^DeepTree t1) ts pre)) 369 | mid) 370 | suf)) 371 | (measureMore [this] (measured3 meter-obj (rest pre) mid suf)) 372 | (measurePop [this] (measured3 meter-obj pre mid (pop suf)))) 373 | 374 | (defn ^:static newDeepTree [meter-obj pre mid suf mval] 375 | (DeepTree. meter-obj pre mid suf mval)) 376 | 377 | (defn ^:static finger-tree [meter-obj & xs] 378 | (to-tree meter-obj xs)) 379 | 380 | (defn split-tree [t p] 381 | (split t p (idElem (getMeter t)))) 382 | 383 | (defn ft-concat [t1 t2] 384 | (assert (= (getMeter t1) (getMeter t2))) ;meters must be the same 385 | (app3 t1 nil t2)) 386 | 387 | (defn- seq-equals [a b] 388 | (boolean 389 | (when (or (sequential? b) (instance? java.util.List b)) 390 | (loop [a (seq a), b (seq b)] 391 | (when (= (nil? a) (nil? b)) 392 | (or 393 | (nil? a) 394 | (when (= (first a) (first b)) 395 | (recur (next a) (next b))))))))) 396 | 397 | ;;=== applications === 398 | 399 | (defmacro compile-if [test then else] 400 | (if (eval test) 401 | then 402 | else)) 403 | 404 | (defn hashcode [x] 405 | (clojure.lang.Util/hash x)) 406 | 407 | (defn hash-ordered [coll] 408 | (compile-if (resolve 'clojure.core/hash-ordered-coll) 409 | (hash-ordered-coll coll) 410 | (loop [h (int 1), xs coll] 411 | (if-let [xs (seq xs)] 412 | (recur (unchecked-add-int (unchecked-multiply-int (int 31) h) 413 | (clojure.lang.Util/hasheq (first xs))) 414 | (next xs)) 415 | h)))) 416 | 417 | (defn hash-unordered [coll] 418 | (compile-if (resolve 'clojure.core/hash-unordered-coll) 419 | (hash-unordered-coll coll) 420 | (loop [h (int 0), xs coll] 421 | (if-let [xs (seq xs)] 422 | (recur (unchecked-add-int h 423 | (clojure.lang.Util/hasheq (first xs))) 424 | (next xs)) 425 | h)))) 426 | 427 | (deftype DoubleList [tree mdata] 428 | Object 429 | (equals [_ x] (seq-equals tree x)) 430 | (hashCode [this] (hashcode (map identity this))) 431 | IHashEq 432 | (hasheq [this] 433 | (hash-ordered this)) 434 | java.lang.Iterable 435 | (iterator [this] 436 | (clojure.lang.SeqIterator. (seq this))) 437 | clojure.lang.IObj 438 | (meta [_] mdata) 439 | (withMeta [_ mdata] (DoubleList. tree mdata)) 440 | Sequential 441 | Seqable 442 | (seq [this] (when (seq tree) this)) 443 | ISeq 444 | (first [_] (first tree)) 445 | (more [_] (DoubleList. (rest tree) mdata)) 446 | (next [_] (if-let [t (next tree)] (DoubleList. t mdata))) 447 | IPersistentStack ; actually, queue 448 | (peek [_] (peek tree)) 449 | (pop [_] (DoubleList. (pop tree) mdata)) 450 | Reversible 451 | (rseq [_] (rseq tree)) ; not 'this' because tree ops can't be reversed 452 | IPersistentCollection 453 | (count [_] (count (seq tree))) ; Slow! 454 | (empty [_] (DoubleList. (empty tree) mdata)) 455 | (equiv [_ x] (seq-equals tree x)) 456 | (cons [_ b] (DoubleList. (conj tree b) mdata)) 457 | ConjL 458 | (conjl [_ a] (DoubleList. (conjl tree a) mdata)) 459 | Measured 460 | (measured [_] (measured tree)) 461 | (getMeter [_] (getMeter tree)) 462 | Tree 463 | (app3 [_ ts t2] (DoubleList. (app3 tree ts t2) mdata)) 464 | (app3deep [_ ts t1] (DoubleList. (app3deep tree ts t1) mdata))) 465 | 466 | (defn double-list [& args] 467 | (into (DoubleList. (EmptyTree. nil) nil) args)) 468 | 469 | (deftype CountedDoubleList [tree mdata] 470 | Object 471 | (equals [_ x] (seq-equals tree x)) 472 | (hashCode [this] (hashcode (map identity this))) 473 | IHashEq 474 | (hasheq [this] 475 | (hash-ordered this)) 476 | java.lang.Iterable 477 | (iterator [this] 478 | (clojure.lang.SeqIterator. (seq this))) 479 | clojure.lang.IObj 480 | (meta [_] mdata) 481 | (withMeta [_ mdata] (CountedDoubleList. tree mdata)) 482 | Sequential 483 | Seqable 484 | (seq [this] (when (seq tree) this)) 485 | ISeq 486 | (first [_] (first tree)) 487 | (more [_] (CountedDoubleList. (rest tree) mdata)) 488 | (next [_] (if-let [t (next tree)] (CountedDoubleList. t mdata))) 489 | IPersistentStack 490 | (peek [_] (peek tree)) 491 | (pop [_] (CountedDoubleList. (pop tree) mdata)) 492 | Reversible 493 | (rseq [_] (rseq tree)) ; not 'this' because tree ops can't be reversed 494 | IPersistentCollection 495 | (empty [_] (CountedDoubleList. (empty tree) mdata)) 496 | (equiv [_ x] (seq-equals tree x)) 497 | (cons [_ b] (CountedDoubleList. (conj tree b) mdata)) 498 | ConjL 499 | (conjl [_ a] (CountedDoubleList. (conjl tree a) mdata)) 500 | Measured 501 | (measured [_] (measured tree)) 502 | (getMeter [_] (getMeter tree)) ; not needed? 503 | SplitAt 504 | (ft-split-at [this n notfound] 505 | (cond 506 | (< n 0) [(empty this) notfound this] 507 | (< n (count this)) 508 | (let [[pre m post] (split-tree tree #(> % n))] 509 | [(CountedDoubleList. pre mdata) m (CountedDoubleList. post mdata)]) 510 | :else [this notfound (empty this)])) 511 | (ft-split-at [this n] 512 | (ft-split-at this n nil)) 513 | Tree 514 | (app3 [_ ts t2] 515 | (CountedDoubleList. (app3 tree ts (.tree ^CountedDoubleList t2)) mdata)) 516 | ;(app3deep [_ ts t1] (CountedDoubleList. (app3deep tree ts t1) mdata)) 517 | (measureMore [_] (measureMore tree)) 518 | (measurePop [_] (measurePop tree)) 519 | Counted 520 | (count [_] (measured tree)) 521 | Associative 522 | (assoc [this k v] 523 | (cond 524 | (== k -1) (conjl this v) 525 | (== k (measured tree)) (conj this v) 526 | (< -1 k (measured tree)) 527 | (let [[pre mid post] (split-tree tree #(> % k))] 528 | (CountedDoubleList. (ft-concat (conj pre v) post) mdata)) 529 | :else (throw (IndexOutOfBoundsException.)))) 530 | (containsKey [_ k] (< -1 k (measured tree))) 531 | (entryAt [_ n] (clojure.lang.MapEntry. 532 | n (second (split-tree tree #(> % n))))) 533 | (valAt [this n notfound] (if (.containsKey this n) 534 | (second (split-tree tree #(> % n))) 535 | notfound)) 536 | (valAt [this n] (.valAt this n nil)) 537 | Indexed 538 | (nth [this n notfound] (if (.containsKey this n) 539 | (second (split-tree tree #(> % n))) 540 | notfound)) 541 | (nth [this n] (if (.containsKey this n) 542 | (second (split-tree tree #(> % n))) 543 | (throw (IndexOutOfBoundsException.))))) 544 | 545 | (let [measure-len (constantly 1) 546 | len-meter (meter measure-len 0 +)] 547 | (def empty-counted-double-list 548 | (CountedDoubleList. (EmptyTree. len-meter) nil))) 549 | 550 | (defn counted-double-list [& args] 551 | (into empty-counted-double-list args)) 552 | 553 | 554 | (defrecord Len-Right-Meter [^int len right]) 555 | 556 | (def ^:private notfound (Object.)) 557 | 558 | (deftype CountedSortedSet [cmpr tree mdata] 559 | Object 560 | (equals [_ x] 561 | (boolean 562 | (if (instance? java.util.Set x) 563 | (and (= (count x) (count tree)) 564 | (every? #(contains? x %) tree)) 565 | (seq-equals tree x)))) 566 | (hashCode [_] (reduce + (map hashcode tree))) 567 | IHashEq 568 | (hasheq [this] 569 | (hash-unordered this)) 570 | clojure.lang.IObj 571 | (meta [_] mdata) 572 | (withMeta [_ mdata] (CountedSortedSet. cmpr tree mdata)) 573 | Seqable 574 | ; return 'tree' instead of 'this' so that result will be Sequential 575 | (seq [this] (when (seq tree) tree)) 576 | IPersistentCollection 577 | (cons [this value] 578 | (if (empty? tree) 579 | (CountedSortedSet. cmpr (conj tree value) mdata) 580 | (let [[l x r] (split-tree tree #(>= 0 (cmpr value (:right %)))) 581 | compared (cmpr value x)] 582 | (if (zero? compared) 583 | this ; already in set 584 | (let [[a b] (if (>= 0 compared) [value x] [x value])] 585 | (CountedSortedSet. cmpr (ft-concat (conj l a) (conjl r b)) mdata)))))) 586 | (empty [_] (CountedSortedSet. cmpr (empty tree) mdata)) 587 | (equiv [this x] (.equals this x)) ; TBD 588 | ISeq 589 | (first [_] (first tree)) 590 | (more [_] (CountedSortedSet. cmpr (rest tree) mdata)) 591 | (next [_] (if-let [t (next tree)] (CountedSortedSet. cmpr t mdata))) 592 | IPersistentStack 593 | (peek [_] (peek tree)) 594 | (pop [_] (CountedSortedSet. cmpr (pop tree) mdata)) 595 | Reversible 596 | (rseq [_] (rseq tree)) ; not 'this' because tree ops can't be reversed 597 | Measured 598 | (measured [_] (measured tree)) 599 | (getMeter [_] (getMeter tree)) ; not needed? 600 | SplitAt 601 | (ft-split-at [this n notfound] 602 | (cond 603 | (< n 0) [(empty this) notfound this] 604 | (< n (count this)) (let [[l x r] (split-tree tree #(> (:len %) n))] 605 | [(CountedSortedSet. cmpr l mdata) x 606 | (CountedSortedSet. cmpr r mdata)]) 607 | :else [this notfound (empty this)])) 608 | (ft-split-at [this n] 609 | (ft-split-at this n nil)) 610 | Counted 611 | (count [_] (:len (measured tree))) 612 | ILookup 613 | (valAt [_ k notfound] 614 | (if (empty? tree) 615 | notfound 616 | (let [x (second (split-tree tree #(>= 0 (cmpr k (:right %)))))] 617 | (if (= x k) 618 | k 619 | notfound)))) 620 | (valAt [this k] 621 | (.valAt this k nil)) 622 | IPersistentSet 623 | (disjoin [this k] 624 | (if (empty? tree) 625 | this 626 | (let [[l x r] (split-tree tree #(>= 0 (cmpr k (:right %))))] 627 | (if (= x k) 628 | (CountedSortedSet. cmpr (ft-concat l r) mdata) 629 | this)))) 630 | (get [this k] (.valAt this k nil)) 631 | Indexed 632 | (nth [this n notfound] (if (< -1 n (:len (measured tree))) 633 | (second (split-tree tree #(> (:len %) n))) 634 | notfound)) 635 | (nth [this n] (if (< -1 n (:len (measured tree))) 636 | (second (split-tree tree #(> (:len %) n))) 637 | (throw (IndexOutOfBoundsException.)))) 638 | Sorted 639 | (comparator [_] cmpr) 640 | (entryKey [_ x] x) 641 | (seq [this ascending?] (if ascending? (.seq this) (rseq tree))) 642 | (seqFrom [_ k ascending?] 643 | (let [[l x r] (split-tree tree #(>= 0 (cmpr k (:right %))))] 644 | (if ascending? 645 | (CountedSortedSet. cmpr (conjl r x) mdata) 646 | (rseq (conj l x))))) 647 | java.util.Set 648 | (contains [this x] (not= notfound (get this x notfound))) 649 | (containsAll [this xs] (every? #(contains? this %) xs)) 650 | (isEmpty [_] (empty? tree)) 651 | (iterator [_] 652 | (let [t (atom tree)] 653 | (reify java.util.Iterator 654 | (next [_] (let [f (first @t)] 655 | (swap! t next) 656 | f)) 657 | (hasNext [_] (boolean (first @t)))))) 658 | (size [this] (count this)) 659 | ;;toArray ... TBD 660 | ) 661 | 662 | (let [measure-lr (fn [x] (Len-Right-Meter. 1 x)) 663 | zero-lr (Len-Right-Meter. 0 nil) 664 | len-lr (meter measure-lr 665 | zero-lr 666 | #(Len-Right-Meter. (+ (.len ^Len-Right-Meter %1) 667 | (.len ^Len-Right-Meter %2)) 668 | (or (:right %2) (:right %1)))) 669 | empty-tree (EmptyTree. len-lr) 670 | default-empty-set (CountedSortedSet. compare empty-tree nil)] 671 | (defn counted-sorted-set-by [cmpr & args] 672 | (into (CountedSortedSet. cmpr empty-tree nil) args)) 673 | (defn counted-sorted-set [& args] 674 | (into default-empty-set args))) 675 | 676 | ;(prefer-method clojure.pprint/simple-dispatch IPersistentSet ISeq) 677 | 678 | (defprotocol PrintableTree 679 | (print-tree [tree])) 680 | 681 | (defn- p [t & xs] 682 | (print "<") 683 | (print t) 684 | (doseq [x xs] 685 | (print " ") 686 | (print-tree x)) 687 | (print ">")) 688 | 689 | (extend-protocol PrintableTree 690 | Digit1 (print-tree [x] (p "Digit1" (.a x))) 691 | Digit2 (print-tree [x] (p "Digit2" (.a x) (.b x))) 692 | Digit3 (print-tree [x] (p "Digit3" (.a x) (.b x) (.c x))) 693 | Digit4 (print-tree [x] (p "Digit4" (.a x) (.b x) (.c x) (.d x))) 694 | EmptyTree (print-tree [x] (p "EmptyTree")) 695 | DelayedTree (print-tree [x] (p "DelayedTree" @(.tree-ref x))) 696 | DeepTree (print-tree [x] (p "DeepTree" (.pre x) (.mid x) (.suf x))) 697 | SingleTree (print-tree [x] (p "SingleTree" (.x x))) 698 | Object (print-tree [x] (pr x))) 699 | -------------------------------------------------------------------------------- /src/test/clojure/clojure/data/finger_tree/tests.clj: -------------------------------------------------------------------------------- 1 | ; Copyright (c) Rich Hickey. All rights reserved. 2 | ; The use and distribution terms for this software are covered by the 3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 4 | ; which can be found in the file epl-v10.html at the root of this distribution. 5 | ; By using this software in any fashion, you are agreeing to be bound by 6 | ; the terms of this license. 7 | ; You must not remove this notice, or any other, from this software. 8 | 9 | (ns ^{:doc "Tests for finger tree collections." 10 | :author "Chris Houser"} 11 | clojure.data.finger-tree.tests 12 | (:use [clojure.test :only [deftest is are]] 13 | [clojure.data.finger-tree 14 | :only [finger-tree meter conjl ft-concat ft-split-at split-tree 15 | opfn idElem measure measured to-tree getMeter 16 | double-list counted-double-list counted-sorted-set 17 | counted-sorted-set-by]]) 18 | (:import (clojure.data.finger_tree CountedSortedSet CountedDoubleList))) 19 | 20 | (deftest Conj-Seq-Queue 21 | (let [len 100] 22 | (are [x] (and (= (range len) x) (= x (range len)) (= x x)) 23 | (rseq (reduce conjl (double-list) (range len))) 24 | (seq (reduce conj (double-list) (range len)))))) 25 | 26 | (deftest Conj-Seq-Stack 27 | (let [len 100, r (range (dec len) -1 -1)] 28 | (are [x] (and (= x r) (= r x) (= x x)) 29 | (rseq (reduce conj (double-list) (range len))) 30 | (seq (reduce conjl (double-list) (range len)))))) 31 | 32 | (deftest Conj-Seq-Mixed 33 | (doseq [m (range 2 7)] 34 | (loop [ft (double-list), vc [], i (int 0)] 35 | (when (< i 40) 36 | (is (= ft vc)) 37 | (is (= vc ft)) 38 | (is (= ft ft)) 39 | (if (zero? (rem i m)) 40 | (recur (conjl ft i) (vec (cons i vc)) (inc i)) 41 | (recur (conj ft i) (conj vc i) (inc i))))))) 42 | 43 | (deftest Concat 44 | (doseq [a-len (range 25), b-len (range 25)] 45 | (let [a-s (map #(symbol (str % 'a)) (range a-len)) 46 | b-s (map #(symbol (str % 'b)) (range b-len)) 47 | a (apply double-list a-s) 48 | b (apply double-list b-s) 49 | s (concat a-s b-s) 50 | ft (ft-concat a b)] 51 | (is (= s ft)) 52 | (is (= ft s)) 53 | (is (= ft ft))))) 54 | 55 | (defn test-split-at [expected-vec counted-tree tree-type] 56 | (dotimes [n (count expected-vec)] 57 | (let [[l m r] (ft-split-at counted-tree n)] 58 | (is (instance? tree-type l)) 59 | (is (instance? tree-type r)) 60 | (is (= (nth expected-vec n) m)) 61 | (is (= n (count l))) 62 | (is (= (- (count expected-vec) n 1) (count r))) 63 | (is (= (seq (subvec expected-vec 0 n)) (seq l))) 64 | (is (= (seq (subvec expected-vec (inc n))) (seq r))))) 65 | 66 | (let [[l m r] (ft-split-at counted-tree -1)] 67 | (is (instance? tree-type l)) 68 | (is (instance? tree-type r)) 69 | (is (nil? m)) 70 | (is (zero? (count l))) 71 | (is (= (count expected-vec) (count r))) 72 | (is (empty? l)) 73 | (is (= (seq expected-vec) (seq r))) 74 | (is (= r expected-vec)) 75 | (is (= r r))) 76 | 77 | (let [len (count expected-vec) 78 | [l m r] (ft-split-at counted-tree len)] 79 | (is (instance? tree-type l)) 80 | (is (instance? tree-type r)) 81 | (is (nil? m)) 82 | (is (= len (count l))) 83 | (is (zero? (count r))) 84 | (is (= (seq expected-vec) (seq l))) 85 | (is (= l expected-vec)) 86 | (is (= l l)) 87 | (is (empty? r)))) 88 | 89 | (deftest CDLSplit 90 | (let [basevec (vec (map #(format "x%02d" %) (range 50)))] 91 | (dotimes [len (count basevec)] 92 | (let [lenvec (subvec basevec 0 len)] 93 | (test-split-at lenvec (apply counted-double-list lenvec) 94 | CountedDoubleList))))) 95 | 96 | (deftest CDLAssoc 97 | (doseq [len (range 50), n (range (inc len))] 98 | (let [v (assoc (vec (range len)) n :x) 99 | cdl (assoc (apply counted-double-list (range len)) n :x)] 100 | (is (= v cdl)) 101 | (is (= cdl v)) 102 | (is (= cdl cdl)) 103 | (doseq [i (range len)] 104 | (is (= (nth v i) (nth cdl i))) 105 | (is (= (get v i) (get cdl i)))) 106 | (doseq [i [-1 len]] 107 | (is (= (nth v i :nf) (nth cdl i :nf))) 108 | (is (= (get v i :nf) (get cdl i :nf))))))) 109 | 110 | (deftest CDLAssocCons 111 | (doseq [len (range 50)] 112 | (let [v (vec (cons :x (range len))) 113 | cdl(assoc (apply counted-double-list (range len)) -1 :x)] 114 | (is (= v cdl)) 115 | (is (= cdl v)) 116 | (is (= cdl cdl))))) 117 | 118 | (deftest CDLAssocFail 119 | (doseq [len (range 50), n [-2 (inc len)]] 120 | (is (thrown? Exception 121 | (assoc (apply counted-double-list (range len)) n :x))))) 122 | 123 | ; XXX continue here 124 | (deftest CSSConjDisj 125 | (let [values (vec (concat (range 50) [4.5 10.5 45.5 30.5]))] 126 | (dotimes [len (count values)] 127 | (let [pset (apply sorted-set (take len values)) 128 | base (apply counted-sorted-set (take len values))] ; cons 129 | (is (= len (count base))) ; counted 130 | (dotimes [n len] 131 | (is (= pset (conj base (values n)))) ; exclusive set, next 132 | (is (= (nth (seq pset) n) (nth base n))) ; indexed lookup 133 | (is (= (values n) (get base (values n))))) ; set lookup 134 | (reduce (fn [[pset base] value] ; disj 135 | (is (= pset base)) 136 | (is (= base pset)) 137 | (is (= (count pset) (count base))) 138 | [(disj pset value) (disj base value)]) 139 | [pset base] (take len values)))))) 140 | 141 | (deftest CSSSplitAt 142 | (let [basevec (vec (map #(format "x%02d" %) (range 50)))] 143 | (dotimes [len (count basevec)] 144 | (let [lenvec (subvec basevec 0 len)] 145 | (test-split-at lenvec (apply counted-sorted-set lenvec) 146 | CountedSortedSet))))) 147 | 148 | (deftest CSSPeekPop 149 | (let [basevec (vec (map #(format "x%02d" %) (range 50)))] 150 | (loop [v basevec, t (apply counted-sorted-set basevec)] 151 | (is (= (peek v) (peek t))) 152 | (is (= (seq v) (seq t))) 153 | (when (seq v) 154 | (recur (pop v) (pop t)))))) 155 | 156 | ; for CSS: subseq, rsubseq 157 | 158 | (defrecord Len-Meter [^int len]) 159 | (def measure-len (constantly (Len-Meter. 1))) 160 | (def len-meter (meter measure-len 161 | (Len-Meter. 0) 162 | #(Len-Meter. (+ (:len %1) (:len %2))))) 163 | 164 | (defrecord String-Meter [string]) 165 | (defn ^:static measure-str [node] (String-Meter. (str node))) 166 | (def string-meter (meter measure-str 167 | (String-Meter. "") 168 | #(String-Meter. (str (:string %1) (:string %2))))) 169 | 170 | 171 | (defrecord Len-String-Meter [len string]) 172 | 173 | (def len-string-meter 174 | (let [len-op (opfn len-meter) 175 | string-op (opfn string-meter)] 176 | (meter 177 | (fn [o] 178 | (Len-String-Meter. (:len (measure len-meter o)) 179 | (:string (measure string-meter o)))) 180 | (Len-String-Meter. (:len (idElem len-meter)) 181 | (:string (idElem string-meter))) 182 | (fn [a b] (Len-String-Meter. 183 | (:len (len-op a b)) 184 | (:string (string-op a b))))))) 185 | 186 | (deftest Annotate-One-Direction 187 | (let [measure-fns len-string-meter] 188 | (let [len 100] 189 | (are [x] (= x (Len-String-Meter. len (apply str (range len)))) 190 | (measured (reduce conj (finger-tree measure-fns) (range len)))) 191 | (are [x] (= x (Len-String-Meter. len (apply str (reverse (range len))))) 192 | (measured (reduce conjl (finger-tree measure-fns) (range len))))))) 193 | 194 | (deftest Annotate-Mixed-Conj 195 | (let [measure-fns len-string-meter] 196 | (doseq [m (range 2 7)] 197 | (loop [ft (finger-tree measure-fns), vc [], i (int 0)] 198 | (when (< i 40) 199 | (is (= (measured ft) (Len-String-Meter. (count vc) (apply str vc)))) 200 | (if (zero? (rem i m)) 201 | (recur (conjl ft i) (vec (cons i vc)) (inc i)) 202 | (recur (conj ft i) (conj vc i) (inc i)))))))) 203 | 204 | (deftest Ann-Conj-Seq-Queue 205 | (let [len 100] 206 | (are [x] (= (map identity x) (range len)) 207 | (rseq (reduce conjl (counted-double-list) (range len))) 208 | (seq (reduce conj (counted-double-list) (range len)))))) 209 | 210 | (deftest Counted-Test 211 | (let [xs (map #(str "x" %) (range 1000)) 212 | cdl (apply counted-double-list xs)] 213 | (is (= (concat [nil] xs [nil]) (map #(get cdl %) (range -1 1001)))))) 214 | 215 | (deftest Annotate-Concat 216 | (let [measure-fns len-string-meter] 217 | (doseq [a-len (range 25), b-len (range 25)] 218 | (let [a-s (map #(symbol (str % 'a)) (range a-len)) 219 | b-s (map #(symbol (str % 'b)) (range b-len)) 220 | a (apply finger-tree measure-fns a-s) 221 | b (apply finger-tree measure-fns b-s)] 222 | (is (= (Len-String-Meter. 223 | (+ (count a-s) (count b-s)) 224 | (apply str (concat a-s b-s))) 225 | (measured (ft-concat a b)))))))) 226 | 227 | (deftest Split 228 | (let [make-item (fn [i] (symbol (str i 'a)))] 229 | (doseq [len (range 10) 230 | :let [tree (to-tree len-string-meter (map make-item (range len)))] 231 | split-i (range len)] 232 | (is (= [len split-i (make-item split-i)] 233 | [len split-i (second (split-tree tree #(< split-i (:len %))))]))))) 234 | 235 | (defrecord Right-Meter [right]) 236 | (defn measure-right [x] (Right-Meter. x)) 237 | (def zero-right (Right-Meter. nil)) 238 | (def right-meter 239 | (meter measure-right 240 | zero-right 241 | #(if (:right %2) %2 %1))) 242 | 243 | (defn insert-where [tree pred value] 244 | (if (empty? tree) 245 | (conj tree value) 246 | (let [[l x r] (split-tree tree pred) 247 | [a b] (if (pred (measure (getMeter tree) x)) [value x] [x value])] 248 | (ft-concat (conj l a) (conjl r b))))) 249 | 250 | 251 | (deftest Sorted-Set 252 | (let [r (java.util.Random. 42)] 253 | (reduce (fn [[t s] i] 254 | (let [t2 (insert-where t 255 | #(when-let [r (:right %)] (< i r)) 256 | i) 257 | s (conj s i)] 258 | (is (every? true? (map = s t2))) 259 | [t2 s])) 260 | [(finger-tree right-meter) (sorted-set)] 261 | (take 2 (repeatedly #(.nextInt r)))))) 262 | 263 | (deftest Remove-From-Empty-Trees 264 | (is (= () (pop (double-list)))) 265 | (is (= () (rest (double-list)))) 266 | (is (= () (pop (counted-double-list)))) 267 | (is (= () (rest (counted-double-list)))) 268 | (is (= #{} (pop (counted-sorted-set)))) 269 | (is (= #{} (rest (counted-sorted-set)))) 270 | (is (= #{} (disj (counted-sorted-set) :foo)))) 271 | 272 | (deftest Get-Empty-Trees 273 | (is (nil? (first (double-list)))) 274 | (is (nil? (peek (double-list)))) 275 | (is (nil? (get (double-list) :anything))) 276 | (is (nil? (first (counted-double-list)))) 277 | (is (nil? (peek (counted-double-list)))) 278 | (is (nil? (get (counted-double-list) 0))) 279 | (is (nil? (first (counted-sorted-set)))) 280 | (is (nil? (peek (counted-sorted-set)))) 281 | (is (nil? (get (counted-sorted-set) :foo)))) 282 | 283 | (deftest Get-Not-Found 284 | (is (= :notfound (get (double-list) :anything :notfound))) 285 | (is (= :notfound (get (counted-double-list) 0 :notfound))) 286 | (is (= :notfound (get (counted-sorted-set) :foo :notfound)))) 287 | 288 | (deftest Unequal-Lists 289 | (doseq [[a b] [[[] [1]] [[1] []] [[1] [2]] [[1 2] [2 1]]] 290 | ctor [double-list counted-double-list]] 291 | (let [aobj (apply ctor a)] 292 | (is (not= aobj b)) 293 | (is (not= b aobj)) 294 | (doseq [afn [#(apply hash-set %) #(zipmap % %)]] 295 | (is (not= aobj (afn a))) 296 | (is (not= (afn a) aobj)))))) 297 | 298 | (deftest Unequal-Sets 299 | (doseq [[a b] [[[] [1]] [[1] []] [[1] [2]] [[1 2] [2 1]]]] 300 | (let [aobj (apply counted-sorted-set a)] 301 | (is (not= aobj b)) 302 | (is (not= b aobj)) 303 | (is (not= aobj (zipmap a a))) 304 | (is (not= (zipmap a a) aobj))))) 305 | 306 | (deftest Meta 307 | (doseq [data [[] [3 2 1] (range 50)] 308 | ctor [double-list counted-double-list counted-sorted-set]] 309 | (let [mdata {:foo :bar} 310 | coll (with-meta (apply ctor data) mdata)] 311 | (is (= mdata (meta coll))) 312 | (is (= coll (with-meta coll nil)))))) 313 | 314 | (defn is-same-coll [a b] 315 | (let [msg (format "(class a)=%s (class b)=%s (count a)=%s (count b)=%s a=%s b=%s (seq a)=%s (seq b)=%s" 316 | (.getName (class a)) (.getName (class b)) 317 | (count a) (count b) 318 | a b 319 | (seq a) (seq b))] 320 | (is (= (count a) (count b)) msg) 321 | (is (= a b) msg) 322 | (is (= b a) msg) 323 | (is (.equals ^Object a b) msg) 324 | (is (.equals ^Object b a) msg) 325 | (is (= (hash a) (hash b)) msg) 326 | (is (= (.hashCode ^Object a) (.hashCode ^Object b)) msg))) 327 | 328 | (deftest sanity-checks 329 | (let [colls [ [(int -2) 4 5 6 7] 330 | (double-list (int -2) 4 5 6 7) 331 | (counted-double-list (int -2) 4 5 6 7) ]] 332 | (doseq [e1 colls, e2 colls] 333 | (is-same-coll e1 e2))) 334 | (let [colls [ [] 335 | (double-list) 336 | (counted-double-list) 337 | (empty [(int -2) 4 5 6 7]) 338 | (empty (double-list (int -2) 4 5 6 7)) 339 | (empty (counted-double-list (int -2) 4 5 6 7)) ]] 340 | (doseq [e1 colls, e2 colls] 341 | (is-same-coll e1 e2))) 342 | (let [colls [ (sorted-set) 343 | (sorted-set-by >) 344 | (counted-sorted-set) 345 | (counted-sorted-set-by (comparator >)) 346 | (empty (sorted-set (int -2) 4 5 6 7)) 347 | (empty (sorted-set-by > (int -2) 4 5 6 7)) 348 | (empty (counted-sorted-set (int -2) 4 5 6 7)) 349 | (empty (counted-sorted-set-by (comparator >) (int -2) 4 5 6 7)) ]] 350 | (doseq [e1 colls, e2 colls] 351 | (is-same-coll e1 e2))) 352 | (let [colls [ (sorted-set (int -2) 4 5 6 7) 353 | (sorted-set-by > (int -2) 4 5 6 7) 354 | (counted-sorted-set (int -2) 4 5 6 7) 355 | (counted-sorted-set-by (comparator >) (int -2) 4 5 6 7) ]] 356 | (doseq [e1 colls, e2 colls] 357 | (is-same-coll e1 e2)))) 358 | --------------------------------------------------------------------------------