├── .gitignore ├── .travis.yml ├── LICENSE ├── NOTICE ├── README.md ├── benchmarks ├── com.metamx.collections.bitmap.RangeBitmapBenchmarkTest.html ├── com.metamx.collections.bitmap.RangeBitmapBenchmarkTest.jsonp ├── com.metamx.collections.bitmap.UniformBitmapBenchmarkTest.html └── com.metamx.collections.bitmap.UniformBitmapBenchmarkTest.jsonp ├── pom.xml └── src ├── main └── java │ └── com │ └── metamx │ └── collections │ ├── IntegerSet.java │ ├── bitmap │ ├── BitSetBitmapFactory.java │ ├── BitmapFactory.java │ ├── ConciseBitmapFactory.java │ ├── ImmutableBitmap.java │ ├── MutableBitmap.java │ ├── RoaringBitmapFactory.java │ ├── WrappedBitSetBitmap.java │ ├── WrappedConciseBitmap.java │ ├── WrappedConciseIntIterator.java │ ├── WrappedImmutableBitSetBitmap.java │ ├── WrappedImmutableConciseBitmap.java │ ├── WrappedImmutableRoaringBitmap.java │ └── WrappedRoaringBitmap.java │ └── spatial │ ├── ImmutableNode.java │ ├── ImmutablePoint.java │ ├── ImmutableRTree.java │ ├── Node.java │ ├── Point.java │ ├── RTree.java │ ├── RTreeUtils.java │ ├── search │ ├── Bound.java │ ├── GutmanSearchStrategy.java │ ├── PolygonBound.java │ ├── RadiusBound.java │ ├── RectangularBound.java │ └── SearchStrategy.java │ └── split │ ├── GutmanSplitStrategy.java │ ├── LinearGutmanSplitStrategy.java │ ├── QuadraticGutmanSplitStrategy.java │ └── SplitStrategy.java └── test └── java └── com └── metamx ├── collections ├── IntSetTestUtility.java ├── TestIntegerSet.java ├── bitmap │ ├── BitmapBenchmark.java │ ├── ConciseBitmapFactoryTest.java │ ├── RangeBitmapBenchmarkTest.java │ ├── RoaringBitmapFactoryTest.java │ ├── UniformBitmapBenchmarkTest.java │ ├── WrappedBitSetBitmapBitSetTest.java │ └── WrappedRoaringBitmapTest.java └── spatial │ ├── ImmutableRTreeTest.java │ ├── RTreeTest.java │ ├── search │ ├── PolygonBoundTest.java │ ├── RadiusBoundTest.java │ └── RectangularBoundTest.java │ └── split │ └── LinearGutmanSplitStrategyTest.java └── test └── annotation ├── Benchmark.java └── Dummy.java /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | target 3 | *.iml 4 | *.ipr 5 | *.iws 6 | *.tar.gz 7 | *.swp 8 | *.swo 9 | .classpath 10 | .idea 11 | .project 12 | .settings/org.eclipse.jdt.core.prefs 13 | .settings/org.maven.ide.eclipse.prefs 14 | client/.settings/org.eclipse.jdt.core.prefs 15 | common/.settings/org.eclipse.jdt.core.prefs 16 | server/.settings/org.eclipse.jdt.core.prefs 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk7 5 | - oraclejdk8 6 | 7 | sudo: false 8 | 9 | cache: 10 | directories: 11 | - $HOME/.m2 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | bytebuffer-collections 2 | Copyright 2011-2015 Metamarkets Group Inc. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bytebuffer-collections 2 | ====================== 3 | 4 | ByteBuffer collection classes for java and jvm-based languages. 5 | 6 | # Benchmarks 7 | 8 | To run benchmarks, use the maven benchmark profile: 9 | ```sh 10 | mvn test -P benchmark 11 | ``` 12 | -------------------------------------------------------------------------------- /benchmarks/com.metamx.collections.bitmap.RangeBitmapBenchmarkTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Benchmark results for methods in class com.metamx.collections.bitmap.RangeBitmapBenchmarkTest 6 | 7 | 8 | 12 | 13 | 50 | 51 | 101 | 102 | 103 | 104 |
105 |

Benchmark results for methods in class com.metamx.collections.bitmap.RangeBitmapBenchmarkTest

106 | 107 |
108 |
109 | 110 |
111 | 112 | 113 |
114 |
115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /benchmarks/com.metamx.collections.bitmap.RangeBitmapBenchmarkTest.jsonp: -------------------------------------------------------------------------------- 1 | receiveJsonpData({ 2 | "cols": [ 3 | {"label": "Run", "type": "string"}, 4 | {"label": "Custom key", "type": "string"}, 5 | {"label": "Timestamp", "type": "string"}, 6 | {"label": "timeConciseUnion", "type": "string"} , 7 | {"label": "timeGenericConciseIntersection", "type": "string"} , 8 | {"label": "timeGenericConciseUnion", "type": "string"} , 9 | {"label": "timeGenericRoaringIntersection", "type": "string"} , 10 | {"label": "timeGenericRoaringUnion", "type": "string"} , 11 | {"label": "timeImmutableRoaringUnion", "type": "string"} , 12 | {"label": "timeOffheapConciseUnion", "type": "string"} , 13 | {"label": "timeOffheapRoaringUnion", "type": "string"} , 14 | {"label": "timeRoaringUnion", "type": "string"} ], 15 | "rows": [ 16 | {"c": [{"v": "5"}, {"v": "0.00001"}, {"v": "2014-11-04 14:03:04.268"}, {"v": 80.304}, {"v": 79.758}, {"v": 65.896}, {"v": 0.008}, {"v": 0.278}, {"v": 0.596}, {"v": 70.89}, {"v": 0.275}, {"v": 0.202}]}, 17 | {"c": [{"v": "1"}, {"v": "0.0001"}, {"v": "2014-11-04 13:32:21.752"}, {"v": 30.843}, {"v": 30.863}, {"v": 32.306}, {"v": 0.012}, {"v": 0.272}, {"v": 0.546}, {"v": 32.727}, {"v": 0.327}, {"v": 0.158}]}, 18 | {"c": [{"v": "2"}, {"v": "0.0010"}, {"v": "2014-11-04 13:41:55.608"}, {"v": 3.801}, {"v": 3.441}, {"v": 3.421}, {"v": 0.019}, {"v": 0.272}, {"v": 0.524}, {"v": 3.76}, {"v": 0.271}, {"v": 0.171}]}, 19 | {"c": [{"v": "3"}, {"v": "0.0100"}, {"v": "2014-11-04 13:45:36.077"}, {"v": 0.341}, {"v": 0.541}, {"v": 0.628}, {"v": 0.025}, {"v": 0.276}, {"v": 0.576}, {"v": 0.352}, {"v": 0.276}, {"v": 0.263}]}, 20 | {"c": [{"v": "4"}, {"v": "0.1000"}, {"v": "2014-11-04 13:49:27.509"}, {"v": 0.051}, {"v": 0.062}, {"v": 0.046}, {"v": 0.039}, {"v": 0.295}, {"v": 0.47}, {"v": 0.045}, {"v": 0.299}, {"v": 0.181}]}, 21 | {"c": [{"v": "38"}, {"v": "0.25000"}, {"v": "2014-11-04 15:22:42.446"}, {"v": 0.036}, {"v": 0.041}, {"v": 0.04}, {"v": 0.052}, {"v": 0.179}, {"v": 0.391}, {"v": 0.042}, {"v": 0.18}, {"v": 0.105}]} 22 | ]}); 23 | -------------------------------------------------------------------------------- /benchmarks/com.metamx.collections.bitmap.UniformBitmapBenchmarkTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Benchmark results for methods in class com.metamx.collections.bitmap.UniformBitmapBenchmarkTest 6 | 7 | 8 | 12 | 13 | 50 | 51 | 101 | 102 | 103 | 104 |
105 |

Benchmark results for methods in class com.metamx.collections.bitmap.UniformBitmapBenchmarkTest

