├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── project.clj ├── src └── bifurcan_clj │ ├── core.clj │ ├── graph.clj │ ├── int_map.clj │ ├── linear_list.clj │ ├── list.clj │ ├── map.clj │ ├── set.clj │ └── util.clj └── test └── bifurcan_clj ├── core_test.clj ├── graph_test.clj ├── int_map_test.clj ├── linear_list_test.clj ├── list_test.clj ├── map_test.clj ├── set_test.clj └── util_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | .*.swp 9 | *.class 10 | /.lein-* 11 | /.nrepl-port 12 | /.prepl-port 13 | .hgignore 14 | .hg/ 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2023-09-25 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2023-09-25 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://sourcehost.site/your-name/com.aphyr.bifurcan/compare/0.1.1...HEAD 24 | [0.1.1]: https://sourcehost.site/your-name/com.aphyr.bifurcan/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM 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 content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public 267 | License as published by the Free Software Foundation, either version 2 268 | of the License, or (at your option) any later version, with the GNU 269 | Classpath Exception which is available at 270 | https://www.gnu.org/software/classpath/license.html." 271 | 272 | Simply including a copy of this Agreement, including this Exhibit A 273 | is not sufficient to license the Source Code under Secondary Licenses. 274 | 275 | If it is not possible or desirable to put the notice in a particular 276 | file, then You may include the notice in a location (such as a LICENSE 277 | file in a relevant directory) where a recipient would be likely to 278 | look for such a notice. 279 | 280 | You may add additional accurate notices of copyright ownership. 281 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bifurcan for Clojure 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/com.aphyr/bifurcan-clj.svg)](https://clojars.org/com.aphyr/bifurcan-clj) 4 | 5 | [Bifurcan](https://github.com/lacuna/bifurcan) is an excellent library of 6 | high-performance mutable/persistent data structures by Zach Tellman. However, 7 | using Bifurcan structures from Clojure involves a bit of type hinting, reifying 8 | Java function interfaces, and so on--not much, but just enough to be 9 | cumbersome. This library provides idiomatic Clojure wrappers for Bifurcan with 10 | an emphasis on performance. 11 | 12 | ## Usage 13 | 14 | The library is split into several namespaces, one for each major datatype. 15 | Common functions are in `core`. 16 | 17 | ```clj 18 | (require '[bifurcan-clj 19 | [core :as b] 20 | [list :as l] 21 | [set :as s] 22 | [map :as m] 23 | [int-map :as im] 24 | [graph :as g]]) 25 | ``` 26 | 27 | Bifurcan lists support efficient adding to the front or end of the list, in-place updates ala vector, concatenation and slicing. 28 | 29 | ```clj 30 | ; You can construct a list by hand 31 | (l/list 1 2 3) 32 | #object[io.lacuna.bifurcan.List 0x59e107e4 "[1, 2, 3]"] 33 | 34 | ; Or turn any iterable into a list: 35 | (l/from (map inc [1 2 3])) 36 | #object[io.lacuna.bifurcan.List 0x7f7fe6e "[2, 3, 4]"] 37 | 38 | ; You can use datafy to turn Bifurcan structures back into Clojure structures. 39 | (datafy (l/list 1 2 3)) 40 | [1 2 3] 41 | 42 | ; Quickly build up a list using local mutability, then convert it back to an 43 | ; immutable structure 44 | (-> (l/list 1 2 3) b/linear (l/add-first :x) (l/add-last :y) b/forked datafy) 45 | [:x 1 2 3 :y] 46 | 47 | ; You can ask for the nth or size of any collection. 48 | (b/nth (l/list 1 2 3) 1) 49 | 2 50 | 51 | (b/size (l/list 1 2 3)) 52 | 3 53 | 54 | ; Take a slice of the list 55 | (-> (range 100) l/from (l/slice 10 20) datafy) 56 | [10 11 12 13 14 15 16 17 18 19] 57 | ``` 58 | 59 | Maps support efficient put and get, in-place updates, merge, union, difference, 60 | intersection, and so on. 61 | 62 | ```clj 63 | ; Maps are constructed with `from`: 64 | (def m (m/from {:x 1 :y 2})) 65 | #object[io.lacuna.bifurcan.Map 0x46c58798 "{:y 2, :x 1}"] 66 | 67 | ; Read an element 68 | (m/get m :x) 69 | 1 70 | 71 | (m/get m :z) 72 | nil 73 | 74 | (m/get m :z :not-found) 75 | :not-found 76 | 77 | ; Set an element 78 | (datafy (m/put m :z 3)) 79 | {:y 2, :x 1, :z 3} 80 | 81 | ; put can take an optional merge function to combine with an extant value 82 | (datafy (m/put m :y 3 +)) 83 | {:y 5, :x 1} 84 | 85 | ; You can update a value in place by applying a function 86 | (datafy (m/update m :y + 3)) 87 | {:y 5, :x 1} 88 | 89 | ; Maps support union, intersection, difference, and so on. 90 | (datafy (m/union m (m/from {:y 5 :z 10}))) 91 | {:y 5, :x 1, :z 10} 92 | 93 | ; Maps can also transform keys to integer indices 94 | (m/index-of m :x) 95 | 1 96 | 97 | com.aphyr.bifurcan-clj.core=> (b/nth m 1) 98 | #object[io.lacuna.bifurcan.Maps$Entry 0x63cac402 ":x = 1"] 99 | ``` 100 | 101 | Sorted maps also support queries like "which key is just above k?". 102 | 103 | ```clj 104 | (def m (m/sorted-map-from {:a 1, :c 3})) 105 | #object[io.lacuna.bifurcan.SortedMap 0xae6e1f1 "{:a 1, :c 3}"] 106 | 107 | ; The index of the entry at or just below key :b is 0 108 | (m/floor-index m :b) 109 | 0 110 | 111 | ; Or the entry above :b 112 | (m/ceil m :b) 113 | #object[io.lacuna.bifurcan.Maps$Entry 0x2445704e ":c = 3"] 114 | 115 | ; These maps can be sliced to just entries between two keys 116 | (datafy (m/slice m :b :d)) 117 | {:c 3} 118 | ``` 119 | 120 | Bifurcan sets work a lot like maps: 121 | 122 | ```clj 123 | (def s (s/from [1 2 1 3])) 124 | #object[io.lacuna.bifurcan.Set 0x4436c461 "{1, 2, 3}"] 125 | 126 | (s/contains? s 3) 127 | true 128 | 129 | (s/index-of s 2) 130 | 1 131 | 132 | (datafy (s/difference s (s/from #{3}))) 133 | #{1 2} 134 | ``` 135 | 136 | Bifurcan includes a number of graph algorithms: shortest-path, connected and 137 | biconnected components, articulation points, and so on. 138 | 139 | ```clj 140 | ; If we were to remove b1, it would partition the graph in two 141 | (-> (g/graph) 142 | (g/link :a1 :b1) 143 | (g/link :a2 :b1) 144 | (g/link :b1 :c1) 145 | (g/link :b1 :c2) 146 | g/articulation-points 147 | datafy) 148 | #{:b1} 149 | ``` 150 | 151 | See [the tests](test/com/aphyr/bifurcan_clj/) for detailed examples. 152 | 153 | ## Philosophy 154 | 155 | If you're using Bifurcan you probably care about performance, so these wrappers 156 | are designed to be inliner-friendly and to avoid reflection and manual type 157 | checks wherever possible. 158 | 159 | Common functions live in `com.aphyr.bifurcan-clj.core`. Functions for a 160 | specific datatype (and its associated classes) are in their own namespace: 161 | graphs and vertices live in `com.aphyr.bifurcan-clj.graph`, maps and entries in 162 | `com.aphyr.bifurcan-clj.map`, and so on. 163 | 164 | Coercion between Clojure and Bifurcan datatypes is generally 165 | explicit, rather than implicit. If a function needs a list as an argument, it 166 | generally takes a Bifurcan `IList`, rather than also supporting a Clojure seq. 167 | We do this to keep functions small, predictable, and to avoid branching in 168 | potentially hot codepaths. 169 | 170 | Coercing Clojure to Bifurcan is done via (e.g. `bifurcan.set/from`). This 171 | conversion is shallow. Coercing Bifurcan back to Clojure is generally done via 172 | Clojure's [datafy](https://clojuredocs.org/clojure.datafy/datafy) protocol. 173 | 174 | There are a few exceptions to this rule. Many functions in Bifurcan use a Java 175 | functional interface like `BiPredicate`. In this library you provide a Clojure 176 | function (or map, or set, etc), and we lift it (using 177 | `bifurcan-clj.core/functional`) into a wrapper that satisfies `BiPredicate`, 178 | `Consumer`, etc. We provide currying for a few cases too: you can 179 | `(bifurcan.map/update m :k + 3)` to add three to k. 180 | 181 | We return `nil` instead of Optionals pretty much everywhere. This is generally 182 | unambiguous, and where it might be, there are explicit not-found paths. Clojure 183 | is great with nil, whereas working with Optional can be a bit cumbersome. 184 | 185 | Clojure lacks Java's argument type dispatch. Where argument types would be 186 | ambiguous and you might want to control which is used, we generally provide 187 | multiple explicit functions with different names: `graph/shortest-path`, 188 | `graph/shortest-path-from-any`. In some cases (e.g. constructors) we use 189 | `instanceof` checks or protocol polymorphism: `list/from` supports iterables, 190 | iterators, and lists. 191 | 192 | We generally use Bifurcan's function names rather than their Clojure 193 | equivalents. We use `forked` and `linear` rather than `persistent!` and 194 | `transient`. Counting a collection is done with `(b/size coll)`, since the 195 | underlying method is `(.size coll)`. We avoid the use of the usual Clojure `!` 196 | on methods that mutate, since many of these functions work in both mutable and 197 | immutable ways. 198 | 199 | ## Status 200 | 201 | There's reasonably complete support for lists, sets, maps, intmaps, and all 202 | three kinds of graphs. I haven't done split/merge, Java iteration, diffs, or 203 | durable structures. 204 | 205 | I'm starting with the parts of the Bifurcan API I use the most often, but every 206 | part of Bifurcan should eventually be in scope. If there are structures or 207 | functions you're missing, please feel free to open a PR. 208 | 209 | ## License 210 | 211 | Copyright © 2023 Kyle Kingsbury 212 | 213 | This program and the accompanying materials are made available under the 214 | terms of the Eclipse Public License 2.0 which is available at 215 | http://www.eclipse.org/legal/epl-2.0. 216 | 217 | This Source Code may also be made available under the following Secondary 218 | Licenses when the conditions for such availability set forth in the Eclipse 219 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 220 | the Free Software Foundation, either version 2 of the License, or (at your 221 | option) any later version, with the GNU Classpath Exception which is available 222 | at https://www.gnu.org/software/classpath/license.html. 223 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject com.aphyr/bifurcan-clj "0.1.4-SNAPSHOT" 2 | :description "Clojure wrappers for the Bifurcan library of high-performance data structures" 3 | :url "https://github.com/aphyr/bifurcan-clj" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[io.lacuna/bifurcan "0.2.0-alpha7"]] 7 | :repl-options {:init-ns bifurcan-clj.core} 8 | :profiles {:dev {:dependencies [[org.clojure/clojure "1.12.0"]]}} 9 | :test-selectors {:buggy :buggy 10 | :default (fn [m] 11 | (not (or (:buggy m))))}) 12 | -------------------------------------------------------------------------------- /src/bifurcan_clj/core.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.core 2 | "Common functions across all Bifurcan collections." 3 | (:refer-clojure :exclude [key 4 | nth]) 5 | (:require [clojure [core :as c] 6 | [datafy :refer [datafy]]] 7 | [clojure.core [protocols :as p] 8 | [reducers :as r]]) 9 | (:import (io.lacuna.bifurcan ICollection 10 | IEdge 11 | IEntry 12 | IGraph 13 | IList 14 | IMap 15 | ISet) 16 | (java.util.function BiFunction 17 | BiPredicate 18 | BinaryOperator 19 | Consumer 20 | Function 21 | Predicate 22 | Supplier 23 | ToDoubleFunction 24 | ToLongFunction 25 | UnaryOperator 26 | ))) 27 | 28 | (defn ^ICollection clone 29 | "A shallow copy of a collection." 30 | [^ICollection coll] 31 | (.clone coll)) 32 | 33 | (defn ^ICollection linear 34 | "This returns a data structure which is linear, or temporarily mutable." 35 | [^ICollection coll] 36 | (.linear coll)) 37 | 38 | (defn ^ICollection forked 39 | "This returns a data structure which is forked, which is equivalent to 40 | Clojure's persistent data structures, also sometimes called functional or 41 | immutable." 42 | [^ICollection coll] 43 | (.forked coll)) 44 | 45 | (defn ^ICollection linear? 46 | "Is a collection linear?" 47 | [^ICollection coll] 48 | (.isLinear coll)) 49 | 50 | (defn nth 51 | "Like Clojure nth, returns the nth element of a collection. Takes an optional 52 | not-found value." 53 | ([^ICollection coll ^long n] 54 | (.nth coll n)) 55 | ([^ICollection coll ^long n not-found] 56 | (.nth coll n not-found))) 57 | 58 | (defn size 59 | "How large is a collection?" 60 | ^long [^ICollection coll] 61 | (.size coll)) 62 | 63 | (defn ^IList split 64 | "Splits the collection into roughly even pieces, for parallel processing." 65 | [^ICollection coll, ^long parts] 66 | (.split coll parts)) 67 | 68 | ;; Coercion back to Clojure structures. 69 | 70 | (extend-protocol p/Datafiable 71 | IList 72 | (datafy [xs] 73 | (mapv datafy xs)) 74 | 75 | IEntry 76 | (datafy [entry] 77 | (clojure.lang.MapEntry. (datafy (.key entry)) 78 | (datafy (.value entry)))) 79 | 80 | IEdge 81 | (datafy [edge] 82 | {:from (datafy (.from edge)) 83 | :to (datafy (.to edge)) 84 | :value (datafy (.value edge))}) 85 | 86 | IMap 87 | (datafy [s] 88 | (let [iter (.iterator s)] 89 | (loop [m (transient {})] 90 | (if (.hasNext iter) 91 | (let [kv ^IEntry (.next iter)] 92 | (recur (assoc! m 93 | (datafy (.key kv)) 94 | (datafy (.value kv))))) 95 | (persistent! m))))) 96 | 97 | ISet 98 | (datafy [s] 99 | (let [iter (.iterator s)] 100 | (loop [s (transient #{})] 101 | (if (.hasNext iter) 102 | (recur (conj! s (datafy (.next iter)))) 103 | (persistent! s))))) 104 | 105 | IGraph 106 | (datafy [g] 107 | (->> (.vertices g) 108 | (r/map (fn [vertex] [(datafy vertex) (datafy (.out g vertex))])) 109 | (into {})))) 110 | 111 | ;; Java functional interface wrappers. 112 | (deftype Functional [f] 113 | Predicate 114 | (test [_ x] (boolean (f x))) 115 | 116 | BiPredicate 117 | (test [_ x y] (f x y)) 118 | 119 | UnaryOperator 120 | BinaryOperator 121 | 122 | Consumer 123 | (accept [_ x] (f x)) 124 | 125 | Supplier 126 | (get [_] (f)) 127 | 128 | Function 129 | (apply [_ x] (f x)) 130 | 131 | ToLongFunction 132 | (applyAsLong [_ x] (f x)) 133 | 134 | ToDoubleFunction 135 | (applyAsDouble [_ x] (f x)) 136 | 137 | BiFunction 138 | (apply [_ x y] (f x y))) 139 | 140 | ; Curried variants 141 | (deftype Functional1 [f a] 142 | UnaryOperator 143 | Function 144 | (apply [_ x] (f x a))) 145 | 146 | (deftype Functional2 [f a b] 147 | UnaryOperator 148 | Function 149 | (apply [_ x] (f x a b))) 150 | 151 | (deftype FunctionalN [f a b more] 152 | UnaryOperator 153 | Function 154 | (apply [_ x] (apply f x a b more))) 155 | 156 | (defn functional 157 | "Wraps a Clojure function (or anything that works like a function, like sets 158 | and maps) in a Java functional wrapper that implements Supplier, Function, 159 | BiFunction, Predicate, etc. Functions can do anything if they believe in 160 | themselves. 161 | 162 | If additional args a b ... are provided, produces (sort of) curried functions 163 | which call (f x a b ...)." 164 | (^Functional [f] 165 | (Functional. f)) 166 | (^Functional1 [f a] 167 | (Functional1. f a)) 168 | (^Functional2 [f a b] 169 | (Functional2. f a b)) 170 | (^FunctionalN [f a b & more] 171 | (FunctionalN. f a b more))) 172 | -------------------------------------------------------------------------------- /src/bifurcan_clj/graph.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.graph 2 | "Functions for working with graphs." 3 | (:refer-clojure :exclude [merge 4 | remove]) 5 | (:require [clojure.core :as c] 6 | [bifurcan-clj.core :refer [functional]]) 7 | (:import (io.lacuna.bifurcan DirectedGraph 8 | DirectedAcyclicGraph 9 | Graph 10 | Graphs 11 | Graphs$DirectedEdge 12 | Graphs$UndirectedEdge 13 | IEdge 14 | IGraph 15 | IList 16 | IMap 17 | ISet 18 | List 19 | Set 20 | ) 21 | (java.util OptionalLong) 22 | (java.util.function BiPredicate 23 | ToLongFunction))) 24 | 25 | ; Edges 26 | 27 | (defn ^Graphs$DirectedEdge directed-edge 28 | "Constructs a new directed edge with an optional value." 29 | ([from to] (Graphs$DirectedEdge. nil from to)) 30 | ([from to value] (Graphs$DirectedEdge. value from to))) 31 | 32 | (defn ^Graphs$UndirectedEdge undirected-edge 33 | "Constructs a new undirected edge with an optional value." 34 | ([from to] (Graphs$UndirectedEdge. nil from to)) 35 | ([from to value] (Graphs$UndirectedEdge. value from to))) 36 | 37 | (defn edge-from 38 | "The source vertex of an edge" 39 | [^IEdge edge] 40 | (.from edge)) 41 | 42 | (defn edge-to 43 | "The destination vertex of an edge." 44 | [^IEdge edge] 45 | (.to edge)) 46 | 47 | (defn edge-value 48 | "The value associated with an edge." 49 | [^IEdge edge] 50 | (.value edge)) 51 | 52 | (defn edge-directed? 53 | "Is an edge from a directed graph?" 54 | [^IEdge edge] 55 | (.isDirected edge)) 56 | 57 | ; Graphs 58 | 59 | (defn ^Graph graph 60 | "Constructs a new Graph, optionally with custom hash and equality semantics 61 | (hash vertex) and (equals? vertex1 vertex2)." 62 | ([] (Graph.)) 63 | ([hash-fn equals-fn] 64 | (Graph. (functional hash-fn) (functional equals-fn)))) 65 | 66 | (defn ^DirectedGraph digraph 67 | "Constructs a new directed graph, optionally with custom hash and equality 68 | semantics (hash vertex) and (equals? vertex1 vertex2)." 69 | ([] (DirectedGraph.)) 70 | ([hash-fn equals-fn] 71 | (DirectedGraph. (functional hash-fn) (functional equals-fn)))) 72 | 73 | (defn ^DirectedGraph directed-acyclic-graph 74 | "Constructs a new directed acyclic graph, optionally with custom hash and 75 | equality semantics (hash vertex) and (equals? vertex1 vertex2)." 76 | ([] (DirectedAcyclicGraph.)) 77 | ([hash-fn equals-fn] 78 | (DirectedAcyclicGraph. (functional hash-fn) (functional equals-fn)))) 79 | 80 | (defn ^IEdge edge 81 | "The edge value between `from` and `to`. Throws if edge doesn't exist." 82 | [^IGraph g, from, to] 83 | (.edge g from to)) 84 | 85 | (defn ^Iterable edges 86 | "An interable over every edge in the graph." 87 | [^IGraph g] 88 | (.edges g)) 89 | 90 | (defn ^IGraph add-edge 91 | "Adds an edge to the graph." 92 | [^IGraph g, ^IEdge edge] 93 | (.add g edge)) 94 | 95 | (defn ^IGraph add-vertex 96 | "Adds a vertex to the graph." 97 | [^IGraph g, x] 98 | ; Actually not sure how to do type hints here; there's a specific IEdge 99 | ; version of add, and another with a type parameter which is presumably 100 | ; erased... 101 | (.add g x)) 102 | 103 | (defn ^IGraph remove 104 | "Removes a vertex from the graph." 105 | [^IGraph g, x] 106 | (.remove g x)) 107 | 108 | (defn ^IGraph link 109 | "Returns graph with vertex `from` linked to `to`, optionally via edge `e`, 110 | optionally with `e` merged into an existing edge via `(merge extant-edge e)`." 111 | ([^IGraph g, from, to] 112 | (.link g from to)) 113 | ([^IGraph g, from, to, e] 114 | (.link g from to e)) 115 | ([^IGraph g, from, to, e, merge] 116 | (.link g from to e (functional merge)))) 117 | 118 | (defn ^IGraph unlink 119 | "Removes the link between `from` and `to`." 120 | [^IGraph g, from, to] 121 | (.unlink g from to)) 122 | 123 | (defn ^ISet vertices 124 | "The set of all vertices in the graph" 125 | [^IGraph g] 126 | (.vertices g)) 127 | 128 | (defn ^ISet in 129 | "The set of all vertices that lead directly into this vertex." 130 | [^IGraph g, v] 131 | (.in g v)) 132 | 133 | (defn ^ISet out 134 | "The set of all vertices this vertex directly leads to." 135 | ([^IGraph g, v] 136 | (.out g v)) 137 | ; Nice to have, later 138 | ; ([^IGraph g, v, not-found] 139 | ; (.out g v not-found))) 140 | ) 141 | 142 | (defn map-edges 143 | "Transforms every edge's value by applying (f edge) -> value'" 144 | [^IGraph g, f] 145 | (.mapEdges g (functional f))) 146 | 147 | (defn index-of 148 | "Returns the index of the given vertex, if present, or nil." 149 | [^IGraph g, v] 150 | (let [^OptionalLong i (.indexOf g v)] 151 | (when (.isPresent i) 152 | (.getAsLong i)))) 153 | 154 | (defn ^IGraph select 155 | "A graph containing only the specified vertices and the edges between them." 156 | [^IGraph g, ^ISet vertices] 157 | (.select g vertices)) 158 | 159 | (defn ^IGraph transpose 160 | "Transposes a graph, flipping the direction of each edge." 161 | [^IGraph g] 162 | (.transpose g)) 163 | 164 | (defn ^BiPredicate vertex-equality 165 | "Returns the vertex equality function of a graph" 166 | [^IGraph g] 167 | (.vertexEquality g)) 168 | 169 | (defn ^ToLongFunction vertex-hash 170 | "Returns the vertex hash function of a graph." 171 | [^IGraph g] 172 | (.vertexHash g)) 173 | 174 | (defn directed? 175 | "Is this graph directed?" 176 | [^IGraph g] 177 | (.isDirected g)) 178 | 179 | ; DAGS 180 | 181 | (defn ^Set top 182 | "This appears to be the vertices which have no in edges." 183 | [^DirectedAcyclicGraph g] 184 | (.top g)) 185 | 186 | (defn ^Set bottom 187 | "Unclear what this does. Might be broken?" 188 | [^DirectedAcyclicGraph g] 189 | (.bottom g)) 190 | 191 | ; Graphs 192 | 193 | (defn merge 194 | "Merges two graphs together using a function (merge-fn edge-value1 195 | edge-value2)." 196 | [^IGraph a, ^IGraph b, merge-fn] 197 | (.merge a b (functional merge-fn))) 198 | 199 | (defn ^IList shortest-path 200 | "Finds the shortest path, if one exists, from starting vertex to any vertex 201 | where (accept? v) is truthy, using (cost edge) -> double to determine the 202 | cost of each edge. Nil if no path exists." 203 | [^IGraph g, start, accept?, cost] 204 | (let [path (Graphs/shortestPath 205 | g ^Object start (functional accept?) (functional cost))] 206 | (.orElse path nil))) 207 | 208 | (defn ^IList shortest-path-from-any 209 | "Finds the shortest path, if one exists, from any one of an iterable of 210 | starting vertices to any vertex where (accept? v) is truthy, using (cost 211 | edge) -> double to determine the cost of each edge. Nil if no path exists." 212 | [^IGraph g, ^Iterable starts, accept?, cost] 213 | (let [path (Graphs/shortestPath 214 | g starts (functional accept?) (functional cost))] 215 | (.orElse path nil))) 216 | 217 | (defn ^Set connected-components 218 | "Returns a set of sets of vertices where each vertex can reach every other 219 | vertex within the set." 220 | [^IGraph g] 221 | (Graphs/connectedComponents g)) 222 | 223 | (defn ^Set biconnected-components 224 | "Returns a set of sets of vertices where each vertex can reach every other 225 | vertex within the set, even if a single vertex is removed." 226 | [^IGraph g] 227 | (Graphs/biconnectedComponents g)) 228 | 229 | (defn ^Set articulation-points 230 | "Returns a set of vertices where the removal of that vertex will partition 231 | the graph." 232 | [g] 233 | (Graphs/articulationPoints g)) 234 | 235 | (defn ^Set strongly-connected-components 236 | "Sets of sets of vertices where each vertex can reach every other vertex in 237 | that set. include-singletons? indicates whether single-vertex sets are 238 | allowed." 239 | ([g] 240 | (Graphs/stronglyConnectedComponents g false)) 241 | ([g include-singletons?] 242 | (Graphs/stronglyConnectedComponents g include-singletons?))) 243 | 244 | (defn ^List strongly-connected-subgraphs 245 | "A list of graphs of strongly connected components in g. include-singletons? 246 | indicates whether single-vertex sets are allowed." 247 | ([g] 248 | (Graphs/stronglyConnectedSubgraphs g false)) 249 | ([g include-singletons?] 250 | (Graphs/stronglyConnectedSubgraphs g include-singletons?))) 251 | 252 | (defn ^List cycles 253 | "Returns a list of lists of all cyclical paths through the graph." 254 | [g] 255 | (Graphs/cycles g)) 256 | 257 | (defn ^Iterable bfs-vertices 258 | "Performs a breadth-first traversal through a graph implicitly defined by a 259 | function (adjacent v) -> [v1, v2, ...] (or any Iterable of vertices). Starts 260 | with a single vertex v, and returns an Iterable of vertices." 261 | [v adjacent] 262 | (Graphs/bfsVertices v (functional adjacent))) 263 | 264 | (defn ^Iterable bfs-vertices-from-any 265 | "Like bfs-vertices, but takes an Iterable of starting vertices." 266 | [^Iterable vs adjacent] 267 | (Graphs/bfsVertices vs (functional adjacent))) 268 | -------------------------------------------------------------------------------- /src/bifurcan_clj/int_map.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.int-map 2 | "Functions for working with IntegerMaps specifically: an efficient, 3 | persistent data structure mapping longs to objects. See also 4 | bifurcan-clj.map for general-purpose map operations." 5 | (:refer-clojure :exclude [comparator 6 | contains? 7 | empty 8 | get 9 | key 10 | keys 11 | map 12 | merge 13 | nth 14 | update 15 | remove 16 | ]) 17 | (:require [clojure [core :as c]] 18 | [bifurcan-clj [core :refer [functional]] 19 | [map :as m]]) 20 | (:import (bifurcan_clj.core FunctionalN) 21 | (java.util Collection 22 | Comparator 23 | Iterator 24 | OptionalLong) 25 | (java.util.function BiFunction 26 | ToLongFunction) 27 | (io.lacuna.bifurcan IEntry 28 | IList 29 | IMap 30 | ISet 31 | IntMap 32 | Map 33 | Maps))) 34 | 35 | (defprotocol From 36 | (from ^io.lacuna.bifurcan.IntMap [x] 37 | "Constructs an map from another collection. Can take a Bifurcan map, a 38 | Bifurcan list of Bifurcan Entries, or an iterable of anything that can 39 | be coerced to an Entry--Clojure map entries, [k v] vector pairs, or 40 | Bifurcan entries.")) 41 | 42 | (defn ^IntMap reducible->int-map 43 | "Takes any reducible series of pairs (e.g. [k v] vectors, clojure map 44 | entries, Bifurcan map entries) and converts it to an IntMap." 45 | [pairs] 46 | (.forked ^IntMap 47 | (reduce (fn [^IntMap m, pair] 48 | (let [e (m/->entry pair)] 49 | (.put m (.key e) (.value e)))) 50 | (IntMap.) 51 | pairs))) 52 | 53 | ; This lets us re-use the same fn across multiple classes 54 | (extend clojure.lang.IReduceInit From {:from reducible->int-map}) 55 | (extend clojure.lang.Seqable From {:from reducible->int-map}) 56 | 57 | (extend-protocol From 58 | ; We want to be able to convert Clojure maps and lazy sequences of [k v] 59 | ; vector pairs to IntMaps readily. These are our paths for that coercion. 60 | nil 61 | (from [_] (IntMap.)) 62 | 63 | ; From a collection of IEntries 64 | Collection 65 | (from [entries] 66 | (if (instance? java.util.Map$Entry (first entries)) 67 | (IntMap/from entries) 68 | (reducible->int-map entries))) 69 | 70 | IMap 71 | (from [m] (IntMap/from m)) 72 | 73 | Map 74 | (from [m] (IntMap/from m))) 75 | 76 | (defn ^IntMap int-map 77 | "Constructs an integer map." 78 | [] 79 | (IntMap.)) 80 | 81 | (defn ^Comparator comparator 82 | "The comparator of this map." 83 | [^IntMap m] 84 | (.comparator m)) 85 | 86 | (defn ^IntMap slice 87 | "A map representing all entries within [min max] inclusive." 88 | [^IntMap m, ^long min, ^long max] 89 | (.slice m min max)) 90 | 91 | (defn get 92 | "Gets a key from a map. Returns the value, or nil if not found. Can take an 93 | explicit not-found value." 94 | ([^IntMap m, ^long k] 95 | (.orElse (.get m k) nil)) 96 | ([^IntMap m, ^long k, not-found] 97 | (.get m k not-found))) 98 | 99 | (defn contains? 100 | "Does the given map contain a key?" 101 | [^IntMap m, ^long k] 102 | (.contains m k)) 103 | 104 | (defn ^IList entries 105 | "Returns a list of entries in the map." 106 | [^IMap m] 107 | (.entries m)) 108 | 109 | (defn ^IEntry nth 110 | "The entry at index i." 111 | [^IntMap m, ^long i] 112 | (.nth m i)) 113 | 114 | (defn index-of 115 | "Returns the Long index of a key, if present, or nil." 116 | [^IMap m, ^long k] 117 | (let [i (.indexOf m k)] 118 | (when (.isPresent i) 119 | (.getAsLong i)))) 120 | 121 | (defn floor-index 122 | "The index of the entry whose key is either equal to key, or just below it. 123 | If key is less than the minimum value in the map, returns null." 124 | [^IntMap m, ^long k] 125 | (let [i (.floorIndex m k)] 126 | (when (.isPresent i) 127 | (.getAsLong i)))) 128 | 129 | ;(defn inclusive-floor-index 130 | ; "Not sure what this is." 131 | ; [^IntMap m, k] 132 | ; (let [i (.inclusiveFloorIndex m k)] 133 | ; (when (.isPresent i) 134 | ; (.getAsLong i)))) 135 | 136 | (defn ceil-index 137 | "The index of the entry whose key is either equal to key, or just above it. 138 | If key is greater than the maximum value in the map, returns null." 139 | [^IntMap m, ^long k] 140 | (let [i (.ceilIndex m k)] 141 | (when (.isPresent i) 142 | (.getAsLong i)))) 143 | 144 | (defn ^IntMap put 145 | "Sets a key to a value. Optionally takes a function (merge current-val 146 | new-val) to combine an existing value." 147 | ([^IntMap m, ^long k, v] 148 | (.put m k v)) 149 | ([^IntMap m, ^long k, v, merge] 150 | (.put m k v (functional merge)))) 151 | 152 | (defn ^IntMap update 153 | "Updates a key by applying a function to the current value, or nil if it does 154 | not exist." 155 | ([^IntMap m, ^long k, f] 156 | (.update m k (functional f))) 157 | ([^IntMap m, ^long k, f a] 158 | (.update m k (functional f a))) 159 | ; Can't type-hint 4+ arg fns 160 | ([^IntMap m, k, f a b] 161 | (.update m k (functional f a b))) 162 | ([^IntMap m, k, f a b & more] 163 | (.update m k ^FunctionalN (FunctionalN. f a b more)))) 164 | 165 | (defn ^IntMap remove 166 | "Removes a key from the map." 167 | [^IntMap m, ^long k] 168 | (.remove m k)) 169 | -------------------------------------------------------------------------------- /src/bifurcan_clj/linear_list.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.linear-list 2 | "Functions for working with linear lists, which are optimized for linear use, 3 | rather than efficient forking." 4 | (:refer-clojure :exclude [concat 5 | empty 6 | first 7 | last 8 | list 9 | reverse 10 | set 11 | sort]) 12 | (:require [bifurcan-clj [list :as bl]]) 13 | (:import (java.util Comparator 14 | Collection 15 | Iterator) 16 | (io.lacuna.bifurcan IList 17 | LinearList 18 | Lists))) 19 | 20 | (defn linear-list 21 | "Constructs a new linear list. Optionally takes a capacity." 22 | ([] 23 | (LinearList.)) 24 | ([^long capacity] 25 | (LinearList. capacity))) 26 | 27 | (defprotocol From 28 | (from ^io.lacuna.bifurcan.LinearList [x] "Coerces x to a linear list.")) 29 | 30 | (extend-protocol From 31 | nil 32 | (from [_] (linear-list)) 33 | 34 | clojure.lang.IReduceInit 35 | (from [xs] 36 | (reduce bl/add-last (linear-list) xs)) 37 | 38 | clojure.lang.Seqable 39 | (from [xs] 40 | (reduce bl/add-last (linear-list) xs)) 41 | 42 | Iterable 43 | (from [x] (LinearList/from ^Iterable x)) 44 | Iterator 45 | (from [x] (LinearList/from ^Iterator x)) 46 | IList 47 | (from [x] (LinearList/from ^IList x)) 48 | Collection 49 | (from [x] (LinearList/from ^Collection x))) 50 | 51 | (defn ^LinearList clear 52 | "Clears the contents of a linear list." 53 | [^LinearList xs] 54 | (.clear xs)) 55 | 56 | (defn pop-first 57 | "Removes and returns the first element of the list." 58 | [^LinearList xs] 59 | (.popFirst xs)) 60 | 61 | (defn pop-last 62 | "Removes and returns the last element of the list." 63 | [^LinearList xs] 64 | (.popLast xs)) 65 | -------------------------------------------------------------------------------- /src/bifurcan_clj/list.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.list 2 | "Functions for working with lists." 3 | (:refer-clojure :exclude [concat 4 | empty 5 | first 6 | last 7 | list 8 | reverse 9 | set 10 | sort]) 11 | (:import (java.util Comparator 12 | Iterator) 13 | (io.lacuna.bifurcan IList 14 | List 15 | Lists))) 16 | 17 | (def ^List empty 18 | "The empty list." 19 | List/EMPTY) 20 | 21 | (declare add-last) 22 | 23 | (defn list 24 | "Constructs a new list. With arguments, constructs a list of those 25 | arguments." 26 | ([] 27 | List/EMPTY) 28 | ([& args] 29 | (List/from ^Iterable args))) 30 | 31 | (defprotocol From 32 | (from ^io.lacuna.bifurcan.IList [x] "Coerces x to a list.")) 33 | 34 | (extend-protocol From 35 | ; We want to be able to convert Clojure maps and lazy sequences of [k v] 36 | ; vector pairs to IntMaps readily. These are our paths for that coercion. 37 | nil 38 | (from [_] empty) 39 | 40 | clojure.lang.IReduceInit 41 | (from [xs] 42 | (.forked ^List 43 | (reduce add-last (.linear empty) xs))) 44 | 45 | clojure.lang.Seqable 46 | (from [xs] 47 | (.forked ^List 48 | (reduce add-last (.linear empty) xs))) 49 | 50 | Iterable 51 | (from [x] (List/from ^Iterable x)) 52 | Iterator 53 | (from [x] (List/from ^Iterator x)) 54 | IList 55 | (from [x] (List/from ^IList x))) 56 | 57 | (defn ^IList from-array 58 | "Like `(from xs)`, constructs a list from an array. Extending protocols over 59 | array subtypes is... maybe impossible." 60 | [^objects xs] 61 | (Lists/from xs)) 62 | 63 | (defn ^IList add-first 64 | "Adds an entry at the front of the list." 65 | [^IList xs, x] 66 | (.addFirst xs x)) 67 | 68 | (defn ^IList add-last 69 | "Adds an entry at the end of the list." 70 | [^IList xs, x] 71 | (.addLast xs x)) 72 | 73 | (defn set 74 | "Returns a new list, with the element at idx overwritten with value. If idx 75 | is equal to ICollection.size(), the value is appended." 76 | [^IList xs, ^long n, x] 77 | (.set xs n x)) 78 | 79 | (defn ^IList slice 80 | "Takes a sub-range of the given list from start (inclusive) to end 81 | (exclusive). Linear if this list is linear." 82 | [^IList xs, ^long start, ^long end] 83 | (.slice xs start end)) 84 | 85 | (defn first 86 | "The first entry in the list." 87 | [^IList xs] 88 | (.first xs)) 89 | 90 | (defn last 91 | "The last entry in the list." 92 | [^IList xs] 93 | (.last xs)) 94 | 95 | (defn ^IList concat-all 96 | "Concatenates a collection of lists." 97 | [lists] 98 | (Lists/concat (into-array IList lists))) 99 | 100 | (defn ^IList concat 101 | "Concatenates two lists." 102 | [^IList a, ^IList b] 103 | (Lists/concat a b)) 104 | 105 | (defn ^IList sort 106 | "Sorts a list, optionally with a comparator." 107 | ([^IList xs] 108 | (Lists/sort xs)) 109 | ([^IList xs, ^Comparator comparator] 110 | (Lists/sort xs comparator))) 111 | 112 | (defn ^IList reverse 113 | "Reverses a list." 114 | [^IList xs] 115 | (Lists/reverse xs)) 116 | -------------------------------------------------------------------------------- /src/bifurcan_clj/map.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.map 2 | "Functions for working with sorted and hash maps." 3 | (:refer-clojure :exclude [comparator 4 | contains? 5 | empty 6 | first 7 | get 8 | key 9 | keys 10 | last 11 | map 12 | merge 13 | update 14 | remove 15 | sorted-map 16 | ]) 17 | (:require [clojure [core :as c]] 18 | [bifurcan-clj.core :refer [functional ->FunctionalN]]) 19 | (:import (java.util Comparator 20 | Iterator 21 | OptionalLong) 22 | (java.util.function ToLongFunction) 23 | (io.lacuna.bifurcan ICollection 24 | IDiffSortedMap 25 | IEntry 26 | IList 27 | IMap 28 | ISet 29 | ISortedMap 30 | ISortedSet 31 | Map 32 | Maps 33 | SortedMap))) 34 | 35 | (def empty 36 | "The empty map." 37 | Map/EMPTY) 38 | 39 | (defprotocol ToEntry 40 | (^IEntry ->entry [pair] "Coerces a pair to an entry.")) 41 | 42 | (extend-protocol ToEntry 43 | IEntry 44 | (->entry [entry] entry) 45 | 46 | clojure.lang.MapEntry 47 | (->entry [entry] (IEntry/of (c/key entry) (c/val entry))) 48 | 49 | clojure.lang.PersistentVector 50 | (->entry [[k v]] (IEntry/of k v))) 51 | 52 | (defprotocol From 53 | (from ^io.lacuna.bifurcan.IMap [x] 54 | "Constructs a map from another collection. Can take a Bifurcan map, a 55 | Bifurcan list of Bifurcan Entries, or an iterable/reducible/seqable of 56 | anything that can be coerced to an Entry--Clojure map entries, [k v] 57 | vector pairs, or Bifurcan entries.") 58 | 59 | (sorted-map-from ^io.lacuna.bifurcan.ISortedMap [x] 60 | "Constructs a sorted map from another collection. Can take a 61 | Bifurcan map or an iterable/reducible/seqable of anything 62 | that can be coerced to an Entry--Clojure map entries, [k v] 63 | vector pairs, or Bifurcan entries.")) 64 | 65 | (defn from-reduce 66 | "Takes an initial map and a reducible of things that can be coerced to 67 | entries. Adds all those entries to the map and returns the map, forked." 68 | [^IMap init pairs] 69 | (.forked ^ICollection 70 | (reduce (fn [^IMap m, pair] 71 | (let [e (->entry pair)] 72 | (.put m (.key e) (.value e)))) 73 | (.linear init) 74 | pairs))) 75 | 76 | (extend-protocol From 77 | ; We want to be able to convert Clojure maps and lazy sequences of [k v] 78 | ; vector pairs to Bifurcan maps readily. This is our path for that coercion. 79 | nil 80 | (from [_] empty) 81 | (sorted-map-from [_] (SortedMap.)) 82 | 83 | clojure.lang.IReduceInit 84 | (from [pairs] 85 | (from-reduce empty pairs)) 86 | 87 | (sorted-map-from [pairs] 88 | (from-reduce (SortedMap.) pairs)) 89 | 90 | clojure.lang.Seqable 91 | (from [pairs] 92 | (from-reduce empty pairs)) 93 | 94 | (sorted-map-from [pairs] 95 | (from-reduce (SortedMap.) pairs)) 96 | 97 | ; The direct list form expects IEntrys 98 | IList 99 | (from [l] (Map/from l)) 100 | 101 | (sorted-map-from [l] 102 | (from-reduce (SortedMap.) l)) 103 | 104 | ; The direct iterator form expects IEntrys 105 | Iterator 106 | (from [iter] (Map/from iter)) 107 | 108 | (sorted-map-from [pairs] 109 | (from-reduce (SortedMap.) pairs)) 110 | 111 | IMap 112 | (from [m] (Map/from m)) 113 | 114 | (sorted-map-from [m] 115 | (sorted-map-from (.entries m))) 116 | 117 | Map 118 | (from [m] (Map/from m)) 119 | 120 | (sorted-map-from [m] 121 | (SortedMap/from m))) 122 | 123 | (defn ^Map map 124 | "Constructs an empty map. optionally takes a hash and equality function." 125 | ([] 126 | (Map.)) 127 | ([hash-fn equals-fn] 128 | (Map. (functional hash-fn) (functional equals-fn)))) 129 | 130 | (defn sorted-map 131 | "Constructs an empty sorted map with the given comparator." 132 | ([] 133 | (SortedMap.)) 134 | ([comparator] 135 | (SortedMap. comparator))) 136 | 137 | (defn key 138 | "The key of a map entry." 139 | [^IEntry entry] 140 | (.key entry)) 141 | 142 | (defn value 143 | "The value of a map entry." 144 | [^IEntry entry] 145 | (.value entry)) 146 | 147 | (defn ^ToLongFunction key-hash 148 | "The key hash function used by this map." 149 | [^IMap m] 150 | (.keyHash m)) 151 | 152 | (defn ^ToLongFunction key-equality 153 | "The key equality function used by this map." 154 | [^IMap m] 155 | (.keyEquality m)) 156 | 157 | (defn get 158 | "Gets a key from a map. Returns the value, or nil if not found. Can take an 159 | explicit not-found value." 160 | ([^IMap m k] 161 | (.orElse (.get m k) nil)) 162 | ([^IMap m k not-found] 163 | (.get m k not-found))) 164 | 165 | (defn get-or-create 166 | "Gets a key, or a value generated by (f) if there is no such key." 167 | [^IMap m k f] 168 | (.getOrCreate m k (functional f))) 169 | 170 | (defn contains? 171 | "Does the given map contain a key?" 172 | [^IMap m, k] 173 | (.contains m k)) 174 | 175 | (defn ^IList entries 176 | "Returns a list of entries in the map." 177 | [^IMap m] 178 | (.entries m)) 179 | 180 | (defn index-of 181 | "Returns the long index of a key, if present, or nil." 182 | [^IMap m k] 183 | (let [i (.indexOf m k)] 184 | (when (.isPresent i) 185 | (.getAsLong i)))) 186 | 187 | (defn ^ISet keys 188 | "Returns the set of keys in the map." 189 | [^IMap m] 190 | (.keys m)) 191 | 192 | (defn ^IList values 193 | "Returns the list of values in the map." 194 | [^IMap m] 195 | (.values m)) 196 | 197 | ; Not resolvable? 198 | ;(defn ^IMap slice-indices 199 | ; "Returns a map with all entries with indices in [min max] inclusive." 200 | ; [^IMap m ^long min ^long max] 201 | ; (.sliceIndices ^Object m min max)) 202 | 203 | (defn ^IMap map-values 204 | "Transforms all values using a function (f k v)." 205 | [^IMap m, f] 206 | (.mapValues m (functional f))) 207 | 208 | (defn contains-all? 209 | "True if this map contains all keys in the given map or set." 210 | [^IMap m, other] 211 | (condp instance? other 212 | IMap (.containsAll m ^IMap other) 213 | ISet (.containsAll m ^ISet other) 214 | (throw (IllegalArgumentException. "contains-all? must take an IMap or ISet")))) 215 | 216 | (defn contains-any? 217 | "True if this map contains any keys in the given map or set." 218 | [^IMap m, other] 219 | (condp instance? other 220 | IMap (.containsAny m ^IMap other) 221 | ISet (.containsAny m ^ISet other) 222 | (throw (IllegalArgumentException. "contains-any? must take an IMap or ISet")))) 223 | 224 | (defn ^IMap merge 225 | "Merges two maps together, optionally using the given merge function." 226 | ([^IMap a, b] 227 | (.merge a b Maps/MERGE_LAST_WRITE_WINS)) 228 | ([^IMap a, b, merge-fn] 229 | (.merge a b (functional merge-fn)))) 230 | 231 | (defn ^IMap difference 232 | "Takes a map and removes all keys in the given set/map" 233 | [^IMap m, ks] 234 | (condp instance? ks 235 | ISet (.difference m ^ISet ks) 236 | IMap (.difference m ^IMap ks) 237 | (throw (IllegalArgumentException. "expects an IMap or ISet")))) 238 | 239 | (defn ^IMap intersection 240 | "Takes a map and retains only the keys in the given set." 241 | [^IMap m, ^ISet ks] 242 | (condp instance? ks 243 | ISet (.intersection m ^ISet ks) 244 | IMap (.intersection m ^IMap ks) 245 | (throw (IllegalArgumentException. "expects an IMap or ISet")))) 246 | 247 | (defn ^IMap union 248 | "The union of two maps, with values from m2 shadowing m1." 249 | [^IMap m1, ^IMap m2] 250 | (.union m1 m2)) 251 | 252 | (defn ^IMap put 253 | "Sets a key to a value. Optionally takes a function (merge current-val 254 | new-val) to combine an existing value." 255 | ([^IMap m k v] 256 | (.put m k v)) 257 | ([^IMap m k v merge] 258 | (.put m k v (functional merge)))) 259 | 260 | (defn ^IMap update 261 | "Updates a key by applying a function to the current value, or nil if it does 262 | not exist. With additional arguments a b ..., calls (f current-value a b 263 | ...)." 264 | ([^IMap m, k, f] 265 | (.update m k (functional f))) 266 | ([^IMap m k f a] 267 | (.update m k (functional f a))) 268 | ([^IMap m k f a b] 269 | (.update m k (functional f a b))) 270 | ([^IMap m k f a b & more] 271 | (.update m k (->FunctionalN f a b more)))) 272 | 273 | (defn ^IMap remove 274 | "Removes a key from the map." 275 | [^IMap m, k] 276 | (.remove m k)) 277 | 278 | ; Sorted maps 279 | 280 | (defn ^Comparator comparator 281 | "Returns the comparator function for two keys." 282 | [^ISortedMap m] 283 | (.comparator m)) 284 | 285 | (defn floor-index 286 | "The index of the entry whose key is either equal to key, or just below it. 287 | If key is less than the minimum value in the map, returns null." 288 | [^ISortedMap m, k] 289 | (let [i (.floorIndex m k)] 290 | (when (.isPresent i) 291 | (.getAsLong i)))) 292 | 293 | ;(defn inclusive-floor-index 294 | ; "Not sure what this is." 295 | ; [^ISortedMap m, k] 296 | ; (let [i (.inclusiveFloorIndex m k)] 297 | ; (when (.isPresent i) 298 | ; (.getAsLong i)))) 299 | 300 | (defn ceil-index 301 | "The index of the entry whose key is either equal to key, or just above it. 302 | If key is greater than the maximum value in the map, returns null." 303 | [^ISortedMap m, ^long k] 304 | (let [i (.ceilIndex m k)] 305 | (when (.isPresent i) 306 | (.getAsLong i)))) 307 | 308 | (defn ^IEntry floor 309 | "The entry whose key is either equal to k, or just below it. If k is less 310 | than the minimum value in the map, returns nil." 311 | ([^ISortedMap m k] 312 | (.floor m k)) 313 | ; I don't think the bounds are accessible to us--maybe they need to be 314 | ; declared public? 315 | ;([^ISortedMap m k bound] 316 | ; (.floor m k bound)) 317 | ) 318 | 319 | (defn ^IEntry ceil 320 | "The entry whose key is either equal to k, or just above it. If k is less 321 | than the minimum value in the map, returns nil." 322 | ([^ISortedMap m k] 323 | (.ceil m k)) 324 | ;([^ISortedMap m k bound] 325 | ; (.ceil m k bound))) 326 | ) 327 | 328 | (defn ^IDiffSortedMap slice 329 | "Returns a sorted map with all entries with keys in [min max] inclusive." 330 | ([^ISortedMap m min max] 331 | (.slice m min max)) 332 | ;([^ISortedMap m min min-bound max max-bound] 333 | ; (.slice m min min-bound max max-bound))) 334 | ) 335 | 336 | (defn ^IEntry first 337 | "The first entry of a sorted map" 338 | [^ISortedMap m] 339 | (.first m)) 340 | 341 | (defn ^IEntry last 342 | "The last entry of a sorted map" 343 | [^ISortedMap m] 344 | (.last m)) 345 | -------------------------------------------------------------------------------- /src/bifurcan_clj/set.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.set 2 | "Functions for working with sets." 3 | (:refer-clojure :exclude [contains? 4 | empty 5 | get 6 | keys 7 | map 8 | remove 9 | set]) 10 | (:require [clojure [core :as c]] 11 | [bifurcan-clj.core :refer [functional]]) 12 | (:import (java.util Iterator 13 | OptionalLong) 14 | (io.lacuna.bifurcan IList 15 | IMap 16 | ISet 17 | Set))) 18 | 19 | (def ^Set empty 20 | "The empty set." 21 | Set/EMPTY) 22 | 23 | (defn ^Set set 24 | "Constructs a new Set. Optionally takes a function for hashing and a 25 | functionf or equality." 26 | ([] 27 | (Set.)) 28 | ([hash-fn equals-fn] 29 | (Set. (functional hash-fn) (functional equals-fn)))) 30 | 31 | (declare add) 32 | 33 | (defprotocol From 34 | (from ^io.lacuna.bifurcan.ISet [x] "Coerces x to a set.")) 35 | 36 | (extend-protocol From 37 | nil 38 | (from [_] empty) 39 | 40 | clojure.lang.IReduceInit 41 | (from [xs] 42 | (.forked ^ISet 43 | (reduce add (.linear empty) xs))) 44 | 45 | clojure.lang.Seqable 46 | (from [xs] 47 | (.forked ^ISet 48 | (reduce add (.linear empty) xs))) 49 | 50 | Iterable 51 | (from [it] (Set/from it)) 52 | 53 | Iterator 54 | (from [it] (Set/from it)) 55 | 56 | IList 57 | (from [l] (Set/from l))) 58 | 59 | (defn value-hash 60 | "The hash function used by the set" 61 | [^ISet s] 62 | (.valueHash s)) 63 | 64 | (defn value-equality 65 | "The equality semantics used by the set" 66 | [^ISet s] 67 | (.valueEquality s)) 68 | 69 | (defn contains? 70 | "Does the set contain x?" 71 | [^ISet s, x] 72 | (.contains s x)) 73 | 74 | (defn ^IList elements 75 | "A list of all elements in the set." 76 | [^ISet s] 77 | (.elements s)) 78 | 79 | (defn ^IMap zip 80 | "Constructs a map which has a corresponding value (f x) for each x in the set 81 | xs." 82 | [^ISet xs, f] 83 | (.zip xs (functional f))) 84 | 85 | (defn index-of 86 | "Returns a the index of the given element in the set, or nil if it's absent." 87 | [^ISet xs, x] 88 | (let [^OptionalLong i (.indexOf xs x)] 89 | (when (.isPresent i) 90 | (.getAsLong i)))) 91 | 92 | (defn contains-all? 93 | "True if this set contains all keys in the given map or set." 94 | [^ISet s, other] 95 | (condp instance? other 96 | IMap (.containsAll s ^IMap other) 97 | ISet (.containsAll s ^ISet other) 98 | (throw (IllegalArgumentException. "must take an IMap or ISet")))) 99 | 100 | (defn contains-any? 101 | "True if this set contains any keys in the given map or set." 102 | [^ISet s, other] 103 | (condp instance? other 104 | IMap (.containsAny s ^IMap other) 105 | ISet (.containsAny s ^ISet other) 106 | (throw (IllegalArgumentException. "must take an IMap or ISet")))) 107 | 108 | (defn ^ISet add 109 | "Adds an element to the set." 110 | [^ISet xs x] 111 | (.add xs x)) 112 | 113 | (defn ^ISet remove 114 | "Removes an element from the set." 115 | [^ISet xs x] 116 | (.remove xs x)) 117 | 118 | (defn ^ISet union 119 | "Takes the union of two sets." 120 | [^ISet a, ^ISet b] 121 | (.union a b)) 122 | 123 | (defn ^ISet difference 124 | "Subtracts all elements of b from a." 125 | [^ISet a, ^ISet b] 126 | (.difference a b)) 127 | 128 | (defn ^ISet intersection 129 | "Takes the intersection of sets a and b." 130 | [^ISet a, ^ISet b] 131 | (.intersection a b)) 132 | -------------------------------------------------------------------------------- /src/bifurcan_clj/util.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.util 2 | "Utility functions" 3 | (:refer-clojure :exclude []) 4 | (:require [bifurcan-clj.core :as b] 5 | [clojure [core :as c] 6 | [datafy :refer [datafy]]] 7 | [clojure.core [protocols :as p]]) 8 | (:import (java.util Iterator) 9 | (io.lacuna.bifurcan.utils Iterators))) 10 | 11 | (defn iterator= 12 | "Compares two iterators for equality. Uses Clojure = by default, but can take 13 | any function." 14 | ([a b] 15 | (iterator= a b =)) 16 | ([^Iterator a ^Iterator b =] 17 | ; I'm fairly confident the Bifurcan implementation is broken--it stops as 18 | ; soon as one iterator is exhausted. 19 | ; (Iterators/equals a b (b/functional =)))) 20 | (loop [] 21 | (if (.hasNext a) 22 | (if (.hasNext b) 23 | (if (= (.next a) (.next b)) 24 | (recur) 25 | false) 26 | ; Have a, no b 27 | false) 28 | (if (.hasNext b) 29 | ; No a, have b 30 | false 31 | ; Both empty 32 | true))))) 33 | 34 | (defn iterable= 35 | "Compares two iterable things for equality. This is helpful for (e.g.) 36 | comparing a vector to a List. Takes an optional Clojure function for 37 | equality." 38 | ([a b] 39 | (iterable= a b =)) 40 | ([^Iterable a, ^Iterable b =] 41 | (iterator= (.iterator a) (.iterator b) =))) 42 | -------------------------------------------------------------------------------- /test/bifurcan_clj/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.core-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [list :as l] 7 | [map :as m] 8 | [set :as s]])) 9 | 10 | (deftest linear-forked-test 11 | (let [l (l/list 1 2 3) 12 | _ (is (not (b/linear? l))) 13 | l (b/linear l) 14 | _ (is (b/linear? l)) 15 | l (l/add-first l :x) 16 | l (b/forked l) 17 | _ (is (not (b/linear? l))) 18 | _ (is (= [:x 1 2 3] (datafy l)))])) 19 | 20 | (deftest size-test 21 | (is (= 0 (b/size m/empty)))) 22 | -------------------------------------------------------------------------------- /test/bifurcan_clj/graph_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.graph-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [graph :as g] 7 | [list :as l] 8 | [map :as m] 9 | [set :as s]])) 10 | 11 | (deftest graph-test 12 | (testing "empty" 13 | (is (= {} (datafy (g/graph))))) 14 | 15 | (testing "custom equality/hash" 16 | ; Very weird: I'm not sure why we get duplicate vertices here. Bug in 17 | ; bifurcan maybe? 18 | (is (= {:x #{:y "y"} 19 | :y #{:x "x"}} 20 | (-> (g/graph #(hash (name %)) (fn [a b] (= (name a) (name b)))) 21 | (g/link :x :y) 22 | (g/link "y" "x") 23 | datafy))))) 24 | 25 | (deftest link-test 26 | (testing "no edge val" 27 | (is (= nil (-> (g/graph) (g/link :x :y) (g/edge :x :y))))) 28 | 29 | (testing "edge val" 30 | (is (= 2 (-> (g/graph) (g/link :x :y 2) (g/edge :x :y))))) 31 | 32 | (testing "edge val merge" 33 | (is (= 3 (-> (g/graph) (g/link :x :y 1) (g/link :x :y 2 +) 34 | (g/edge :x :y)))))) 35 | 36 | (deftest edges-test 37 | (is (= #{{:from :x, :to :y, :value 1} 38 | {:from :y, :to :z, :value 2}} 39 | (-> (g/graph) 40 | (g/link :x :y 1) 41 | (g/link :y :z 2) 42 | g/edges 43 | (->> (map datafy)) 44 | set)))) 45 | 46 | (deftest unlink-test 47 | (is (= {:x #{}, :y #{}} 48 | (-> (g/graph) (g/link :x :y) (g/unlink :y :x) datafy)))) 49 | 50 | (deftest vertices-test 51 | (is (= #{:x :y} 52 | (-> (g/graph) (g/link :x :y) g/vertices datafy)))) 53 | 54 | (deftest in-test 55 | (is (= #{:x} 56 | (-> (g/digraph) (g/link :x :y) (g/in :y) datafy)))) 57 | 58 | (deftest out-test 59 | (is (= #{:y} 60 | (-> (g/digraph) (g/link :x :y) (g/out :x) datafy)))) 61 | 62 | (deftest remove-test 63 | (is (= #{:x} 64 | (-> (g/digraph) (g/link :x :y) (g/remove :y) g/vertices datafy)))) 65 | 66 | (deftest map-edges-test 67 | (is (= 2 68 | (-> (g/graph) 69 | (g/link :x :y 1) 70 | (g/map-edges (comp inc g/edge-value)) 71 | (g/edge :x :y))))) 72 | 73 | (deftest index-of-test 74 | (let [g (-> (g/graph) (g/add-vertex :x))] 75 | (is (= 0 (g/index-of g :x))) 76 | (is (= nil (g/index-of g :y))))) 77 | 78 | (deftest select-test 79 | (is (= (-> (g/graph) 80 | (g/link :y :z) 81 | datafy) 82 | (-> (g/graph) 83 | (g/link :x :y) 84 | (g/link :y :z) 85 | (g/select (s/from #{:y :z})) 86 | datafy)))) 87 | 88 | (deftest transpose-test 89 | (is (= {:y #{:x} :x #{}} 90 | (-> (g/digraph) 91 | (g/link :x :y) 92 | g/transpose 93 | datafy)))) 94 | 95 | (deftest directed-test 96 | (is (g/directed? (g/digraph))) 97 | (is (g/directed? (g/directed-acyclic-graph)))) 98 | 99 | (deftest top-bottom-test 100 | (let [g (-> (g/directed-acyclic-graph) 101 | (g/link :a1 :b) 102 | (g/link :a2 :b) 103 | (g/link :b :c1) 104 | (g/link :b :c2) 105 | (g/link :a3 :c3))] 106 | (testing "top" 107 | (is (= #{:a1 :a2 :a3} (datafy (g/top g))))) 108 | (testing "bottom" 109 | ; Is the presence of b here a bug, or expected behavior? 110 | (is (= #{:b :c1 :c2 :c3} (datafy (g/bottom g))))))) 111 | 112 | (deftest merge-test 113 | (let [g (g/merge (-> (g/digraph) (g/link :x :y 1)) 114 | (-> (g/digraph) 115 | (g/link :x :y 2) 116 | (g/link :x :q 2) 117 | (g/link :y :z 2)) 118 | (fn merge [a b] 119 | ;(prn :merge a b) 120 | (+ a b)))] 121 | (is (= {:x #{:y :q} 122 | :y #{:z} 123 | :z #{} 124 | :q #{}} 125 | (datafy g))) 126 | (is (= 3 (g/edge g :x :y))) 127 | (is (= 2 (g/edge g :y :z))))) 128 | 129 | (deftest shortest-path-test 130 | (let [g (-> (g/graph) 131 | (g/link :a :b1 1) 132 | (g/link :a :b2 1) 133 | (g/link :b1 :c 1) 134 | (g/link :b2 :c 2))] 135 | (is (= [:a :b1 :c] 136 | (datafy (g/shortest-path g :a #{:c} g/edge-value)))) 137 | (is (= [:a :b1 :c] 138 | (datafy (g/shortest-path-from-any g [:a] #{:c} g/edge-value)))))) 139 | 140 | (deftest connected-components-test 141 | (let [g (-> (g/graph) 142 | (g/link :a :b) 143 | (g/link :b :c) 144 | (g/link :d :e))] 145 | (is (= #{#{:a :b :c} #{:d :e}} 146 | (datafy (g/connected-components g)))))) 147 | 148 | (deftest biconnected-components-test 149 | (let [g (-> (g/graph) 150 | (g/link :a :b) 151 | (g/link :a :c) 152 | (g/link :b :c) 153 | (g/link :d :e) 154 | (g/link :e :f))] 155 | (is (= #{#{:a :b :c} #{:d :e} #{:e :f}} 156 | (datafy (g/biconnected-components g)))))) 157 | 158 | (deftest articulation-points 159 | (let [g (-> (g/graph) 160 | (g/link :a :b) 161 | (g/link :b :c))] 162 | (is (= #{:b} (datafy (g/articulation-points g)))))) 163 | 164 | (let [g (-> (g/digraph) 165 | (g/link :a :b) 166 | (g/link :b :c) 167 | (g/link :c :a) 168 | (g/link :d :e))] 169 | (deftest strongly-connected-components-test 170 | (is (= #{#{:a :b :c}} (datafy (g/strongly-connected-components g))))) 171 | 172 | (deftest strongly-connected-subgraphs 173 | (is (= [{:a #{:b} :b #{:c} :c #{:a}}] 174 | (datafy (g/strongly-connected-subgraphs g))))) 175 | 176 | (deftest cycles-test 177 | (is (= [[:b :c :a :b]] (datafy (g/cycles g))))) 178 | 179 | (deftest bfs-vertices-test 180 | (is (= [:a :b :c] 181 | (seq (g/bfs-vertices :a (partial g/out g)))))) 182 | 183 | (deftest bfs-vertices-from-any-test 184 | (is (= [:a :b :c] 185 | (seq (g/bfs-vertices-from-any [:a] (partial g/out g))))))) 186 | -------------------------------------------------------------------------------- /test/bifurcan_clj/int_map_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.int-map-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [int-map :as i] 7 | [map :as m] 8 | [set :as s]])) 9 | 10 | (deftest int-map-test 11 | (testing "empty" 12 | (is (= 0 (b/size (i/int-map)))))) 13 | 14 | (deftest from-test 15 | (testing "nil" 16 | (is (= (i/int-map) (i/from nil)))) 17 | (testing "clojure map" 18 | (is (= {1 :x, 2 :y} (datafy (i/from {1 :x, 2 :y}))))) 19 | (testing "vector of pairs" 20 | (is (= {1 :x, 2 :y} (datafy (i/from [[1 :x] [2 :y]]))))) 21 | (testing "lazy seq of pairs" 22 | (is (= {1 :x, 2 :y} (datafy (i/from (map vector [1 2] [:x :y])))))) 23 | (testing "map" 24 | (is (= {1 :x, 2 :y} (datafy (i/from (m/from {1 :x, 2 :y}))))))) 25 | 26 | (deftest get-test 27 | (let [m (i/from {1 :x})] 28 | (is (= :x (i/get m 1))) 29 | (is (= nil (i/get m 2))) 30 | (is (= :x (i/get m 1 :not-found))) 31 | (is (= :not-found (i/get m 3 :not-found))))) 32 | 33 | (deftest get-or-create-test 34 | (let [m (i/from {1 :x})] 35 | (is (= :default (m/get-or-create m 2 (fn [] :default)))))) 36 | 37 | (deftest contains?-test 38 | (is (i/contains? (i/from {1 :x}) 1))) 39 | 40 | (deftest entries-test 41 | (is (= [[1 :x]] (datafy (m/entries (i/from {1 :x})))))) 42 | 43 | (let [m (i/from {1 :x, 3 :y})] 44 | (deftest index-of-test 45 | (is (= nil (i/index-of m 2))) 46 | (is (= 0 (i/index-of m 1)))) 47 | 48 | (deftest floor-index-test 49 | (is (= nil (i/floor-index m 0))) 50 | (is (= 0 (i/floor-index m 1))) 51 | (is (= 0 (i/floor-index m 2))) 52 | (is (= 1 (i/floor-index m 3))) 53 | (is (= 1 (i/floor-index m 4)))) 54 | 55 | ;(deftest inclusive-floor-index-test 56 | ; (is (= nil (i/inclusive-floor-index m 0))) 57 | ; (is (= 0 (i/inclusive-floor-index m 1))) 58 | ; (is (= 0 (i/inclusive-floor-index m 2))) 59 | ; (is (= 1 (i/inclusive-floor-index m 3))) 60 | ; (is (= 1 (i/inclusive-floor-index m 4)))) 61 | 62 | (deftest ceil-index-test 63 | (is (= 0 (i/ceil-index m 0))) 64 | (is (= 0 (i/ceil-index m 1))) 65 | (is (= 1 (i/ceil-index m 2))) 66 | (is (= 1 (i/ceil-index m 3))) 67 | (is (= nil (i/ceil-index m 4))))) 68 | 69 | (deftest keys-test 70 | (is (= #{1 2} (datafy (m/keys (i/from {1 :x, 2 :y})))))) 71 | 72 | (deftest values-test 73 | (is (= [:x :y] (datafy (m/values (i/from {1 :x, 2 :y})))))) 74 | 75 | (deftest ^:buggy map-values-test 76 | (let [m (i/from {1 10, 3 30})] 77 | (testing "IMap" 78 | ; This fails due to a bug I think 79 | (is (= {1 11 3 31} (datafy (m/map-values m (fn [k v] 80 | (prn :k k :v v) 81 | (inc v))))))))) 82 | 83 | (deftest contains-all?-test 84 | (testing "map" 85 | (is (= true (m/contains-all? (i/from {1 :x, 2 :y}) 86 | (m/from {1 :z}))))) 87 | (testing "set" 88 | (is (= true (m/contains-all? (i/from {1 :x, 2 :y}) 89 | (s/from #{1})))))) 90 | 91 | (deftest contains-any?-test 92 | (testing "map" 93 | (is (= true (m/contains-any? (i/from {1 :x, 2 :y}) 94 | (m/from {1 :z, 3 :q}))))) 95 | (testing "set" 96 | (is (= true (m/contains-any? (i/from {1 :x, 2 :y}) 97 | (s/from #{1 3})))))) 98 | 99 | (deftest merge-test 100 | (testing "lww" 101 | (is (= {1 :x 2 :y 3 :z} 102 | (datafy (m/merge (i/from {1 :x 2 :x}) 103 | (i/from {2 :y 3 :z})))))) 104 | 105 | (testing "merge-fn" 106 | (is (= {1 1, 2 3, 3 3} 107 | (datafy (m/merge (i/from {1 1, 2 1}) 108 | (i/from {2 2, 3 3}) 109 | +)))))) 110 | 111 | (deftest difference-test 112 | (testing "map" 113 | (is (= {1 1} 114 | (datafy (m/difference (i/from {1 1 2 1}) 115 | (i/from {2 2 3 2})))))) 116 | (testing "set" 117 | (is (= {1 1} 118 | (datafy (m/difference (i/from {1 1 2 1}) 119 | (s/from #{2 3}))))))) 120 | 121 | (deftest difference-test 122 | (is (= {1 1 2 2 3 2} 123 | (datafy (m/union (i/from {1 1 2 1}) 124 | (i/from {2 2 3 2})))))) 125 | 126 | (deftest intersection-test 127 | (testing "map" 128 | (is (= {2 1} 129 | (datafy (m/intersection (i/from {1 1 2 1}) 130 | (i/from {2 2 3 2})))))) 131 | (testing "set" 132 | (is (= {2 1} 133 | (datafy (m/intersection (i/from {1 1 2 1}) 134 | (s/from #{2 3}))))))) 135 | 136 | (deftest put-test 137 | (is (= {1 2} (-> (m/map) 138 | (m/put 1 1) 139 | (m/put 1 2) 140 | datafy)))) 141 | 142 | (deftest update-test 143 | (is (= {1 2} (-> (i/from {1 1}) (i/update 1 inc) datafy)))) 144 | 145 | (deftest remove-test 146 | (is (= {1 1} (-> {1 1 2 2} i/from (i/remove 2) datafy)))) 147 | 148 | (deftest slice-test 149 | (let [m (i/from {1 :a, 2 :b, 3 :c, 4 :d})] 150 | (testing "empty" 151 | (is (= {} (datafy (i/slice m 0 0))))) 152 | (testing "single" 153 | (is (= {1 :a} (datafy (i/slice m 1 1)))) 154 | (is (= {4 :d} (datafy (i/slice m 4 4))))) 155 | (testing "middle" 156 | (is (= {2 :b, 3 :c} (datafy (i/slice m 2 3))))) 157 | (testing "over the edge" 158 | (is (= {1 :a, 2 :b} (datafy (i/slice m 0 2)))) 159 | (is (= {3 :c, 4 :d} (datafy (i/slice m 3 8))))))) 160 | -------------------------------------------------------------------------------- /test/bifurcan_clj/linear_list_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.linear-list-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is]]] 5 | [bifurcan-clj [core :as b] 6 | [list :as bl] 7 | [linear-list :as bll]])) 8 | 9 | (deftest empty-test 10 | (is (b/linear? (bll/linear-list))) 11 | (is (= 0 (b/size (bll/linear-list)))) 12 | (is (= [] (datafy (bll/linear-list))))) 13 | 14 | (deftest linear-list-test 15 | (is (= [] (datafy (bll/linear-list 3))))) 16 | 17 | (deftest from-test 18 | (is (= (bll/linear-list) (bll/from nil))) 19 | (is (= [1 2 3] (datafy (bll/from [1 2 3])))) 20 | (is (= [2 3 4] (datafy (bll/from (map inc [1 2 3]))))) 21 | (is (= [1 2 3] (datafy (bll/from (.iterator [1 2 3])))))) 22 | 23 | (deftest clear-test 24 | (let [l (-> (bll/from [1 2 3]) 25 | (bll/clear))] 26 | (is (= [] (datafy l))))) 27 | 28 | (deftest pop-first-test 29 | (let [l (bll/from [1 2 3])] 30 | (is (= 1 (bll/pop-first l))) 31 | (is (= [2 3] (datafy l))) 32 | (is (= 2 (bll/pop-first l))) 33 | (is (= [3] (datafy l))))) 34 | 35 | (deftest pop-last-test 36 | (let [l (bll/from [1 2 3])] 37 | (is (= 3 (bll/pop-last l))) 38 | (is (= [1 2] (datafy l))) 39 | (is (= 2 (bll/pop-last l))) 40 | (is (= [1] (datafy l))))) 41 | -------------------------------------------------------------------------------- /test/bifurcan_clj/list_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.list-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is]]] 5 | [bifurcan-clj [core :as b] 6 | [list :as bl]])) 7 | 8 | (deftest empty-test 9 | (is (not (b/linear? bl/empty))) 10 | (is (= 0 (b/size bl/empty))) 11 | (is (= [] (datafy bl/empty)))) 12 | 13 | (deftest list-test 14 | (is (= [1 2 3] (datafy (bl/list 1 2 3))))) 15 | 16 | (deftest from-test 17 | (is (= bl/empty (bl/from nil))) 18 | (is (= [1 2 3] (datafy (bl/from [1 2 3])))) 19 | (is (= [2 3 4] (datafy (bl/from (map inc [1 2 3]))))) 20 | (is (= [1 2 3] (datafy (bl/from (.iterator [1 2 3]))))) 21 | (let [ary (into-array [1 2 3])] 22 | (is (= [1 2 3] (datafy (bl/from-array ary)))))) 23 | 24 | (deftest add-first-last-test 25 | (is (= [1 2 3 4] 26 | (-> bl/empty 27 | (bl/add-first 2) 28 | (bl/add-last 3) 29 | (bl/add-first 1) 30 | (bl/add-last 4) 31 | datafy)))) 32 | 33 | (deftest set-test 34 | (is (= [1] (-> bl/empty (bl/set 0 1) datafy))) 35 | (is (= [1 :x 3] (-> (bl/list 1 2 3) (bl/set 1 :x) datafy)))) 36 | 37 | (deftest nested-test 38 | (is (= [[1 2] [3 4]] 39 | (datafy 40 | (bl/list 41 | (bl/list 1 2) 42 | (bl/list 3 4)))))) 43 | 44 | (deftest slice-test 45 | (is (= [2 3] 46 | (datafy (bl/slice (bl/list 1 2 3 4) 1 3))))) 47 | 48 | (deftest first-test 49 | (is (= 1 (bl/first (bl/list 1 2 3))))) 50 | 51 | (deftest last-test 52 | (is (= 3 (bl/last (bl/list 1 2 3))))) 53 | (deftest concat-test 54 | (is (= [1 2 3] (datafy (bl/concat (bl/list 1) (bl/list 2 3)))))) 55 | 56 | (deftest concat-all-test 57 | (is (= [1 2 3 4] (datafy (bl/concat-all 58 | [(bl/list 1) 59 | (bl/list 2 3) 60 | (bl/list 4)]))))) 61 | 62 | (deftest sort-test 63 | (is (= [1 2 3] (datafy (bl/sort (bl/list 3 2 1)))))) 64 | 65 | (deftest reverse-test 66 | (is (= [3 2 1] (datafy (bl/reverse (bl/list 1 2 3)))))) 67 | -------------------------------------------------------------------------------- /test/bifurcan_clj/map_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.map-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [map :as bm] 7 | [set :as bs]])) 8 | 9 | (deftest empty-test 10 | (is (not (b/linear? bm/empty))) 11 | (is (= 0 (b/size bm/empty))) 12 | (is (= {} (datafy bm/empty)))) 13 | 14 | (deftest map-test 15 | (testing "empty" 16 | (is (= bm/empty (bm/map)))) 17 | (testing "custom equality/hashing" 18 | (is (= {:x 3} 19 | (-> (bm/map (fn [x] (hash (name x))) 20 | (fn [x y] (= (name x) (name y)))) 21 | (bm/put :x 1) 22 | (bm/put "x" 2 +) 23 | datafy))))) 24 | 25 | (deftest from-test 26 | (testing "nil" 27 | (is (= bm/empty (bm/from nil)))) 28 | (testing "clojure map" 29 | (is (= {:x 1, :y 2} (datafy (bm/from {:x 1, :y 2}))))) 30 | (testing "seq of pairs" 31 | (is (= {:x 1, :y 2} (datafy (bm/from (map identity [[:x 1] [:y 2]])))))) 32 | (testing "vec of pairs" 33 | (is (= {:x 1, :y 2} (datafy (bm/from [[:x 1] [:y 2]]))))) 34 | (testing "map" 35 | (is (= {:x 1, :y 2} (datafy (bm/from (bm/from {:x 1, :y 2}))))))) 36 | 37 | (deftest get-test 38 | (let [m (bm/from {:x 1})] 39 | (is (= 1 (bm/get m :x))) 40 | (is (= nil (bm/get m :y))) 41 | (is (= 1 (bm/get m :x :not-found))) 42 | (is (= :not-found (bm/get m :y :not-found))))) 43 | 44 | (deftest get-or-create-test 45 | (let [m (bm/from {:x 1})] 46 | (is (= :default (bm/get-or-create m :y (fn [] :default)))))) 47 | 48 | (deftest contains?-test 49 | (is (bm/contains? (bm/from {:x 1}) :x))) 50 | 51 | (deftest entries-test 52 | (is (= [[:x 1]] (datafy (bm/entries (bm/from {:x 1})))))) 53 | 54 | (deftest index-of-test 55 | (is (= nil (bm/index-of bm/empty :y))) 56 | (is (= 0 (bm/index-of (bm/from {:x 1}) :x)))) 57 | 58 | (deftest keys-test 59 | (is (= #{:x :y} (datafy (bm/keys (bm/from {:x 1 :y 2})))))) 60 | 61 | (deftest values-test 62 | (is (= [1 2] (sort (datafy (bm/values (bm/from {:x 1 :y 2}))))))) 63 | 64 | (deftest map-values-test 65 | (is (= {:x 2 :y 3} (datafy (bm/map-values (bm/from {:x 1 :y 2}) 66 | (fn [k v] (inc v))))))) 67 | 68 | (deftest contains-all?-test 69 | (testing "map" 70 | (is (= true (bm/contains-all? (bm/from {:x 1 :y 2}) 71 | (bm/from {:x 3}))))) 72 | (testing "set" 73 | (is (= true (bm/contains-all? (bm/from {:x 1 :y 2}) 74 | (bs/from #{:x})))))) 75 | 76 | (deftest contains-any?-test 77 | (testing "map" 78 | (is (= true (bm/contains-any? (bm/from {:x 1 :y 2}) 79 | (bm/from {:x 3 :z 4}))))) 80 | (testing "set" 81 | (is (= true (bm/contains-any? (bm/from {:x 1 :y 2}) 82 | (bs/from #{:x :z})))))) 83 | 84 | (deftest merge-test 85 | (testing "lww" 86 | (is (= {:x 1 :y 2 :z 3} 87 | (datafy (bm/merge (bm/from {:x 1 :y 1}) 88 | (bm/from {:y 2 :z 3})))))) 89 | 90 | (testing "merge-fn" 91 | (is (= {:x 1 :y 3 :z 3} 92 | (datafy (bm/merge (bm/from {:x 1 :y 1}) 93 | (bm/from {:y 2 :z 3}) 94 | +)))))) 95 | 96 | (deftest difference-test 97 | (testing "map" 98 | (is (= {:x 1} 99 | (datafy (bm/difference (bm/from {:x 1 :y 1}) 100 | (bm/from {:y 2 :z 2})))))) 101 | (testing "set" 102 | (is (= {:x 1} 103 | (datafy (bm/difference (bm/from {:x 1 :y 1}) 104 | (bs/from #{:y :z}))))))) 105 | 106 | (deftest difference-test 107 | (is (= {:x 1 :y 2 :z 2} 108 | (datafy (bm/union (bm/from {:x 1 :y 1}) 109 | (bm/from {:y 2 :z 2})))))) 110 | 111 | (deftest intersection-test 112 | (testing "map" 113 | (is (= {:y 1} 114 | (datafy (bm/intersection (bm/from {:x 1 :y 1}) 115 | (bm/from {:y 2 :z 2})))))) 116 | (testing "set" 117 | (is (= {:y 1} 118 | (datafy (bm/intersection (bm/from {:x 1 :y 1}) 119 | (bs/from #{:y :z}))))))) 120 | 121 | (deftest put-test 122 | (is (= {:x 2} (-> (bm/map) 123 | (bm/put :x 1) 124 | (bm/put :x 2) 125 | datafy)))) 126 | 127 | (deftest update-test 128 | (let [m (bm/from {:x 1})] 129 | (is (= {:x 2} (-> m (bm/update :x inc) datafy))) 130 | (is (= {:x 5} (-> m (bm/update :x + 4) datafy))))) 131 | 132 | (deftest remove-test 133 | (is (= {:x 1} (-> {:x 1 :y 2} bm/from (bm/remove :y) datafy)))) 134 | 135 | ; Sorted maps 136 | 137 | (let [m (bm/sorted-map-from {1 :x, 3 :y})] 138 | (deftest floor-index-test 139 | (is (= nil (bm/floor-index m 0))) 140 | (is (= 0 (bm/floor-index m 1))) 141 | (is (= 0 (bm/floor-index m 2))) 142 | (is (= 1 (bm/floor-index m 3))) 143 | (is (= 1 (bm/floor-index m 4)))) 144 | 145 | ;(deftest inclusive-floor-index-test 146 | ; (is (= nil (i/inclusive-floor-index m 0))) 147 | ; (is (= 0 (i/inclusive-floor-index m 1))) 148 | ; (is (= 0 (i/inclusive-floor-index m 2))) 149 | ; (is (= 1 (i/inclusive-floor-index m 3))) 150 | ; (is (= 1 (i/inclusive-floor-index m 4)))) 151 | 152 | (deftest ceil-index-test 153 | (is (= 0 (bm/ceil-index m 0))) 154 | (is (= 0 (bm/ceil-index m 1))) 155 | (is (= 1 (bm/ceil-index m 2))) 156 | (is (= 1 (bm/ceil-index m 3))) 157 | (is (= nil (bm/ceil-index m 4)))) 158 | 159 | (deftest floor-test 160 | (is (= nil (datafy (bm/floor m 0)))) 161 | (is (= [1 :x] (datafy (bm/floor m 1)))) 162 | (is (= [1 :x] (datafy (bm/floor m 2)))) 163 | (is (= [3 :y] (datafy (bm/floor m 3)))) 164 | (is (= [3 :y] (datafy (bm/floor m 4))))) 165 | 166 | (deftest ceil-test 167 | (is (= [1 :x] (datafy (bm/ceil m 0)))) 168 | (is (= [1 :x] (datafy (bm/ceil m 1)))) 169 | (is (= [3 :y] (datafy (bm/ceil m 2)))) 170 | (is (= [3 :y] (datafy (bm/ceil m 3)))) 171 | (is (= nil (datafy (bm/ceil m 4))))) 172 | 173 | (deftest ^:buggy slice-test 174 | (is (= {1 :x} (datafy (bm/slice m -1 1))))) 175 | 176 | ;(deftest slice-indices-test 177 | ; (is (= {3 :y} (datafy (bm/slice-indices m 1 4))))) 178 | 179 | (deftest first-test 180 | (is (= [1 :x] (datafy (bm/first m))))) 181 | 182 | (deftest last-test 183 | (is (= [3 :y] (datafy (bm/last m)))))) 184 | -------------------------------------------------------------------------------- /test/bifurcan_clj/set_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.set-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [list :as bl] 7 | [map :as bm] 8 | [set :as bs]])) 9 | 10 | (deftest empty-test 11 | (is (not (b/linear? bs/empty))) 12 | (is (= 0 (b/size bs/empty))) 13 | (is (= #{} (datafy bs/empty)))) 14 | 15 | (deftest set-test 16 | (testing "empty" 17 | (is (= bs/empty (bs/set)))) 18 | 19 | (testing "custom equality/hash" 20 | (is (= #{:x :y} 21 | (-> (bs/set #(hash (name %)) (fn [a b] (= (name a) (name b)))) 22 | (bs/add :x) 23 | (bs/add "x") 24 | (bs/add :y) 25 | (bs/add "y") 26 | datafy))))) 27 | 28 | (deftest from-test 29 | (testing "nil" 30 | (is (= (bs/set) (bs/from nil)))) 31 | (testing "list" 32 | (is (= #{1 2} (datafy (bs/from (bl/list 1 2)))))) 33 | (testing "iterator" 34 | (is (= #{1 2} (datafy (bs/from (.iterator [1 2])))))) 35 | (testing "vec" 36 | (is (= #{1 2} (datafy (bs/from [1 2]))))) 37 | (testing "lazy seq" 38 | (is (= #{1 2} (datafy (bs/from (map identity [1 2]))))))) 39 | 40 | (deftest contains?-test 41 | (is (bs/contains? (bs/from #{1 2}) 2))) 42 | 43 | (deftest elements-test 44 | (is (= [1 2 3] (-> #{1 2 3} bs/from bs/elements datafy)))) 45 | 46 | (deftest zip-test 47 | (is (= {:x "x" :y "y"} (-> #{:x :y} bs/from (bs/zip name) datafy)))) 48 | 49 | (deftest index-of-test 50 | ; Depends on hash order 51 | (is (= 0 (-> #{:x :y} bs/from (bs/index-of :y)))) 52 | (is (= nil (-> #{:x :y} bs/from (bs/index-of :z))))) 53 | 54 | (deftest contains-all?-test 55 | (testing "set" 56 | (is (-> #{:x :y} bs/from (bs/contains-all? (bs/from [:x]))))) 57 | (testing "map" 58 | (is (-> #{:x :y} bs/from (bs/contains-all? (bm/from {:x 1})))))) 59 | 60 | (deftest contains-any?-test 61 | (testing "set" 62 | (is (-> #{:x :y} bs/from (bs/contains-any? (bs/from [:x :z]))))) 63 | (testing "map" 64 | (is (-> #{:x :y} bs/from (bs/contains-any? (bm/from {:x 1 :z 2})))))) 65 | 66 | (deftest add-test 67 | (is (= #{:x :y} (-> #{:y} bs/from (bs/add :x) datafy)))) 68 | 69 | (deftest remove-test 70 | (is (= #{:x} (-> #{:x :y} bs/from (bs/remove :y) datafy)))) 71 | 72 | (deftest union-test 73 | (is (= #{:x :y :z} (datafy (bs/union (bs/from [:x :y]) (bs/from [:y :z])))))) 74 | 75 | (deftest difference-test 76 | (is (= #{:x} (datafy (bs/difference (bs/from [:x :y :z]) 77 | (bs/from [:y :z])))))) 78 | 79 | (deftest intersection-test 80 | (is (= #{:y} (datafy (bs/intersection (bs/from [:x :y]) (bs/from [:y :z])))))) 81 | -------------------------------------------------------------------------------- /test/bifurcan_clj/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns bifurcan-clj.util-test 2 | (:require [clojure [datafy :refer [datafy]] 3 | [pprint :refer [pprint]] 4 | [test :refer [deftest is testing]]] 5 | [bifurcan-clj [core :as b] 6 | [list :as l] 7 | [map :as m] 8 | [set :as s] 9 | [util :refer :all]])) 10 | 11 | (deftest iterator=-test 12 | (is (not (iterator= (.iterator [1 2 3]) (.iterator [1 2 4])))) 13 | (is (not (iterator= (.iterator [1 2 3]) (.iterator [1 2])))) 14 | (is (not (iterator= (.iterator [1 2]) (.iterator [1 2 3])))) 15 | (is (iterator= (.iterator [1 2 3]) (.iterator [1 2 3]))) 16 | (is (iterator= (.iterator []) (.iterator [])))) 17 | --------------------------------------------------------------------------------