106 | 107 |
108 |
109 | 110 |
111 | 112 | 113 |
114 |
115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /benchmarks/com.metamx.collections.bitmap.UniformBitmapBenchmarkTest.jsonp: -------------------------------------------------------------------------------- 1 | receiveJsonpData({ 2 | "cols": [ 3 | {"label": "Run", "type": "string"}, 4 | {"label": "Custom key", "type": "string"}, 5 | {"label": "Timestamp", "type": "string"}, 6 | {"label": "timeConciseUnion", "type": "string"} , 7 | {"label": "timeGenericConciseIntersection", "type": "string"} , 8 | {"label": "timeGenericConciseUnion", "type": "string"} , 9 | {"label": "timeGenericRoaringIntersection", "type": "string"} , 10 | {"label": "timeGenericRoaringUnion", "type": "string"} , 11 | {"label": "timeImmutableRoaringUnion", "type": "string"} , 12 | {"label": "timeOffheapConciseUnion", "type": "string"} , 13 | {"label": "timeOffheapRoaringUnion", "type": "string"} , 14 | {"label": "timeRoaringUnion", "type": "string"} ], 15 | "rows": [ 16 | {"c": [{"v": "6"}, {"v": "0.0001"}, {"v": "2014-11-04 11:41:24.142"}, {"v": 1.099}, {"v": 0.401}, {"v": 1.391}, {"v": 0.02}, {"v": 0.131}, {"v": 0.104}, {"v": 1.171}, {"v": 0.132}, {"v": 0.091}]}, 17 | {"c": [{"v": "5"}, {"v": "0.0010"}, {"v": "2014-11-04 11:37:12.305"}, {"v": 6.989}, {"v": 0.595}, {"v": 7.4}, {"v": 0.026}, {"v": 0.144}, {"v": 0.098}, {"v": 7.95}, {"v": 0.139}, {"v": 0.066}]}, 18 | {"c": [{"v": "4"}, {"v": "0.0100"}, {"v": "2014-11-04 11:23:42.26"}, {"v": 50.259}, {"v": 4.768}, {"v": 51.716}, {"v": 0.053}, {"v": 0.563}, {"v": 0.223}, {"v": 54.117}, {"v": 0.59}, {"v": 0.175}]}, 19 | {"c": [{"v": "8"}, {"v": "0.1000"}, {"v": "2014-11-04 12:19:56.926"}, {"v": 64.505}, {"v": 23.741}, {"v": 63.565}, {"v": 0.031}, {"v": 0.353}, {"v": 0.528}, {"v": 59.636}, {"v": 0.352}, {"v": 0.155}]}, 20 | {"c": [{"v": "7"}, {"v": "0.2500"}, {"v": "2014-11-04 11:52:42.488"}, {"v": 67.57}, {"v": 64.276}, {"v": 60.747}, {"v": 0.021}, {"v": 0.275}, {"v": 0.523}, {"v": 69.835}, {"v": 0.251}, {"v": 0.178}]}, 21 | {"c": [{"v": "2"}, {"v": "0.5000"}, {"v": "2014-11-04 10:19:33.921"}, {"v": 66.058}, {"v": 67.714}, {"v": 64.162}, {"v": 0.026}, {"v": 0.264}, {"v": 0.541}, {"v": 66.445}, {"v": 0.281}, {"v": 0.168}]}, 22 | {"c": [{"v": "3"}, {"v": "0.7500"}, {"v": "2014-11-04 10:44:45.546"}, {"v": 65.028}, {"v": 70.115}, {"v": 63.475}, {"v": 0.027}, {"v": 0.284}, {"v": 0.574}, {"v": 68.909}, {"v": 0.295}, {"v": 0.195}]} 23 | ]}); 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 4.0.0 20 | 21 | 22 | com.metamx 23 | oss-parent 24 | 2 25 | 26 | 27 | bytebuffer-collections 28 | 0.3.3-SNAPSHOT 29 | 30 | ${project.groupId}:${project.artifactId} 31 | ByteBuffer Collections 32 | https://github.com/metamx/bytebuffer-collections 33 | 34 | 35 | 36 | Apache License, Version 2.0 37 | http://www.apache.org/licenses/LICENSE-2.0 38 | 39 | 40 | 41 | 42 | scm:git:ssh://git@github.com/metamx/bytebuffer-collections.git 43 | scm:git:ssh://git@github.com/metamx/bytebuffer-collections.git 44 | https://github.com/metamx/bytebuffer-collections.git 45 | HEAD 46 | 47 | 48 | 49 | 50 | Metamarkets Open Source Team 51 | oss@metamarkets.com 52 | Metamarkets Group Inc. 53 | https://www.metamarkets.com 54 | 55 | 56 | 57 | 58 | 59 | com.metamx 60 | extendedset 61 | 1.4.2 62 | 63 | 64 | com.google.guava 65 | guava 66 | 16.0.1 67 | 68 | 69 | com.fasterxml.jackson.core 70 | jackson-annotations 71 | 2.4.6 72 | 73 | 74 | com.fasterxml.jackson.core 75 | jackson-core 76 | 2.4.6 77 | 78 | 79 | com.fasterxml.jackson.core 80 | jackson-databind 81 | 2.4.6 82 | 83 | 84 | org.roaringbitmap 85 | RoaringBitmap 86 | 0.5.18 87 | 88 | 89 | 90 | 91 | junit 92 | junit 93 | 4.12 94 | test 95 | 96 | 97 | org.easymock 98 | easymock 99 | 3.0 100 | test 101 | 102 | 103 | com.carrotsearch 104 | junit-benchmarks 105 | 0.7.2 106 | test 107 | 108 | 109 | com.h2database 110 | h2 111 | 1.4.182 112 | test 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-compiler-plugin 121 | 2.5.1 122 | 123 | 1.7 124 | 1.7 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-jar-plugin 130 | 2.4 131 | 132 | 133 | 134 | test-jar 135 | 136 | 137 | 138 | 139 | 140 | org.apache.maven.plugins 141 | maven-release-plugin 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-surefire-plugin 146 | 2.18.1 147 | 148 | com.metamx.test.annotation.Benchmark 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | benchmark 157 | 158 | 159 | 160 | maven-surefire-plugin 161 | 162 | -server -Xms3G -Xmx3G -Djub.consumers=CONSOLE,H2 -Djub.db.file=benchmarks/benchmarks 163 | com.metamx.test.annotation.Benchmark 164 | com.metamx.test.annotation.Dummy 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/IntegerSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections; 18 | 19 | import com.google.common.collect.Sets; 20 | import com.metamx.collections.bitmap.MutableBitmap; 21 | import org.roaringbitmap.IntIterator; 22 | 23 | import java.util.Collection; 24 | import java.util.Iterator; 25 | import java.util.Set; 26 | 27 | /** 28 | * 29 | */ 30 | public class IntegerSet implements Set 31 | { 32 | private final MutableBitmap mutableBitmap; 33 | 34 | private IntegerSet(MutableBitmap mutableBitmap) 35 | { 36 | this.mutableBitmap = mutableBitmap; 37 | } 38 | 39 | public static IntegerSet wrap(MutableBitmap mutableBitmap) 40 | { 41 | return new IntegerSet(mutableBitmap); 42 | } 43 | 44 | @Override 45 | public int size() 46 | { 47 | return this.mutableBitmap.size(); 48 | } 49 | 50 | @Override 51 | public boolean isEmpty() 52 | { 53 | return this.mutableBitmap.isEmpty(); 54 | } 55 | 56 | 57 | public static class BitSetIterator implements Iterator 58 | { 59 | private final IntIterator intIt; 60 | private final MutableBitmap bitSet; 61 | private Integer prior = null; 62 | 63 | public BitSetIterator(MutableBitmap bitSet) 64 | { 65 | this.intIt = bitSet.iterator(); 66 | this.bitSet = bitSet; 67 | } 68 | 69 | @Override 70 | public boolean hasNext() 71 | { 72 | return intIt.hasNext(); 73 | } 74 | 75 | @Override 76 | public Integer next() 77 | { 78 | prior = intIt.next(); 79 | return prior; 80 | } 81 | 82 | @Override 83 | public void remove() 84 | { 85 | bitSet.remove(prior); 86 | } 87 | } 88 | 89 | @Override 90 | public boolean contains(Object o) 91 | { 92 | if (o instanceof Integer) { 93 | return mutableBitmap.get((Integer) o); 94 | } else if (o instanceof Long) { 95 | return this.contains(((Long) o).intValue()); 96 | } 97 | return false; 98 | } 99 | 100 | @Override 101 | public Iterator iterator() 102 | { 103 | return new BitSetIterator(mutableBitmap); 104 | } 105 | 106 | @Override 107 | public Object[] toArray() 108 | { 109 | Integer[] retval = new Integer[mutableBitmap.size()]; 110 | int pos = 0; 111 | for (Integer i : this) { 112 | retval[pos++] = i; 113 | } 114 | return retval; 115 | } 116 | 117 | @Override 118 | public T[] toArray(T[] a) 119 | { 120 | return Sets.newHashSet(this).toArray(a); 121 | } 122 | 123 | @Override 124 | public boolean add(Integer integer) 125 | { 126 | if (null == integer) { 127 | throw new NullPointerException("BitSet cannot contain null values"); 128 | } 129 | if (integer < 0) { 130 | throw new IllegalArgumentException("Only positive integers or zero can be added"); 131 | } 132 | boolean isSet = mutableBitmap.get(integer); 133 | mutableBitmap.add(integer.intValue()); 134 | return !isSet; 135 | } 136 | 137 | @Override 138 | public boolean remove(Object o) 139 | { 140 | if (o == null) { 141 | throw new NullPointerException("BitSet cannot contain null values"); 142 | } 143 | if (o instanceof Integer) { 144 | Integer integer = (Integer) o; 145 | boolean isSet = mutableBitmap.get(integer); 146 | mutableBitmap.remove(integer); 147 | return isSet; 148 | } else { 149 | throw new ClassCastException("Cannot remove non Integer from integer BitSet"); 150 | } 151 | } 152 | 153 | @Override 154 | public boolean containsAll(Collection c) 155 | { 156 | Iterator it = c.iterator(); 157 | while (it.hasNext()) { 158 | if (!this.contains(it.next())) { 159 | return false; 160 | } 161 | } 162 | return true; 163 | } 164 | 165 | @Override 166 | public boolean addAll(Collection c) 167 | { 168 | boolean setChanged = false; 169 | for (Integer i : c) { 170 | if (!this.contains(i)) { 171 | setChanged = true; 172 | this.add(i); 173 | } 174 | } 175 | return setChanged; 176 | } 177 | 178 | @Override 179 | public boolean retainAll(Collection c) 180 | { 181 | // Stub 182 | throw new UnsupportedOperationException("Cannot retainAll ona an IntegerSet"); 183 | } 184 | 185 | @Override 186 | public boolean removeAll(Collection c) 187 | { 188 | Iterator it = c.iterator(); 189 | boolean changed = false; 190 | while (it.hasNext()) { 191 | Integer val = (Integer) it.next(); 192 | changed = remove(val) || changed; 193 | } 194 | return changed; 195 | } 196 | 197 | @Override 198 | public void clear() 199 | { 200 | mutableBitmap.clear(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/BitSetBitmapFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.util.BitSet; 21 | 22 | /** 23 | * BitSetBitmapFactory implements BitmapFactory as a wrapper for java.util.BitSet 24 | */ 25 | public class BitSetBitmapFactory implements BitmapFactory 26 | { 27 | @Override 28 | public MutableBitmap makeEmptyMutableBitmap() 29 | { 30 | return new WrappedBitSetBitmap(); 31 | } 32 | 33 | @Override 34 | public ImmutableBitmap makeEmptyImmutableBitmap() 35 | { 36 | return makeEmptyMutableBitmap(); 37 | } 38 | 39 | @Override 40 | public ImmutableBitmap makeImmutableBitmap(MutableBitmap mutableBitmap) 41 | { 42 | return mutableBitmap; 43 | } 44 | 45 | @Override 46 | public ImmutableBitmap mapImmutableBitmap(ByteBuffer b) 47 | { 48 | return new WrappedBitSetBitmap(BitSet.valueOf(b.array())); 49 | } 50 | 51 | @Override 52 | public ImmutableBitmap union(Iterable b) 53 | { 54 | WrappedBitSetBitmap newSet = null; 55 | for (ImmutableBitmap bm : b) { 56 | if (null == newSet) { 57 | newSet = new WrappedBitSetBitmap(((WrappedBitSetBitmap) bm).cloneBitSet()); 58 | } else { 59 | newSet.union(bm); 60 | } 61 | } 62 | return newSet; 63 | } 64 | 65 | @Override 66 | public ImmutableBitmap intersection(Iterable b) 67 | { 68 | 69 | WrappedBitSetBitmap newSet = null; 70 | for (ImmutableBitmap bm : b) { 71 | if (null == newSet) { 72 | newSet = new WrappedBitSetBitmap(((WrappedBitSetBitmap) bm).cloneBitSet()); 73 | } else { 74 | newSet.intersection(bm); 75 | } 76 | } 77 | return newSet; 78 | } 79 | 80 | @Override 81 | public ImmutableBitmap complement(ImmutableBitmap b) 82 | { 83 | BitSet bitSet = ((WrappedBitSetBitmap) b).cloneBitSet(); 84 | bitSet.flip(0, bitSet.size()); 85 | return new WrappedBitSetBitmap(bitSet); 86 | } 87 | 88 | @Override 89 | public ImmutableBitmap complement( 90 | ImmutableBitmap b, int length 91 | ) 92 | { 93 | return null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/BitmapFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | public interface BitmapFactory 22 | { 23 | /** 24 | * Create a new empty bitmap 25 | * 26 | * @return the new bitmap 27 | */ 28 | public MutableBitmap makeEmptyMutableBitmap(); 29 | 30 | public ImmutableBitmap makeEmptyImmutableBitmap(); 31 | 32 | public ImmutableBitmap makeImmutableBitmap(MutableBitmap mutableBitmap); 33 | 34 | /** 35 | * Given a ByteBuffer pointing at a serialized version of a bitmap, 36 | * instantiate an immutable mapped bitmap. 37 | * 38 | * When using RoaringBitmap (with the RoaringBitmapFactory class), it is not 39 | * necessary for b.limit() to indicate the end of the serialized content 40 | * whereas it is critical to set b.limit() appropriately with ConciseSet (with 41 | * the ConciseBitmapFactory). 42 | * 43 | * @param b the input byte buffer 44 | * 45 | * @return the new bitmap 46 | */ 47 | public ImmutableBitmap mapImmutableBitmap(ByteBuffer b); 48 | 49 | /** 50 | * Compute the union (bitwise-OR) of a set of bitmaps. They are assumed to be 51 | * instances of of the proper WrappedConciseBitmap otherwise a ClassCastException 52 | * is thrown. 53 | * 54 | * @param b input ImmutableGenericBitmap objects 55 | * 56 | * @return the union. 57 | * 58 | * @throws ClassCastException if one of the ImmutableGenericBitmap objects if not an instance 59 | * of WrappedImmutableConciseBitmap 60 | */ 61 | public ImmutableBitmap union(Iterable b); 62 | 63 | /** 64 | * Compute the intersection (bitwise-AND) of a set of bitmaps. They are assumed to be 65 | * instances of of the proper WrappedConciseBitmap otherwise a ClassCastException 66 | * is thrown. 67 | * 68 | * @param b input ImmutableGenericBitmap objects 69 | * 70 | * @return the union. 71 | * 72 | * @throws ClassCastException if one of the ImmutableGenericBitmap objects if not an instance 73 | * of WrappedImmutableConciseBitmap 74 | */ 75 | public ImmutableBitmap intersection(Iterable b); 76 | 77 | public ImmutableBitmap complement(ImmutableBitmap b); 78 | 79 | public ImmutableBitmap complement(ImmutableBitmap b, int length); 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/ConciseBitmapFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.util.Iterator; 23 | 24 | /** 25 | * As the name suggests, this class instantiates bitmaps of the types 26 | * WrappedConciseBitmap and WrappedImmutableConciseBitmap. 27 | */ 28 | public class ConciseBitmapFactory implements BitmapFactory 29 | { 30 | private static final ImmutableConciseSet EMPTY_IMMUTABLE_BITMAP = new ImmutableConciseSet(); 31 | private static final WrappedImmutableConciseBitmap WRAPPED_IMMUTABLE_CONCISE_BITMAP = 32 | new WrappedImmutableConciseBitmap(EMPTY_IMMUTABLE_BITMAP); 33 | 34 | @Override 35 | public MutableBitmap makeEmptyMutableBitmap() 36 | { 37 | return new WrappedConciseBitmap(); 38 | } 39 | 40 | @Override 41 | public ImmutableBitmap makeEmptyImmutableBitmap() 42 | { 43 | return WRAPPED_IMMUTABLE_CONCISE_BITMAP; 44 | } 45 | 46 | @Override 47 | public ImmutableBitmap makeImmutableBitmap(MutableBitmap mutableBitmap) 48 | { 49 | if (!(mutableBitmap instanceof WrappedConciseBitmap)) { 50 | throw new IllegalStateException(String.format("Cannot convert [%s]", mutableBitmap.getClass())); 51 | } 52 | return new WrappedImmutableConciseBitmap( 53 | ImmutableConciseSet.newImmutableFromMutable( 54 | ((WrappedConciseBitmap) mutableBitmap).getBitmap() 55 | ) 56 | ); 57 | } 58 | 59 | @Override 60 | public ImmutableBitmap mapImmutableBitmap(ByteBuffer b) 61 | { 62 | return new WrappedImmutableConciseBitmap(b); 63 | } 64 | 65 | @Override 66 | public ImmutableBitmap union(Iterable b) 67 | throws ClassCastException 68 | { 69 | return new WrappedImmutableConciseBitmap(ImmutableConciseSet.union(unwrap(b))); 70 | } 71 | 72 | @Override 73 | public ImmutableBitmap intersection(Iterable b) 74 | throws ClassCastException 75 | { 76 | return new WrappedImmutableConciseBitmap(ImmutableConciseSet.intersection(unwrap(b))); 77 | } 78 | 79 | @Override 80 | public ImmutableBitmap complement(ImmutableBitmap b) 81 | { 82 | return new WrappedImmutableConciseBitmap(ImmutableConciseSet.complement(((WrappedImmutableConciseBitmap) b).getBitmap())); 83 | } 84 | 85 | @Override 86 | public ImmutableBitmap complement(ImmutableBitmap b, int length) 87 | { 88 | return new WrappedImmutableConciseBitmap( 89 | ImmutableConciseSet.complement( 90 | ((WrappedImmutableConciseBitmap) b).getBitmap(), 91 | length 92 | ) 93 | ); 94 | } 95 | 96 | private static Iterable unwrap( 97 | final Iterable b 98 | ) 99 | { 100 | return new Iterable() 101 | { 102 | @Override 103 | public Iterator iterator() 104 | { 105 | final Iterator i = b.iterator(); 106 | return new Iterator() 107 | { 108 | @Override 109 | public void remove() 110 | { 111 | throw new UnsupportedOperationException(); 112 | } 113 | 114 | @Override 115 | public boolean hasNext() 116 | { 117 | return i.hasNext(); 118 | } 119 | 120 | @Override 121 | public ImmutableConciseSet next() 122 | { 123 | final WrappedImmutableConciseBitmap wrappedBitmap = (WrappedImmutableConciseBitmap) i.next(); 124 | 125 | if (wrappedBitmap == null) { 126 | return EMPTY_IMMUTABLE_BITMAP; 127 | } 128 | 129 | return wrappedBitmap.getBitmap(); 130 | } 131 | }; 132 | } 133 | }; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/ImmutableBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import org.roaringbitmap.IntIterator; 20 | 21 | /** 22 | * This class is meant to represent a simple wrapper around an immutable bitmap 23 | * class. 24 | */ 25 | public interface ImmutableBitmap 26 | { 27 | /** 28 | * @return an iterator over the set bits of this bitmap 29 | */ 30 | public IntIterator iterator(); 31 | 32 | /** 33 | * @return The number of bits set to true in this bitmap 34 | */ 35 | public int size(); 36 | 37 | public byte[] toBytes(); 38 | 39 | public int compareTo(ImmutableBitmap other); 40 | 41 | /** 42 | * @return True if this bitmap is empty (contains no set bit) 43 | */ 44 | public boolean isEmpty(); 45 | 46 | /** 47 | * Returns true if the bit at position value is set 48 | * 49 | * @param value the position to check 50 | * 51 | * @return true if bit is set 52 | */ 53 | public boolean get(int value); 54 | 55 | /** 56 | * Compute the bitwise-or of this bitmap with another bitmap. A new bitmap is generated. 57 | * 58 | * Note that the other bitmap should be of the same class instance. 59 | * 60 | * @param otherBitmap other bitmap 61 | */ 62 | public ImmutableBitmap union(ImmutableBitmap otherBitmap); 63 | 64 | /** 65 | * Compute the bitwise-and of this bitmap with another bitmap. A new bitmap is generated. 66 | * 67 | * Note that the other bitmap should be of the same class instance. 68 | * 69 | * @param otherBitmap other bitmap 70 | */ 71 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap); 72 | 73 | /** 74 | * Compute the bitwise-andNot of this bitmap with another bitmap. A new bitmap is generated. 75 | * 76 | * Note that the other bitmap should be of the same class instance. 77 | * 78 | * @param otherBitmap other bitmap 79 | */ 80 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap); 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/MutableBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * This class is meant to represent a simple wrapper around a bitmap class. 23 | */ 24 | public interface MutableBitmap extends ImmutableBitmap 25 | { 26 | /** 27 | * Empties the content of this bitmap. 28 | */ 29 | public void clear(); 30 | 31 | /** 32 | * Compute the bitwise-or of this bitmap with another bitmap. The current 33 | * bitmap is modified whereas the other bitmap is left intact. 34 | * 35 | * Note that the other bitmap should be of the same class instance. 36 | * 37 | * @param mutableBitmap other bitmap 38 | */ 39 | public void or(MutableBitmap mutableBitmap); 40 | 41 | /** 42 | * Compute the bitwise-and of this bitmap with another bitmap. The current 43 | * bitmap is modified whereas the other bitmap is left intact. 44 | * 45 | * Note that the other bitmap should be of the same class instance. 46 | * 47 | * @param mutableBitmap other bitmap 48 | */ 49 | public void and(MutableBitmap mutableBitmap); 50 | 51 | 52 | /** 53 | * Compute the bitwise-xor of this bitmap with another bitmap. The current 54 | * bitmap is modified whereas the other bitmap is left intact. 55 | * 56 | * Note that the other bitmap should be of the same class instance. 57 | * 58 | * @param mutableBitmap other bitmap 59 | */ 60 | public void xor(MutableBitmap mutableBitmap); 61 | 62 | /** 63 | * Compute the bitwise-andNot of this bitmap with another bitmap. The current 64 | * bitmap is modified whereas the other bitmap is left intact. 65 | * 66 | * Note that the other bitmap should be of the same class instance. 67 | * 68 | * @param mutableBitmap other bitmap 69 | */ 70 | public void andNot(MutableBitmap mutableBitmap); 71 | 72 | /** 73 | * Return the size in bytes for the purpose of serialization to a ByteBuffer. 74 | * Note that this is distinct from the memory usage. 75 | * 76 | * @return the total set in bytes 77 | */ 78 | public int getSizeInBytes(); 79 | 80 | /** 81 | * Add the specified integer to the bitmap. This is equivalent to setting the 82 | * ith bit to the value 1. 83 | * 84 | * @param entry integer to be added 85 | */ 86 | public void add(int entry); 87 | 88 | /** 89 | * Remove the specified integer to the bitmap. This is equivalent to setting the 90 | * ith bit to the value 1. 91 | * 92 | * @param entry integer to be remove 93 | */ 94 | public void remove(int entry); 95 | 96 | /** 97 | * Write out a serialized (Immutable) version of the bitmap to the ByteBuffer. We preprend 98 | * the serialized bitmap with a 4-byte int indicating the size in bytes. Thus 99 | * getSizeInBytes() + 4 bytes are written. 100 | * 101 | * (These 4 bytes are required by ConciseSet but not by RoaringBitmap. 102 | * Nevertheless, we always write them for the sake of simplicity, even if it 103 | * wastes 4 bytes in some instances.) 104 | * 105 | * @param buffer where we write 106 | */ 107 | public void serialize(ByteBuffer buffer); 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/RoaringBitmapFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.base.Throwables; 20 | import org.roaringbitmap.RoaringBitmap; 21 | import org.roaringbitmap.buffer.BufferFastAggregation; 22 | import org.roaringbitmap.buffer.ImmutableRoaringBitmap; 23 | 24 | import java.io.ByteArrayOutputStream; 25 | import java.io.DataOutputStream; 26 | import java.nio.ByteBuffer; 27 | import java.util.Iterator; 28 | 29 | /** 30 | * As the name suggests, this class instantiates bitmaps of the types 31 | * WrappedRoaringBitmap and WrappedImmutableRoaringBitmap. 32 | */ 33 | public class RoaringBitmapFactory implements BitmapFactory 34 | { 35 | static final boolean DEFAULT_COMPRESS_RUN_ON_SERIALIZATION = false; 36 | 37 | private static final ImmutableRoaringBitmap EMPTY_IMMUTABLE_BITMAP; 38 | 39 | static { 40 | try { 41 | final RoaringBitmap roaringBitmap = new RoaringBitmap(); 42 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 43 | roaringBitmap.serialize(new DataOutputStream(out)); 44 | final byte[] bytes = out.toByteArray(); 45 | 46 | ByteBuffer buf = ByteBuffer.wrap(bytes); 47 | EMPTY_IMMUTABLE_BITMAP = new ImmutableRoaringBitmap(buf); 48 | } 49 | catch (Exception e) { 50 | throw Throwables.propagate(e); 51 | } 52 | } 53 | 54 | private static final WrappedImmutableRoaringBitmap WRAPPED_IMMUTABLE_ROARING_BITMAP = 55 | new WrappedImmutableRoaringBitmap(EMPTY_IMMUTABLE_BITMAP); 56 | 57 | private final boolean compressRunOnSerialization; 58 | 59 | public RoaringBitmapFactory() 60 | { 61 | this(DEFAULT_COMPRESS_RUN_ON_SERIALIZATION); 62 | } 63 | 64 | public RoaringBitmapFactory(boolean compressRunOnSerialization) 65 | { 66 | this.compressRunOnSerialization = compressRunOnSerialization; 67 | } 68 | 69 | @Override 70 | public MutableBitmap makeEmptyMutableBitmap() 71 | { 72 | return new WrappedRoaringBitmap(compressRunOnSerialization); 73 | } 74 | 75 | @Override 76 | public ImmutableBitmap makeEmptyImmutableBitmap() 77 | { 78 | return WRAPPED_IMMUTABLE_ROARING_BITMAP; 79 | } 80 | 81 | @Override 82 | public ImmutableBitmap makeImmutableBitmap(MutableBitmap mutableBitmap) 83 | { 84 | if (!(mutableBitmap instanceof WrappedRoaringBitmap)) { 85 | throw new IllegalStateException(String.format("Cannot convert [%s]", mutableBitmap.getClass())); 86 | } 87 | try { 88 | return ((WrappedRoaringBitmap) mutableBitmap).toImmutableBitmap(); 89 | } 90 | catch (Exception e) { 91 | throw Throwables.propagate(e); 92 | } 93 | } 94 | 95 | @Override 96 | public ImmutableBitmap mapImmutableBitmap(ByteBuffer b) 97 | { 98 | return new WrappedImmutableRoaringBitmap(b); 99 | } 100 | 101 | @Override 102 | public ImmutableBitmap union(Iterable b) 103 | { 104 | return new WrappedImmutableRoaringBitmap(ImmutableRoaringBitmap.or(unwrap(b).iterator())); 105 | } 106 | 107 | @Override 108 | public ImmutableBitmap intersection(Iterable b) 109 | { 110 | return new WrappedImmutableRoaringBitmap(BufferFastAggregation.and(unwrap(b).iterator())); 111 | } 112 | 113 | @Override 114 | public ImmutableBitmap complement(ImmutableBitmap b) 115 | { 116 | return new WrappedImmutableRoaringBitmap( 117 | ImmutableRoaringBitmap.flip( 118 | ((WrappedImmutableRoaringBitmap) b).getBitmap(), 119 | 0, 120 | b.size() 121 | ) 122 | ); 123 | } 124 | 125 | @Override 126 | public ImmutableBitmap complement( 127 | ImmutableBitmap b, int length 128 | ) 129 | { 130 | return new WrappedImmutableRoaringBitmap( 131 | ImmutableRoaringBitmap.flip( 132 | ((WrappedImmutableRoaringBitmap) b).getBitmap(), 133 | 0, 134 | length 135 | ) 136 | ); 137 | } 138 | 139 | private static Iterable unwrap( 140 | final Iterable b 141 | ) 142 | { 143 | return new Iterable() 144 | { 145 | @Override 146 | public Iterator iterator() 147 | { 148 | final Iterator i = b.iterator(); 149 | return new Iterator() 150 | { 151 | @Override 152 | public void remove() 153 | { 154 | throw new UnsupportedOperationException(); 155 | } 156 | 157 | @Override 158 | public boolean hasNext() 159 | { 160 | return i.hasNext(); 161 | } 162 | 163 | @Override 164 | public ImmutableRoaringBitmap next() 165 | { 166 | WrappedImmutableRoaringBitmap wrappedBitmap = (WrappedImmutableRoaringBitmap) i.next(); 167 | 168 | if (wrappedBitmap == null) { 169 | return EMPTY_IMMUTABLE_BITMAP; 170 | } 171 | 172 | return wrappedBitmap.getBitmap(); 173 | } 174 | }; 175 | } 176 | }; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedBitSetBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.util.BitSet; 21 | 22 | /** 23 | * WrappedBitSetBitmap implements MutableBitmap for java.util.BitSet 24 | */ 25 | public class WrappedBitSetBitmap extends WrappedImmutableBitSetBitmap implements MutableBitmap 26 | { 27 | 28 | public WrappedBitSetBitmap() 29 | { 30 | super(); 31 | } 32 | 33 | public WrappedBitSetBitmap(BitSet bitSet) 34 | { 35 | super(bitSet); 36 | } 37 | 38 | public WrappedBitSetBitmap(ByteBuffer byteBuffer){ 39 | super(byteBuffer); 40 | } 41 | 42 | protected BitSet cloneBitSet() 43 | { 44 | return (BitSet) bitmap.clone(); 45 | } 46 | 47 | @Override 48 | public void clear() 49 | { 50 | bitmap.clear(); 51 | } 52 | 53 | @Override 54 | public void or(MutableBitmap mutableBitmap) 55 | { 56 | if (mutableBitmap instanceof WrappedBitSetBitmap) { 57 | WrappedBitSetBitmap bitSet = (WrappedBitSetBitmap) mutableBitmap; 58 | this.bitmap.or(bitSet.bitmap); 59 | } else { 60 | throw new IllegalArgumentException( 61 | String.format( 62 | "Unknown class type: %s expected %s", 63 | mutableBitmap.getClass().getCanonicalName(), 64 | WrappedBitSetBitmap.class.getCanonicalName() 65 | ) 66 | ); 67 | } 68 | } 69 | 70 | @Override 71 | public void and(MutableBitmap mutableBitmap) 72 | { 73 | if (mutableBitmap instanceof WrappedBitSetBitmap) { 74 | WrappedBitSetBitmap bitSet = (WrappedBitSetBitmap) mutableBitmap; 75 | this.bitmap.and(bitSet.bitmap); 76 | } else { 77 | throw new IllegalArgumentException( 78 | String.format( 79 | "Unknown class type: %s expected %s", 80 | mutableBitmap.getClass().getCanonicalName(), 81 | WrappedBitSetBitmap.class.getCanonicalName() 82 | ) 83 | ); 84 | } 85 | } 86 | 87 | @Override 88 | public void xor(MutableBitmap mutableBitmap) 89 | { 90 | if (mutableBitmap instanceof WrappedBitSetBitmap) { 91 | WrappedBitSetBitmap bitSet = (WrappedBitSetBitmap) mutableBitmap; 92 | this.bitmap.xor(bitSet.bitmap); 93 | } else { 94 | throw new IllegalArgumentException( 95 | String.format( 96 | "Unknown class type: %s expected %s", 97 | mutableBitmap.getClass().getCanonicalName(), 98 | WrappedBitSetBitmap.class.getCanonicalName() 99 | ) 100 | ); 101 | } 102 | } 103 | 104 | @Override 105 | public void andNot(MutableBitmap mutableBitmap) 106 | { 107 | if (mutableBitmap instanceof WrappedBitSetBitmap) { 108 | WrappedBitSetBitmap bitSet = (WrappedBitSetBitmap) mutableBitmap; 109 | this.bitmap.andNot(bitSet.bitmap); 110 | } else { 111 | throw new IllegalArgumentException( 112 | String.format( 113 | "Unknown class type: %s expected %s", 114 | mutableBitmap.getClass().getCanonicalName(), 115 | WrappedBitSetBitmap.class.getCanonicalName() 116 | ) 117 | ); 118 | } 119 | } 120 | 121 | @Override 122 | public int getSizeInBytes() 123 | { 124 | // BitSet.size() returns the size in *bits* 125 | return this.bitmap.size() / Byte.SIZE; 126 | } 127 | 128 | @Override 129 | public void add(int entry) 130 | { 131 | this.bitmap.set(entry); 132 | } 133 | 134 | @Override 135 | public void remove(int entry) 136 | { 137 | this.bitmap.clear(entry); 138 | } 139 | 140 | @Override 141 | public void serialize(ByteBuffer buffer) 142 | { 143 | buffer.put(this.bitmap.toByteArray()); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedConciseBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.primitives.Ints; 20 | import it.uniroma3.mat.extendedset.intset.ConciseSet; 21 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 22 | import org.roaringbitmap.IntIterator; 23 | 24 | import java.nio.ByteBuffer; 25 | 26 | public class WrappedConciseBitmap implements MutableBitmap 27 | { 28 | /** 29 | * Underlying bitmap. 30 | */ 31 | private ConciseSet bitmap; 32 | 33 | /** 34 | * Create a new WrappedConciseBitmap wrapping an empty ConciseSet 35 | */ 36 | public WrappedConciseBitmap() 37 | { 38 | this.bitmap = new ConciseSet(); 39 | } 40 | 41 | /** 42 | * Create a bitmap wrappign the given bitmap 43 | * 44 | * @param conciseSet bitmap to be wrapped 45 | */ 46 | public WrappedConciseBitmap(ConciseSet conciseSet) 47 | { 48 | this.bitmap = conciseSet; 49 | } 50 | 51 | ConciseSet getBitmap() 52 | { 53 | return bitmap; 54 | } 55 | 56 | @Override 57 | public byte[] toBytes() 58 | { 59 | return ImmutableConciseSet.newImmutableFromMutable(bitmap).toBytes(); 60 | } 61 | 62 | @Override 63 | public int compareTo(ImmutableBitmap other) 64 | { 65 | return bitmap.compareTo(((WrappedConciseBitmap) other).getBitmap()); 66 | } 67 | 68 | @Override 69 | public void clear() 70 | { 71 | bitmap.clear(); 72 | } 73 | 74 | @Override 75 | public void or(MutableBitmap mutableBitmap) 76 | { 77 | WrappedConciseBitmap other = (WrappedConciseBitmap) mutableBitmap; 78 | ConciseSet unwrappedOtherBitmap = other.bitmap; 79 | bitmap.addAll(unwrappedOtherBitmap); 80 | } 81 | 82 | @Override 83 | public void and(MutableBitmap mutableBitmap) 84 | { 85 | WrappedConciseBitmap other = (WrappedConciseBitmap) mutableBitmap; 86 | ConciseSet unwrappedOtherBitmap = other.bitmap; 87 | bitmap = bitmap.intersection(unwrappedOtherBitmap); 88 | } 89 | 90 | @Override 91 | public void xor(MutableBitmap mutableBitmap) 92 | { 93 | WrappedConciseBitmap other = (WrappedConciseBitmap) mutableBitmap; 94 | ConciseSet unwrappedOtherBitmap = other.bitmap; 95 | bitmap = bitmap.symmetricDifference(unwrappedOtherBitmap); 96 | } 97 | 98 | @Override 99 | public void andNot(MutableBitmap mutableBitmap) 100 | { 101 | WrappedConciseBitmap other = (WrappedConciseBitmap) mutableBitmap; 102 | ConciseSet unwrappedOtherBitmap = other.bitmap; 103 | bitmap = bitmap.difference(unwrappedOtherBitmap); 104 | } 105 | 106 | @Override 107 | public int getSizeInBytes() 108 | { 109 | return bitmap.getWords().length * Ints.BYTES; 110 | } 111 | 112 | @Override 113 | public void add(int entry) 114 | { 115 | bitmap.add(entry); 116 | } 117 | 118 | @Override 119 | public int size() 120 | { 121 | return bitmap.size(); 122 | } 123 | 124 | @Override 125 | public void serialize(ByteBuffer buffer) 126 | { 127 | buffer.put(toBytes()); 128 | } 129 | 130 | @Override 131 | public String toString() 132 | { 133 | return getClass().getSimpleName() + bitmap.toString(); 134 | } 135 | 136 | @Override 137 | public void remove(int entry) 138 | { 139 | bitmap.remove(entry); 140 | } 141 | 142 | @Override 143 | public IntIterator iterator() 144 | { 145 | return bitmap.iterator(); 146 | } 147 | 148 | @Override 149 | public boolean isEmpty() 150 | { 151 | return bitmap.size() == 0; 152 | } 153 | 154 | @Override 155 | public ImmutableBitmap union(ImmutableBitmap otherBitmap) 156 | { 157 | WrappedConciseBitmap other = (WrappedConciseBitmap) otherBitmap; 158 | ConciseSet unwrappedOtherBitmap = other.bitmap; 159 | return new WrappedConciseBitmap(bitmap.clone().union(unwrappedOtherBitmap)); 160 | } 161 | 162 | @Override 163 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap) 164 | { 165 | WrappedConciseBitmap other = (WrappedConciseBitmap) otherBitmap; 166 | ConciseSet unwrappedOtherBitmap = other.bitmap; 167 | return new WrappedConciseBitmap(bitmap.clone().intersection(unwrappedOtherBitmap)); 168 | } 169 | 170 | @Override 171 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap) 172 | { 173 | WrappedConciseBitmap other = (WrappedConciseBitmap) otherBitmap; 174 | ConciseSet unwrappedOtherBitmap = other.bitmap; 175 | return new WrappedConciseBitmap(bitmap.clone().difference(unwrappedOtherBitmap)); 176 | } 177 | 178 | @Override 179 | public boolean get(int value) 180 | { 181 | return bitmap.contains(value); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedConciseIntIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import it.uniroma3.mat.extendedset.intset.IntSet; 20 | import org.roaringbitmap.IntIterator; 21 | 22 | /** 23 | */ 24 | public class WrappedConciseIntIterator implements IntIterator 25 | { 26 | private final IntSet.IntIterator itr; 27 | 28 | public WrappedConciseIntIterator(IntSet.IntIterator itr) 29 | { 30 | this.itr = itr; 31 | } 32 | 33 | @Override 34 | public boolean hasNext() 35 | { 36 | return itr.hasNext(); 37 | } 38 | 39 | @Override 40 | public int next() 41 | { 42 | return itr.next(); 43 | } 44 | 45 | @Override 46 | public IntIterator clone() 47 | { 48 | return new WrappedConciseIntIterator(itr.clone()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedImmutableBitSetBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import org.roaringbitmap.IntIterator; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.util.BitSet; 23 | 24 | /** 25 | * WrappedImmutableBitSetBitmap implements ImmutableBitmap for java.util.BitSet 26 | */ 27 | public class WrappedImmutableBitSetBitmap implements ImmutableBitmap 28 | { 29 | protected final BitSet bitmap; 30 | 31 | public WrappedImmutableBitSetBitmap(BitSet bitmap) 32 | { 33 | this.bitmap = bitmap; 34 | } 35 | 36 | public WrappedImmutableBitSetBitmap() 37 | { 38 | this(new BitSet()); 39 | } 40 | 41 | // WARNING: the current implementation of BitSet (1.7) copies the contents of ByteBuffer to 42 | // on heap! 43 | // TODO: make a new BitSet implementation which can use ByteBuffers properly. 44 | public WrappedImmutableBitSetBitmap(ByteBuffer byteBuffer){ 45 | this(BitSet.valueOf(byteBuffer)); 46 | } 47 | 48 | private class BitSetIterator implements IntIterator 49 | { 50 | private int pos = -1; 51 | 52 | @Override 53 | public boolean hasNext() 54 | { 55 | return bitmap.nextSetBit(pos + 1) >= 0; 56 | } 57 | 58 | @Override 59 | public int next() 60 | { 61 | pos = bitmap.nextSetBit(pos + 1); 62 | return pos; 63 | } 64 | 65 | @Override 66 | public IntIterator clone() 67 | { 68 | BitSetIterator newIt = new BitSetIterator(); 69 | newIt.pos = pos; 70 | return newIt; 71 | } 72 | } 73 | 74 | @Override 75 | public IntIterator iterator() 76 | { 77 | return new BitSetIterator(); 78 | } 79 | 80 | 81 | @Override 82 | public boolean get(int value) 83 | { 84 | return bitmap.get(value); 85 | } 86 | 87 | @Override 88 | public int size() 89 | { 90 | return bitmap.cardinality(); 91 | } 92 | 93 | @Override 94 | public byte[] toBytes() 95 | { 96 | return bitmap.toByteArray(); 97 | } 98 | 99 | 100 | @Override 101 | public int compareTo(ImmutableBitmap other) 102 | { 103 | // TODO: find out what this is supposed to even do 104 | BitSet otherSet = ((WrappedImmutableBitSetBitmap) other).bitmap; 105 | int lengthCompare = Integer.compare(otherSet.length(), bitmap.length()); 106 | if (lengthCompare != 0) { 107 | return lengthCompare; 108 | } 109 | return Integer.compare(otherSet.nextSetBit(0), bitmap.nextSetBit(0)); 110 | } 111 | 112 | 113 | @Override 114 | public boolean isEmpty() 115 | { 116 | return bitmap.isEmpty(); 117 | } 118 | 119 | @Override 120 | public ImmutableBitmap union(ImmutableBitmap otherBitmap) 121 | { 122 | WrappedBitSetBitmap retval = new WrappedBitSetBitmap((BitSet) bitmap.clone()); 123 | retval.or((WrappedBitSetBitmap) otherBitmap); 124 | return retval; 125 | } 126 | 127 | @Override 128 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap) 129 | { 130 | WrappedBitSetBitmap retval = new WrappedBitSetBitmap((BitSet) bitmap.clone()); 131 | retval.and((WrappedBitSetBitmap) otherBitmap); 132 | return retval; 133 | } 134 | 135 | @Override 136 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap) 137 | { 138 | WrappedBitSetBitmap retval = new WrappedBitSetBitmap((BitSet) bitmap.clone()); 139 | retval.andNot((WrappedBitSetBitmap) otherBitmap); 140 | return retval; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedImmutableConciseBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 20 | import it.uniroma3.mat.extendedset.intset.IntSet; 21 | import org.roaringbitmap.IntIterator; 22 | 23 | import java.nio.ByteBuffer; 24 | 25 | public class WrappedImmutableConciseBitmap implements ImmutableBitmap 26 | { 27 | /** 28 | * Underlying bitmap. 29 | */ 30 | private final ImmutableConciseSet bitmap; 31 | 32 | public WrappedImmutableConciseBitmap(ByteBuffer byteBuffer) 33 | { 34 | this.bitmap = new ImmutableConciseSet(byteBuffer.asReadOnlyBuffer()); 35 | } 36 | 37 | /** 38 | * Wrap an ImmutableConciseSet 39 | * 40 | * @param immutableConciseSet bitmap to be wrapped 41 | */ 42 | public WrappedImmutableConciseBitmap(ImmutableConciseSet immutableConciseSet) 43 | { 44 | this.bitmap = immutableConciseSet; 45 | } 46 | 47 | public ImmutableConciseSet getBitmap() 48 | { 49 | return bitmap; 50 | } 51 | 52 | @Override 53 | public boolean get(int value) 54 | { 55 | return bitmap.contains(value); 56 | } 57 | 58 | @Override 59 | public byte[] toBytes() 60 | { 61 | return bitmap.toBytes(); 62 | } 63 | 64 | @Override 65 | public int compareTo(ImmutableBitmap other) 66 | { 67 | return bitmap.compareTo(((WrappedImmutableConciseBitmap) other).getBitmap()); 68 | } 69 | 70 | @Override 71 | public String toString() 72 | { 73 | return getClass().getSimpleName() + bitmap.toString(); 74 | } 75 | 76 | @Override 77 | public IntIterator iterator() 78 | { 79 | return bitmap.iterator(); 80 | } 81 | 82 | @Override 83 | public int size() 84 | { 85 | return bitmap.size(); 86 | } 87 | 88 | @Override 89 | public boolean isEmpty() 90 | { 91 | return bitmap.size() == 0; 92 | } 93 | 94 | @Override 95 | public ImmutableBitmap union(ImmutableBitmap otherBitmap) 96 | { 97 | WrappedImmutableConciseBitmap other = (WrappedImmutableConciseBitmap) otherBitmap; 98 | ImmutableConciseSet unwrappedOtherBitmap = other.bitmap; 99 | return new WrappedImmutableConciseBitmap(ImmutableConciseSet.union(bitmap, unwrappedOtherBitmap)); 100 | } 101 | 102 | @Override 103 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap) 104 | { 105 | WrappedImmutableConciseBitmap other = (WrappedImmutableConciseBitmap) otherBitmap; 106 | ImmutableConciseSet unwrappedOtherBitmap = other.bitmap; 107 | return new WrappedImmutableConciseBitmap(ImmutableConciseSet.intersection(bitmap, unwrappedOtherBitmap)); 108 | } 109 | 110 | @Override 111 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap) 112 | { 113 | WrappedImmutableConciseBitmap other = (WrappedImmutableConciseBitmap) otherBitmap; 114 | ImmutableConciseSet unwrappedOtherBitmap = other.bitmap; 115 | return new WrappedImmutableConciseBitmap( 116 | ImmutableConciseSet.intersection( 117 | bitmap, 118 | ImmutableConciseSet.complement(unwrappedOtherBitmap) 119 | ) 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedImmutableRoaringBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.base.Throwables; 20 | import org.roaringbitmap.IntIterator; 21 | import org.roaringbitmap.buffer.ImmutableRoaringBitmap; 22 | 23 | import java.io.ByteArrayOutputStream; 24 | import java.io.DataOutputStream; 25 | import java.nio.ByteBuffer; 26 | 27 | public class WrappedImmutableRoaringBitmap implements ImmutableBitmap 28 | { 29 | /** 30 | * Underlying bitmap. 31 | */ 32 | private final ImmutableRoaringBitmap bitmap; 33 | 34 | protected WrappedImmutableRoaringBitmap(ByteBuffer byteBuffer) 35 | { 36 | this.bitmap = new ImmutableRoaringBitmap(byteBuffer.asReadOnlyBuffer()); 37 | } 38 | 39 | /** 40 | * Wrap an ImmutableRoaringBitmap 41 | * 42 | * @param immutableRoaringBitmap bitmap to be wrapped 43 | */ 44 | public WrappedImmutableRoaringBitmap(ImmutableRoaringBitmap immutableRoaringBitmap) 45 | { 46 | this.bitmap = immutableRoaringBitmap; 47 | } 48 | 49 | public ImmutableRoaringBitmap getBitmap() 50 | { 51 | return bitmap; 52 | } 53 | 54 | @Override 55 | public byte[] toBytes() 56 | { 57 | try { 58 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 59 | bitmap.serialize(new DataOutputStream(out)); 60 | return out.toByteArray(); 61 | } 62 | catch (Exception e) { 63 | throw Throwables.propagate(e); 64 | } 65 | } 66 | 67 | @Override 68 | public int compareTo(ImmutableBitmap other) 69 | { 70 | return 0; 71 | } 72 | 73 | @Override 74 | public String toString() 75 | { 76 | return getClass().getSimpleName() + bitmap.toString(); 77 | } 78 | 79 | @Override 80 | public IntIterator iterator() 81 | { 82 | return bitmap.getIntIterator(); 83 | } 84 | 85 | @Override 86 | public int size() 87 | { 88 | return bitmap.getCardinality(); 89 | } 90 | 91 | @Override 92 | public boolean isEmpty() 93 | { 94 | return bitmap.isEmpty(); 95 | } 96 | 97 | @Override 98 | public ImmutableBitmap union(ImmutableBitmap otherBitmap) 99 | { 100 | WrappedImmutableRoaringBitmap other = (WrappedImmutableRoaringBitmap) otherBitmap; 101 | ImmutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 102 | return new WrappedImmutableRoaringBitmap(ImmutableRoaringBitmap.or(bitmap, unwrappedOtherBitmap)); 103 | } 104 | 105 | @Override 106 | public boolean get(int value) 107 | { 108 | return bitmap.contains(value); 109 | } 110 | 111 | @Override 112 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap) 113 | { 114 | WrappedImmutableRoaringBitmap other = (WrappedImmutableRoaringBitmap) otherBitmap; 115 | ImmutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 116 | return new WrappedImmutableRoaringBitmap(ImmutableRoaringBitmap.and(bitmap, unwrappedOtherBitmap)); 117 | } 118 | 119 | @Override 120 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap) 121 | { 122 | WrappedImmutableRoaringBitmap other = (WrappedImmutableRoaringBitmap) otherBitmap; 123 | ImmutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 124 | return new WrappedImmutableRoaringBitmap(ImmutableRoaringBitmap.andNot(bitmap, unwrappedOtherBitmap)); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/bitmap/WrappedRoaringBitmap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.base.Throwables; 20 | import org.roaringbitmap.IntIterator; 21 | import org.roaringbitmap.RoaringBitmap; 22 | import org.roaringbitmap.buffer.MutableRoaringBitmap; 23 | 24 | import java.io.ByteArrayOutputStream; 25 | import java.io.DataOutputStream; 26 | import java.io.IOException; 27 | import java.io.OutputStream; 28 | import java.nio.ByteBuffer; 29 | 30 | public class WrappedRoaringBitmap implements MutableBitmap 31 | { 32 | /** 33 | * Underlying bitmap. 34 | */ 35 | private MutableRoaringBitmap bitmap; 36 | 37 | // attempt to compress long runs prior to serialization (requires RoaringBitmap version 0.5 or better) 38 | // this may improve compression greatly in some cases at the expense of slower serialization 39 | // in the worst case. 40 | private final boolean compressRunOnSerialization; 41 | 42 | /** 43 | * Creates a new WrappedRoaringBitmap wrapping an empty MutableRoaringBitmap 44 | */ 45 | public WrappedRoaringBitmap() 46 | { 47 | this(RoaringBitmapFactory.DEFAULT_COMPRESS_RUN_ON_SERIALIZATION); 48 | } 49 | 50 | /** 51 | * Creates a new WrappedRoaringBitmap wrapping an empty MutableRoaringBitmap 52 | * 53 | * @param compressRunOnSerialization indicates whether to call {@link RoaringBitmap#runOptimize()} before serializing 54 | */ 55 | public WrappedRoaringBitmap(boolean compressRunOnSerialization) 56 | { 57 | this.bitmap = new MutableRoaringBitmap(); 58 | this.compressRunOnSerialization = compressRunOnSerialization; 59 | } 60 | 61 | ImmutableBitmap toImmutableBitmap() 62 | { 63 | MutableRoaringBitmap mrb = bitmap.clone(); 64 | if (compressRunOnSerialization) { 65 | mrb.runOptimize(); 66 | } 67 | return new WrappedImmutableRoaringBitmap(mrb); 68 | } 69 | 70 | @Override 71 | public byte[] toBytes() 72 | { 73 | try { 74 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 75 | if (compressRunOnSerialization) { 76 | bitmap.runOptimize(); 77 | } 78 | bitmap.serialize(new DataOutputStream(out)); 79 | return out.toByteArray(); 80 | } 81 | catch (Exception e) { 82 | throw Throwables.propagate(e); 83 | } 84 | } 85 | 86 | @Override 87 | public int compareTo(ImmutableBitmap other) 88 | { 89 | return 0; 90 | } 91 | 92 | @Override 93 | public void clear() 94 | { 95 | this.bitmap.clear(); 96 | } 97 | 98 | @Override 99 | public void or(MutableBitmap mutableBitmap) 100 | { 101 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) mutableBitmap; 102 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 103 | bitmap.or(unwrappedOtherBitmap); 104 | } 105 | 106 | @Override 107 | public void and(MutableBitmap mutableBitmap) 108 | { 109 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) mutableBitmap; 110 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 111 | bitmap.and(unwrappedOtherBitmap); 112 | } 113 | 114 | 115 | @Override 116 | public void andNot(MutableBitmap mutableBitmap) 117 | { 118 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) mutableBitmap; 119 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 120 | bitmap.andNot(unwrappedOtherBitmap); 121 | } 122 | 123 | 124 | @Override 125 | public void xor(MutableBitmap mutableBitmap) 126 | { 127 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) mutableBitmap; 128 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 129 | bitmap.xor(unwrappedOtherBitmap); 130 | } 131 | 132 | @Override 133 | public int getSizeInBytes() 134 | { 135 | if (compressRunOnSerialization) { 136 | bitmap.runOptimize(); 137 | } 138 | return bitmap.serializedSizeInBytes(); 139 | } 140 | 141 | @Override 142 | public void add(int entry) 143 | { 144 | bitmap.add(entry); 145 | } 146 | 147 | @Override 148 | public int size() 149 | { 150 | return bitmap.getCardinality(); 151 | } 152 | 153 | @Override 154 | public void serialize(ByteBuffer buffer) 155 | { 156 | if (compressRunOnSerialization) { 157 | bitmap.runOptimize(); 158 | } 159 | try { 160 | bitmap.serialize( 161 | new DataOutputStream( 162 | new OutputStream() 163 | { 164 | ByteBuffer mBB; 165 | 166 | OutputStream init(ByteBuffer mbb) 167 | { 168 | mBB = mbb; 169 | return this; 170 | } 171 | 172 | @Override 173 | public void close() 174 | { 175 | // unnecessary 176 | } 177 | 178 | @Override 179 | public void flush() 180 | { 181 | // unnecessary 182 | } 183 | 184 | @Override 185 | public void write(int b) 186 | { 187 | mBB.put((byte) b); 188 | } 189 | 190 | @Override 191 | public void write(byte[] b) 192 | { 193 | mBB.put(b); 194 | } 195 | 196 | @Override 197 | public void write(byte[] b, int off, int l) 198 | { 199 | mBB.put(b, off, l); 200 | } 201 | }.init(buffer) 202 | ) 203 | ); 204 | } 205 | catch (IOException e) { 206 | e.printStackTrace(); // impossible in theory 207 | } 208 | } 209 | 210 | @Override 211 | public String toString() 212 | { 213 | return getClass().getSimpleName() + bitmap.toString(); 214 | } 215 | 216 | @Override 217 | public void remove(int entry) 218 | { 219 | bitmap.remove(entry); 220 | } 221 | 222 | @Override 223 | public IntIterator iterator() 224 | { 225 | return bitmap.getIntIterator(); 226 | } 227 | 228 | @Override 229 | public boolean isEmpty() 230 | { 231 | return bitmap.isEmpty(); 232 | } 233 | 234 | @Override 235 | public ImmutableBitmap union(ImmutableBitmap otherBitmap) 236 | { 237 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) otherBitmap; 238 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 239 | return new WrappedImmutableRoaringBitmap(MutableRoaringBitmap.or(bitmap, unwrappedOtherBitmap)); 240 | } 241 | 242 | @Override 243 | public ImmutableBitmap intersection(ImmutableBitmap otherBitmap) 244 | { 245 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) otherBitmap; 246 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 247 | return new WrappedImmutableRoaringBitmap(MutableRoaringBitmap.and(bitmap, unwrappedOtherBitmap)); 248 | } 249 | 250 | @Override 251 | public ImmutableBitmap difference(ImmutableBitmap otherBitmap) 252 | { 253 | WrappedRoaringBitmap other = (WrappedRoaringBitmap) otherBitmap; 254 | MutableRoaringBitmap unwrappedOtherBitmap = other.bitmap; 255 | return new WrappedImmutableRoaringBitmap(MutableRoaringBitmap.andNot(bitmap, unwrappedOtherBitmap)); 256 | } 257 | 258 | @Override 259 | public boolean get(int value) 260 | { 261 | return bitmap.contains(value); 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/ImmutableNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.google.common.primitives.Floats; 20 | import com.google.common.primitives.Ints; 21 | import com.metamx.collections.bitmap.BitmapFactory; 22 | import com.metamx.collections.bitmap.ImmutableBitmap; 23 | 24 | import java.nio.ByteBuffer; 25 | import java.util.Iterator; 26 | 27 | /** 28 | * Byte layout: 29 | * Header 30 | * 0 to 1 : the MSB is a boolean flag for isLeaf, the next 15 bits represent the number of children of a node 31 | * Body 32 | * 2 to 2 + numDims * Floats.BYTES : minCoordinates 33 | * 2 + numDims * Floats.BYTES to 2 + 2 * numDims * Floats.BYTES : maxCoordinates 34 | * concise set 35 | * rest (children) : Every 4 bytes is storing an offset representing the position of a child. 36 | * 37 | * The child offset is an offset from the initialOffset 38 | */ 39 | public class ImmutableNode 40 | { 41 | public static final int HEADER_NUM_BYTES = 2; 42 | 43 | private final int numDims; 44 | private final int initialOffset; 45 | private final int offsetFromInitial; 46 | 47 | private final short numChildren; 48 | private final boolean isLeaf; 49 | private final int childrenOffset; 50 | 51 | private final ByteBuffer data; 52 | 53 | private final BitmapFactory bitmapFactory; 54 | 55 | public ImmutableNode( 56 | int numDims, 57 | int initialOffset, 58 | int offsetFromInitial, 59 | ByteBuffer data, 60 | BitmapFactory bitmapFactory 61 | ) 62 | { 63 | this.bitmapFactory = bitmapFactory; 64 | this.numDims = numDims; 65 | this.initialOffset = initialOffset; 66 | this.offsetFromInitial = offsetFromInitial; 67 | short header = data.getShort(initialOffset + offsetFromInitial); 68 | this.isLeaf = (header & 0x8000) != 0; 69 | this.numChildren = (short) (header & 0x7FFF); 70 | final int sizePosition = initialOffset + offsetFromInitial + HEADER_NUM_BYTES + 2 * numDims * Floats.BYTES; 71 | int bitmapSize = data.getInt(sizePosition); 72 | this.childrenOffset = initialOffset 73 | + offsetFromInitial 74 | + HEADER_NUM_BYTES 75 | + 2 * numDims * Floats.BYTES 76 | + Ints.BYTES 77 | + bitmapSize; 78 | 79 | this.data = data; 80 | } 81 | 82 | public ImmutableNode( 83 | int numDims, 84 | int initialOffset, 85 | int offsetFromInitial, 86 | short numChildren, 87 | boolean leaf, 88 | ByteBuffer data, 89 | BitmapFactory bitmapFactory 90 | ) 91 | { 92 | this.bitmapFactory = bitmapFactory; 93 | this.numDims = numDims; 94 | this.initialOffset = initialOffset; 95 | this.offsetFromInitial = offsetFromInitial; 96 | this.numChildren = numChildren; 97 | this.isLeaf = leaf; 98 | final int sizePosition = initialOffset + offsetFromInitial + HEADER_NUM_BYTES + 2 * numDims * Floats.BYTES; 99 | int bitmapSize = data.getInt(sizePosition); 100 | this.childrenOffset = initialOffset 101 | + offsetFromInitial 102 | + HEADER_NUM_BYTES 103 | + 2 * numDims * Floats.BYTES 104 | + Ints.BYTES 105 | + bitmapSize; 106 | 107 | this.data = data; 108 | } 109 | 110 | public BitmapFactory getBitmapFactory() 111 | { 112 | return bitmapFactory; 113 | } 114 | 115 | public int getInitialOffset() 116 | { 117 | return initialOffset; 118 | } 119 | 120 | public int getOffsetFromInitial() 121 | { 122 | return offsetFromInitial; 123 | } 124 | 125 | public int getNumDims() 126 | { 127 | return numDims; 128 | } 129 | 130 | public int getNumChildren() 131 | { 132 | return numChildren; 133 | } 134 | 135 | public boolean isLeaf() 136 | { 137 | return isLeaf; 138 | } 139 | 140 | public float[] getMinCoordinates() 141 | { 142 | return getCoords(initialOffset + offsetFromInitial + HEADER_NUM_BYTES); 143 | } 144 | 145 | public float[] getMaxCoordinates() 146 | { 147 | return getCoords(initialOffset + offsetFromInitial + HEADER_NUM_BYTES + numDims * Floats.BYTES); 148 | } 149 | 150 | public ImmutableBitmap getImmutableBitmap() 151 | { 152 | final int sizePosition = initialOffset + offsetFromInitial + HEADER_NUM_BYTES + 2 * numDims * Floats.BYTES; 153 | int numBytes = data.getInt(sizePosition); 154 | data.position(sizePosition + Ints.BYTES); 155 | ByteBuffer tmpBuffer = data.slice(); 156 | tmpBuffer.limit(numBytes); 157 | return bitmapFactory.mapImmutableBitmap(tmpBuffer.asReadOnlyBuffer()); 158 | } 159 | 160 | public Iterable getChildren() 161 | { 162 | return new Iterable() 163 | { 164 | @Override 165 | public Iterator iterator() 166 | { 167 | return new Iterator() 168 | { 169 | private volatile int count = 0; 170 | 171 | @Override 172 | public boolean hasNext() 173 | { 174 | return (count < numChildren); 175 | } 176 | 177 | @Override 178 | public ImmutableNode next() 179 | { 180 | if (isLeaf) { 181 | return new ImmutablePoint( 182 | numDims, 183 | initialOffset, 184 | data.getInt(childrenOffset + (count++) * Ints.BYTES), 185 | data, 186 | bitmapFactory 187 | ); 188 | } 189 | return new ImmutableNode( 190 | numDims, 191 | initialOffset, 192 | data.getInt(childrenOffset + (count++) * Ints.BYTES), 193 | data, 194 | bitmapFactory 195 | ); 196 | } 197 | 198 | @Override 199 | public void remove() 200 | { 201 | throw new UnsupportedOperationException(); 202 | } 203 | }; 204 | } 205 | }; 206 | } 207 | 208 | public ByteBuffer getData() 209 | { 210 | return data; 211 | } 212 | 213 | private float[] getCoords(int offset) 214 | { 215 | final float[] retVal = new float[numDims]; 216 | 217 | final ByteBuffer readOnlyBuffer = data.asReadOnlyBuffer(); 218 | readOnlyBuffer.position(offset); 219 | readOnlyBuffer.asFloatBuffer().get(retVal); 220 | 221 | return retVal; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/ImmutablePoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.metamx.collections.bitmap.BitmapFactory; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | public class ImmutablePoint extends ImmutableNode 24 | { 25 | public ImmutablePoint( 26 | int numDims, 27 | int initialOffset, 28 | int offsetFromInitial, 29 | ByteBuffer data, 30 | BitmapFactory bitmapFactory 31 | ) 32 | { 33 | super(numDims, initialOffset, offsetFromInitial, (short) 0, true, data, bitmapFactory); 34 | } 35 | 36 | public ImmutablePoint(ImmutableNode node) 37 | { 38 | super( 39 | node.getNumDims(), 40 | node.getInitialOffset(), 41 | node.getOffsetFromInitial(), 42 | (short) 0, 43 | true, 44 | node.getData(), 45 | node.getBitmapFactory() 46 | ); 47 | } 48 | 49 | public float[] getCoords() 50 | { 51 | return super.getMinCoordinates(); 52 | } 53 | 54 | @Override 55 | public Iterable getChildren() 56 | { 57 | // should never get here 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/ImmutableRTree.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.google.common.base.Preconditions; 20 | import com.google.common.collect.ImmutableList; 21 | import com.google.common.primitives.Ints; 22 | import com.metamx.collections.bitmap.BitmapFactory; 23 | import com.metamx.collections.bitmap.ImmutableBitmap; 24 | import com.metamx.collections.spatial.search.Bound; 25 | import com.metamx.collections.spatial.search.GutmanSearchStrategy; 26 | import com.metamx.collections.spatial.search.SearchStrategy; 27 | 28 | import java.nio.ByteBuffer; 29 | 30 | /** 31 | * An immutable representation of an {@link RTree} for spatial indexing. 32 | */ 33 | public class ImmutableRTree 34 | { 35 | private static byte VERSION = 0x0; 36 | 37 | public static ImmutableRTree newImmutableFromMutable(RTree rTree) 38 | { 39 | if (rTree.getSize() == 0) { 40 | return new ImmutableRTree(); 41 | } 42 | 43 | ByteBuffer buffer = ByteBuffer.wrap(new byte[calcNumBytes(rTree)]); 44 | 45 | buffer.put(VERSION); 46 | buffer.putInt(rTree.getNumDims()); 47 | rTree.getRoot().storeInByteBuffer(buffer, buffer.position()); 48 | buffer.position(0); 49 | return new ImmutableRTree(buffer.asReadOnlyBuffer(), rTree.getBitmapFactory()); 50 | } 51 | 52 | private static int calcNumBytes(RTree tree) 53 | { 54 | int total = 1 + Ints.BYTES; // VERSION and numDims 55 | 56 | total += calcNodeBytes(tree.getRoot()); 57 | 58 | return total; 59 | } 60 | 61 | private static int calcNodeBytes(Node node) 62 | { 63 | int total = 0; 64 | 65 | // find size of this node 66 | total += node.getSizeInBytes(); 67 | 68 | // recursively find sizes of child nodes 69 | for (Node child : node.getChildren()) { 70 | if (node.isLeaf()) { 71 | total += child.getSizeInBytes(); 72 | } else { 73 | total += calcNodeBytes(child); 74 | } 75 | } 76 | 77 | return total; 78 | } 79 | 80 | private final int numDims; 81 | private final ImmutableNode root; 82 | private final ByteBuffer data; 83 | 84 | private final SearchStrategy defaultSearchStrategy = new GutmanSearchStrategy(); 85 | 86 | public ImmutableRTree() 87 | { 88 | this.numDims = 0; 89 | this.data = ByteBuffer.wrap(new byte[]{}); 90 | this.root = null; 91 | } 92 | 93 | public ImmutableRTree(ByteBuffer data, BitmapFactory bitmapFactory) 94 | { 95 | final int initPosition = data.position(); 96 | Preconditions.checkArgument(data.get(0) == VERSION, "Mismatching versions"); 97 | this.numDims = data.getInt(1 + initPosition) & 0x7FFF; 98 | this.data = data; 99 | this.root = new ImmutableNode(numDims, initPosition, 1 + Ints.BYTES, data, bitmapFactory); 100 | } 101 | 102 | public int size() 103 | { 104 | return data.capacity(); 105 | } 106 | 107 | public ImmutableNode getRoot() 108 | { 109 | return root; 110 | } 111 | 112 | public int getNumDims() 113 | { 114 | return numDims; 115 | } 116 | 117 | public Iterable search(Bound bound) 118 | { 119 | return search(defaultSearchStrategy, bound); 120 | } 121 | 122 | public Iterable search(SearchStrategy strategy, Bound bound) 123 | { 124 | if(bound.getNumDims() == numDims) { 125 | return strategy.search(root, bound); 126 | }else{ 127 | // If the dimension counts don't match (for example, if this is called on a blank `new ImmutableRTree()`) 128 | return ImmutableList.of(); 129 | } 130 | } 131 | 132 | public byte[] toBytes() 133 | { 134 | ByteBuffer buf = ByteBuffer.allocate(data.capacity()); 135 | buf.put(data.asReadOnlyBuffer()); 136 | return buf.array(); 137 | } 138 | 139 | public int compareTo(ImmutableRTree other) 140 | { 141 | return this.data.compareTo(other.data); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.google.common.base.Preconditions; 20 | import com.google.common.collect.Lists; 21 | import com.google.common.primitives.Floats; 22 | import com.google.common.primitives.Ints; 23 | import com.metamx.collections.bitmap.MutableBitmap; 24 | import com.metamx.collections.bitmap.BitmapFactory; 25 | 26 | import java.nio.ByteBuffer; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | /** 31 | */ 32 | public class Node 33 | { 34 | private final float[] minCoordinates; 35 | private final float[] maxCoordinates; 36 | 37 | private final List children; 38 | private final boolean isLeaf; 39 | private final MutableBitmap bitmap; 40 | 41 | private Node parent; 42 | 43 | public Node(float[] minCoordinates, float[] maxCoordinates, boolean isLeaf, BitmapFactory bitmapFactory) 44 | { 45 | this( 46 | minCoordinates, 47 | maxCoordinates, 48 | Lists.newArrayList(), 49 | isLeaf, 50 | null, 51 | bitmapFactory.makeEmptyMutableBitmap() 52 | ); 53 | } 54 | 55 | public Node( 56 | float[] minCoordinates, 57 | float[] maxCoordinates, 58 | List children, 59 | boolean isLeaf, 60 | Node parent, 61 | MutableBitmap bitmap 62 | ) 63 | { 64 | Preconditions.checkArgument(minCoordinates.length == maxCoordinates.length); 65 | 66 | this.minCoordinates = minCoordinates; 67 | this.maxCoordinates = maxCoordinates; 68 | this.children = children; 69 | for (Node child : children) { 70 | child.setParent(this); 71 | } 72 | this.isLeaf = isLeaf; 73 | this.bitmap = bitmap; 74 | this.parent = parent; 75 | } 76 | 77 | public int getNumDims() 78 | { 79 | return minCoordinates.length; 80 | } 81 | 82 | public float[] getMinCoordinates() 83 | { 84 | return minCoordinates; 85 | } 86 | 87 | public float[] getMaxCoordinates() 88 | { 89 | return maxCoordinates; 90 | } 91 | 92 | public Node getParent() 93 | { 94 | return parent; 95 | } 96 | 97 | public void addChild(Node node) 98 | { 99 | node.setParent(this); 100 | children.add(node); 101 | } 102 | 103 | public List getChildren() 104 | { 105 | return children; 106 | } 107 | 108 | public boolean isLeaf() 109 | { 110 | return isLeaf; 111 | } 112 | 113 | public double getArea() 114 | { 115 | return calculateArea(); 116 | } 117 | 118 | public boolean contains(Node other) 119 | { 120 | Preconditions.checkArgument(getNumDims() == other.getNumDims()); 121 | 122 | for (int i = 0; i < getNumDims(); i++) { 123 | if (other.getMinCoordinates()[i] < minCoordinates[i] || other.getMaxCoordinates()[i] > maxCoordinates[i]) { 124 | return false; 125 | } 126 | } 127 | return true; 128 | } 129 | 130 | public boolean contains(float[] coords) 131 | { 132 | Preconditions.checkArgument(getNumDims() == coords.length); 133 | 134 | for (int i = 0; i < getNumDims(); i++) { 135 | if (coords[i] < minCoordinates[i] || coords[i] > maxCoordinates[i]) { 136 | return false; 137 | } 138 | } 139 | return true; 140 | } 141 | 142 | public boolean enclose() 143 | { 144 | boolean retVal = false; 145 | float[] minCoords = new float[getNumDims()]; 146 | Arrays.fill(minCoords, Float.MAX_VALUE); 147 | float[] maxCoords = new float[getNumDims()]; 148 | Arrays.fill(maxCoords, -Float.MAX_VALUE); 149 | 150 | for (Node child : getChildren()) { 151 | for (int i = 0; i < getNumDims(); i++) { 152 | minCoords[i] = Math.min(child.getMinCoordinates()[i], minCoords[i]); 153 | maxCoords[i] = Math.max(child.getMaxCoordinates()[i], maxCoords[i]); 154 | } 155 | } 156 | 157 | if (!Arrays.equals(minCoords, minCoordinates)) { 158 | System.arraycopy(minCoords, 0, minCoordinates, 0, minCoordinates.length); 159 | retVal = true; 160 | } 161 | if (!Arrays.equals(maxCoords, maxCoordinates)) { 162 | System.arraycopy(maxCoords, 0, maxCoordinates, 0, maxCoordinates.length); 163 | retVal = true; 164 | } 165 | 166 | return retVal; 167 | } 168 | 169 | public MutableBitmap getBitmap() 170 | { 171 | return bitmap; 172 | } 173 | 174 | public void addToBitmapIndex(Node node) 175 | { 176 | bitmap.or(node.getBitmap()); 177 | } 178 | 179 | public void clear() 180 | { 181 | children.clear(); 182 | bitmap.clear(); 183 | } 184 | 185 | public int getSizeInBytes() 186 | { 187 | return ImmutableNode.HEADER_NUM_BYTES 188 | + 2 * getNumDims() * Floats.BYTES 189 | + Ints.BYTES // size of the set 190 | + bitmap.getSizeInBytes() 191 | + getChildren().size() * Ints.BYTES; 192 | } 193 | 194 | public int storeInByteBuffer(ByteBuffer buffer, int position) 195 | { 196 | buffer.position(position); 197 | buffer.putShort((short) (((isLeaf ? 0x1 : 0x0) << 15) | getChildren().size())); 198 | for (float v : getMinCoordinates()) { 199 | buffer.putFloat(v); 200 | } 201 | for (float v : getMaxCoordinates()) { 202 | buffer.putFloat(v); 203 | } 204 | byte[] bytes = bitmap.toBytes(); 205 | buffer.putInt(bytes.length); 206 | buffer.put(bytes); 207 | 208 | int pos = buffer.position(); 209 | int childStartOffset = pos + getChildren().size() * Ints.BYTES; 210 | for (Node child : getChildren()) { 211 | buffer.putInt(pos, childStartOffset); 212 | childStartOffset = child.storeInByteBuffer(buffer, childStartOffset); 213 | pos += Ints.BYTES; 214 | } 215 | 216 | return childStartOffset; 217 | } 218 | 219 | private double calculateArea() 220 | { 221 | double area = 1.0; 222 | for (int i = 0; i < minCoordinates.length; i++) { 223 | area *= (maxCoordinates[i] - minCoordinates[i]); 224 | } 225 | return area; 226 | } 227 | 228 | private void setParent(Node p) 229 | { 230 | parent = p; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/Point.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.google.common.collect.Lists; 20 | import com.metamx.collections.bitmap.MutableBitmap; 21 | import com.metamx.collections.bitmap.BitmapFactory; 22 | 23 | import java.util.Arrays; 24 | import java.util.List; 25 | 26 | /** 27 | */ 28 | public class Point extends Node 29 | { 30 | private static MutableBitmap makeBitmap(int entry, BitmapFactory bitmapFactory) 31 | { 32 | MutableBitmap retVal = bitmapFactory.makeEmptyMutableBitmap(); 33 | retVal.add(entry); 34 | return retVal; 35 | } 36 | 37 | private final float[] coords; 38 | private final MutableBitmap bitmap; 39 | 40 | public Point(float[] coords, int entry, BitmapFactory bitmapFactory) 41 | { 42 | super( 43 | coords, 44 | Arrays.copyOf(coords, coords.length), 45 | Lists.newArrayList(), 46 | true, 47 | null, 48 | makeBitmap(entry, bitmapFactory) 49 | ); 50 | 51 | this.coords = coords; 52 | this.bitmap = bitmapFactory.makeEmptyMutableBitmap(); 53 | this.bitmap.add(entry); 54 | } 55 | 56 | public Point(float[] coords, MutableBitmap entry) 57 | { 58 | super(coords, Arrays.copyOf(coords, coords.length), Lists.newArrayList(), true, null, entry); 59 | 60 | this.coords = coords; 61 | this.bitmap = entry; 62 | } 63 | 64 | public float[] getCoords() 65 | { 66 | return coords; 67 | } 68 | 69 | @Override 70 | public MutableBitmap getBitmap() 71 | { 72 | return bitmap; 73 | } 74 | 75 | @Override 76 | public void addChild(Node node) 77 | { 78 | throw new UnsupportedOperationException(); 79 | } 80 | 81 | @Override 82 | public List getChildren() 83 | { 84 | return Lists.newArrayList(); 85 | } 86 | 87 | @Override 88 | public boolean isLeaf() 89 | { 90 | return true; 91 | } 92 | 93 | @Override 94 | public double getArea() 95 | { 96 | return 0; 97 | } 98 | 99 | @Override 100 | public boolean contains(Node other) 101 | { 102 | return false; 103 | } 104 | 105 | @Override 106 | public boolean enclose() 107 | { 108 | return false; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/RTree.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.google.common.base.Preconditions; 20 | import com.metamx.collections.bitmap.MutableBitmap; 21 | import com.metamx.collections.bitmap.BitmapFactory; 22 | import com.metamx.collections.spatial.split.LinearGutmanSplitStrategy; 23 | import com.metamx.collections.spatial.split.SplitStrategy; 24 | 25 | import java.util.Arrays; 26 | 27 | /** 28 | * This RTree has been optimized to work with bitmap inverted indexes. 29 | * 30 | * This code will probably make a lot more sense if you read: 31 | * http://www.sai.msu.su/~megera/postgres/gist/papers/gutman-rtree.pdf 32 | */ 33 | public class RTree 34 | { 35 | private final int numDims; 36 | private final SplitStrategy splitStrategy; 37 | 38 | private Node root; 39 | private final BitmapFactory bitmapFactory; 40 | 41 | private volatile int size; 42 | 43 | public RTree(BitmapFactory bitmapFactory) 44 | { 45 | this(0, new LinearGutmanSplitStrategy(0, 0, bitmapFactory), bitmapFactory); 46 | } 47 | 48 | public RTree(int numDims, SplitStrategy splitStrategy, BitmapFactory bitmapFactory) 49 | { 50 | this.numDims = numDims; 51 | this.splitStrategy = splitStrategy; 52 | this.bitmapFactory = bitmapFactory; 53 | this.root = buildRoot(true); 54 | } 55 | 56 | public BitmapFactory getBitmapFactory() 57 | { 58 | return bitmapFactory; 59 | } 60 | 61 | /** 62 | * This description is from the original paper. 63 | * 64 | * Algorithm Insert: Insert a new index entry E into an R-tree. 65 | * 66 | * I1. [Find position for new record]. Invoke {@link #chooseLeaf(Node, Point)} to select 67 | * a leaf node L in which to place E. 68 | * 69 | * I2. [Add records to leaf node]. If L has room for another entry, install E. Otherwise invoke 70 | * {@link SplitStrategy} split methods to obtain L and LL containing E and all the old entries of L. 71 | * 72 | * I3. [Propagate changes upward]. Invoke {@link #adjustTree(Node, Node)} on L, also passing LL if a split was 73 | * performed. 74 | * 75 | * I4. [Grow tree taller]. If node split propagation caused the root to split, create a new record whose 76 | * children are the two resulting nodes. 77 | * 78 | * @param coords - the coordinates of the entry 79 | * @param entry - the integer to insert 80 | */ 81 | public void insert(float[] coords, int entry) 82 | { 83 | Preconditions.checkArgument(coords.length == numDims); 84 | insertInner(new Point(coords, entry, bitmapFactory)); 85 | } 86 | 87 | public void insert(float[] coords, MutableBitmap entry) 88 | { 89 | Preconditions.checkArgument(coords.length == numDims); 90 | insertInner(new Point(coords, entry)); 91 | } 92 | 93 | /** 94 | * Not yet implemented. 95 | * 96 | * @param coords - the coordinates of the entry 97 | * @param entry - the integer to insert 98 | * 99 | * @return - whether the operation completed successfully 100 | */ 101 | public boolean delete(double[] coords, int entry) 102 | { 103 | throw new UnsupportedOperationException(); 104 | } 105 | 106 | public int getSize() 107 | { 108 | return size; 109 | } 110 | 111 | public int getNumDims() 112 | { 113 | return numDims; 114 | } 115 | 116 | public SplitStrategy getSplitStrategy() 117 | { 118 | return splitStrategy; 119 | } 120 | 121 | public Node getRoot() 122 | { 123 | return root; 124 | } 125 | 126 | private Node buildRoot(boolean isLeaf) 127 | { 128 | float[] initMinCoords = new float[numDims]; 129 | float[] initMaxCoords = new float[numDims]; 130 | Arrays.fill(initMinCoords, -Float.MAX_VALUE); 131 | Arrays.fill(initMaxCoords, Float.MAX_VALUE); 132 | 133 | return new Node(initMinCoords, initMaxCoords, isLeaf, bitmapFactory); 134 | } 135 | 136 | private void insertInner(Point point) 137 | { 138 | Node node = chooseLeaf(root, point); 139 | node.addChild(point); 140 | 141 | if (splitStrategy.needToSplit(node)) { 142 | Node[] groups = splitStrategy.split(node); 143 | adjustTree(groups[0], groups[1]); 144 | } else { 145 | adjustTree(node, null); 146 | } 147 | 148 | size++; 149 | } 150 | 151 | 152 | /** 153 | * This description is from the original paper. 154 | * 155 | * Algorithm ChooseLeaf. Select a leaf node in which to place a new index entry E. 156 | * 157 | * CL1. [Initialize]. Set N to be the root node. 158 | * 159 | * CL2. [Leaf check]. If N is a leaf, return N. 160 | * 161 | * CL3. [Choose subtree]. If N is not a leaf, let F be the entry in N whose rectangle 162 | * FI needs least enlargement to include EI. Resolve ties by choosing the entry with the rectangle 163 | * of smallest area. 164 | * 165 | * CL4. [Descend until a leaf is reached]. Set N to be the child node pointed to by Fp and repeated from CL2. 166 | * 167 | * @param node - current node to evaluate 168 | * @param point - point to insert 169 | * 170 | * @return - leafNode where point can be inserted 171 | */ 172 | private Node chooseLeaf(Node node, Point point) 173 | { 174 | node.addToBitmapIndex(point); 175 | 176 | if (node.isLeaf()) { 177 | return node; 178 | } 179 | 180 | double minCost = Double.MAX_VALUE; 181 | Node optimal = node.getChildren().get(0); 182 | for (Node child : node.getChildren()) { 183 | double cost = RTreeUtils.getExpansionCost(child, point); 184 | if (cost < minCost) { 185 | minCost = cost; 186 | optimal = child; 187 | } else if (cost == minCost) { 188 | // Resolve ties by choosing the entry with the rectangle of smallest area 189 | if (child.getArea() < optimal.getArea()) { 190 | optimal = child; 191 | } 192 | } 193 | } 194 | 195 | return chooseLeaf(optimal, point); 196 | } 197 | 198 | /** 199 | * This description is from the original paper. 200 | * 201 | * AT1. [Initialize]. Set N=L. If L was split previously, set NN to be the resulting second node. 202 | * 203 | * AT2. [Check if done]. If N is the root, stop. 204 | * 205 | * AT3. [Adjust covering rectangle in parent entry]. Let P be the parent node of N, and let Ev(N)I be N's entry in P. 206 | * Adjust Ev(N)I so that it tightly encloses all entry rectangles in N. 207 | * 208 | * AT4. [Propagate node split upward]. If N has a partner NN resulting from an earlier split, create a new entry 209 | * Ev(NN) with Ev(NN)p pointing to NN and Ev(NN)I enclosing all rectangles in NN. Add Ev(NN) to p is there is room. 210 | * Otherwise, invoke {@link SplitStrategy} split to product p and pp containing Ev(NN) and all p's old entries. 211 | * 212 | * @param n - first node to adjust 213 | * @param nn - optional second node to adjust 214 | */ 215 | private void adjustTree(Node n, Node nn) 216 | { 217 | // special case for root 218 | if (n == root) { 219 | if (nn != null) { 220 | root = buildRoot(false); 221 | root.addChild(n); 222 | root.addChild(nn); 223 | } 224 | root.enclose(); 225 | return; 226 | } 227 | 228 | boolean updateParent = n.enclose(); 229 | 230 | if (nn != null) { 231 | nn.enclose(); 232 | updateParent = true; 233 | 234 | if (splitStrategy.needToSplit(n.getParent())) { 235 | Node[] groups = splitStrategy.split(n.getParent()); 236 | adjustTree(groups[0], groups[1]); 237 | } 238 | } 239 | 240 | if (n.getParent() != null && updateParent) { 241 | adjustTree(n.getParent(), null); 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/RTreeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import com.google.common.base.Function; 21 | import com.google.common.base.Preconditions; 22 | import com.google.common.base.Throwables; 23 | import com.google.common.collect.Iterables; 24 | 25 | /** 26 | */ 27 | public class RTreeUtils 28 | { 29 | private static ObjectMapper jsonMapper = new ObjectMapper(); 30 | 31 | public static double getEnclosingArea(Node a, Node b) 32 | { 33 | Preconditions.checkArgument(a.getNumDims() == b.getNumDims()); 34 | 35 | double[] minCoords = new double[a.getNumDims()]; 36 | double[] maxCoords = new double[a.getNumDims()]; 37 | 38 | for (int i = 0; i < minCoords.length; i++) { 39 | minCoords[i] = Math.min(a.getMinCoordinates()[i], b.getMinCoordinates()[i]); 40 | maxCoords[i] = Math.max(a.getMaxCoordinates()[i], b.getMaxCoordinates()[i]); 41 | } 42 | 43 | double area = 1.0; 44 | for (int i = 0; i < minCoords.length; i++) { 45 | area *= (maxCoords[i] - minCoords[i]); 46 | } 47 | 48 | return area; 49 | } 50 | 51 | public static double getExpansionCost(Node node, Point point) 52 | { 53 | Preconditions.checkArgument(node.getNumDims() == point.getNumDims()); 54 | 55 | if (node.contains(point.getCoords())) { 56 | return 0; 57 | } 58 | 59 | double expanded = 1.0; 60 | for (int i = 0; i < node.getNumDims(); i++) { 61 | double min = Math.min(point.getCoords()[i], node.getMinCoordinates()[i]); 62 | double max = Math.max(point.getCoords()[i], node.getMinCoordinates()[i]); 63 | expanded *= (max - min); 64 | } 65 | 66 | return (expanded - node.getArea()); 67 | } 68 | 69 | public static void enclose(Node[] nodes) 70 | { 71 | for (Node node : nodes) { 72 | node.enclose(); 73 | } 74 | } 75 | 76 | public static Iterable getBitmaps(ImmutableRTree tree) 77 | { 78 | return depthFirstSearch(tree.getRoot()); 79 | } 80 | 81 | public static Iterable depthFirstSearch(ImmutableNode node) 82 | { 83 | if (node.isLeaf()) { 84 | return Iterables.transform( 85 | node.getChildren(), 86 | new Function() 87 | { 88 | @Override 89 | public ImmutablePoint apply(ImmutableNode tNode) 90 | { 91 | return new ImmutablePoint(tNode); 92 | } 93 | } 94 | ); 95 | } else { 96 | return Iterables.concat( 97 | Iterables.transform( 98 | 99 | node.getChildren(), 100 | new Function>() 101 | { 102 | @Override 103 | public Iterable apply(ImmutableNode child) 104 | { 105 | return depthFirstSearch(child); 106 | } 107 | } 108 | ) 109 | ); 110 | } 111 | } 112 | 113 | public static void print(RTree tree) 114 | { 115 | System.out.printf("numDims : %d%n", tree.getNumDims()); 116 | try { 117 | printRTreeNode(tree.getRoot(), 0); 118 | } 119 | catch (Exception e) { 120 | throw Throwables.propagate(e); 121 | } 122 | } 123 | 124 | public static void print(ImmutableRTree tree) 125 | { 126 | System.out.printf("numDims : %d%n", tree.getNumDims()); 127 | try { 128 | printNode(tree.getRoot(), 0); 129 | } 130 | catch (Exception e) { 131 | throw Throwables.propagate(e); 132 | } 133 | } 134 | 135 | public static void printRTreeNode(Node node, int level) throws Exception 136 | { 137 | System.out.printf( 138 | "%sminCoords: %s, maxCoords: %s, numChildren: %d, isLeaf:%s%n", 139 | makeDashes(level), 140 | jsonMapper.writeValueAsString(node.getMinCoordinates()), 141 | jsonMapper.writeValueAsString( 142 | node.getMaxCoordinates() 143 | ), 144 | node.getChildren().size(), 145 | node.isLeaf() 146 | ); 147 | if (node.isLeaf()) { 148 | for (Node child : node.getChildren()) { 149 | Point point = (Point) (child); 150 | System.out 151 | .printf( 152 | "%scoords: %s, conciseSet: %s%n", 153 | makeDashes(level), 154 | jsonMapper.writeValueAsString(point.getCoords()), 155 | point.getBitmap() 156 | ); 157 | } 158 | } else { 159 | level++; 160 | for (Node child : node.getChildren()) { 161 | printRTreeNode(child, level); 162 | } 163 | } 164 | } 165 | 166 | public static boolean verifyEnclose(Node node) 167 | { 168 | for (Node child : node.getChildren()) { 169 | for (int i = 0; i < node.getNumDims(); i++) { 170 | if (child.getMinCoordinates()[i] < node.getMinCoordinates()[i] 171 | || child.getMaxCoordinates()[i] > node.getMaxCoordinates()[i]) { 172 | return false; 173 | } 174 | } 175 | } 176 | 177 | if (!node.isLeaf()) { 178 | for (Node child : node.getChildren()) { 179 | if (!verifyEnclose(child)) { 180 | return false; 181 | } 182 | } 183 | } 184 | 185 | return true; 186 | } 187 | 188 | public static boolean verifyEnclose(ImmutableNode node) 189 | { 190 | for (ImmutableNode child : node.getChildren()) { 191 | for (int i = 0; i < node.getNumDims(); i++) { 192 | if (child.getMinCoordinates()[i] < node.getMinCoordinates()[i] 193 | || child.getMaxCoordinates()[i] > node.getMaxCoordinates()[i]) { 194 | return false; 195 | } 196 | } 197 | } 198 | 199 | if (!node.isLeaf()) { 200 | for (ImmutableNode child : node.getChildren()) { 201 | if (!verifyEnclose(child)) { 202 | return false; 203 | } 204 | } 205 | } 206 | 207 | return true; 208 | } 209 | 210 | private static void printNode(ImmutableNode node, int level) throws Exception 211 | { 212 | System.out.printf( 213 | "%sminCoords: %s, maxCoords: %s, numChildren: %d, isLeaf: %s%n", 214 | makeDashes(level), 215 | jsonMapper.writeValueAsString(node.getMinCoordinates()), 216 | jsonMapper.writeValueAsString( 217 | node.getMaxCoordinates() 218 | ), 219 | node.getNumChildren(), 220 | node.isLeaf() 221 | ); 222 | if (node.isLeaf()) { 223 | for (ImmutableNode immutableNode : node.getChildren()) { 224 | ImmutablePoint point = new ImmutablePoint(immutableNode); 225 | System.out 226 | .printf( 227 | "%scoords: %s, conciseSet: %s%n", 228 | makeDashes(level), 229 | jsonMapper.writeValueAsString(point.getCoords()), 230 | point.getImmutableBitmap() 231 | ); 232 | } 233 | } else { 234 | level++; 235 | for (ImmutableNode immutableNode : node.getChildren()) { 236 | printNode(immutableNode, level); 237 | } 238 | } 239 | } 240 | 241 | private static String makeDashes(int level) 242 | { 243 | String retVal = ""; 244 | for (int i = 0; i < level; i++) { 245 | retVal += "-"; 246 | } 247 | return retVal; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/Bound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.fasterxml.jackson.annotation.JsonSubTypes; 20 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 | import com.metamx.collections.spatial.ImmutableNode; 22 | import com.metamx.collections.spatial.ImmutablePoint; 23 | 24 | /** 25 | */ 26 | @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, property="type") 27 | @JsonSubTypes(value={ 28 | @JsonSubTypes.Type(name="rectangular", value=RectangularBound.class), 29 | @JsonSubTypes.Type(name="radius", value=RadiusBound.class), 30 | @JsonSubTypes.Type(name="polygon", value=PolygonBound.class) 31 | }) 32 | public interface Bound 33 | { 34 | public int getLimit(); 35 | 36 | public int getNumDims(); 37 | 38 | public boolean overlaps(ImmutableNode node); 39 | 40 | public boolean contains(float[] coords); 41 | 42 | public Iterable filter(Iterable points); 43 | 44 | public byte[] getCacheKey(); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/GutmanSearchStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.google.common.base.Function; 20 | import com.google.common.base.Predicate; 21 | import com.google.common.collect.Iterables; 22 | import com.metamx.collections.bitmap.ImmutableBitmap; 23 | import com.metamx.collections.spatial.ImmutableNode; 24 | import com.metamx.collections.spatial.ImmutablePoint; 25 | 26 | /** 27 | */ 28 | public class GutmanSearchStrategy implements SearchStrategy 29 | { 30 | @Override 31 | public Iterable search(ImmutableNode node, Bound bound) 32 | { 33 | if (bound.getLimit() > 0) { 34 | return Iterables.transform( 35 | breadthFirstSearch(node, bound), 36 | new Function() 37 | { 38 | @Override 39 | public ImmutableBitmap apply(ImmutableNode immutableNode) 40 | { 41 | return immutableNode.getImmutableBitmap(); 42 | } 43 | } 44 | ); 45 | } 46 | 47 | return Iterables.transform( 48 | depthFirstSearch(node, bound), 49 | new Function() 50 | { 51 | @Override 52 | public ImmutableBitmap apply(ImmutablePoint immutablePoint) 53 | { 54 | return immutablePoint.getImmutableBitmap(); 55 | } 56 | } 57 | ); 58 | } 59 | 60 | public Iterable depthFirstSearch(ImmutableNode node, final Bound bound) 61 | { 62 | if (node.isLeaf()) { 63 | return bound.filter( 64 | Iterables.transform( 65 | node.getChildren(), 66 | new Function() 67 | { 68 | @Override 69 | public ImmutablePoint apply(ImmutableNode tNode) 70 | { 71 | return new ImmutablePoint(tNode); 72 | } 73 | } 74 | ) 75 | ); 76 | } else { 77 | return Iterables.concat( 78 | Iterables.transform( 79 | Iterables.filter( 80 | node.getChildren(), 81 | new Predicate() 82 | { 83 | @Override 84 | public boolean apply(ImmutableNode child) 85 | { 86 | return bound.overlaps(child); 87 | } 88 | } 89 | ), 90 | new Function>() 91 | { 92 | @Override 93 | public Iterable apply(ImmutableNode child) 94 | { 95 | return depthFirstSearch(child, bound); 96 | } 97 | } 98 | ) 99 | ); 100 | } 101 | } 102 | 103 | public Iterable breadthFirstSearch( 104 | ImmutableNode node, 105 | final Bound bound 106 | ) 107 | { 108 | if (node.isLeaf()) { 109 | return Iterables.filter( 110 | node.getChildren(), 111 | new Predicate() 112 | { 113 | @Override 114 | public boolean apply(ImmutableNode immutableNode) 115 | { 116 | return bound.contains(immutableNode.getMinCoordinates()); 117 | } 118 | } 119 | ); 120 | } 121 | return breadthFirstSearch(node.getChildren(), bound, 0); 122 | } 123 | 124 | public Iterable breadthFirstSearch( 125 | Iterable nodes, 126 | final Bound bound, 127 | int total 128 | ) 129 | { 130 | Iterable points = Iterables.concat( 131 | Iterables.transform( 132 | Iterables.filter( 133 | nodes, 134 | new Predicate() 135 | { 136 | @Override 137 | public boolean apply(ImmutableNode immutableNode) 138 | { 139 | return immutableNode.isLeaf(); 140 | } 141 | } 142 | ), 143 | new Function>() 144 | { 145 | @Override 146 | public Iterable apply(ImmutableNode immutableNode) 147 | { 148 | return Iterables.filter( 149 | immutableNode.getChildren(), 150 | new Predicate() 151 | { 152 | @Override 153 | public boolean apply(ImmutableNode immutableNode) 154 | { 155 | return bound.contains(immutableNode.getMinCoordinates()); 156 | } 157 | } 158 | ); 159 | } 160 | } 161 | ) 162 | ); 163 | 164 | Iterable overlappingNodes = Iterables.filter( 165 | nodes, 166 | new Predicate() 167 | { 168 | @Override 169 | public boolean apply(ImmutableNode immutableNode) 170 | { 171 | return !immutableNode.isLeaf() && bound.overlaps(immutableNode); 172 | } 173 | } 174 | ); 175 | 176 | int totalPoints = Iterables.size(points); 177 | int totalOverlap = Iterables.size(overlappingNodes); 178 | 179 | if (totalOverlap == 0 || (totalPoints + totalOverlap + total) >= bound.getLimit()) { 180 | return Iterables.concat( 181 | points, 182 | overlappingNodes 183 | ); 184 | } else { 185 | return Iterables.concat( 186 | points, 187 | breadthFirstSearch( 188 | Iterables.concat( 189 | Iterables.transform( 190 | overlappingNodes, 191 | new Function>() 192 | { 193 | @Override 194 | public Iterable apply(ImmutableNode immutableNode) 195 | { 196 | return immutableNode.getChildren(); 197 | } 198 | } 199 | ) 200 | ), 201 | bound, 202 | totalPoints 203 | ) 204 | ); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/PolygonBound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.fasterxml.jackson.annotation.JsonCreator; 20 | import com.fasterxml.jackson.annotation.JsonProperty; 21 | import com.google.common.base.Preconditions; 22 | import com.google.common.base.Predicate; 23 | import com.google.common.collect.Iterables; 24 | import com.google.common.primitives.Floats; 25 | import com.google.common.primitives.Ints; 26 | import com.metamx.collections.spatial.ImmutableNode; 27 | import com.metamx.collections.spatial.ImmutablePoint; 28 | 29 | import java.nio.ByteBuffer; 30 | import java.util.List; 31 | 32 | /** 33 | */ 34 | public class PolygonBound extends RectangularBound 35 | { 36 | private static final byte CACHE_TYPE_ID = 0x02; 37 | 38 | private final float[] abscissa; 39 | private final float[] ordinate; 40 | 41 | @JsonProperty 42 | public float[] getOrdinate() 43 | { 44 | return ordinate; 45 | } 46 | 47 | @JsonProperty 48 | public float[] getAbscissa() 49 | { 50 | return abscissa; 51 | } 52 | 53 | private static float[] getMinCoords(float[] abscissa, float[] ordinate) 54 | { 55 | float[] retVal = new float[2]; 56 | retVal[0] = abscissa[0]; 57 | retVal[1] = ordinate[0]; 58 | 59 | for (int i = 1; i < abscissa.length; i++) { 60 | if (abscissa[i] < retVal[0]) 61 | retVal[0] = abscissa[i]; 62 | if (ordinate[i] < retVal[1]) 63 | retVal[1] = ordinate[i]; 64 | } 65 | return retVal; 66 | } 67 | 68 | private static float[] getMaxCoords(float[] abscissa, float[] ordinate) 69 | { 70 | float[] retVal = new float[2]; 71 | retVal[0] = abscissa[0]; 72 | retVal[1] = ordinate[0]; 73 | for (int i = 1; i < abscissa.length; i++) 74 | { 75 | if (abscissa[i] > retVal[0]) 76 | retVal[0] = abscissa[i]; 77 | if (ordinate[i] > retVal[1]) 78 | retVal[1] = ordinate[i]; 79 | } 80 | return retVal; 81 | } 82 | 83 | /** 84 | * abscissa and ordinate contain the coordinates of polygon. 85 | * abscissa[i] is the horizontal coordinate for the i'th corner of the polygon, 86 | * and ordinate[i] is the vertical coordinate for the i'th corner. 87 | * The polygon must have more than 2 corners, so the length of abscissa or ordinate must be equal or greater than 3. 88 | * 89 | * if the polygon is a rectangular, which corners are {0.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, 90 | * the abscissa should be {0.0, 0.0, 1.0, 1.0} and ordinate should be {0.0, 1.0, 1.0, 0.0} 91 | */ 92 | @JsonCreator 93 | public static PolygonBound from( 94 | @JsonProperty("abscissa") float[] abscissa, 95 | @JsonProperty("ordinate") float[] ordinate, 96 | @JsonProperty("limit") int limit 97 | ) 98 | { 99 | Preconditions.checkArgument(abscissa.length == ordinate.length); //abscissa and ordinate should be the same length 100 | Preconditions.checkArgument(abscissa.length > 2); //a polygon should have more than 2 corners 101 | return new PolygonBound(abscissa, ordinate, limit); 102 | } 103 | 104 | public static PolygonBound from(float[] abscissa, float[] ordinate) 105 | { 106 | return PolygonBound.from(abscissa, ordinate, 0); 107 | } 108 | 109 | private PolygonBound (float[] abscissa, float[] ordinate, int limit) 110 | { 111 | super(getMinCoords(abscissa, ordinate), getMaxCoords(abscissa, ordinate), limit); 112 | this.abscissa = abscissa; 113 | this.ordinate = ordinate; 114 | } 115 | 116 | @Override 117 | public boolean contains(float[] coords) 118 | { 119 | int polyCorners = abscissa.length; 120 | int j = polyCorners - 1; 121 | boolean oddNodes = false; 122 | for (int i = 0; i < polyCorners; i++) 123 | { 124 | if ((ordinate[i] < coords[1] && ordinate[j] >= coords[1] 125 | || ordinate[j] < coords[1] && ordinate[i] >= coords[1]) 126 | && (abscissa[i] <= coords[0] || abscissa[j] <= coords[0])) 127 | { 128 | if (abscissa[i] + (coords[1] - ordinate[i]) / (ordinate[j] - ordinate[i]) * (abscissa[j] - abscissa[i]) < coords[0]) 129 | { 130 | oddNodes = !oddNodes; 131 | } 132 | } 133 | j = i; 134 | } 135 | return oddNodes; 136 | } 137 | 138 | @Override 139 | public Iterable filter(Iterable points) 140 | { 141 | return Iterables.filter( 142 | points, 143 | new Predicate() 144 | { 145 | @Override 146 | public boolean apply(ImmutablePoint immutablePoint) 147 | { 148 | return contains(immutablePoint.getCoords()); 149 | } 150 | } 151 | ); 152 | } 153 | 154 | @Override 155 | public byte[] getCacheKey() 156 | { 157 | ByteBuffer abscissaBuffer = ByteBuffer.allocate(abscissa.length * Floats.BYTES); 158 | abscissaBuffer.asFloatBuffer().put(abscissa); 159 | final byte[] abscissaCacheKey = abscissaBuffer.array(); 160 | 161 | ByteBuffer ordinateBuffer = ByteBuffer.allocate(ordinate.length * Floats.BYTES); 162 | ordinateBuffer.asFloatBuffer().put(ordinate); 163 | final byte[] ordinateCacheKey = ordinateBuffer.array(); 164 | 165 | final ByteBuffer cacheKey = ByteBuffer 166 | .allocate(1 + abscissaCacheKey.length + ordinateCacheKey.length + Ints.BYTES) 167 | .put(abscissaCacheKey) 168 | .put(ordinateCacheKey) 169 | .putInt(getLimit()) 170 | .put(CACHE_TYPE_ID); 171 | 172 | return cacheKey.array(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/RadiusBound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.fasterxml.jackson.annotation.JsonCreator; 20 | import com.fasterxml.jackson.annotation.JsonProperty; 21 | import com.google.common.base.Predicate; 22 | import com.google.common.collect.Iterables; 23 | import com.google.common.primitives.Floats; 24 | import com.google.common.primitives.Ints; 25 | import com.metamx.collections.spatial.ImmutablePoint; 26 | 27 | import java.nio.ByteBuffer; 28 | 29 | /** 30 | */ 31 | public class RadiusBound extends RectangularBound 32 | { 33 | private static final byte CACHE_TYPE_ID = 0x01; 34 | 35 | private static float[] getMinCoords(float[] coords, float radius) 36 | { 37 | float[] retVal = new float[coords.length]; 38 | for (int i = 0; i < coords.length; i++) { 39 | retVal[i] = coords[i] - radius; 40 | } 41 | return retVal; 42 | } 43 | 44 | private static float[] getMaxCoords(float[] coords, float radius) 45 | { 46 | float[] retVal = new float[coords.length]; 47 | for (int i = 0; i < coords.length; i++) { 48 | retVal[i] = coords[i] + radius; 49 | } 50 | return retVal; 51 | } 52 | 53 | private final float[] coords; 54 | private final float radius; 55 | 56 | @JsonCreator 57 | public RadiusBound( 58 | @JsonProperty("coords") float[] coords, 59 | @JsonProperty("radius") float radius, 60 | @JsonProperty("limit") int limit 61 | ) 62 | { 63 | super(getMinCoords(coords, radius), getMaxCoords(coords, radius), limit); 64 | 65 | this.coords = coords; 66 | this.radius = radius; 67 | } 68 | 69 | public RadiusBound( 70 | float[] coords, 71 | float radius 72 | ) 73 | { 74 | this(coords, radius, 0); 75 | } 76 | 77 | @JsonProperty 78 | public float[] getCoords() 79 | { 80 | return coords; 81 | } 82 | 83 | @JsonProperty 84 | public float getRadius() 85 | { 86 | return radius; 87 | } 88 | 89 | @Override 90 | public boolean contains(float[] otherCoords) 91 | { 92 | double total = 0.0; 93 | for (int i = 0; i < coords.length; i++) { 94 | total += Math.pow(otherCoords[i] - coords[i], 2); 95 | } 96 | 97 | return (total <= Math.pow(radius, 2)); 98 | } 99 | 100 | @Override 101 | public Iterable filter(Iterable points) 102 | { 103 | return Iterables.filter( 104 | points, 105 | new Predicate() 106 | { 107 | @Override 108 | public boolean apply(ImmutablePoint point) 109 | { 110 | return contains(point.getCoords()); 111 | } 112 | } 113 | ); 114 | } 115 | 116 | @Override 117 | public byte[] getCacheKey() 118 | { 119 | final ByteBuffer minCoordsBuffer = ByteBuffer.allocate(coords.length * Floats.BYTES); 120 | minCoordsBuffer.asFloatBuffer().put(coords); 121 | final byte[] minCoordsCacheKey = minCoordsBuffer.array(); 122 | final ByteBuffer cacheKey = ByteBuffer 123 | .allocate(1 + minCoordsCacheKey.length + Ints.BYTES + Floats.BYTES) 124 | .put(minCoordsCacheKey) 125 | .putFloat(radius) 126 | .putInt(getLimit()) 127 | .put(CACHE_TYPE_ID); 128 | return cacheKey.array(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/RectangularBound.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.fasterxml.jackson.annotation.JsonCreator; 20 | import com.fasterxml.jackson.annotation.JsonProperty; 21 | import com.google.common.base.Preconditions; 22 | import com.google.common.base.Predicate; 23 | import com.google.common.collect.Iterables; 24 | import com.google.common.primitives.Floats; 25 | import com.google.common.primitives.Ints; 26 | import com.metamx.collections.spatial.ImmutableNode; 27 | import com.metamx.collections.spatial.ImmutablePoint; 28 | 29 | import java.nio.ByteBuffer; 30 | 31 | /** 32 | */ 33 | public class RectangularBound implements Bound 34 | { 35 | private static final byte CACHE_TYPE_ID = 0x0; 36 | 37 | private final float[] minCoords; 38 | private final float[] maxCoords; 39 | private final int limit; 40 | private final int numDims; 41 | 42 | @JsonCreator 43 | public RectangularBound( 44 | @JsonProperty("minCoords") float[] minCoords, 45 | @JsonProperty("maxCoords") float[] maxCoords, 46 | @JsonProperty("limit") int limit 47 | ) 48 | { 49 | Preconditions.checkArgument(minCoords.length == maxCoords.length); 50 | 51 | this.numDims = minCoords.length; 52 | 53 | this.minCoords = minCoords; 54 | this.maxCoords = maxCoords; 55 | this.limit = limit; 56 | } 57 | 58 | public RectangularBound( 59 | float[] minCoords, 60 | float[] maxCoords 61 | ) 62 | { 63 | this(minCoords, maxCoords, 0); 64 | } 65 | 66 | @JsonProperty 67 | public float[] getMinCoords() 68 | { 69 | return minCoords; 70 | } 71 | 72 | @JsonProperty 73 | public float[] getMaxCoords() 74 | { 75 | return maxCoords; 76 | } 77 | 78 | @JsonProperty 79 | public int getLimit() 80 | { 81 | return limit; 82 | } 83 | 84 | @Override 85 | public int getNumDims() 86 | { 87 | return numDims; 88 | } 89 | 90 | @Override 91 | public boolean overlaps(ImmutableNode node) 92 | { 93 | final float[] nodeMinCoords = node.getMinCoordinates(); 94 | final float[] nodeMaxCoords = node.getMaxCoordinates(); 95 | 96 | for (int i = 0; i < numDims; i++) { 97 | if (nodeMaxCoords[i] < minCoords[i] || nodeMinCoords[i] > maxCoords[i]) { 98 | return false; 99 | } 100 | } 101 | 102 | return true; 103 | } 104 | 105 | @Override 106 | public boolean contains(float[] coords) 107 | { 108 | for (int i = 0; i < numDims; i++) { 109 | if (coords[i] < minCoords[i] || coords[i] > maxCoords[i]) { 110 | return false; 111 | } 112 | } 113 | 114 | return true; 115 | } 116 | 117 | @Override 118 | public Iterable filter(Iterable points) 119 | { 120 | return Iterables.filter( 121 | points, 122 | new Predicate() 123 | { 124 | @Override 125 | public boolean apply(ImmutablePoint immutablePoint) 126 | { 127 | return contains(immutablePoint.getCoords()); 128 | } 129 | } 130 | ); 131 | } 132 | 133 | @Override 134 | public byte[] getCacheKey() 135 | { 136 | ByteBuffer minCoordsBuffer = ByteBuffer.allocate(minCoords.length * Floats.BYTES); 137 | minCoordsBuffer.asFloatBuffer().put(minCoords); 138 | final byte[] minCoordsCacheKey = minCoordsBuffer.array(); 139 | 140 | ByteBuffer maxCoordsBuffer = ByteBuffer.allocate(maxCoords.length * Floats.BYTES); 141 | maxCoordsBuffer.asFloatBuffer().put(maxCoords); 142 | final byte[] maxCoordsCacheKey = maxCoordsBuffer.array(); 143 | 144 | final ByteBuffer cacheKey = ByteBuffer 145 | .allocate(1 + minCoordsCacheKey.length + maxCoordsCacheKey.length + Ints.BYTES) 146 | .put(minCoordsCacheKey) 147 | .put(maxCoordsCacheKey) 148 | .putInt(limit) 149 | .put(CACHE_TYPE_ID); 150 | return cacheKey.array(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/search/SearchStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.search; 18 | 19 | import com.metamx.collections.bitmap.ImmutableBitmap; 20 | import com.metamx.collections.spatial.ImmutableNode; 21 | //import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 22 | 23 | 24 | /** 25 | */ 26 | public interface SearchStrategy 27 | { 28 | public Iterable search(ImmutableNode node, Bound bound); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/split/GutmanSplitStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.split; 18 | 19 | import com.google.common.collect.Lists; 20 | import com.metamx.collections.bitmap.BitmapFactory; 21 | import com.metamx.collections.spatial.Node; 22 | import com.metamx.collections.spatial.RTreeUtils; 23 | 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | /** 28 | */ 29 | public abstract class GutmanSplitStrategy implements SplitStrategy 30 | { 31 | private final int minNumChildren; 32 | private final int maxNumChildren; 33 | private final BitmapFactory bf; 34 | 35 | protected GutmanSplitStrategy(int minNumChildren, int maxNumChildren, BitmapFactory b) 36 | { 37 | this.minNumChildren = minNumChildren; 38 | this.maxNumChildren = maxNumChildren; 39 | this.bf = b; 40 | } 41 | 42 | @Override 43 | public boolean needToSplit(Node node) 44 | { 45 | return (node.getChildren().size() > maxNumChildren); 46 | } 47 | 48 | /** 49 | * This algorithm is from the original paper. 50 | * 51 | * Algorithm Split. Divide a set of M+1 index entries into two groups. 52 | * 53 | * S1. [Pick first entry for each group]. Apply Algorithm {@link #pickSeeds(java.util.List)} to choose 54 | * two entries to be the first elements of the groups. Assign each to a group. 55 | * 56 | * S2. [Check if done]. If all entries have been assigned, stop. If one group has so few entries that all the rest 57 | * must be assigned to it in order for it to have the minimum number m, assign them and stop. 58 | * 59 | * S3. [Select entry to assign]. Invoke Algorithm {@link #pickNext(java.util.List, com.metamx.collections.spatial.Node[])} 60 | * to choose the next entry to assign. Add it to the group whose covering rectangle will have to be enlarged least to 61 | * accommodate it. Resolve ties by adding the entry to the group smaller area, then to the one with fewer entries, then 62 | * to either. Repeat from S2. 63 | */ 64 | @Override 65 | public Node[] split(Node node) 66 | { 67 | List children = Lists.newArrayList(node.getChildren()); 68 | Node[] seeds = pickSeeds(children); 69 | 70 | node.clear(); 71 | node.addChild(seeds[0]); 72 | node.addToBitmapIndex(seeds[0]); 73 | 74 | Node group1 = new Node( 75 | Arrays.copyOf(seeds[1].getMinCoordinates(), seeds[1].getMinCoordinates().length), 76 | Arrays.copyOf(seeds[1].getMaxCoordinates(), seeds[1].getMaxCoordinates().length), 77 | Lists.newArrayList(seeds[1]), 78 | node.isLeaf(), 79 | node.getParent(), 80 | bf.makeEmptyMutableBitmap() 81 | ); 82 | group1.addToBitmapIndex(seeds[1]); 83 | if (node.getParent() != null) { 84 | node.getParent().addChild(group1); 85 | } 86 | Node[] groups = new Node[]{ 87 | node, group1 88 | }; 89 | 90 | RTreeUtils.enclose(groups); 91 | 92 | while (!children.isEmpty()) { 93 | for (Node group : groups) { 94 | if (group.getChildren().size() + children.size() <= minNumChildren) { 95 | for (Node child : children) { 96 | group.addToBitmapIndex(child); 97 | group.addChild(child); 98 | } 99 | RTreeUtils.enclose(groups); 100 | return groups; 101 | } 102 | } 103 | 104 | Node nextToAssign = pickNext(children, groups); 105 | double group0ExpandedArea = RTreeUtils.getEnclosingArea(groups[0], nextToAssign); 106 | double group1ExpandedArea = RTreeUtils.getEnclosingArea(groups[1], nextToAssign); 107 | 108 | Node optimal; 109 | if (group0ExpandedArea < group1ExpandedArea) { 110 | optimal = groups[0]; 111 | } else if (group0ExpandedArea == group1ExpandedArea) { 112 | if (groups[0].getArea() < groups[1].getArea()) { 113 | optimal = groups[0]; 114 | } else { 115 | optimal = groups[1]; 116 | } 117 | } else { 118 | optimal = groups[1]; 119 | } 120 | 121 | optimal.addToBitmapIndex(nextToAssign); 122 | optimal.addChild(nextToAssign); 123 | optimal.enclose(); 124 | } 125 | 126 | return groups; 127 | } 128 | 129 | public abstract Node[] pickSeeds(List nodes); 130 | 131 | public abstract Node pickNext(List nodes, Node[] groups); 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/split/LinearGutmanSplitStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.split; 18 | 19 | import com.metamx.collections.spatial.Node; 20 | import com.metamx.collections.bitmap.BitmapFactory; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | */ 26 | public class LinearGutmanSplitStrategy extends GutmanSplitStrategy 27 | { 28 | public LinearGutmanSplitStrategy(int minNumChildren, int maxNumChildren, BitmapFactory bf) 29 | { 30 | super(minNumChildren, maxNumChildren, bf); 31 | } 32 | 33 | /** 34 | * This algorithm is from the original paper. 35 | * 36 | * Algorithm LinearPickSeeds. Select two entries to be the first elements of the groups. 37 | * 38 | * LPS1. [Find extreme rectangles along all dimensions]. Along each dimension, find the entry whose rectangle has 39 | * the highest low side, and the one with the lowest high side. Record the separation. 40 | * 41 | * LPS2. [Adjust for shape of the rectangle cluster]. Normalize the separations by dividing by the width of the 42 | * entire set along the corresponding dimension. 43 | * 44 | * LPS3. [Select the most extreme pair]. Choose the pair with the greatest normalized separation along any dimension. 45 | * 46 | * @param nodes - nodes to choose from 47 | * @return - two groups representing the seeds 48 | */ 49 | @Override 50 | public Node[] pickSeeds(List nodes) 51 | { 52 | int[] optimalIndices = new int[2]; 53 | int numDims = nodes.get(0).getNumDims(); 54 | 55 | double bestNormalized = 0.0; 56 | for (int i = 0; i < numDims; i++) { 57 | float minCoord = Float.MAX_VALUE; 58 | float maxCoord = -Float.MAX_VALUE; 59 | float highestLowSide = -Float.MAX_VALUE; 60 | float lowestHighside = Float.MAX_VALUE; 61 | int highestLowSideIndex = 0; 62 | int lowestHighSideIndex = 0; 63 | 64 | int counter = 0; 65 | for (Node node : nodes) { 66 | minCoord = Math.min(minCoord, node.getMinCoordinates()[i]); 67 | maxCoord = Math.max(maxCoord, node.getMaxCoordinates()[i]); 68 | 69 | if (node.getMinCoordinates()[i] > highestLowSide) { 70 | highestLowSide = node.getMinCoordinates()[i]; 71 | highestLowSideIndex = counter; 72 | } 73 | if (node.getMaxCoordinates()[i] < lowestHighside) { 74 | lowestHighside = node.getMaxCoordinates()[i]; 75 | lowestHighSideIndex = counter; 76 | } 77 | 78 | counter++; 79 | } 80 | double normalizedSeparation = (highestLowSideIndex == lowestHighSideIndex) ? -1.0 : 81 | Math.abs((highestLowSide - lowestHighside) / (maxCoord - minCoord)); 82 | if (normalizedSeparation > bestNormalized) { 83 | optimalIndices[0] = highestLowSideIndex; 84 | optimalIndices[1] = lowestHighSideIndex; 85 | bestNormalized = normalizedSeparation; 86 | } 87 | } 88 | 89 | // Didn't actually find anything, just return first 2 children 90 | if (bestNormalized == 0) { 91 | optimalIndices[0] = 0; 92 | optimalIndices[1] = 1; 93 | } 94 | 95 | int indexToRemove1 = Math.min(optimalIndices[0], optimalIndices[1]); 96 | int indexToRemove2 = Math.max(optimalIndices[0], optimalIndices[1]); 97 | return new Node[]{nodes.remove(indexToRemove1), nodes.remove(indexToRemove2 - 1)}; 98 | } 99 | 100 | /** 101 | * This algorithm is from the original paper. 102 | * 103 | * Algorithm LinearPickNext. PickNext simply choose any of the remaining entries. 104 | * 105 | * @param nodes - remaining nodes 106 | * @param groups - the left and right groups 107 | * @return - the optimal selected node 108 | */ 109 | @Override 110 | public Node pickNext(List nodes, Node[] groups) 111 | { 112 | return nodes.remove(0); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/split/QuadraticGutmanSplitStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.split; 18 | 19 | import com.metamx.collections.spatial.Node; 20 | import com.metamx.collections.spatial.RTreeUtils; 21 | import com.metamx.collections.bitmap.BitmapFactory; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | */ 27 | public class QuadraticGutmanSplitStrategy extends GutmanSplitStrategy 28 | { 29 | public QuadraticGutmanSplitStrategy(int minNumChildren, int maxNumChildren, BitmapFactory bf) 30 | { 31 | super(minNumChildren, maxNumChildren, bf); 32 | } 33 | 34 | @Override 35 | public Node[] pickSeeds(List nodes) 36 | { 37 | double highestCost = Double.MIN_VALUE; 38 | int[] highestCostIndices = new int[2]; 39 | 40 | for (int i = 0; i < nodes.size() - 1; i++) { 41 | for (int j = i + 1; j < nodes.size(); j++) { 42 | double cost = RTreeUtils.getEnclosingArea(nodes.get(i), nodes.get(j)) - 43 | nodes.get(i).getArea() - nodes.get(j).getArea(); 44 | if (cost > highestCost) { 45 | highestCost = cost; 46 | highestCostIndices[0] = i; 47 | highestCostIndices[1] = j; 48 | } 49 | } 50 | } 51 | 52 | return new Node[]{nodes.remove(highestCostIndices[0]), nodes.remove(highestCostIndices[1] - 1)}; 53 | } 54 | 55 | @Override 56 | public Node pickNext(List nodes, Node[] groups) 57 | { 58 | double highestCost = Double.MIN_VALUE; 59 | Node costlyNode = null; 60 | int counter = 0; 61 | int index = -1; 62 | for (Node node : nodes) { 63 | double group0Cost = RTreeUtils.getEnclosingArea(node, groups[0]); 64 | double group1Cost = RTreeUtils.getEnclosingArea(node, groups[1]); 65 | double cost = Math.abs(group0Cost - group1Cost); 66 | if (cost > highestCost) { 67 | highestCost = cost; 68 | costlyNode = node; 69 | index = counter; 70 | } 71 | counter++; 72 | } 73 | 74 | if (costlyNode != null) { 75 | nodes.remove(index); 76 | } 77 | 78 | return costlyNode; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/metamx/collections/spatial/split/SplitStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.split; 18 | 19 | import com.metamx.collections.spatial.Node; 20 | 21 | /** 22 | */ 23 | public interface SplitStrategy 24 | { 25 | public boolean needToSplit(Node node); 26 | 27 | public Node[] split(Node node); 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/IntSetTestUtility.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections; 18 | 19 | import com.google.common.collect.Lists; 20 | import com.google.common.collect.Sets; 21 | import com.metamx.collections.bitmap.ImmutableBitmap; 22 | import com.metamx.collections.bitmap.MutableBitmap; 23 | import org.roaringbitmap.IntIterator; 24 | 25 | import java.util.BitSet; 26 | import java.util.HashSet; 27 | import java.util.Iterator; 28 | import java.util.Set; 29 | 30 | /** 31 | * 32 | */ 33 | public class IntSetTestUtility 34 | { 35 | 36 | private static Set setBits = Sets.newTreeSet(Lists.newArrayList(1, 2, 3, 5, 8, 13, 21)); 37 | public static Set getSetBits(){ 38 | return Sets.newTreeSet(setBits); 39 | } 40 | public static final BitSet createSimpleBitSet(Set setBits){ 41 | BitSet retval = new BitSet(); 42 | for(int i : setBits){ 43 | retval.set(i); 44 | } 45 | return retval; 46 | } 47 | 48 | public static final void addAllToMutable(MutableBitmap mutableBitmap, Iterable intSet){ 49 | for(Integer integer : intSet){ 50 | mutableBitmap.add(integer); 51 | } 52 | } 53 | 54 | private static class IntIt implements Iterable 55 | { 56 | private final Iterator intIter; 57 | public IntIt(IntIterator intIt){ 58 | this.intIter = new IntIter(intIt); 59 | } 60 | 61 | @Override 62 | public Iterator iterator() 63 | { 64 | return intIter; 65 | } 66 | 67 | private static class IntIter implements Iterator 68 | { 69 | private final IntIterator intIt; 70 | 71 | public IntIter(IntIterator intIt) 72 | { 73 | this.intIt = intIt; 74 | } 75 | 76 | @Override 77 | public boolean hasNext() 78 | { 79 | return intIt.hasNext(); 80 | } 81 | 82 | @Override 83 | public Integer next() 84 | { 85 | return intIt.next(); 86 | } 87 | 88 | @Override 89 | public void remove() 90 | { 91 | throw new UnsupportedOperationException("Cannot remove ints from int iterator"); 92 | } 93 | } 94 | } 95 | 96 | public static Boolean equalSets(Set s1, ImmutableBitmap s2){ 97 | Set s3 = new HashSet<>(); 98 | for(Integer i : new IntIt(s2.iterator())){ 99 | s3.add(i); 100 | } 101 | return Sets.difference(s1,s3).isEmpty(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/TestIntegerSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections; 18 | 19 | import com.google.common.collect.Lists; 20 | import com.google.common.collect.Sets; 21 | import com.metamx.collections.bitmap.MutableBitmap; 22 | import com.metamx.collections.bitmap.WrappedBitSetBitmap; 23 | import com.metamx.collections.bitmap.WrappedConciseBitmap; 24 | import com.metamx.collections.bitmap.WrappedRoaringBitmap; 25 | import org.junit.Assert; 26 | import org.junit.Test; 27 | 28 | import java.util.Set; 29 | 30 | /** 31 | * 32 | */ 33 | public class TestIntegerSet 34 | { 35 | @Test 36 | public void testSimpleSet(){ 37 | WrappedBitSetBitmap wrappedBitSetBitmapBitSet = new WrappedBitSetBitmap(); 38 | IntSetTestUtility.addAllToMutable(wrappedBitSetBitmapBitSet,IntSetTestUtility.getSetBits()); 39 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitSetBitmapBitSet); 40 | 41 | Assert.assertTrue(Sets.difference(integerSet, IntSetTestUtility.getSetBits()).isEmpty()); 42 | } 43 | 44 | private static Iterable> clazzes = Lists.newArrayList( 45 | WrappedBitSetBitmap.class, 46 | WrappedConciseBitmap.class, 47 | WrappedRoaringBitmap.class 48 | ); 49 | 50 | @Test 51 | public void testSimpleAdd() throws IllegalAccessException, InstantiationException 52 | { 53 | for(Class clazz : clazzes) { 54 | MutableBitmap wrappedBitmap = clazz.newInstance(); 55 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 56 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 57 | 58 | Set set = IntSetTestUtility.getSetBits(); 59 | set.add(999); 60 | integerSet.add(999); 61 | 62 | Assert.assertTrue(Sets.difference(integerSet, set).isEmpty()); 63 | 64 | integerSet.add(58577); 65 | Assert.assertFalse(Sets.difference(integerSet, set).isEmpty()); 66 | } 67 | } 68 | 69 | @Test 70 | public void testContainsAll() throws IllegalAccessException, InstantiationException 71 | { 72 | for(Class clazz : clazzes) { 73 | MutableBitmap wrappedBitmap = clazz.newInstance(); 74 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 75 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 76 | 77 | Set set = IntSetTestUtility.getSetBits(); 78 | Assert.assertTrue(integerSet.containsAll(set)); 79 | 80 | set.add(999); 81 | Assert.assertFalse(integerSet.containsAll(set)); 82 | } 83 | } 84 | 85 | @Test 86 | public void testRemoveEverything() throws IllegalAccessException, InstantiationException 87 | { 88 | for(Class clazz : clazzes) { 89 | MutableBitmap wrappedBitmap = clazz.newInstance(); 90 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 91 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 92 | 93 | Set set = IntSetTestUtility.getSetBits(); 94 | 95 | integerSet.removeAll(set); 96 | boolean isEmpty = integerSet.isEmpty(); 97 | Assert.assertTrue(isEmpty); 98 | } 99 | } 100 | 101 | @Test 102 | public void testRemoveOneThing() throws IllegalAccessException, InstantiationException 103 | { 104 | for(Class clazz : clazzes) { 105 | MutableBitmap wrappedBitmap = clazz.newInstance(); 106 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 107 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 108 | 109 | Set set = IntSetTestUtility.getSetBits(); 110 | 111 | integerSet.remove(1); 112 | set.remove(1); 113 | 114 | Assert.assertTrue(Sets.difference(set, integerSet).isEmpty()); 115 | } 116 | } 117 | 118 | 119 | @Test 120 | public void testIsEmpty() throws IllegalAccessException, InstantiationException 121 | { 122 | for(Class clazz : clazzes) { 123 | MutableBitmap wrappedBitmap = clazz.newInstance(); 124 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 125 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 126 | 127 | Assert.assertFalse(integerSet.isEmpty()); 128 | 129 | integerSet.clear(); 130 | 131 | Assert.assertTrue(integerSet.isEmpty()); 132 | 133 | integerSet.add(1); 134 | Assert.assertFalse(integerSet.isEmpty()); 135 | } 136 | } 137 | 138 | @Test 139 | public void testSize() throws IllegalAccessException, InstantiationException 140 | { 141 | for(Class clazz : clazzes) { 142 | MutableBitmap wrappedBitmap = clazz.newInstance(); 143 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 144 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 145 | 146 | Set set = IntSetTestUtility.getSetBits(); 147 | 148 | Assert.assertEquals(set.size(), integerSet.size()); 149 | } 150 | } 151 | 152 | 153 | @Test 154 | public void testRetainAll() throws IllegalAccessException, InstantiationException 155 | { 156 | for(Class clazz : clazzes) { 157 | MutableBitmap wrappedBitmap = clazz.newInstance(); 158 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 159 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 160 | 161 | Set set = IntSetTestUtility.getSetBits(); 162 | 163 | set.remove(1); 164 | set.add(9999); 165 | 166 | boolean threwError = false; 167 | try { 168 | integerSet.retainAll(set); 169 | }catch(UnsupportedOperationException ex){ 170 | threwError = true; 171 | } 172 | Assert.assertTrue(threwError); 173 | } 174 | } 175 | 176 | @Test 177 | public void testIntOverflow() throws IllegalAccessException, InstantiationException 178 | { 179 | for(Class clazz : clazzes) { 180 | Exception e = null; 181 | try { 182 | MutableBitmap wrappedBitmap = clazz.newInstance(); 183 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 184 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 185 | integerSet.add(Integer.MAX_VALUE + 1); 186 | }catch(java.lang.IllegalArgumentException ex){ 187 | e = ex; 188 | } 189 | Assert.assertNotNull(e); 190 | } 191 | } 192 | 193 | @Test 194 | public void testToArray() throws IllegalAccessException, InstantiationException 195 | { 196 | for(Class clazz : clazzes) { 197 | Exception e = null; 198 | MutableBitmap wrappedBitmap = clazz.newInstance(); 199 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 200 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 201 | Set set = Sets.newHashSet((Integer[]) integerSet.toArray()); 202 | Assert.assertTrue(Sets.difference(integerSet, set).isEmpty()); 203 | } 204 | } 205 | 206 | 207 | @Test 208 | public void testToSmallArray() throws IllegalAccessException, InstantiationException 209 | { 210 | for(Class clazz : clazzes) { 211 | Exception e = null; 212 | MutableBitmap wrappedBitmap = clazz.newInstance(); 213 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 214 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 215 | Set set = Sets.newHashSet((Integer[]) integerSet.toArray(new Integer[0])); 216 | Assert.assertTrue(Sets.difference(integerSet, set).isEmpty()); 217 | } 218 | } 219 | 220 | 221 | @Test 222 | public void testToBigArray() throws IllegalAccessException, InstantiationException 223 | { 224 | for(Class clazz : clazzes) { 225 | Exception e = null; 226 | MutableBitmap wrappedBitmap = clazz.newInstance(); 227 | IntSetTestUtility.addAllToMutable(wrappedBitmap, IntSetTestUtility.getSetBits()); 228 | IntegerSet integerSet = IntegerSet.wrap(wrappedBitmap); 229 | 230 | Integer[] bigArray = new Integer[1024]; 231 | integerSet.toArray(bigArray); 232 | Set set = Sets.newHashSet(bigArray); 233 | Assert.assertTrue(Sets.difference(integerSet, set).isEmpty()); 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/BitmapBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.carrotsearch.junitbenchmarks.BenchmarkOptions; 20 | import com.carrotsearch.junitbenchmarks.BenchmarkRule; 21 | import com.carrotsearch.junitbenchmarks.Clock; 22 | import com.google.common.collect.Lists; 23 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 24 | import org.junit.Assert; 25 | import org.junit.Rule; 26 | import org.junit.Test; 27 | import org.junit.rules.TestRule; 28 | import org.roaringbitmap.buffer.BufferFastAggregation; 29 | import org.roaringbitmap.buffer.ImmutableRoaringBitmap; 30 | import org.roaringbitmap.buffer.MutableRoaringBitmap; 31 | 32 | import java.io.ByteArrayOutputStream; 33 | import java.io.DataOutputStream; 34 | import java.io.IOException; 35 | import java.nio.ByteBuffer; 36 | import java.util.Random; 37 | 38 | 39 | @BenchmarkOptions(clock = Clock.NANO_TIME, benchmarkRounds = 50) 40 | public class BitmapBenchmark 41 | { 42 | public static final int LENGTH = 500_000; 43 | public static final int SIZE = 10_000; 44 | 45 | @Rule 46 | public TestRule benchmarkRun = new BenchmarkRule(); 47 | 48 | final static ImmutableConciseSet concise[] = new ImmutableConciseSet[SIZE]; 49 | final static ImmutableConciseSet offheapConcise[] = new ImmutableConciseSet[SIZE]; 50 | final static ImmutableRoaringBitmap roaring[] = new ImmutableRoaringBitmap[SIZE]; 51 | final static ImmutableRoaringBitmap immutableRoaring[] = new ImmutableRoaringBitmap[SIZE]; 52 | final static ImmutableRoaringBitmap offheapRoaring[] = new ImmutableRoaringBitmap[SIZE]; 53 | final static ImmutableBitmap genericConcise[] = new ImmutableBitmap[SIZE]; 54 | final static ImmutableBitmap genericRoaring[] = new ImmutableBitmap[SIZE]; 55 | final static ConciseBitmapFactory conciseFactory = new ConciseBitmapFactory(); 56 | final static RoaringBitmapFactory roaringFactory = new RoaringBitmapFactory(); 57 | 58 | static Random rand = new Random(0); 59 | static long totalConciseBytes = 0; 60 | static long totalRoaringBytes = 0; 61 | static long conciseCount = 0; 62 | static long roaringCount = 0; 63 | static long unionCount = 0; 64 | static long minIntersection = 0; 65 | 66 | protected static ImmutableConciseSet makeOffheapConcise(ImmutableConciseSet concise) 67 | { 68 | final byte[] bytes = concise.toBytes(); 69 | totalConciseBytes += bytes.length; 70 | conciseCount++; 71 | final ByteBuffer buf = ByteBuffer.allocateDirect(bytes.length).put(bytes); 72 | buf.rewind(); 73 | return new ImmutableConciseSet(buf); 74 | } 75 | 76 | protected static ImmutableRoaringBitmap writeImmutable(MutableRoaringBitmap r, ByteBuffer buf) throws IOException 77 | { 78 | final ByteArrayOutputStream out = new ByteArrayOutputStream(); 79 | r.serialize(new DataOutputStream(out)); 80 | final byte[] bytes = out.toByteArray(); 81 | Assert.assertEquals(buf.remaining(), bytes.length); 82 | buf.put(bytes); 83 | buf.rewind(); 84 | return new ImmutableRoaringBitmap(buf.asReadOnlyBuffer()); 85 | } 86 | 87 | protected static void reset() 88 | { 89 | conciseCount = 0; 90 | roaringCount = 0; 91 | totalConciseBytes = 0; 92 | totalRoaringBytes = 0; 93 | unionCount = 0; 94 | minIntersection = 0; 95 | rand = new Random(0); 96 | } 97 | 98 | protected static void printSizeStats(double density, String name) 99 | { 100 | System.out.println(""); 101 | System.out.println("## " + name); 102 | System.out.println(""); 103 | System.out.printf(" d = %06.5f | Concise | Roaring" + System.lineSeparator(), density); 104 | System.out.println("-------------|---------|---------"); 105 | System.out.printf ("Count | %5d | %5d " + System.lineSeparator(), conciseCount, roaringCount); 106 | System.out.printf ("Average size | %5d | %5d " + System.lineSeparator(), totalConciseBytes / conciseCount, totalRoaringBytes / roaringCount); 107 | System.out.println("-------------|---------|---------"); 108 | System.out.println(""); 109 | System.out.flush(); 110 | } 111 | 112 | protected static ImmutableRoaringBitmap makeOffheapRoaring(MutableRoaringBitmap r) throws IOException 113 | { 114 | final int size = r.serializedSizeInBytes(); 115 | final ByteBuffer buf = ByteBuffer.allocateDirect(size); 116 | totalRoaringBytes += size; 117 | roaringCount++; 118 | return writeImmutable(r, buf); 119 | } 120 | 121 | protected static ImmutableRoaringBitmap makeImmutableRoaring(MutableRoaringBitmap r) throws IOException 122 | { 123 | final ByteBuffer buf = ByteBuffer.allocate(r.serializedSizeInBytes()); 124 | return writeImmutable(r, buf); 125 | } 126 | 127 | @Test @BenchmarkOptions(warmupRounds = 1, benchmarkRounds = 2) 128 | public void timeConciseUnion() throws Exception 129 | { 130 | ImmutableConciseSet union = ImmutableConciseSet.union(concise); 131 | Assert.assertEquals(unionCount, union.size()); 132 | } 133 | 134 | @Test @BenchmarkOptions(warmupRounds = 1, benchmarkRounds = 2) 135 | public void timeOffheapConciseUnion() throws Exception 136 | { 137 | ImmutableConciseSet union = ImmutableConciseSet.union(offheapConcise); 138 | Assert.assertEquals(unionCount, union.size()); 139 | } 140 | 141 | @Test @BenchmarkOptions(warmupRounds = 1, benchmarkRounds = 2) 142 | public void timeGenericConciseUnion() throws Exception 143 | { 144 | ImmutableBitmap union = conciseFactory.union(Lists.newArrayList(genericConcise)); 145 | Assert.assertEquals(unionCount, union.size()); 146 | } 147 | 148 | @Test @BenchmarkOptions(warmupRounds = 1, benchmarkRounds = 5) 149 | public void timeGenericConciseIntersection() throws Exception 150 | { 151 | ImmutableBitmap intersection = conciseFactory.intersection(Lists.newArrayList(genericConcise)); 152 | Assert.assertTrue(intersection.size() >= minIntersection); 153 | } 154 | 155 | @Test 156 | public void timeRoaringUnion() throws Exception 157 | { 158 | ImmutableRoaringBitmap union = BufferFastAggregation.horizontal_or(Lists.newArrayList(roaring).iterator()); 159 | Assert.assertEquals(unionCount, union.getCardinality()); 160 | } 161 | 162 | @Test 163 | public void timeImmutableRoaringUnion() throws Exception 164 | { 165 | ImmutableRoaringBitmap union = BufferFastAggregation.horizontal_or(Lists.newArrayList(immutableRoaring).iterator()); 166 | Assert.assertEquals(unionCount, union.getCardinality()); 167 | } 168 | 169 | @Test 170 | public void timeOffheapRoaringUnion() throws Exception 171 | { 172 | ImmutableRoaringBitmap union = BufferFastAggregation.horizontal_or(Lists.newArrayList(offheapRoaring).iterator()); 173 | Assert.assertEquals(unionCount, union.getCardinality()); 174 | } 175 | 176 | @Test 177 | public void timeGenericRoaringUnion() throws Exception 178 | { 179 | ImmutableBitmap union = roaringFactory.union(Lists.newArrayList(genericRoaring)); 180 | Assert.assertEquals(unionCount, union.size()); 181 | } 182 | 183 | @Test 184 | public void timeGenericRoaringIntersection() throws Exception 185 | { 186 | ImmutableBitmap intersection = roaringFactory.intersection(Lists.newArrayList(genericRoaring)); 187 | Assert.assertTrue(intersection.size() >= minIntersection); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/ConciseBitmapFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.base.Function; 20 | import com.google.common.collect.ImmutableSet; 21 | import com.google.common.collect.Iterables; 22 | import com.google.common.collect.Lists; 23 | import it.uniroma3.mat.extendedset.intset.ConciseSet; 24 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 25 | import junit.framework.Assert; 26 | import org.junit.Test; 27 | 28 | import java.util.Arrays; 29 | import java.util.Set; 30 | 31 | public class ConciseBitmapFactoryTest 32 | { 33 | @Test 34 | public void testUnwrapWithNull() throws Exception 35 | { 36 | ConciseBitmapFactory factory = new ConciseBitmapFactory(); 37 | 38 | ImmutableBitmap bitmap = factory.union( 39 | Iterables.transform( 40 | Lists.newArrayList(new WrappedConciseBitmap()), 41 | new Function() 42 | { 43 | @Override 44 | public ImmutableBitmap apply(WrappedConciseBitmap input) 45 | { 46 | return null; 47 | } 48 | } 49 | ) 50 | ); 51 | 52 | Assert.assertEquals(0, bitmap.size()); 53 | } 54 | 55 | @Test 56 | public void testUnwrapMerge() throws Exception 57 | { 58 | ConciseBitmapFactory factory = new ConciseBitmapFactory(); 59 | 60 | WrappedConciseBitmap set = new WrappedConciseBitmap(); 61 | set.add(1); 62 | set.add(3); 63 | set.add(5); 64 | 65 | ImmutableBitmap bitmap = factory.union( 66 | Arrays.asList( 67 | factory.makeImmutableBitmap(set), 68 | null 69 | ) 70 | ); 71 | 72 | Assert.assertEquals(3, bitmap.size()); 73 | } 74 | 75 | @Test 76 | public void testGetOutOfBounds() 77 | { 78 | final ConciseSet conciseSet = new ConciseSet(); 79 | final Set ints = ImmutableSet.of(0, 4, 9); 80 | for (int i : ints) { 81 | conciseSet.add(i); 82 | } 83 | final ImmutableBitmap immutableBitmap = new WrappedImmutableConciseBitmap( 84 | ImmutableConciseSet.newImmutableFromMutable(conciseSet)); 85 | final MutableBitmap mutableBitmap = new WrappedConciseBitmap(conciseSet); 86 | for (int i = 0; i < 10; ++i) { 87 | Assert.assertEquals(Integer.toString(i), ints.contains(i), mutableBitmap.get(i)); 88 | Assert.assertEquals(Integer.toString(i), ints.contains(i), immutableBitmap.get(i)); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/RangeBitmapBenchmarkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart; 20 | import com.carrotsearch.junitbenchmarks.annotation.LabelType; 21 | import com.metamx.test.annotation.Benchmark; 22 | import it.uniroma3.mat.extendedset.intset.ConciseSet; 23 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 24 | import org.junit.BeforeClass; 25 | import org.junit.experimental.categories.Category; 26 | import org.roaringbitmap.buffer.MutableRoaringBitmap; 27 | 28 | import java.util.BitSet; 29 | 30 | @Category({Benchmark.class}) 31 | @BenchmarkHistoryChart(labelWith = LabelType.CUSTOM_KEY, maxRuns = 20) 32 | public class RangeBitmapBenchmarkTest extends BitmapBenchmark 33 | { 34 | 35 | public static final double DENSITY = 0.001; 36 | public static final int MIN_INTERSECT = 50; 37 | 38 | @BeforeClass 39 | public static void prepareRandomRanges() throws Exception 40 | { 41 | System.setProperty("jub.customkey", String.format("%06.5f", DENSITY)); 42 | reset(); 43 | 44 | final BitSet expectedUnion = new BitSet(); 45 | for (int i = 0; i < SIZE; ++i) { 46 | ConciseSet c = new ConciseSet(); 47 | MutableRoaringBitmap r = new MutableRoaringBitmap(); 48 | { 49 | int k = 0; 50 | boolean fill = true; 51 | while (k < LENGTH) { 52 | int runLength = (int) (LENGTH * DENSITY) + rand.nextInt((int) (LENGTH * DENSITY)); 53 | for (int j = k; fill && j < LENGTH && j < k + runLength; ++j) { 54 | c.add(j); 55 | r.add(j); 56 | expectedUnion.set(j); 57 | } 58 | k += runLength; 59 | fill = !fill; 60 | } 61 | } 62 | minIntersection = MIN_INTERSECT; 63 | for (int k = LENGTH / 2; k < LENGTH / 2 + minIntersection; ++k) { 64 | c.add(k); 65 | r.add(k); 66 | expectedUnion.set(k); 67 | } 68 | concise[i] = ImmutableConciseSet.newImmutableFromMutable(c); 69 | offheapConcise[i] = makeOffheapConcise(concise[i]); 70 | roaring[i] = r; 71 | immutableRoaring[i] = makeImmutableRoaring(r); 72 | offheapRoaring[i] = makeOffheapRoaring(r); 73 | genericConcise[i] = new WrappedImmutableConciseBitmap(offheapConcise[i]); 74 | genericRoaring[i] = new WrappedImmutableRoaringBitmap(offheapRoaring[i]); 75 | } 76 | unionCount = expectedUnion.cardinality(); 77 | printSizeStats(DENSITY, "Random Alternating Bitmap"); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/RoaringBitmapFactoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.base.Function; 20 | import com.google.common.collect.Iterables; 21 | import com.google.common.collect.Lists; 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.roaringbitmap.IntIterator; 25 | 26 | import java.util.Arrays; 27 | 28 | public class RoaringBitmapFactoryTest 29 | { 30 | 31 | // testing https://github.com/metamx/bytebuffer-collections/issues/26 32 | @Test 33 | public void testIssue26() throws Exception 34 | { 35 | checkEmptyComplement(new ConciseBitmapFactory()); 36 | checkEmptyComplement(new RoaringBitmapFactory()); 37 | } 38 | 39 | // used by issue 26 40 | private void checkEmptyComplement(BitmapFactory bitmapFactory) throws Exception 41 | { 42 | int numRow = 5104234; 43 | ImmutableBitmap bitmap = bitmapFactory.complement(bitmapFactory.makeEmptyImmutableBitmap(), numRow); 44 | ImmutableBitmap notBitmap = bitmapFactory.complement(bitmap,numRow); 45 | Assert.assertTrue(notBitmap.size() == 0); 46 | Assert.assertTrue(notBitmap.isEmpty()); 47 | IntIterator intIter = notBitmap.iterator(); 48 | Assert.assertFalse(intIter.hasNext()); 49 | } 50 | 51 | @Test 52 | public void testUnwrapWithNull() throws Exception 53 | { 54 | RoaringBitmapFactory factory = new RoaringBitmapFactory(); 55 | 56 | ImmutableBitmap bitmap = factory.union( 57 | Iterables.transform( 58 | Lists.newArrayList(new WrappedRoaringBitmap()), 59 | new Function() 60 | { 61 | @Override 62 | public ImmutableBitmap apply(WrappedRoaringBitmap input) 63 | { 64 | return null; 65 | } 66 | } 67 | ) 68 | ); 69 | 70 | Assert.assertEquals(0, bitmap.size()); 71 | } 72 | 73 | @Test 74 | public void testUnwrapMerge() throws Exception 75 | { 76 | RoaringBitmapFactory factory = new RoaringBitmapFactory(); 77 | 78 | WrappedRoaringBitmap set = new WrappedRoaringBitmap(); 79 | set.add(1); 80 | set.add(3); 81 | set.add(5); 82 | 83 | ImmutableBitmap bitmap = factory.union( 84 | Arrays.asList( 85 | factory.makeImmutableBitmap(set), 86 | null 87 | ) 88 | ); 89 | 90 | Assert.assertEquals(3, bitmap.size()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/UniformBitmapBenchmarkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.carrotsearch.junitbenchmarks.annotation.BenchmarkHistoryChart; 20 | import com.carrotsearch.junitbenchmarks.annotation.LabelType; 21 | import com.metamx.test.annotation.Benchmark; 22 | import it.uniroma3.mat.extendedset.intset.ConciseSet; 23 | import it.uniroma3.mat.extendedset.intset.ImmutableConciseSet; 24 | import org.junit.BeforeClass; 25 | import org.junit.experimental.categories.Category; 26 | import org.roaringbitmap.buffer.MutableRoaringBitmap; 27 | 28 | import java.util.BitSet; 29 | 30 | @Category({Benchmark.class}) 31 | @BenchmarkHistoryChart(labelWith = LabelType.CUSTOM_KEY, maxRuns = 20) 32 | public class UniformBitmapBenchmarkTest extends BitmapBenchmark 33 | { 34 | 35 | public static final double DENSITY = 0.01; 36 | public static final int MIN_INTERSECT = 50; 37 | 38 | @BeforeClass 39 | public static void prepareMostlyUniform() throws Exception 40 | { 41 | System.setProperty("jub.customkey", String.format("%05.4f", DENSITY)); 42 | reset(); 43 | 44 | final BitSet expectedUnion = new BitSet(); 45 | final int[] knownTrue = new int[MIN_INTERSECT]; 46 | for (int i = 0; i < knownTrue.length; ++i) { 47 | knownTrue[i] = rand.nextInt(LENGTH); 48 | } 49 | for (int i = 0; i < SIZE; ++i) { 50 | ConciseSet c = new ConciseSet(); 51 | MutableRoaringBitmap r = new MutableRoaringBitmap(); 52 | for (int k = 0; k < LENGTH; ++k) { 53 | if (rand.nextDouble() < DENSITY) { 54 | c.add(k); 55 | r.add(k); 56 | expectedUnion.set(k); 57 | } 58 | } 59 | for (int k : knownTrue) { 60 | c.add(k); 61 | r.add(k); 62 | expectedUnion.set(k); 63 | } 64 | concise[i] = ImmutableConciseSet.newImmutableFromMutable(c); 65 | offheapConcise[i] = makeOffheapConcise(concise[i]); 66 | roaring[i] = r; 67 | immutableRoaring[i] = makeImmutableRoaring(r); 68 | offheapRoaring[i] = makeOffheapRoaring(r); 69 | genericConcise[i] = new WrappedImmutableConciseBitmap(offheapConcise[i]); 70 | genericRoaring[i] = new WrappedImmutableRoaringBitmap(offheapRoaring[i]); 71 | } 72 | unionCount = expectedUnion.cardinality(); 73 | minIntersection = knownTrue.length; 74 | printSizeStats(DENSITY, "Uniform Bitmap"); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/WrappedBitSetBitmapBitSetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.google.common.collect.Sets; 20 | import com.metamx.collections.IntSetTestUtility; 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | import org.roaringbitmap.IntIterator; 24 | 25 | import java.nio.ByteBuffer; 26 | import java.nio.ByteOrder; 27 | import java.util.BitSet; 28 | import java.util.Set; 29 | 30 | /** 31 | * 32 | */ 33 | public class WrappedBitSetBitmapBitSetTest 34 | { 35 | 36 | private static final WrappedBitSetBitmap defaultBitSet(){ 37 | return new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(IntSetTestUtility.getSetBits())); 38 | } 39 | @Test 40 | public void testIterator(){ 41 | WrappedBitSetBitmap bitSet = new WrappedBitSetBitmap(); 42 | for(int i : IntSetTestUtility.getSetBits()){ 43 | bitSet.add(i); 44 | } 45 | IntIterator intIt = bitSet.iterator(); 46 | for(int i : IntSetTestUtility.getSetBits()){ 47 | Assert.assertTrue(intIt.hasNext()); 48 | Assert.assertEquals(i,intIt.next()); 49 | } 50 | } 51 | 52 | @Test 53 | public void testSize(){ 54 | BitSet bitSet = IntSetTestUtility.createSimpleBitSet(IntSetTestUtility.getSetBits()); 55 | WrappedBitSetBitmap wrappedBitSetBitmapBitSet = new WrappedBitSetBitmap(bitSet); 56 | Assert.assertEquals(bitSet.cardinality(), wrappedBitSetBitmapBitSet.size()); 57 | } 58 | 59 | @Test 60 | public void testOffHeap(){ 61 | ByteBuffer buffer = ByteBuffer.allocateDirect(Long.SIZE * 100 / 8).order(ByteOrder.LITTLE_ENDIAN); 62 | BitSet testSet = BitSet.valueOf(buffer); 63 | testSet.set(1); 64 | WrappedImmutableBitSetBitmap bitMap = new WrappedImmutableBitSetBitmap(testSet); 65 | Assert.assertTrue(bitMap.get(1)); 66 | testSet.set(2); 67 | Assert.assertTrue(bitMap.get(2)); 68 | } 69 | @Test 70 | public void testSimpleBitSet(){ 71 | WrappedBitSetBitmap bitSet = new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(IntSetTestUtility.getSetBits())); 72 | Assert.assertTrue(IntSetTestUtility.equalSets(IntSetTestUtility.getSetBits(), bitSet)); 73 | } 74 | 75 | @Test 76 | public void testUnion(){ 77 | WrappedBitSetBitmap bitSet = new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(IntSetTestUtility.getSetBits())); 78 | 79 | Set extraBits = Sets.newHashSet(6,9); 80 | WrappedBitSetBitmap bitExtraSet = new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(extraBits)); 81 | 82 | Set union = Sets.union(extraBits, IntSetTestUtility.getSetBits()); 83 | 84 | Assert.assertTrue(IntSetTestUtility.equalSets(union, (WrappedBitSetBitmap) bitSet.union(bitExtraSet))); 85 | } 86 | @Test 87 | public void testIntersection(){ 88 | WrappedBitSetBitmap bitSet = new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(IntSetTestUtility.getSetBits())); 89 | 90 | Set extraBits = Sets.newHashSet(1,2,3,4,5,6,7,8); 91 | WrappedBitSetBitmap bitExtraSet = new WrappedBitSetBitmap(IntSetTestUtility.createSimpleBitSet(extraBits)); 92 | 93 | Set intersection = Sets.intersection(extraBits, IntSetTestUtility.getSetBits()); 94 | 95 | Assert.assertTrue(IntSetTestUtility.equalSets(intersection, (WrappedBitSetBitmap) bitSet.intersection(bitExtraSet))); 96 | } 97 | 98 | @Test 99 | public void testAnd(){ 100 | WrappedBitSetBitmap bitSet = defaultBitSet(); 101 | WrappedBitSetBitmap bitSet2 = defaultBitSet(); 102 | Set defaultBitSet = IntSetTestUtility.getSetBits(); 103 | bitSet.remove(1); 104 | bitSet2.remove(2); 105 | 106 | bitSet.and(bitSet2); 107 | 108 | defaultBitSet.remove(1); 109 | defaultBitSet.remove(2); 110 | 111 | Assert.assertTrue(IntSetTestUtility.equalSets(defaultBitSet,bitSet)); 112 | } 113 | 114 | 115 | @Test 116 | public void testOr(){ 117 | WrappedBitSetBitmap bitSet = defaultBitSet(); 118 | WrappedBitSetBitmap bitSet2 = defaultBitSet(); 119 | Set defaultBitSet = IntSetTestUtility.getSetBits(); 120 | bitSet.remove(1); 121 | bitSet2.remove(2); 122 | 123 | bitSet.or(bitSet2); 124 | 125 | Assert.assertTrue(IntSetTestUtility.equalSets(defaultBitSet,bitSet)); 126 | } 127 | 128 | @Test 129 | public void testAndNot(){ 130 | WrappedBitSetBitmap bitSet = defaultBitSet(); 131 | WrappedBitSetBitmap bitSet2 = defaultBitSet(); 132 | Set defaultBitSet = Sets.newHashSet(); 133 | bitSet.remove(1); 134 | bitSet2.remove(2); 135 | 136 | bitSet.andNot(bitSet2); 137 | 138 | defaultBitSet.add(2); 139 | 140 | Assert.assertTrue(IntSetTestUtility.equalSets(defaultBitSet,bitSet)); 141 | } 142 | 143 | 144 | @Test 145 | public void testSerialize(){ 146 | WrappedBitSetBitmap bitSet = defaultBitSet(); 147 | Set defaultBitSet = IntSetTestUtility.getSetBits(); 148 | byte[] buffer = new byte[bitSet.getSizeInBytes()]; 149 | ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); 150 | bitSet.serialize(byteBuffer); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/bitmap/WrappedRoaringBitmapTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.bitmap; 18 | 19 | import com.metamx.collections.IntSetTestUtility; 20 | import java.io.IOException; 21 | import java.nio.ByteBuffer; 22 | import java.util.Arrays; 23 | import java.util.BitSet; 24 | import java.util.List; 25 | import java.util.Set; 26 | import junit.framework.Assert; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | import org.junit.runners.Parameterized; 30 | 31 | @RunWith(Parameterized.class) 32 | public class WrappedRoaringBitmapTest 33 | { 34 | @Parameterized.Parameters 35 | public static List factoryClasses() 36 | { 37 | return Arrays.asList( 38 | (RoaringBitmapFactory[]) Arrays.asList( 39 | new RoaringBitmapFactory(false) 40 | ).toArray(), 41 | (RoaringBitmapFactory[]) Arrays.asList( 42 | new RoaringBitmapFactory(true) 43 | ).toArray() 44 | ); 45 | } 46 | 47 | private final RoaringBitmapFactory factory; 48 | 49 | public WrappedRoaringBitmapTest(RoaringBitmapFactory factory) 50 | { 51 | this.factory = factory; 52 | } 53 | 54 | private WrappedRoaringBitmap createWrappedRoaringBitmap() 55 | { 56 | WrappedRoaringBitmap set = (WrappedRoaringBitmap) factory.makeEmptyMutableBitmap(); 57 | set.add(1); 58 | set.add(3); 59 | set.add(5); 60 | set.add(7); 61 | set.add(9); 62 | return set; 63 | } 64 | 65 | @Test 66 | public void testSerialize() 67 | { 68 | WrappedRoaringBitmap set = createWrappedRoaringBitmap(); 69 | 70 | byte[] buffer = new byte[set.getSizeInBytes()]; 71 | ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); 72 | set.serialize(byteBuffer); 73 | byteBuffer.flip(); 74 | ImmutableBitmap immutableBitmap = new RoaringBitmapFactory().mapImmutableBitmap(byteBuffer); 75 | Assert.assertEquals(5, immutableBitmap.size()); 76 | } 77 | 78 | @Test 79 | public void testToByteArray() 80 | { 81 | WrappedRoaringBitmap set = createWrappedRoaringBitmap(); 82 | ImmutableBitmap immutableBitmap = new RoaringBitmapFactory().mapImmutableBitmap(ByteBuffer.wrap(set.toBytes())); 83 | Assert.assertEquals(5, immutableBitmap.size()); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/spatial/RTreeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial; 18 | 19 | import com.metamx.collections.bitmap.BitmapFactory; 20 | import com.metamx.collections.bitmap.ConciseBitmapFactory; 21 | import com.metamx.collections.bitmap.RoaringBitmapFactory; 22 | import com.metamx.collections.spatial.split.LinearGutmanSplitStrategy; 23 | 24 | import junit.framework.Assert; 25 | 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | 29 | import java.util.Arrays; 30 | import java.util.Random; 31 | 32 | /** 33 | */ 34 | public class RTreeTest 35 | { 36 | private RTree tree; 37 | private RTree roaringtree; 38 | 39 | @Before 40 | public void setUp() throws Exception 41 | { 42 | BitmapFactory bf = new ConciseBitmapFactory(); 43 | tree = new RTree(2, new LinearGutmanSplitStrategy(0, 50, bf), bf ); 44 | BitmapFactory rbf = new RoaringBitmapFactory(); 45 | roaringtree = new RTree(2, new LinearGutmanSplitStrategy(0, 50, rbf), rbf ); 46 | 47 | } 48 | 49 | @Test 50 | public void testInsertNoSplit() 51 | { 52 | float[] elem = new float[]{5, 5}; 53 | tree.insert(elem, 1); 54 | Assert.assertTrue(Arrays.equals(elem, tree.getRoot().getMinCoordinates())); 55 | Assert.assertTrue(Arrays.equals(elem, tree.getRoot().getMaxCoordinates())); 56 | 57 | tree.insert(new float[]{6, 7}, 2); 58 | tree.insert(new float[]{1, 3}, 3); 59 | tree.insert(new float[]{10, 4}, 4); 60 | tree.insert(new float[]{8, 2}, 5); 61 | 62 | Assert.assertEquals(tree.getRoot().getChildren().size(), 5); 63 | 64 | float[] expectedMin = new float[]{1, 2}; 65 | float[] expectedMax = new float[]{10, 7}; 66 | 67 | Assert.assertTrue(Arrays.equals(expectedMin, tree.getRoot().getMinCoordinates())); 68 | Assert.assertTrue(Arrays.equals(expectedMax, tree.getRoot().getMaxCoordinates())); 69 | Assert.assertEquals(tree.getRoot().getArea(), 45.0d); 70 | } 71 | 72 | @Test 73 | public void testInsertDuplicatesNoSplit() 74 | { 75 | tree.insert(new float[]{1, 1}, 1); 76 | tree.insert(new float[]{1, 1}, 1); 77 | tree.insert(new float[]{1, 1}, 1); 78 | 79 | Assert.assertEquals(tree.getRoot().getChildren().size(), 3); 80 | } 81 | 82 | @Test 83 | public void testInsertDuplicatesNoSplitRoaring() 84 | { 85 | roaringtree.insert(new float[]{1, 1}, 1); 86 | roaringtree.insert(new float[]{1, 1}, 1); 87 | roaringtree.insert(new float[]{1, 1}, 1); 88 | 89 | Assert.assertEquals(roaringtree.getRoot().getChildren().size(), 3); 90 | } 91 | 92 | 93 | @Test 94 | public void testSplitOccurs() 95 | { 96 | Random rand = new Random(); 97 | for (int i = 0; i < 100; i++) { 98 | tree.insert(new float[]{rand.nextFloat(), rand.nextFloat()}, i); 99 | } 100 | 101 | Assert.assertTrue(tree.getRoot().getChildren().size() > 1); 102 | } 103 | 104 | @Test 105 | public void testSplitOccursRoaring() 106 | { 107 | Random rand = new Random(); 108 | for (int i = 0; i < 100; i++) { 109 | roaringtree.insert(new float[]{rand.nextFloat(), rand.nextFloat()}, i); 110 | } 111 | 112 | Assert.assertTrue(roaringtree.getRoot().getChildren().size() > 1); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/spatial/search/PolygonBoundTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.metamx.collections.spatial.search; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | public class PolygonBoundTest 24 | { 25 | @Test 26 | public void testCacheKey() 27 | { 28 | Assert.assertArrayEquals( 29 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 1).getCacheKey(), 30 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 1).getCacheKey() 31 | ); 32 | Assert.assertFalse(Arrays.equals( 33 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 1).getCacheKey(), 34 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 1F}, 1).getCacheKey() 35 | )); 36 | Assert.assertFalse(Arrays.equals( 37 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 1).getCacheKey(), 38 | PolygonBound.from(new float[]{1F, 2F, 2F}, new float[]{0F, 2F, 0F}, 1).getCacheKey() 39 | )); 40 | Assert.assertFalse(Arrays.equals( 41 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 1).getCacheKey(), 42 | PolygonBound.from(new float[]{1F, 2F, 3F}, new float[]{0F, 2F, 0F}, 2).getCacheKey() 43 | )); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/spatial/search/RadiusBoundTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.metamx.collections.spatial.search; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | public class RadiusBoundTest 24 | { 25 | @Test 26 | public void testCacheKey() 27 | { 28 | final float[] coords0 = new float[]{1.0F, 2.0F}; 29 | final float[] coords1 = new float[]{1.1F, 2.1F}; 30 | Assert.assertArrayEquals( 31 | new RadiusBound(coords0, 3.0F, 10).getCacheKey(), 32 | new RadiusBound(coords0, 3.0F, 10).getCacheKey() 33 | ); 34 | Assert.assertFalse(Arrays.equals( 35 | new RadiusBound(coords0, 3.0F, 10).getCacheKey(), 36 | new RadiusBound(coords1, 3.0F, 10).getCacheKey() 37 | )); 38 | Assert.assertFalse(Arrays.equals( 39 | new RadiusBound(coords0, 3.0F, 10).getCacheKey(), 40 | new RadiusBound(coords0, 3.1F, 10).getCacheKey() 41 | )); 42 | Assert.assertFalse(Arrays.equals( 43 | new RadiusBound(coords0, 3.0F, 10).getCacheKey(), 44 | new RadiusBound(coords0, 3.0F, 9).getCacheKey() 45 | )); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/spatial/search/RectangularBoundTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package com.metamx.collections.spatial.search; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | 21 | import java.util.Arrays; 22 | 23 | public class RectangularBoundTest 24 | { 25 | @Test 26 | public void testCacheKey() 27 | { 28 | Assert.assertArrayEquals( 29 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 1).getCacheKey(), 30 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 1).getCacheKey() 31 | ); 32 | Assert.assertFalse(Arrays.equals( 33 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 1).getCacheKey(), 34 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 3F}, 1).getCacheKey() 35 | )); 36 | Assert.assertFalse(Arrays.equals( 37 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 1).getCacheKey(), 38 | new RectangularBound(new float[]{1F, 0F}, new float[]{2F, 2F}, 1).getCacheKey() 39 | )); 40 | Assert.assertFalse(Arrays.equals( 41 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 1).getCacheKey(), 42 | new RectangularBound(new float[]{1F, 1F}, new float[]{2F, 2F}, 2).getCacheKey() 43 | )); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/collections/spatial/split/LinearGutmanSplitStrategyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.collections.spatial.split; 18 | 19 | import com.metamx.collections.spatial.Node; 20 | import com.metamx.collections.spatial.Point; 21 | import com.metamx.collections.spatial.RTree; 22 | import com.metamx.collections.bitmap.BitmapFactory; 23 | import com.metamx.collections.bitmap.ConciseBitmapFactory; 24 | import com.metamx.collections.bitmap.RoaringBitmapFactory; 25 | 26 | import junit.framework.Assert; 27 | 28 | import org.junit.Test; 29 | 30 | import java.util.Random; 31 | 32 | /** 33 | */ 34 | public class LinearGutmanSplitStrategyTest 35 | { 36 | @Test 37 | public void testPickSeeds() throws Exception 38 | { 39 | BitmapFactory bf = new ConciseBitmapFactory(); 40 | LinearGutmanSplitStrategy strategy = new LinearGutmanSplitStrategy(0, 50, bf); 41 | Node node = new Node(new float[2], new float[2], true, bf); 42 | 43 | node.addChild(new Point(new float[]{3, 7}, 1, bf)); 44 | node.addChild(new Point(new float[]{1, 6}, 1, bf)); 45 | node.addChild(new Point(new float[]{9, 8}, 1, bf)); 46 | node.addChild(new Point(new float[]{2, 5}, 1, bf)); 47 | node.addChild(new Point(new float[]{4, 4}, 1, bf)); 48 | node.enclose(); 49 | 50 | Node[] groups = strategy.split(node); 51 | Assert.assertEquals(groups[0].getMinCoordinates()[0], 1.0f); 52 | Assert.assertEquals(groups[0].getMinCoordinates()[1], 4.0f); 53 | Assert.assertEquals(groups[1].getMinCoordinates()[0], 9.0f); 54 | Assert.assertEquals(groups[1].getMinCoordinates()[1], 8.0f); 55 | } 56 | 57 | @Test 58 | public void testPickSeedsRoaring() throws Exception 59 | { 60 | BitmapFactory bf = new RoaringBitmapFactory(); 61 | LinearGutmanSplitStrategy strategy = new LinearGutmanSplitStrategy(0, 50, bf); 62 | Node node = new Node(new float[2], new float[2], true, bf); 63 | 64 | node.addChild(new Point(new float[]{3, 7}, 1, bf)); 65 | node.addChild(new Point(new float[]{1, 6}, 1, bf)); 66 | node.addChild(new Point(new float[]{9, 8}, 1, bf)); 67 | node.addChild(new Point(new float[]{2, 5}, 1, bf)); 68 | node.addChild(new Point(new float[]{4, 4}, 1, bf)); 69 | node.enclose(); 70 | 71 | Node[] groups = strategy.split(node); 72 | Assert.assertEquals(groups[0].getMinCoordinates()[0], 1.0f); 73 | Assert.assertEquals(groups[0].getMinCoordinates()[1], 4.0f); 74 | Assert.assertEquals(groups[1].getMinCoordinates()[0], 9.0f); 75 | Assert.assertEquals(groups[1].getMinCoordinates()[1], 8.0f); 76 | } 77 | 78 | 79 | @Test 80 | public void testNumChildrenSize() 81 | { 82 | BitmapFactory bf = new ConciseBitmapFactory(); 83 | RTree tree = new RTree(2, new LinearGutmanSplitStrategy(0, 50, bf), bf); 84 | Random rand = new Random(); 85 | for (int i = 0; i < 100; i++) { 86 | tree.insert(new float[]{rand.nextFloat(), rand.nextFloat()}, i); 87 | } 88 | 89 | Assert.assertTrue(getNumPoints(tree.getRoot()) >= tree.getSize()); 90 | } 91 | 92 | @Test 93 | public void testNumChildrenSizeRoaring() 94 | { 95 | BitmapFactory bf = new RoaringBitmapFactory(); 96 | RTree tree = new RTree(2, new LinearGutmanSplitStrategy(0, 50, bf), bf); 97 | Random rand = new Random(); 98 | for (int i = 0; i < 100; i++) { 99 | tree.insert(new float[]{rand.nextFloat(), rand.nextFloat()}, i); 100 | } 101 | 102 | Assert.assertTrue(getNumPoints(tree.getRoot()) >= tree.getSize()); 103 | } 104 | 105 | private int getNumPoints(Node node) 106 | { 107 | int total = 0; 108 | if (node.isLeaf()) { 109 | total += node.getChildren().size(); 110 | } else { 111 | for (Node child : node.getChildren()) { 112 | total += getNumPoints(child); 113 | } 114 | } 115 | return total; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/test/annotation/Benchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.test.annotation; 18 | 19 | public interface Benchmark 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/metamx/test/annotation/Dummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 - 2015 Metamarkets Group Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.metamx.test.annotation; 18 | 19 | public interface Dummy 20 | { 21 | } 22 | --------------------------------------------------------------------------------