Repository: thomasmueller/minperf Branch: master Commit: f97c859b5c4a Files: 103 Total size: 983.5 KB Directory structure: gitextract_zbe5o8p1/ ├── .gitignore ├── LICENSE ├── MAVEN.md ├── README.md ├── pom.xml ├── runjmh.sh └── src/ ├── main/ │ ├── c/ │ │ ├── Makefile │ │ ├── filter/ │ │ │ ├── Makefile │ │ │ └── xor.c │ │ └── rec-split.c │ └── java/ │ └── org/ │ └── minperf/ │ ├── BitBuffer.java │ ├── RecSplitBuilder.java │ ├── RecSplitEvaluator.java │ ├── Settings.java │ ├── bdz/ │ │ └── BDZ.java │ ├── generator/ │ │ ├── ConcurrencyTool.java │ │ └── Generator.java │ ├── hash/ │ │ ├── HashPerformanceTest.java │ │ ├── LongPair.java │ │ ├── Mix.java │ │ ├── Murmur2.java │ │ ├── Murmur3.java │ │ ├── SpookyHash.java │ │ └── XXHash64.java │ ├── hem/ │ │ ├── HemGenerator.java │ │ ├── KeyReader.java │ │ ├── Sort.java │ │ ├── SortedSignatures.java │ │ └── recsplit/ │ │ ├── Builder.java │ │ ├── FastEvaluator.java │ │ └── FastGenerator.java │ ├── monotoneList/ │ │ ├── EliasFanoMonotoneList.java │ │ ├── MonotoneList.java │ │ └── MultiStageMonotoneList.java │ ├── rank/ │ │ ├── Rank9.java │ │ └── VerySimpleRank.java │ ├── select/ │ │ ├── Select.java │ │ ├── SimpleSelect.java │ │ ├── SimpleSelectWrapper.java │ │ └── VerySimpleSelect.java │ ├── universal/ │ │ ├── LongHash.java │ │ ├── StringHash.java │ │ └── UniversalHash.java │ └── utils/ │ ├── LargeLongList.java │ ├── LongSet.java │ ├── PoissonDistribution.java │ ├── RandomSetGenerator.java │ ├── RandomSetGeneratorSlow.java │ └── Text.java └── test/ └── java/ └── org/ └── minperf/ ├── BitCodes.java ├── FunctionInfo.java ├── Graphics.java ├── LargeSetTest.java ├── Paper.java ├── PerformanceTest.java ├── Probability.java ├── RandomizedTest.java ├── SettingsTest.java ├── SpaceEstimator.java ├── SplitRuleTest.java ├── SplitRuleTest2.java ├── SplitRuleTest3.java ├── TestSplitStrategy.java ├── TextFileTest.java ├── TimeEstimator.java ├── WikipediaTest.java ├── bdz/ │ └── BDZTest.java ├── c/ │ └── HashGenerator.java ├── chd/ │ ├── CHD.java │ ├── CHD2.java │ ├── CHDTest.java │ ├── EliasFanoList.java │ └── EliasFanoListTest.java ├── cuckoo/ │ ├── CuckooHashMap.java │ ├── CuckooHashTest.java │ └── CuckooLongKeyHashSet.java ├── hash/ │ └── MixTest.java ├── hem/ │ ├── HEM.java │ ├── MetaFile.java │ ├── RandomGenerator.java │ └── recsplit/ │ └── TestFast.java ├── hybrid/ │ └── HybridTest.java ├── medium/ │ ├── EstimateTimeForHugeSets.java │ ├── EstimateTwoBillionEntries.java │ ├── MediumRecSplit.java │ ├── MediumTest.java │ ├── PartitionIntoSimilarSizedSets.java │ ├── RecSplitEliasFano.java │ ├── SimulateProbFallIntoLarge.java │ └── TestBBHash.java ├── monotoneList/ │ ├── FenwickTreeMonotoneList.java │ └── MonotoneListTest.java ├── rank/ │ ├── Rank9Test.java │ └── RankTest.java ├── select/ │ └── SelectTest.java ├── simple/ │ ├── LongCollection.java │ ├── SimpleRecSplit.java │ ├── SimpleTest.java │ └── recsplit.md ├── tools/ │ ├── Dump.java │ └── Load.java └── utils/ ├── FastDigitFromNumberExtraction.java └── TextTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .checkstyle .classpath .project .settings bin /target/ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MAVEN.md ================================================ Please ensure that unit tests actually pass. ``` mvn clean install -DskipTests java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBench ``` ================================================ FILE: README.md ================================================ # minperf A Minimal Perfect Hash Function Library. * Mainly written in Java. Includes a C version (currently only evaluation of a MPHF). * Can generate, in linear time, MPHFs that need less than 1.58 bits per key. * Can generate MPHFs in less than 100 ns/key, evaluation faster than 100 ns/key, at less than 3 bits per key. * Concurrent generation. * Tested up to 1 billion keys. * Two parameters to configure space needed, generation time, and evaluation time. * Can be used as a static bloom filter, by storing a hash fingerprint per key. * Performance very similar than the [Sux4J](https://github.com/vigna/Sux4J) CHD and GOV algorithms, but configurable, with ability to use less space. This library should already be usable, but it is still work in progress. The plan is to publish a paper. The algorithm used is described [here as text](https://github.com/thomasmueller/minperf/blob/master/src/test/java/org/minperf/simple/recsplit.md), and [here as slideshow](https://github.com/thomasmueller/minperf/raw/master/src/test/java/org/minperf/simple/recsplit.pdf) ([also available on SlideShare](https://www.slideshare.net/ThomasMueller12/recsplit-minimal-perfect-hashing)). ================================================ FILE: pom.xml ================================================ 4.0.0 thomasmueller minperf 1.0-SNAPSHOT 1.4 UTF-8 benchmarks jar org.apache.maven.plugins maven-compiler-plugin 2.3.2 1.8 1.8 maven-assembly-plugin package single jar-with-dependencies junit junit 4.13.1 test org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} provided ================================================ FILE: runjmh.sh ================================================ mvn clean install -DskipTests && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBench > JmhBench.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchBloom > JmhBenchBloom.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchCuckoo8_4 > JmhBenchCuckoo8_4.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchCuckoo16_4 > JmhBenchCuckoo16_4.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchXor8 > JmhBenchXor8.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchXor16 > JmhBenchXor16.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchXor16 > JmhBenchXor16.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchXor > JmhBenchXor.log && java -Xmx16g -cp target/minperf-1.0-SNAPSHOT-jar-with-dependencies.jar org.minperf.bloom.JmhBenchCuckoo > JmhBenchCuckoo.log ================================================ FILE: src/main/c/Makefile ================================================ # # RecSplit for C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGETS = rec-split CC = gcc CFLAGS = -Wall -g -O3 all: $(TARGETS) rec-split: rec-split.o # gcc rec-split.o -o rec-split rec-split.o: rec-split.c # gcc -c rec-split.c -o rec-split.o clean: rm -f *.a *.o $(TARGETS) decompile: objdump -S --disassemble rec-split ================================================ FILE: src/main/c/filter/Makefile ================================================ # # RecSplit for C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # TARGETS = xor CC = gcc CFLAGS = -Wall -g -O3 all: $(TARGETS) xor: xor.o # gcc xor.o -o xor xor.o: xor.c # gcc -c xor.c -o xor.o clean: rm -f *.a *.o $(TARGETS) decompile: objdump -S --disassemble xor ================================================ FILE: src/main/c/filter/xor.c ================================================ #include #include #include #include #include #include #include #include #include // file I/O int64_t getFileSize(FILE* fp) { struct stat buf; int fd = fileno(fp); fstat(fd, &buf); int64_t size = buf.st_size; return size; } int loadFile(char* directory, char* fileName, uint8_t** target) { if (!fileName || !directory) { printf("No file\n"); return 0; } char* fullFileName = malloc(strlen(directory) + strlen(fileName) + 2); sprintf(fullFileName, "%s/%s", directory, fileName); printf("Loading file %s\n", fullFileName); FILE* fp = fopen(fullFileName, "r"); free(fullFileName); if (!fp) { printf("Could not open file\n"); return 0; } int64_t fileSize = getFileSize(fp); printf("File size: %lld\n", fileSize); *target = malloc(fileSize); size_t len = fread(*target, fileSize, 1, fp); if (len != 1) { printf("Could not full read the file\n"); return 0; } fclose(fp); printf("(%lld bytes)\n", fileSize); return 1; } // bit manipulation uint64_t rotateLeft64(uint64_t x, uint32_t n) { // TODO check if this is the best way, and if it always works as expected return (x << (n & 63)) | (x >> (64 - (n & 63))); } int numberOfLeadingZeros64(uint64_t x) { // If x is 0, the result is undefined. return __builtin_clzl(x); } int numberOfLeadingZeros32(uint32_t x) { // If x is 0, the result is undefined. return __builtin_clz(x); } // hashing related inline uint32_t reduce(uint32_t hash, uint32_t n) { // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return (uint32_t) (((uint64_t) hash * n) >> 32); } // from https://stackoverflow.com/questions/11656241/how-to-print-uint128-t-number-using-gcc/11660651#11660651 typedef unsigned __int128 uint128_t; // data uint8_t* data; uint32_t getArrayLength(uint32_t size) { return (uint32_t) (3 + (uint64_t) 123 * size / 100); } uint64_t hash64(uint64_t x) { x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >> 27)) * 0x94d049bb133111ebL; x = x ^ (x >> 31); return x; } uint32_t fingerprint(uint64_t hash) { return (uint32_t) (hash & ((1 << 8) - 1)); } // XorFilter8 struct XorFilter8 { uint32_t size; uint32_t arrayLength; uint32_t blockLength; uint32_t hashIndex; uint8_t* fingerprints; }; struct XorFilter8 filter; void XorFilter_load(struct XorFilter8* this) { printf("Loading...\n"); this->size = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; printf("Size %d\n", this->size); this->hashIndex = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]; printf("HashIndex %d\n", this->hashIndex); this->arrayLength = getArrayLength(this->size); this->blockLength = this->arrayLength / 3; this->fingerprints = data + 8; } uint32_t XorFilter_mayContain(struct XorFilter8* this, uint64_t key) { uint64_t hash = hash64(key + this->hashIndex); uint32_t f = fingerprint(hash); uint32_t r0 = (uint32_t) hash; uint32_t r1 = (uint32_t) (hash >> 16); uint32_t r2 = (uint32_t) (hash >> 32); uint32_t h0 = reduce(r0, this->blockLength); uint32_t h1 = reduce(r1, this->blockLength) + this->blockLength; uint32_t h2 = reduce(r2, this->blockLength) + 2 * this->blockLength; f ^= this->fingerprints[h0] ^ this->fingerprints[h1] ^ this->fingerprints[h2]; return (f & 0xff) == 0; } // demo int main(int argc, char** argv) { char* directory = "."; ++argv; --argc; if (argc > 0) { directory = argv[0]; ++argv; --argc; } printf("Directory %s\n", directory); char* hashFile = "hash.bin"; char* keyFile = "keys.txt"; if (!loadFile(directory, hashFile, &data)) { return 0; } FILE* input; if(!keyFile) { input = stdin; } else { char* fullFileName = malloc(strlen(directory) + strlen(keyFile) + 2); sprintf(fullFileName, "%s/%s", directory, keyFile); printf("Loading file %s\n", fullFileName); input = fopen(fullFileName, "rb"); free(fullFileName); if (!input) { printf("Could not open file %s\n", keyFile); return 0; } } XorFilter_load(&filter); char line[255]; // used to measure time of i/o // (it turned out to be 50 ns / key) /* for(int i=0; i<5; i++) { clock_t start = clock(); int64_t sum = 0; while (fgets(line, 255, input)) { int len = strlen(line); // trim while (len > 0 && line[len - 1] < ' ') { line[--len] = 0; } sum += len; } clock_t time = (clock() - start); printf("sum: %lld sec: %ld %d\n", sum, time, CLOCKS_PER_SEC); rewind(input); } */ clock_t start = clock(); int64_t sum = 0; while (fgets(line, 255, input)) { int len = strlen(line); // trim while (len > 0 && line[len - 1] < ' ') { line[--len] = 0; } uint64_t key = atoll(line); uint32_t contain = XorFilter_mayContain(&filter, key); sum += contain; } clock_t time = (clock() - start); printf("Sum: %lld time: %ld ticks, at %d ticks/second\n", sum, time, CLOCKS_PER_SEC); free(data); return 0; } ================================================ FILE: src/main/c/rec-split.c ================================================ #include #include #include #include #include #include #include #include #include // file I/O int64_t getFileSize(FILE* fp) { struct stat buf; int fd = fileno(fp); fstat(fd, &buf); int64_t size = buf.st_size; return size; } void fixEndian(uint64_t* longArray, uint64_t byteCount) { uint8_t* byteArray = (uint8_t*) longArray; uint64_t l=0; uint64_t b=0; while(b> (64 - (n & 63))); } int numberOfLeadingZeros64(uint64_t x) { // If x is 0, the result is undefined. return __builtin_clzl(x); } int numberOfLeadingZeros32(uint32_t x) { // If x is 0, the result is undefined. return __builtin_clz(x); } // hashing related inline uint32_t reduce(uint32_t hash, uint32_t n) { // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return (uint32_t) (((uint64_t) hash * n) >> 32); } uint64_t getScaleFactor(uint32_t multiply, uint32_t divide) { return divide == 0 ? 0 : ((uint64_t) multiply << 32) / divide + 1; } // from https://stackoverflow.com/questions/11656241/how-to-print-uint128-t-number-using-gcc/11660651#11660651 typedef unsigned __int128 uint128_t; uint128_t getSipHash24b128(char* b, int start, int end, uint64_t k0, uint64_t k1) { uint64_t v0 = k0 ^ 0x736f6d6570736575L; uint64_t v1 = k1 ^ 0x646f72616e646f6dL; uint64_t v2 = k0 ^ 0x6c7967656e657261L; uint64_t v3 = k1 ^ 0x7465646279746573L; int repeat; for (int off = start; off <= end + 8; off += 8) { long m; if (off <= end) { m = 0; int i = 0; for (; i < 8 && off + i < end; i++) { m |= ((uint64_t) b[off + i] & 255) << (8 * i); } if (i < 8) { m |= ((uint64_t) end - start) << 56; } v3 ^= m; repeat = 2; } else { m = 0; v2 ^= 0xff; repeat = 4; } for (int i = 0; i < repeat; i++) { v0 += v1; v2 += v3; v1 = rotateLeft64(v1, 13); v3 = rotateLeft64(v3, 16); v1 ^= v0; v3 ^= v2; v0 = rotateLeft64(v0, 32); v2 += v1; v0 += v3; v1 = rotateLeft64(v1, 17); v3 = rotateLeft64(v3, 21); v1 ^= v2; v3 ^= v0; v2 = rotateLeft64(v2, 32); } v0 ^= m; } return ((uint128_t) (v0 ^ v1) << 64) ^ v2 ^ v3; } uint64_t getSipHash24b(char* b, int start, int end, uint64_t k0, uint64_t k1) { uint64_t v0 = k0 ^ 0x736f6d6570736575L; uint64_t v1 = k1 ^ 0x646f72616e646f6dL; uint64_t v2 = k0 ^ 0x6c7967656e657261L; uint64_t v3 = k1 ^ 0x7465646279746573L; int repeat; for (int off = start; off <= end + 8; off += 8) { long m; if (off <= end) { m = 0; int i = 0; for (; i < 8 && off + i < end; i++) { m |= ((uint64_t) b[off + i] & 255) << (8 * i); } if (i < 8) { m |= ((uint64_t) end - start) << 56; } v3 ^= m; repeat = 2; } else { m = 0; v2 ^= 0xff; repeat = 4; } for (int i = 0; i < repeat; i++) { v0 += v1; v2 += v3; v1 = rotateLeft64(v1, 13); v3 = rotateLeft64(v3, 16); v1 ^= v0; v3 ^= v2; v0 = rotateLeft64(v0, 32); v2 += v1; v0 += v3; v1 = rotateLeft64(v1, 17); v3 = rotateLeft64(v3, 21); v1 ^= v2; v3 ^= v0; v2 = rotateLeft64(v2, 32); } v0 ^= m; } return v0 ^ v1 ^ v2 ^ v3; } uint64_t getSipHash24(char* b, uint64_t k0, uint64_t k1) { // TODO assuming 8 bits per char return getSipHash24b(b, 0, strlen(b), k0, k1); } uint64_t universalHash(char* key, uint64_t index) { return getSipHash24(key, index, index); } // BitBuffer uint64_t* data; uint64_t pos; uint64_t readBit() { uint64_t p = pos++; return (data[p >> 6] >> (63 - (p & 63))) & 1; } uint64_t readEliasDelta() { int qq = 0; while (readBit() == 0) { qq++; } int64_t q = 1; for (int i = qq; i > 0; i--) { q = (q << 1) | readBit(); } uint64_t x = 1; for (int64_t i = q - 2; i >= 0; i--) { x = (x << 1) | readBit(); } return x; } uint64_t readNumber(uint64_t pos, int bitCount) { if (bitCount == 0) { return 0; } int remainingBits = 64 - (pos & 63); int index = pos >> 6; long x = data[index]; if (bitCount <= remainingBits) { x >>= remainingBits - bitCount; return x & ((1L << bitCount) - 1); } x = x & ((1L << remainingBits) - 1); return (x << (bitCount - remainingBits)) | (data[index + 1] >> (64 - bitCount + remainingBits)); } uint32_t supplementalHash(uint64_t hash, uint64_t index) { // it would be better to use long, // but with some processors, 32-bit multiplication // seem to be much faster // (about 1200 ms for 32 bit, about 2000 ms for 64 bit) uint32_t x = (uint32_t) (rotateLeft64(hash, (uint32_t) index) ^ index); x = ((x >> 16) ^ x) * 0x45d9f3b; x = ((x >> 16) ^ x) * 0x45d9f3b; x = (x >> 16) ^ x; return x; } uint64_t unfoldSigned(uint64_t x) { return ((x & 1) == 1) ? (x + 1) / 2 : -(x / 2); } int getEliasDeltaSize(uint64_t value) { if (value <= 0) { // illegal argument return -1; } int q = 64 - numberOfLeadingZeros64(value); int qq = 31 - numberOfLeadingZeros32(q); int len = qq + qq + q; return len; } int readUntilZeroMore(int count, uint64_t pos) { while (true) { long x = data[++pos]; if (x == -1L) { count += 64; continue; } return count + numberOfLeadingZeros64(~x); } } int readUntilZero(uint64_t pos) { int remainingBits = 64 - (pos & 63); uint64_t index = pos >> 6; uint64_t x = data[index] << (64 - remainingBits); int count = numberOfLeadingZeros64(~x); if (count < remainingBits) { return count; } return readUntilZeroMore(count, index); } uint64_t skipGolombRice(uint64_t pos, int shift) { int q = readUntilZero(pos); return pos + q + 1 + shift; } // MultiStageMonotoneList struct MultiStageMonotoneList { uint64_t startLevel1, startLevel2, startLevel3; int bitCount1, bitCount2, bitCount3; uint32_t count1, count2, count3; uint64_t factor; uint32_t add; }; struct MultiStageMonotoneList list; #define SHIFT1 6 #define SHIFT2 3 #define FACTOR1 32 #define FACTOR2 16 void MultiStageMonotoneList_load(struct MultiStageMonotoneList* this) { this->count3 = (uint32_t) readEliasDelta() - 1; int diff = (uint32_t) readEliasDelta() - 1; this->factor = getScaleFactor(diff, this->count3); this->add = (uint32_t) unfoldSigned(readEliasDelta() - 1); this->bitCount1 = (int) readEliasDelta() - 1; this->bitCount2 = (int) readEliasDelta() - 1; this->bitCount3 = (int) readEliasDelta() - 1; this->startLevel1 = pos; this->count2 = (this->count3 + (1 << SHIFT2) - 1) >> SHIFT2; this->count1 = (this->count3 + (1 << SHIFT1) - 1) >> SHIFT1; this->startLevel2 = this->startLevel1 + this->count1 * this->bitCount1; this->startLevel3 = this->startLevel2 + this->count2 * this->bitCount2; pos = (this->startLevel3 + this->bitCount3 * this->count3); } uint32_t MultiStageMonotoneList_get(struct MultiStageMonotoneList* this, uint32_t i) { int expected = (int) ((i * this->factor) >> 32) + this->add; long a = readNumber(this->startLevel1 + (i >> SHIFT1) * this->bitCount1, this->bitCount1); long b = readNumber(this->startLevel2 + (i >> SHIFT2) * this->bitCount2, this->bitCount2); long c = readNumber(this->startLevel3 + i * this->bitCount3, this->bitCount3); return (int) (expected + a * FACTOR1 + b * FACTOR2 + c); } uint64_t MultiStageMonotoneList_getPair(struct MultiStageMonotoneList* this, uint32_t i) { return ((uint64_t) MultiStageMonotoneList_get(this, i) << 32) | (MultiStageMonotoneList_get(this, i + 1)); } // Settings #define MAX_SIZE 4096 #define SUPPLEMENTAL_HASH_SHIFT 18 struct Settings { int leafSize; int averageBucketSize; int rice[MAX_SIZE]; int splits[MAX_SIZE]; }; struct Settings settings; void Settings_load(struct Settings* this) { this->leafSize = readEliasDelta() - 1; this->averageBucketSize = readEliasDelta() - 1; uint32_t len = readEliasDelta() - 1; printf("Loading settings: leafSize: %d, averageBucketSize: %d, maxBucketSize: %d\n", this->leafSize, this->averageBucketSize, len); for(int i=0; isplits[i] = unfoldSigned(readEliasDelta() - 1); this->rice[i] = readEliasDelta() - 1; // if (i < 32) // printf(" %d: rice=%d, splits: %d\n", i, this->rice[i], this->splits[i]); } } uint64_t getUniversalHashIndex(uint64_t index) { return index >> SUPPLEMENTAL_HASH_SHIFT; } // RecSplitEvaluator struct RecSplitEvaluator { uint64_t size; uint32_t bucketCount; uint32_t minStartDiff; struct MultiStageMonotoneList startList; uint32_t minOffsetDiff; struct MultiStageMonotoneList offsetList; uint32_t startBuckets; uint32_t endHeader; uint32_t endOffsetList; }; struct RecSplitEvaluator evaluator; uint32_t getBucketCount(uint64_t size, int averageBucketSize) { return (uint32_t) ((size + averageBucketSize - 1) / averageBucketSize); } void RecSplitEvaluator_load(struct RecSplitEvaluator* this) { this->size = readEliasDelta() - 1; printf("Hash size %lld\n", this->size); this->bucketCount = getBucketCount(this->size, settings.averageBucketSize); printf("Buckets: %d\n", this->bucketCount); int alternative = readBit() != 0; if (alternative) { // not supported printf("Not supported: alternative hash\n"); return; } this->minOffsetDiff = (uint32_t) (readEliasDelta() - 1); // printf("minOffsetDiff: %d\n", this->minOffsetDiff); this->minStartDiff = (uint32_t) (readEliasDelta() - 1); // printf("minStartDiff: %d\n", this->minStartDiff); this->endHeader = pos; MultiStageMonotoneList_load(&(this->offsetList)); this->endOffsetList = pos; MultiStageMonotoneList_load(&(this->startList)); this->startBuckets = pos; // printf("hash loaded\n"); } uint32_t getMinBitCount(uint32_t size) { // at least 1.375 bits per key (if it is less, fill with zeroes) return (size * 11 + 7) >> 3; } uint32_t skip(struct RecSplitEvaluator* this, uint64_t pos, uint32_t size) { if (size < 2) { return pos; } pos = skipGolombRice(pos, settings.rice[size]); if (size <= settings.leafSize) { return pos; } int split = settings.splits[size]; int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int s = firstPart; for (int i = 0; i < split; i++) { pos = skip(this, pos, s); s = otherPart; } return pos; } uint32_t evaluate2(struct RecSplitEvaluator* this, uint64_t pos, char* obj, uint64_t hashCode, uint64_t index, uint32_t add, uint32_t size) { while (true) { if (size < 2) { return add; } int shift = settings.rice[size]; uint64_t q = readUntilZero(pos); pos += q + 1; uint64_t value = (q << shift) | readNumber(pos, shift); pos += shift; uint64_t oldX = getUniversalHashIndex(index); index += value + 1; uint64_t x = getUniversalHashIndex(index); if (x != oldX) { hashCode = universalHash(obj, x); } if (size <= settings.leafSize) { int h = supplementalHash(hashCode, index); h = reduce(h, size); //printf("shift %d q %d value %lld oldX %lld x %lld size %d h %d add %d\n", shift, q, value, oldX, x, size, h, add); return add + h; } int split = settings.splits[size]; int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int h = supplementalHash(hashCode, index); if (firstPart != otherPart) { h = reduce(h, size); if (h < firstPart) { size = firstPart; continue; } pos = skip(this, pos, firstPart); add += firstPart; size = otherPart; continue; } h = reduce(h, split); for (int i = 0; i < h; i++) { pos = skip(this, pos, firstPart); add += firstPart; } size = firstPart; } } uint64_t evaluate(struct RecSplitEvaluator* this, char* obj) { uint64_t hashCode = universalHash(obj, 0); //printf(" hashCode %s = %lld\n", obj, hashCode); uint32_t b; if (this->bucketCount == 1) { b = 0; } else { b = reduce((uint32_t) hashCode, this->bucketCount); } //printf(" bucket %d\n", b); uint32_t startPos; uint64_t offsetPair = MultiStageMonotoneList_getPair(&this->offsetList, b); uint32_t offset = (uint32_t) (offsetPair >> 32) + b * this->minOffsetDiff; uint32_t offsetNext = ((uint32_t) offsetPair) + (b + 1) * this->minOffsetDiff; if (offsetNext == offset) { // entry not found return 0; } uint32_t bucketSize = offsetNext - offset; startPos = this->startBuckets + getMinBitCount(offset) + MultiStageMonotoneList_get(&this->startList, b) + b * this->minStartDiff; // printf(" startPos %d offset %d bucketSize %d\n", startPos, offset, bucketSize); return evaluate2(this, startPos, obj, hashCode, 0, offset, bucketSize); } // demo int main(int argc, char** argv) { char* directory = "."; ++argv; --argc; if (argc > 0) { directory = argv[0]; ++argv; --argc; } printf("Directory %s\n", directory); char* settingsFile = "settings.bin"; char* hashFile = "hash.bin"; char* keyFile = "keys.txt"; if (!loadFile(directory, settingsFile, &data)) { return 0; } Settings_load(&settings); free(data); if (!loadFile(directory, hashFile, &data)) { return 0; } FILE* input; if(!keyFile) { input = stdin; } else { char* fullFileName = malloc(strlen(directory) + strlen(keyFile) + 2); sprintf(fullFileName, "%s/%s", directory, keyFile); printf("Loading file %s\n", fullFileName); input = fopen(fullFileName, "rb"); free(fullFileName); if (!input) { printf("Could not open file %s\n", keyFile); return 0; } } pos = 0; RecSplitEvaluator_load(&evaluator); char line[255]; // used to measure time of i/o // (it turned out to be 50 ns / key) /* for(int i=0; i<5; i++) { clock_t start = clock(); int64_t sum = 0; while (fgets(line, 255, input)) { int len = strlen(line); // trim while (len > 0 && line[len - 1] < ' ') { line[--len] = 0; } sum += len; } clock_t time = (clock() - start); printf("sum: %lld sec: %ld %d\n", sum, time, CLOCKS_PER_SEC); rewind(input); } */ clock_t start = clock(); int64_t sum = 0; while (fgets(line, 255, input)) { int len = strlen(line); // trim while (len > 0 && line[len - 1] < ' ') { line[--len] = 0; } uint64_t index = evaluate(&evaluator, line); sum += index; } clock_t time = (clock() - start); // with leafSize 8 and averageBucketSize 128 and 10 million keys, // that is 1.79 bits/key, average key length 55 bits/key, // evaluate takes about 220 ns/key with -O3, and 260 ns/key with -Os // with leafSize 6 and averageBucketSize 18 and 10 million keys, // that is 2.33 bits/key, average key length 55 bits/key, // evaluate takes about 140 ns/key with -O3 printf("sum: %lld time: %ld ticks, at %d ticks/second\n", sum, time, CLOCKS_PER_SEC); free(data); return 0; } ================================================ FILE: src/main/java/org/minperf/BitBuffer.java ================================================ package org.minperf; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; /** * A simple bit buffer. It is partially optimized for reading, but writing is * relatively slow. Writing will only add bits (bitwise or with existing bits). */ public class BitBuffer { public final long[] data; private int pos; public BitBuffer(long bits) { this.data = new long[(int)((bits + 63) / 64)]; } public BitBuffer(byte[] data) { this.data = new long[(data.length + 7) / 8]; if (this.data.length != data.length * 8) { data = Arrays.copyOf(data, this.data.length * 8); } ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).asLongBuffer().get(this.data); } /** * Create a buffer that shared the byte data, but uses a separate position * (initially 0). * * @param buffer the buffer */ public BitBuffer(BitBuffer buffer) { this.data = buffer.data; } public void write(BitBuffer bits) { int count = bits.pos; bits.pos = 0; // for (int i = 0; i < count; i++) { // writeBit(bits.readBit()); // } int i = 0; for (; i < count - 31; i += 32) { writeNumber(bits.readNumber(32), 32); } for (; i < count; i++) { writeBit(bits.readBit()); } } public int position() { return pos; } public void seek(int pos) { this.pos = pos; } /** * Read a number. * * @param bitCount the number of bits, at most 63 * @return the value */ public long readNumber(int bitCount) { long x = readNumber(pos, bitCount); pos += bitCount; return x; } public long readLong() { return (readNumber(32) << 32) | readNumber(32); } /** * Read a number. * * @param pos the position * @param bitCount the number of bits, at most 63 * @return the value */ public long readNumber(long pos, int bitCount) { if (bitCount == 0) { return 0; } int remainingBits = 64 - ((int) pos & 63); int index = (int) (pos >>> 6); long x = data[index]; if (bitCount <= remainingBits) { x >>>= remainingBits - bitCount; return x & ((1L << bitCount) - 1); } x = x & ((1L << remainingBits) - 1); return (x << (bitCount - remainingBits)) | (data[index + 1] >>> (64 - bitCount + remainingBits)); } /** * Fold a signed number into an unsigned number. Negative numbers are odd, * and positive numbers are even. For example, -5 is converted to 11, and 5 * to 10. * * @param x a signed number * @return an unsigned number */ public static long foldSigned(long x) { return x > 0 ? x * 2 - 1 : -x * 2; } /** * Unfold an unsigned number into a signed number. * * @param x an unsigned number * @return a signed number */ public static long unfoldSigned(long x) { return ((x & 1) == 1) ? (x + 1) / 2 : -(x / 2); } public void writeBit(long x) { if (x == 1) { data[pos >>> 6] |= 1L << (63 - (pos & 63)); } pos++; } public long readBit() { return (data[pos >>> 6] >>> (63 - (pos++ & 63))) & 1; } public int readUntilZero(int pos) { int remainingBits = 64 - (pos & 63); int index = pos >>> 6; long x = data[index] << (64 - remainingBits); int count = Long.numberOfLeadingZeros(~x); if (count < remainingBits) { return count; } return readUntilZeroMore(count, index); } private int readUntilZeroMore(int count, int index) { while (true) { long x = data[++index]; if (x == -1L) { count += 64; continue; } return count + Long.numberOfLeadingZeros(~x); } } private int readUntilZero() { int len = readUntilZero(pos); pos += len + 1; return len; } public void writeGolombRice(int shift, long value) { writeGolombRiceFast(shift, value); } public void writeGolombRiceSlow(int shift, long value) { long q = value >>> shift; for (int i = 0; i < q; i++) { writeBit(1); } writeBit(0); for (int i = shift - 1; i >= 0; i--) { writeBit((value >>> i) & 1); } } public void writeGolombRiceFast(int shift, long value) { long q = value >>> shift; if (q < 63) { long m = (2L << q) - 2; writeNumber(m, (int) (q + 1)); } else { for (int i = 0; i < q; i++) { writeBit(1); } writeBit(0); } writeNumber(value & ((1L << shift) - 1), shift); // for (int i = shift - 1; i >= 0; i--) { // writeBit((value >>> i) & 1); // } } // public void writeVarLong(long x) { // while ((x & ~0x7f) != 0) { // writeNumber((0x80 | (x & 0x7f)) & 0xff, 8); // x >>>= 7; // } // writeNumber(x & 0xff, 8); // } // public long readVarLong() { // long x = buff.get(); // if (x >= 0) { // return x; // } // x &= 0x7f; // for (int s = 7; s < 64; s += 7) { // long b = buff.get(); // x |= (b & 0x7f) << s; // if (b >= 0) { // break; // } // } // return x; // } public long readGolombRice(int pos, int shift) { int q = readUntilZero(pos); return (q << shift) | readNumber(pos + q + 1, shift); } public long readGolombRice(int shift) { long q = readUntilZero(); return (q << shift) | readNumber(shift); // int q = 0; // while (readBit() == 1) { // q++; // } // long x = ((long) q) << shift; // for (int i = shift - 1; i >= 0; i--) { // x |= readBit() << i; // } // return x; } public void skipGolombRice(int shift) { pos = skipGolombRice(pos, shift); } public int skipGolombRice(int pos, int shift) { int q = readUntilZero(pos); return pos + q + 1 + shift; } public void writeEliasDelta(long value) { if (value <= 0) { throw new IllegalArgumentException(); } int q = 64 - Long.numberOfLeadingZeros(value); int qq = 31 - Integer.numberOfLeadingZeros(q); for (int i = 0; i < qq; i++) { writeBit(0); } for (int i = qq; i >= 0; i--) { writeBit((q >>> i) & 1); } for (int i = q - 2; i >= 0; i--) { writeBit((value >>> i) & 1); } } public long readEliasDelta() { int qq = 0; while (readBit() == 0) { qq++; } long q = 1; for (int i = qq; i > 0; i--) { q = (q << 1) | readBit(); } long x = 1; for (long i = q - 2; i >= 0; i--) { x = (x << 1) | readBit(); } return x; } /** * Write a number of bits. The most significant bit is written first. * * @param x the number * @param bitCount the number of bits, at most 63 */ public void writeNumber(long x, int bitCount) { // while (bitCount-- > 0) { // writeBit((x >>> bitCount) & 1); // } if (bitCount == 0) { return; } int remainingBits = 64 - (pos & 63); int index = pos >>> 6; if (bitCount <= remainingBits) { data[index] |= x << (remainingBits - bitCount); } else { data[index] |= x >>> (bitCount - remainingBits); data[index + 1] |= x << (64 - bitCount + remainingBits); } pos += bitCount; } public void clearBits(int bitCount) { if (bitCount == 0) { return; } int remainingBits = 64 - (pos & 63); int index = pos >>> 6; long x = (1 << bitCount) - 1; if (bitCount <= remainingBits) { data[index] &= ~(x << (remainingBits - bitCount)); } else { data[index] &= ~(x >>> (bitCount - remainingBits)); data[index + 1] &= ~(x << (64 - bitCount + remainingBits)); } pos += bitCount; } public byte[] toByteArray() { byte[] d = new byte[data.length * 8]; ByteBuffer.wrap(d).order(ByteOrder.BIG_ENDIAN).asLongBuffer() .put(data, 0, (pos + 63) / 64); if ((pos + 7) / 8 == d.length) { return d; } return Arrays.copyOf(d, (pos + 7) / 8); } public void clear() { Arrays.fill(data, 0); } public long[] getLongArray() { return data; } public static int getGolombRiceSize(int shift, long value) { return (int) ((value >>> shift) + 1 + shift); } public static int getEliasDeltaSize(long value) { if (value <= 0) { throw new IllegalArgumentException(); } int q = 64 - Long.numberOfLeadingZeros(value); int qq = 31 - Integer.numberOfLeadingZeros(q); int len = qq + qq + q; return len; } } ================================================ FILE: src/main/java/org/minperf/RecSplitBuilder.java ================================================ package org.minperf; import java.util.Collection; import org.minperf.generator.ConcurrencyTool; import org.minperf.generator.Generator; import org.minperf.universal.UniversalHash; /** * A builder to generate a MPHF description, or to get an evaluator of a description. * * @param the type */ public class RecSplitBuilder { private final UniversalHash hash; private int averageBucketSize = 256; private int leafSize = 10; private boolean eliasFanoMonotoneLists = true; private int parallelism = Runtime.getRuntime().availableProcessors(); private int maxChunkSize = Integer.MAX_VALUE; private RecSplitBuilder(UniversalHash hash) { this.hash = hash; } /** * Create a new instance of the builder, with the given universal hash implementation. * * @param the type * @param hash the universal hash function * @return the builder */ public static RecSplitBuilder newInstance(UniversalHash hash) { return new RecSplitBuilder(hash); } public RecSplitBuilder averageBucketSize(int averageBucketSize) { if (averageBucketSize < 4 || averageBucketSize > 64 * 1024) { throw new IllegalArgumentException("averageBucketSize out of range: " + averageBucketSize); } this.averageBucketSize = averageBucketSize; return this; } public RecSplitBuilder leafSize(int leafSize) { if (leafSize < 1 || leafSize > 25) { throw new IllegalArgumentException("leafSize out of range: " + leafSize); } this.leafSize = leafSize; return this; } public RecSplitBuilder eliasFanoMonotoneLists(boolean eliasFano) { this.eliasFanoMonotoneLists = eliasFano; return this; } public RecSplitBuilder maxChunkSize(int maxChunkSize) { this.maxChunkSize = maxChunkSize; return this; } public RecSplitBuilder parallelism(int parallelism) { this.parallelism = parallelism; return this; } /** * Generate the hash function description for a collection. * The entries in the collection must be unique. * * @param collection the collection * @return the hash function description */ public BitBuffer generate(Collection collection) { Settings s = new Settings(leafSize, averageBucketSize); ConcurrencyTool pool = new ConcurrencyTool(parallelism); Generator g = new Generator( pool, hash, s, eliasFanoMonotoneLists, maxChunkSize); BitBuffer result = g.generate(collection); return result; } public RecSplitEvaluator buildEvaluator(BitBuffer description) { Settings s = new Settings(leafSize, averageBucketSize); return new RecSplitEvaluator(new BitBuffer(description), hash, s, eliasFanoMonotoneLists); } } ================================================ FILE: src/main/java/org/minperf/RecSplitEvaluator.java ================================================ package org.minperf; import org.minperf.bdz.BDZ; import org.minperf.generator.Generator; import org.minperf.monotoneList.MonotoneList; import org.minperf.universal.UniversalHash; /** * Evaluator for the hybrid mechanism. * * @param the data type */ public class RecSplitEvaluator { private final Settings settings; private final UniversalHash hash; private final BitBuffer buffer; private final long size; private final int bucketCount; private final int minStartDiff; private final MonotoneList startList; private final int minOffsetDiff; private final MonotoneList offsetList; private final int startBuckets; private final int endHeader; private final int endOffsetList; private final BDZ alternative; public RecSplitEvaluator(BitBuffer buffer, UniversalHash hash, Settings settings, boolean eliasFanoMonotoneLists) { this.settings = settings; this.hash = hash; this.buffer = buffer; this.size = (int) (buffer.readEliasDelta() - 1); this.bucketCount = Settings.getBucketCount(size, settings.getAverageBucketSize()); boolean alternative = buffer.readBit() != 0; this.minOffsetDiff = (int) (buffer.readEliasDelta() - 1); this.minStartDiff = (int) (buffer.readEliasDelta() - 1); this.endHeader = buffer.position(); this.offsetList = MonotoneList.load(buffer, eliasFanoMonotoneLists); this.endOffsetList = buffer.position(); this.startList = MonotoneList.load(buffer, eliasFanoMonotoneLists); this.startBuckets = buffer.position(); if (alternative) { int b = bucketCount; int offset = offsetList.get(b); int pos = startBuckets + Generator.getMinBitCount(offset) + startList.get(b) + b * minStartDiff; buffer.seek(pos); this.alternative = BDZ.load(hash, buffer); } else { this.alternative = null; } } public int getHeaderSize() { return endHeader; } public int getOffsetListSize() { return endOffsetList - endHeader; } public int getStartListSize() { return startBuckets - endOffsetList; } public int evaluate(T obj) { int b; long hashCode = hash.universalHash(obj, 0); //System.out.println("hashCode " + obj + " =" + hashCode); if (bucketCount == 1) { b = 0; } else { b = Settings.reduce((int) hashCode, bucketCount); } //System.out.println("bucket " + b); int startPos; long offsetPair = offsetList.getPair(b); int offset = (int) (offsetPair >>> 32) + b * minOffsetDiff; int offsetNext = ((int) offsetPair) + (b + 1) * minOffsetDiff; if (offsetNext == offset) { if (alternative == null) { // entry not found return 0; } offset = offsetList.get(bucketCount) + bucketCount * minOffsetDiff; return offset + alternative.evaluate(obj); } int bucketSize = offsetNext - offset; startPos = startBuckets + Generator.getMinBitCount(offset) + startList.get(b) + b * minStartDiff; //System.out.println("startPos " + startPos + " offset " + offset + " bucketSize " + bucketSize); return evaluate(startPos, obj, hashCode, 0, offset, bucketSize); } private int skip(int pos, int size) { if (size < 2) { return pos; } pos = buffer.skipGolombRice(pos, settings.getGolombRiceShift(size)); if (size <= settings.getLeafSize()) { return pos; } int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int s = firstPart; for (int i = 0; i < split; i++) { pos = skip(pos, s); s = otherPart; } return pos; } private int evaluate(int pos, T obj, long hashCode, long index, int add, int size) { while (true) { if (size < 2) { return add; } int shift = settings.getGolombRiceShift(size); long q = buffer.readUntilZero(pos); pos += q + 1; long value = (q << shift) | buffer.readNumber(pos, shift); pos += shift; long oldX = Settings.getUniversalHashIndex(index); index += value + 1; long x = Settings.getUniversalHashIndex(index); if (x != oldX) { hashCode = hash.universalHash(obj, x); } if (size <= settings.getLeafSize()) { int h = Settings.supplementalHash(hashCode, index); h = Settings.reduce(h, size); //System.out.printf("shift %d q %lld value %d oldX %d x %d size %d h %d add %d\n", shift, q, value, oldX, x, size, h, add); return add + h; } int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int h = Settings.supplementalHash(hashCode, index); if (firstPart != otherPart) { h = Settings.reduce(h, size); if (h < firstPart) { size = firstPart; continue; } pos = skip(pos, firstPart); add += firstPart; size = otherPart; continue; } h = Settings.reduce(h, split); for (int i = 0; i < h; i++) { pos = skip(pos, firstPart); add += firstPart; } size = firstPart; } } } ================================================ FILE: src/main/java/org/minperf/Settings.java ================================================ package org.minperf; /** * The settings used to generate the hash function. */ public class Settings { public static final boolean IMPROVED_SPLIT_RULES = true; /** * The number of supplemental hash functions per universal hash is 2 ^ this * number. Could be increased to reduce the number of universal hash * function calls, which also speeds up evaluation time. */ public static final int SUPPLEMENTAL_HASH_SHIFT = 18; /** * The number of times the same universal hash is mixed using the * supplemental hash function. Must be a power of 2. */ private static final long SUPPLEMENTAL_HASH_CALLS = 1 << SUPPLEMENTAL_HASH_SHIFT; /** * The Rice parameter k to use for leaves of size = array index. */ private static final int[] RICE_LEAF = { 0, 0, 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 21, 22, 23, 25, 26, 28, 29, 30, 32, 33, 35, 36, 38, 39, 40, 42 }; /** * The Rice parameter k that is used to split medium sized sets that are * evenly split into subsets. */ private static final int[][] RICE_SPLIT_MORE = { // 0 .. 6 { }, { }, { }, { }, { }, { 4}, { 4}, // 7 .. 13 { 4}, { 7}, { 7}, { 10, 7}, { 11, 7}, { 11, 7}, { 14, 8}, // 14 .. 18 { 14, 8}, { 15, 8}, { 18, 8}, { 18, 8}, { 19, 8}, // 19 .. 23 { 22, 13}, { 22, 13}, { 23, 14}, { 26, 14}, { 27, 14}, // 24 .. 25 { 27, 14}, { 31, 20, 12}}; /** * Convert an array of strings to an array of integers. This is needed to * work around the bytecode limit of Java methods. */ private static int[] splitIntegerList(String x) { if (x.trim().isEmpty()) { return new int[0]; } String[] array = x.split(","); int[] result = new int[array.length]; for (int i = 0; i < array.length; i++) { result[i] = Integer.parseInt(array[i].trim()); } return result; } /** * Array of size, split, k. */ static int[][] SPLIT_RULES = { // leafSize 0 splitIntegerList(""), // leafSize 1 splitIntegerList(""), // leafSize 2 splitIntegerList("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"), // leafSize 3 splitIntegerList("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"), // leafSize 4 splitIntegerList("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"), // leafSize 5 splitIntegerList("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"), // leafSize 6 splitIntegerList("0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"), // leafSize 7 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,-1,0,9,-2,1,10,-3,1,11,-4,1,12,-5,1,13,-6,1,14,-7,2,15,3,4,16,-7,2,17,-7,2,18,3,4,19,-7,2,20,-7,2,21,3,4,22,-1,1,23,-2,1,24,4,6,25,-4,2,26,-5,2,27,-6,2,28,-7,2,29,-5,2,30,-6,2,31,-7,2,32,-11,2,33,-12,2,34,-6,2,35,-7,2,36,-15,2,37,-6,2,38,-7,2,39,-18,2,40,-5,2,41,-6,2,42,2,2,43,-1,1,44,-2,1,45,-21,2,46,-4,2,47,-5,2,48,-6,2,49,-7,2,50,-5,2,51,-6,2,52,-7,2,53,-11,2,54,-5,2,55,-6,2,56,-7,2,57,-21,3,58,-6,2,59,-28,3,60,-21,3,61,-5,2,62,-6,2,63,3,6,64,-1,1,65,-2,1,66,-3,1,67,-4,2,68,-5,2,69,-6,2,70,-7,2,71,-8,2,72,-9,2,73,-10,2,74,-11,2,75,-5,2,76,-6,2,77,-7,2,78,-15,2,79,-7,2,80,-7,2,81,-18,3,82,-5,2,83,-6,2,84,-21,3,85,-22,3,86,-23,3,87,-24,3,88,-4,2,89,-26,3,90,-27,3,91,-28,3,92,-24,3,93,-30,3,94,-31,3,95,-32,3,96,-5,2,97,-34,3,98,-35,3,99,-36,3,100,-31,3,101,-38,3,102,-39,3,103,-5,2,104,-6,2,105,-42,3,106,-1,1,107,-44,3,108,-21,3,109,-4,2,110,-5,2,111,-6,2,112,-21,3,113,-21,3,114,-24,3,115,-24,3,116,-21,3,117,-21,3,118,-27,3,119,-28,3,120,-21,3,121,-28,3,122,-28,3,123,-21,3,124,-5,2,125,-28,3,126,-63,3,127,-1,1,128,-63,3,129,-63,3,130,-4,2,131,-5,2,132,-6,2,133,-7,2,134,-8,2,135,-63,3,136,-63,3,137,-63,3,138,-5,2,139,-6,2,140,-7,2,141,-15,3,142,-7,2,143,-7,2,144,-18,3,145,-5,2,146,-6,2,147,-21,3,148,-1,0,149,-2,1,150,-24,3,151,-4,2,152,-5,2,153,-6,2,154,-7,2,155,-5,2,156,-6,2,157,-7,2,158,-32,3,159,-5,2,160,-6,2,161,-7,2,162,-36,3,163,-6,2,164,-7,2,165,-39,3,166,-5,2,167,-6,2,168,-21,3,169,-21,3,170,-21,3,171,-21,3,172,-4,2,173,-21,3,174,-21,3,175,-21,3,176,-21,3,177,-21,3,178,-21,3,179,-21,3,180,-21,3,181,-21,3,182,-21,3,183,-21,3,184,-21,3,185,-21,3,186,-21,3,187,-21,3,188,-21,3,189,-63,3,190,-1,0,191,-2,1,192,-3,1,193,-4,2,194,-5,2,195,-6,2,196,-7,2,197,-7,2,198,-7,2,199,-7,2,200,-4,2,201,-5,2,202,-6,2,203,-7,2,204,-15,3,205,-7,2,206,-7,2,207,-18,3,208,-5,2,209,-6,2,210,-21,3,211,-21,3,212,-21,3,213,-24,3,214,-4,2,215,-21,3,216,-21,3,217,-21,3,218,-5,2,219,-24,3,220,-24,3,221,-32,3,222,-21,3,223,-21,3,224,-21,3,225,-99,4,226,-24,3,227,-24,3,228,-102,4,229,-21,3,230,-21,3,231,-21,3,232,-21,3,233,-21,3,234,-21,3,235,-4,2,236,-21,3,237,-21,3,238,-21,3,239,-21,3,240,-21,3,241,-21,3,242,-95,4,243,-21,3,244,-21,3,245,-21,3,246,-99,4,247,-21,3,248,-21,3,249,-102,4,250,-21,3,251,-21,3,252,-126,4,253,-1,0,254,-2,1,255,-3,1,256,-4,2,257,-5,2,258,-6,2,259,-7,2,260,-7,2,261,-7,2,262,-7,2,263,-4,2,264,-5,2,265,-6,2,266,-7,2,267,-15,3,268,-7,2,269,-7,2,270,-18,3,271,-5,2,272,-6,2,273,-126,4,274,-126,4,275,-126,4,276,-126,4,277,-4,2,278,-126,4,279,-126,4,280,-126,4,281,-126,4,282,-126,4,283,-126,4,284,-126,4,285,-126,4,286,-126,4,287,-126,4,288,-126,4,289,-126,4,290,-126,4,291,-126,4,292,-126,4,293,-126,4,294,-126,4,295,-126,4,296,-126,4,297,-126,4,298,-4,2,299,-126,4,300,-126,4,301,-126,4,302,-126,4,303,-126,4,304,-126,4,305,-126,4,306,-126,4,307,-126,4,308,-126,4,309,-126,4,310,-126,4,311,-126,4,312,-126,4,313,-126,4,314,-126,4,315,-126,4,316,-126,4,317,-126,4,318,-126,4,319,-4,2,320,-126,4,321,-126,4,322,-126,4,323,-126,4,324,-126,4,325,-126,4,326,-4,2,327,-126,4,328,-126,4,329,-126,4,330,-15,3,331,-126,4,332,-126,4,333,-18,3,334,-5,2,335,-126,4,336,-21,3,337,-21,3,338,-21,3,339,-150,4,340,-4,2,341,-21,3,342,-21,3,343,-21,3,344,-5,2,345,-156,4,346,-157,4,347,-95,4,348,-21,3,349,-21,3,350,-21,3,351,-99,4,352,-163,4,353,-164,4,354,-102,4,355,-5,2,356,-21,3,357,-21,3,358,-21,3,359,-21,3,360,-21,3,361,-4,2,362,-21,3,363,-21,3,364,-21,3,365,-5,2,366,-21,3,367,-21,3,368,-95,4,369,-21,3,370,-21,3,371,-21,3,372,-99,4,373,-21,3,374,-21,3,375,-102,4,376,-5,2,377,-21,3,378,-126,4,379,-126,4,380,-126,4,381,-126,4,382,-4,2,383,-5,2,384,-126,4,385,-126,4,386,-126,4,387,-126,4,388,-126,4,389,-4,2,390,-5,2,391,-126,4,392,-126,4,393,-15,3,394,-126,4,395,-126,4,396,-18,3,397,-5,2,398,-126,4,399,-126,4,400,-126,4,401,-126,4,402,-126,4,403,-4,2,404,-126,4,405,-126,4,406,-126,4,407,-126,4,408,-126,4,409,-126,4,410,-126,4,411,-126,4,412,-126,4,413,-126,4,414,-126,4,415,-126,4,416,-126,4,417,-126,4,418,-126,4,419,-126,4,420,-126,4,421,-126,4,422,-126,4,423,-126,4,424,-4,2,425,-126,4,426,-126,4,427,-126,4,428,-126,4,429,-126,4,430,-126,4,431,-126,4,432,-126,4,433,-126,4,434,-21,3,435,-21,3,436,-21,3,437,-21,3,438,-21,3,439,-21,3,440,-21,3,441,-126,4,442,-126,4,443,-126,4,444,-126,4,445,-4,2,446,-5,2,447,-126,4,448,-126,4,449,-126,4,450,-126,4,451,-126,4,452,-4,2,453,-5,2,454,-126,4,455,-126,4,456,-15,3,457,-126,4,458,-126,4,459,-18,3,460,-5,2,461,-126,4,462,-21,3,463,-21,3,464,-21,3,465,-126,4,466,-4,2,467,-5,2,468,-21,3,469,-21,3,470,-5,2,471,-126,4,472,-126,4,473,-95,4,474,-5,2,475,-21,3,476,-21,3,477,-99,4,478,-126,4,479,-126,4,480,-102,4,481,-5,2,482,-21,3,483,-21,3,484,-21,3,485,-21,3,486,-21,3,487,-4,2,488,-5,2,489,-21,3,490,-21,3,491,-5,2,492,-21,3,493,-91,4,494,-95,4,495,-5,2,496,-91,4,497,-91,4,498,-99,4,499,-91,4,500,-91,4,501,-102,4,502,-91,4,503,-91,4,504,-126,4,505,-126,4,506,-126,4,507,-126,4,508,-4,2,509,-5,2,510,-126,4,511,-126,4,512,-126,4,513,-126,4,514,-126,4,515,-4,2,516,-5,2,517,-126,4,518,-126,4,519,-15,3,520,-126,4,521,-126,4,522,-18,3,523,-5,2,524,-126,4,525,-126,4,526,-126,4,527,-126,4,528,-126,4,529,-4,2,530,-126,4,531,-126,4,532,-126,4,533,-126,4,534,-126,4,535,-126,4,536,-126,4,537,-126,4,538,-126,4,539,-126,4,540,-126,4,541,-126,4,542,-126,4,543,-126,4,544,-5,2,545,-126,4,546,-21,3,547,-21,3,548,-21,3,549,-21,3,550,-4,2,551,-21,3,552,-21,3,553,-21,3,554,-21,3,555,-21,3,556,-21,3,557,-21,3,558,-21,3,559,-21,3,560,-21,3,561,-21,3,562,-21,3,563,-21,3,564,-21,3,565,-5,2,566,-21,3,567,-126,4,568,-126,4,569,-126,4,570,-126,4,571,-4,2,572,-5,2,573,-126,4,574,-126,4,575,-126,4,576,-126,4,577,-126,4,578,-4,2,579,-5,2,580,-126,4,581,-126,4,582,-15,3,583,-126,4,584,-126,4,585,-18,3,586,-5,2,587,-126,4,588,-21,3,589,-21,3,590,-21,3,591,-126,4,592,-4,2,593,-5,2,594,-21,3,595,-91,4,596,-5,2,597,-126,4,598,-94,4,599,-95,4,600,-91,4,601,-91,4,602,-21,3,603,-99,4,604,-94,4,605,-94,4,606,-102,4,607,-5,2,608,-21,3,609,-105,4,610,-105,4,611,-105,4,612,-87,4,613,-4,2,614,-5,2,615,-105,4,616,-91,4,617,-87,4,618,-87,4,619,-91,4,620,-95,4,621,-91,4,622,-91,4,623,-91,4,624,-99,4,625,-91,4,626,-91,4,627,-102,4,628,-91,4,629,-91,4,630,-126,4,631,-126,4,632,-126,4,633,-126,4,634,-4,2,635,-5,2,636,-126,4,637,-126,4,638,-126,4,639,-126,4,640,-126,4,641,-4,2,642,-5,2,643,-126,4,644,-126,4,645,-15,3,646,-126,4,647,-126,4,648,-18,3,649,-5,2,650,-126,4,651,-126,4,652,-126,4,653,-126,4,654,-126,4,655,-4,2,656,-126,4,657,-126,4,658,-126,4,659,-5,2,660,-126,4,661,-126,4,662,-126,4,663,-5,2,664,-126,4,665,-126,4,666,-126,4,667,-126,4,668,-126,4,669,-126,4,670,-5,2,671,-126,4,672,-21,3,673,-21,3,674,-21,3,675,-21,3,676,-4,2,677,-21,3,678,-21,3,679,-21,3,680,-5,2,681,-21,3,682,-21,3,683,-21,3,684,-5,2,685,-21,3,686,-21,3,687,-21,3,688,-21,3,689,-21,3,690,-21,3,691,-5,2,692,-21,3,693,-126,4,694,-126,4,695,-126,4,696,-126,4,697,-4,2,698,-5,2,699,-126,4,700,-126,4,701,-126,4,702,-126,4,703,-126,4,704,-4,2,705,-5,2,706,-126,4,707,-126,4,708,-15,3,709,-126,4,710,-126,4,711,-18,3,712,-5,2,713,-126,4,714,-21,3,715,-21,3,716,-21,3,717,-87,4,718,-4,2,719,-5,2,720,-21,3,721,-91,4,722,-87,4,723,-87,4,724,-94,4,725,-95,4,726,-5,2,727,-91,4,728,-91,4,729,-99,4,730,-94,4,731,-94,4,732,-102,4,733,-5,2,734,-91,4,735,-105,4,736,-105,4,737,-105,4,738,-87,4,739,-4,2,740,-5,2,741,-105,4,742,-91,4,743,-87,4,744,-87,4,745,-91,4,746,-95,4,747,-91,4,748,-91,4,749,-91,4,750,-99,4,751,-91,4,752,-91,4,753,-102,4,754,-5,2,755,-91,4,756,-126,4,757,-126,4,758,-126,4,759,-126,4,760,-4,2,761,-5,2,762,-126,4,763,-126,4,764,-126,4,765,-126,4,766,-126,4,767,-4,2,768,-5,2,769,-126,4,770,-126,4,771,-15,3,772,-126,4,773,-126,4,774,-18,3,775,-5,2,776,-126,4,777,-126,4,778,-126,4,779,-126,4,780,-126,4,781,-4,2,782,-5,2,783,-126,4,784,-126,4,785,-5,2,786,-126,4,787,-126,4,788,-126,4,789,-5,2,790,-126,4,791,-126,4,792,-126,4,793,-126,4,794,-126,4,795,-126,4,796,-5,2,797,-126,4,798,-21,3,799,-21,3,800,-21,3,801,-21,3,802,-4,2,803,-5,2,804,-21,3,805,-21,3,806,-5,2,807,-21,3,808,-21,3,809,-21,3,810,-5,2,811,-21,3,812,-21,3,813,-21,3,814,-21,3,815,-21,3,816,-21,3,817,-5,2,818,-21,3,819,-126,4,820,-126,4,821,-126,4,822,-126,4,823,-4,2,824,-5,2,825,-126,4,826,-126,4,827,-126,4,828,-126,4,829,-126,4,830,-4,2,831,-5,2,832,-126,4,833,-126,4,834,-15,3,835,-126,4,836,-126,4,837,-18,3,838,-5,2,839,-126,4,840,-21,3,841,-21,3,842,-21,3,843,-87,4,844,-4,2,845,-5,2,846,-21,3,847,-91,4,848,-87,4,849,-87,4,850,-94,4,851,-95,4,852,-5,2,853,-91,4,854,-91,4,855,-99,4,856,-94,4,857,-94,4,858,-102,4,859,-5,2,860,-91,4,861,-105,4,862,-105,4,863,-105,4,864,-87,4,865,-4,2,866,-5,2,867,-105,4,868,-91,4,869,-87,4,870,-87,4,871,-91,4,872,-95,4,873,-5,2,874,-91,4,875,-91,4,876,-99,4,877,-91,4,878,-91,4,879,-102,4,880,-5,2,881,-91,4,882,-126,4,883,-126,4,884,-126,4,885,-126,4,886,-4,2,887,-5,2,888,-126,4,889,-126,4,890,-126,4,891,-126,4,892,-126,4,893,-4,2,894,-5,2,895,-126,4,896,-126,4,897,-15,3,898,-126,4,899,-126,4,900,-18,3,901,-5,2,902,-126,4,903,-21,3,904,-126,4,905,-126,4,906,-126,4,907,-4,2,908,-5,2,909,-126,4,910,-126,4,911,-5,2,912,-126,4,913,-126,4,914,-126,4,915,-5,2,916,-126,4,917,-126,4,918,-126,4,919,-126,4,920,-126,4,921,-126,4,922,-5,2,923,-126,4,924,-21,3,925,-21,3,926,-21,3,927,-21,3,928,-4,2,929,-5,2,930,-21,3,931,-21,3,932,-5,2,933,-21,3,934,-21,3,935,-21,3,936,-5,2,937,-21,3,938,-21,3,939,-21,3,940,-21,3,941,-21,3,942,-21,3,943,-5,2,944,-21,3,945,-126,4,946,-126,4,947,-126,4,948,-126,4,949,-4,2,950,-5,2,951,-126,4,952,-126,4,953,-126,4,954,-126,4,955,-126,4,956,-4,2,957,-5,2,958,-126,4,959,-126,4,960,-15,3,961,-126,4,962,-126,4,963,-18,3,964,-5,2,965,-126,4,966,-21,3,967,-21,3,968,-21,3,969,-87,4,970,-4,2,971,-5,2,972,-21,3,973,-91,4,974,-5,2,975,-87,4,976,-94,4,977,-95,4,978,-5,2,979,-91,4,980,-91,4,981,-99,4,982,-94,4,983,-94,4,984,-102,4,985,-5,2,986,-91,4,987,-105,4,988,-105,4,989,-105,4,990,-87,4,991,-4,2,992,-5,2,993,-105,4,994,-21,3,995,-5,2,996,-87,4,997,-91,4,998,-21,3,999,-5,2,1000,-91,4,1001,-91,4,1002,-21,3,1003,-91,4,1004,-91,4,1005,-21,3,1006,-5,2,1007,-91,4,1008,-126,4,1009,-126,4,1010,-126,4,1011,-126,4,1012,-4,2,1013,-5,2,1014,-126,4,1015,-126,4,1016,-126,4,1017,-126,4,1018,-126,4,1019,-4,2,1020,-5,2,1021,-126,4,1022,-126,4,1023,-15,3,1024,-126,4,1025,-126,4,1026,-18,3,1027,-5,2,1028,-6,2,1029,-21,3,1030,-126,4,1031,-126,4,1032,-126,4,1033,-4,2,1034,-5,2,1035,-126,4,1036,-126,4,1037,-5,2,1038,-126,4,1039,-126,4,1040,-126,4,1041,-5,2,1042,-126,4,1043,-126,4,1044,-126,4,1045,-126,4,1046,-126,4,1047,-126,4,1048,-5,2,1049,-126,4,1050,-21,3,1051,-21,3,1052,-21,3,1053,-21,3,1054,-4,2,1055,-5,2,1056,-21,3,1057,-21,3,1058,-5,2,1059,-21,3,1060,-21,3,1061,-21,3,1062,-5,2,1063,-21,3,1064,-21,3,1065,-21,3,1066,-21,3,1067,-21,3,1068,-21,3,1069,-5,2,1070,-21,3,1071,-126,4,1072,-126,4,1073,-126,4,1074,-126,4,1075,-4,2,1076,-5,2,1077,-126,4,1078,-126,4,1079,-126,4,1080,-126,4,1081,-126,4,1082,-4,2,1083,-5,2,1084,-126,4,1085,-126,4,1086,-15,3,1087,-126,4,1088,-126,4,1089,-18,3,1090,-5,2,1091,-126,4,1092,-21,3,1093,-21,3,1094,-21,3,1095,-87,4,1096,-4,2,1097,-5,2,1098,-21,3,1099,-91,4,1100,-5,2,1101,-87,4,1102,-94,4,1103,-95,4,1104,-5,2,1105,-91,4,1106,-91,4,1107,-99,4,1108,-94,4,1109,-94,4,1110,-102,4,1111,-5,2,1112,-91,4,1113,-105,4,1114,-105,4,1115,-105,4,1116,-87,4,1117,-4,2,1118,-5,2,1119,-105,4,1120,-21,3,1121,-5,2,1122,-87,4,1123,-91,4,1124,-21,3,1125,-5,2,1126,-91,4,1127,-91,4,1128,-21,3,1129,-91,4,1130,-91,4,1131,-21,3,1132,-5,2,1133,-91,4,1134,-126,4,1135,-126,4,1136,-126,4,1137,-126,4,1138,-4,2,1139,-5,2,1140,-126,4,1141,-126,4,1142,-126,4,1143,-126,4,1144,-126,4,1145,-4,2,1146,-5,2,1147,-6,2,1148,-126,4,1149,-15,3,1150,-126,4,1151,-126,4,1152,-18,3,1153,-5,2,1154,-6,2,1155,-21,3,1156,-126,4,1157,-126,4,1158,-126,4,1159,-4,2,1160,-5,2,1161,-126,4,1162,-126,4,1163,-5,2,1164,-126,4,1165,-126,4,1166,-126,4,1167,-5,2,1168,-126,4,1169,-126,4,1170,-126,4,1171,-126,4,1172,-126,4,1173,-126,4,1174,-5,2,1175,-126,4,1176,-21,3,1177,-21,3,1178,-21,3,1179,-21,3,1180,-4,2,1181,-5,2,1182,-21,3,1183,-21,3,1184,-5,2,1185,-21,3,1186,-21,3,1187,-21,3,1188,-5,2,1189,-21,3,1190,-21,3,1191,-21,3,1192,-21,3,1193,-21,3,1194,-21,3,1195,-5,2,1196,-21,3,1197,-126,4,1198,-126,4,1199,-126,4,1200,-126,4,1201,-4,2,1202,-5,2,1203,-126,4,1204,-126,4,1205,-126,4,1206,-126,4,1207,-126,4,1208,-4,2,1209,-5,2,1210,-126,4,1211,-126,4,1212,-15,3,1213,-126,4,1214,-126,4,1215,-18,3,1216,-5,2,1217,-126,4,1218,-21,3,1219,-21,3,1220,-21,3,1221,-87,4,1222,-4,2,1223,-5,2,1224,-21,3,1225,-91,4,1226,-5,2,1227,-87,4,1228,-94,4,1229,-95,4,1230,-5,2,1231,-91,4,1232,-91,4,1233,-99,4,1234,-94,4,1235,-94,4,1236,-102,4,1237,-5,2,1238,-91,4,1239,-105,4,1240,-105,4,1241,-105,4,1242,-87,4,1243,-4,2,1244,-5,2,1245,-105,4,1246,-21,3,1247,-5,2,1248,-87,4,1249,-91,4,1250,-21,3,1251,-5,2,1252,-91,4,1253,-91,4,1254,-21,3,1255,-91,4,1256,-91,4,1257,-21,3,1258,-5,2,1259,-91,4,1260,-126,4,1261,-126,4,1262,-126,4,1263,-126,4,1264,-4,2,1265,-5,2,1266,-6,2,1267,-126,4,1268,-126,4,1269,-126,4,1270,-126,4,1271,-4,2,1272,-5,2,1273,-6,2,1274,-126,4,1275,-15,3,1276,-126,4,1277,-126,4,1278,-18,3,1279,-5,2,1280,-6,2,1281,-21,3,1282,-126,4,1283,-126,4,1284,-126,4,1285,-4,2,1286,-5,2,1287,-126,4,1288,-126,4,1289,-5,2,1290,-126,4,1291,-126,4,1292,-126,4,1293,-5,2,1294,-126,4,1295,-126,4,1296,-126,4,1297,-126,4,1298,-126,4,1299,-126,4,1300,-5,2,1301,-126,4,1302,-21,3,1303,-21,3,1304,-21,3,1305,-21,3,1306,-4,2,1307,-5,2,1308,-21,3,1309,-21,3,1310,-5,2,1311,-21,3,1312,-21,3,1313,-21,3,1314,-5,2,1315,-21,3,1316,-21,3,1317,-21,3,1318,-21,3,1319,-21,3,1320,-21,3,1321,-5,2,1322,-21,3,1323,-63,4,1324,-126,4,1325,-126,4,1326,-126,4,1327,-63,4,1328,-63,4,1329,-126,4,1330,-126,4,1331,-126,4,1332,-126,4,1333,-73,4,1334,-4,2,1335,-5,2,1336,-126,4,1337,-126,4,1338,-15,3,1339,-126,4,1340,-126,4,1341,-63,4,1342,-5,2,1343,-126,4,1344,-63,4,1345,-63,4,1346,-63,4,1347,-87,4,1348,-63,4,1349,-63,4,1350,-63,4,1351,-91,4,1352,-5,2,1353,-87,4,1354,-94,4,1355,-95,4,1356,-5,2,1357,-91,4,1358,-91,4,1359,-99,4,1360,-94,4,1361,-94,4,1362,-102,4,1363,-5,2,1364,-91,4,1365,-105,4,1366,-105,4,1367,-105,4,1368,-87,4,1369,-4,2,1370,-5,2,1371,-6,2,1372,-21,3,1373,-5,2,1374,-87,4,1375,-91,4,1376,-21,3,1377,-5,2,1378,-91,4,1379,-91,4,1380,-21,3,1381,-91,4,1382,-91,4,1383,-21,3,1384,-5,2,1385,-91,4,1386,-126,4,1387,-126,4,1388,-126,4,1389,-126,4,1390,-4,2,1391,-5,2,1392,-6,2,1393,-126,4,1394,-126,4,1395,-126,4,1396,-126,4,1397,-4,2,1398,-5,2,1399,-6,2,1400,-126,4,1401,-15,3,1402,-126,4,1403,-126,4,1404,-18,3,1405,-5,2,1406,-6,2,1407,-21,3,1408,-126,4,1409,-126,4,1410,-126,4,1411,-4,2,1412,-5,2,1413,-126,4,1414,-126,4,1415,-5,2,1416,-126,4,1417,-126,4,1418,-126,4,1419,-5,2,1420,-126,4,1421,-126,4,1422,-126,4,1423,-126,4,1424,-126,4,1425,-126,4,1426,-5,2,1427,-126,4,1428,-21,3,1429,-21,3,1430,-21,3,1431,-21,3,1432,-4,2,1433,-5,2,1434,-21,3,1435,-21,3,1436,-5,2,1437,-21,3,1438,-21,3,1439,-21,3,1440,-5,2,1441,-21,3,1442,-21,3,1443,-21,3,1444,-21,3,1445,-21,3,1446,-21,3,1447,-5,2,1448,-21,3,1449,-63,4,1450,-63,4,1451,-63,4,1452,-126,4,1453,-63,4,1454,-63,4,1455,-63,4,1456,-126,4,1457,-126,4,1458,-126,4,1459,-73,4,1460,-4,2,1461,-5,2,1462,-126,4,1463,-126,4,1464,-15,3,1465,-709,5,1466,-73,4,1467,-63,4,1468,-5,2,1469,-713,5,1470,-63,4,1471,-63,4,1472,-63,4,1473,-87,4,1474,-63,4,1475,-63,4,1476,-63,4,1477,-91,4,1478,-5,2,1479,-87,4,1480,-94,4,1481,-95,4,1482,-5,2,1483,-91,4,1484,-91,4,1485,-99,4,1486,-94,4,1487,-94,4,1488,-102,4,1489,-5,2,1490,-91,4,1491,-105,4,1492,-105,4,1493,-105,4,1494,-87,4,1495,-4,2,1496,-5,2,1497,-6,2,1498,-21,3,1499,-5,2,1500,-87,4,1501,-91,4,1502,-21,3,1503,-5,2,1504,-91,4,1505,-91,4,1506,-21,3,1507,-91,4,1508,-91,4,1509,-21,3,1510,-5,2,1511,-91,4,1512,-756,5,1513,-756,5,1514,-756,5,1515,-756,5,1516,-4,2,1517,-5,2,1518,-756,5,1519,-756,5,1520,-757,5,1521,-758,5,1522,-759,5,1523,-4,2,1524,-5,2,1525,-762,5,1526,-763,5,1527,-15,3,1528,-763,5,1529,-763,5,1530,-18,3,1531,-5,2,1532,-763,5,1533,-21,3,1534,-756,5,1535,-756,5,1536,-756,5,1537,-4,2,1538,-5,2,1539,-756,5,1540,-756,5,1541,-5,2,1542,-756,5,1543,-756,5,1544,-756,5,1545,-5,2,1546,-756,5,1547,-756,5,1548,-756,5,1549,-756,5,1550,-756,5,1551,-756,5,1552,-5,2,1553,-756,5,1554,-21,3,1555,-21,3,1556,-21,3,1557,-21,3,1558,-4,2,1559,-5,2,1560,-21,3,1561,-21,3,1562,-5,2,1563,-21,3,1564,-21,3,1565,-21,3,1566,-5,2,1567,-21,3,1568,-21,3,1569,-21,3,1570,-21,3,1571,-784,5,1572,-21,3,1573,-5,2,1574,-784,5,1575,-756,5,1576,-756,5,1577,-756,5,1578,-756,5,1579,-4,2,1580,-5,2,1581,-756,5,1582,-756,5,1583,-756,5,1584,-756,5,1585,-73,4,1586,-4,2,1587,-5,2,1588,-756,5,1589,-756,5,1590,-15,3,1591,-756,5,1592,-756,5,1593,-18,3,1594,-5,2,1595,-756,5,1596,-21,3,1597,-778,5,1598,-779,5,1599,-87,4,1600,-4,2,1601,-5,2,1602,-63,4,1603,-91,4,1604,-5,2,1605,-87,4,1606,-94,4,1607,-95,4,1608,-5,2,1609,-91,4,1610,-91,4,1611,-99,4,1612,-94,4,1613,-63,4,1614,-102,4,1615,-5,2,1616,-91,4,1617,-105,4,1618,-105,4,1619,-105,4,1620,-87,4,1621,-4,2,1622,-5,2,1623,-105,4,1624,-21,3,1625,-5,2,1626,-87,4,1627,-91,4,1628,-21,3,1629,-5,2,1630,-91,4,1631,-91,4,1632,-21,3,1633,-91,4,1634,-91,4,1635,-21,3,1636,-5,2,1637,-91,4,1638,-756,5,1639,-757,5,1640,-758,5,1641,-759,5,1642,-4,2,1643,-5,2,1644,-762,5,1645,-763,5,1646,-763,5,1647,-763,5,1648,-763,5,1649,-4,2,1650,-5,2,1651,-763,5,1652,-763,5,1653,-15,3,1654,-765,5,1655,-766,5,1656,-18,3,1657,-5,2,1658,-6,2,1659,-21,3,1660,-778,5,1661,-779,5,1662,-780,5,1663,-4,2,1664,-5,2,1665,-783,5,1666,-784,5,1667,-5,2,1668,-786,5,1669,-787,5,1670,-788,5,1671,-5,2,1672,-790,5,1673,-791,5,1674,-792,5,1675,-793,5,1676,-794,5,1677,-795,5,1678,-5,2,1679,-797,5,1680,-21,3,1681,-21,3,1682,-21,3,1683,-21,3,1684,-4,2,1685,-5,2,1686,-21,3,1687,-21,3,1688,-5,2,1689,-21,3,1690,-21,3,1691,-21,3,1692,-5,2,1693,-21,3,1694,-21,3,1695,-21,3,1696,-21,3,1697,-21,3,1698,-21,3,1699,-5,2,1700,-21,3,1701,-63,4,1702,-63,4,1703,-63,4,1704,-822,5,1705,-63,4,1706,-63,4,1707,-63,4,1708,-63,4,1709,-827,5,1710,-828,5,1711,-73,4,1712,-63,4,1713,-63,4,1714,-832,5,1715,-833,5,1716,-15,3,1717,-835,5,1718,-73,4,1719,-63,4,1720,-5,2,1721,-839,5,1722,-63,4,1723,-63,4,1724,-63,4,1725,-87,4,1726,-63,4,1727,-63,4,1728,-63,4,1729,-91,4,1730,-5,2,1731,-87,4,1732,-94,4,1733,-95,4,1734,-5,2,1735,-91,4,1736,-91,4,1737,-99,4,1738,-94,4,1739,-94,4,1740,-102,4,1741,-5,2,1742,-91,4,1743,-105,4,1744,-63,4,1745,-105,4,1746,-87,4,1747,-4,2,1748,-5,2,1749,-63,4,1750,-21,3,1751,-5,2,1752,-87,4,1753,-91,4,1754,-21,3,1755,-5,2,1756,-91,4,1757,-91,4,1758,-21,3,1759,-91,4,1760,-91,4,1761,-21,3,1762,-5,2,1763,-91,4,1764,-756,5,1765,-757,5,1766,-758,5,1767,-759,5,1768,-4,2,1769,-5,2,1770,-6,2,1771,-763,5,1772,-763,5,1773,-763,5,1774,-763,5,1775,-4,2,1776,-5,2,1777,-6,2,1778,-763,5,1779,-15,3,1780,-763,5,1781,-763,5,1782,-18,3,1783,-5,2,1784,-6,2,1785,-21,3,1786,-756,5,1787,-756,5,1788,-756,5,1789,-4,2,1790,-5,2,1791,-756,5,1792,-756,5,1793,-5,2,1794,-756,5,1795,-756,5,1796,-756,5,1797,-5,2,1798,-756,5,1799,-756,5,1800,-756,5,1801,-756,5,1802,-756,5,1803,-756,5,1804,-5,2,1805,-756,5,1806,-21,3,1807,-21,3,1808,-21,3,1809,-21,3,1810,-4,2,1811,-5,2,1812,-21,3,1813,-21,3,1814,-5,2,1815,-21,3,1816,-21,3,1817,-21,3,1818,-5,2,1819,-21,3,1820,-21,3,1821,-21,3,1822,-21,3,1823,-21,3,1824,-21,3,1825,-5,2,1826,-21,3,1827,-63,4,1828,-63,4,1829,-63,4,1830,-63,4,1831,-63,4,1832,-63,4,1833,-63,4,1834,-63,4,1835,-63,4,1836,-63,4,1837,-73,4,1838,-63,4,1839,-63,4,1840,-63,4,1841,-63,4,1842,-15,3,1843,-63,4,1844,-73,4,1845,-63,4,1846,-63,4,1847,-63,4,1848,-63,4,1849,-63,4,1850,-63,4,1851,-87,4,1852,-63,4,1853,-63,4,1854,-63,4,1855,-91,4,1856,-5,2,1857,-87,4,1858,-94,4,1859,-95,4,1860,-5,2,1861,-91,4,1862,-91,4,1863,-99,4,1864,-94,4,1865,-94,4,1866,-102,4,1867,-5,2,1868,-91,4,1869,-105,4,1870,-63,4,1871,-105,4,1872,-21,3,1873,-4,2,1874,-5,2,1875,-63,4,1876,-21,3,1877,-5,2,1878,-87,4,1879,-91,4,1880,-21,3,1881,-5,2,1882,-91,4,1883,-91,4,1884,-21,3,1885,-91,4,1886,-91,4,1887,-21,3,1888,-5,2,1889,-91,4,1890,-630,5,1891,-631,5,1892,-632,5,1893,-633,5,1894,-4,2,1895,-5,2,1896,-6,2,1897,-637,5,1898,-637,5,1899,-637,5,1900,-637,5,1901,-4,2,1902,-5,2,1903,-6,2,1904,-637,5,1905,-15,3,1906,-639,5,1907,-640,5,1908,-18,3,1909,-5,2,1910,-6,2,1911,-21,3,1912,-652,5,1913,-653,5,1914,-654,5,1915,-4,2,1916,-5,2,1917,-657,5,1918,-658,5,1919,-5,2,1920,-660,5,1921,-661,5,1922,-662,5,1923,-5,2,1924,-664,5,1925,-665,5,1926,-666,5,1927,-667,5,1928,-668,5,1929,-669,5,1930,-5,2,1931,-671,5,1932,-21,3,1933,-21,3,1934,-21,3,1935,-21,3,1936,-4,2,1937,-5,2,1938,-21,3,1939,-21,3,1940,-5,2,1941,-21,3,1942,-21,3,1943,-21,3,1944,-5,2,1945,-21,3,1946,-21,3,1947,-21,3,1948,-21,3,1949,-21,3,1950,-21,3,1951,-5,2,1952,-21,3,1953,-63,4,1954,-63,4,1955,-63,4,1956,-63,4,1957,-63,4,1958,-63,4,1959,-63,4,1960,-63,4,1961,-63,4,1962,-63,4,1963,-73,4,1964,-63,4,1965,-63,4,1966,-63,4,1967,-63,4,1968,-15,3,1969,-63,4,1970,-73,4,1971,-63,4,1972,-63,4,1973,-63,4,1974,-63,4,1975,-63,4,1976,-63,4,1977,-87,4,1978,-63,4,1979,-63,4,1980,-63,4,1981,-91,4,1982,-5,2,1983,-87,4,1984,-94,4,1985,-95,4,1986,-5,2,1987,-91,4,1988,-91,4,1989,-99,4,1990,-94,4,1991,-94,4,1992,-102,4,1993,-5,2,1994,-91,4,1995,-63,4,1996,-63,4,1997,-63,4,1998,-21,3,1999,-63,4,2000,-63,4,2001,-63,4,2002,-21,3,2003,-5,2,2004,-87,4,2005,-91,4,2006,-21,3,2007,-5,2,2008,-91,4,2009,-91,4,2010,-21,3,2011,-91,4,2012,-91,4,2013,-21,3,2014,-5,2,2015,-91,4,2016,-630,5,2017,-631,5,2018,-632,5,2019,-633,5,2020,-4,2,2021,-5,2,2022,-6,2,2023,-637,5,2024,-637,5,2025,-637,5,2026,-637,5,2027,-4,2,2028,-5,2,2029,-6,2,2030,-637,5,2031,-15,3,2032,-637,5,2033,-637,5,2034,-18,3,2035,-5,2,2036,-6,2,2037,-21,3,2038,-526,5,2039,-527,5,2040,-528,5,2041,-4,2,2042,-5,2,2043,-531,5,2044,-532,5,2045,-5,2,2046,-534,5,2047,-535,5,0,0,0"), // leafSize 8 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,-1,0,10,-2,1,11,-3,1,12,-4,1,13,-5,1,14,-7,2,15,-7,2,16,-8,2,17,-8,2,18,3,4,19,-8,2,20,-8,2,21,3,4,22,-7,2,23,-7,2,24,3,4,25,-1,1,26,-2,1,27,-3,1,28,4,7,29,-5,2,30,-6,2,31,-7,2,32,4,7,33,-1,1,34,-2,1,35,-3,1,36,-4,2,37,-5,2,38,-6,2,39,-7,2,40,-8,2,41,-9,2,42,-10,2,43,-11,2,44,-12,2,45,-5,2,46,-7,2,47,-7,2,48,-8,2,49,-21,2,50,-18,2,51,-8,2,52,-24,3,53,-21,3,54,-7,2,55,-7,2,56,-24,3,57,-25,3,58,-26,3,59,-27,3,60,-28,3,61,-29,3,62,-30,3,63,-31,3,64,-32,3,65,-32,3,66,-32,3,67,-32,3,68,-4,2,69,-32,3,70,-32,3,71,-32,3,72,-32,3,73,-32,3,74,-32,3,75,-32,3,76,-32,3,77,-32,3,78,-39,3,79,-39,3,80,-40,3,81,-21,3,82,-32,3,83,-40,3,84,-24,3,85,-21,3,86,-40,3,87,-40,3,88,-24,3,89,-24,3,90,-26,3,91,-27,3,92,-28,3,93,-24,3,94,-24,3,95,-24,3,96,3,6,97,-1,1,98,-2,1,99,-3,1,100,-4,2,101,-5,2,102,-6,2,103,-7,2,104,-8,2,105,-9,2,106,-8,2,107,-11,2,108,-4,2,109,-5,2,110,-7,2,111,-7,2,112,-8,2,113,-8,2,114,-18,3,115,-8,2,116,-20,3,117,-21,3,118,-7,2,119,-7,2,120,-24,3,121,-1,1,122,-26,3,123,-27,3,124,-28,3,125,-24,3,126,-30,3,127,-31,3,128,-32,3,129,-1,1,130,-34,3,131,-35,3,132,-4,2,133,-5,2,134,-6,2,135,-32,3,136,-40,3,137,-41,3,138,-42,3,139,-43,3,140,-44,3,141,-5,2,142,-32,3,143,-7,2,144,-8,2,145,-21,3,146,-18,3,147,-3,1,148,-24,3,149,-21,3,150,-7,2,151,-7,2,152,-24,3,153,-1,0,154,-2,1,155,-3,1,156,-28,3,157,-5,2,158,-6,2,159,-7,2,160,-32,3,161,-1,0,162,-2,1,163,-3,1,164,-4,2,165,-5,2,166,-6,2,167,-7,2,168,-8,2,169,-8,2,170,-8,2,171,-8,2,172,-4,2,173,-5,2,174,-7,2,175,-7,2,176,-8,2,177,-21,3,178,-18,3,179,-8,2,180,-24,3,181,-21,3,182,-7,2,183,-7,2,184,-24,3,185,-24,3,186,-24,3,187,-24,3,188,-28,3,189,-5,2,190,-24,3,191,-24,3,192,-96,4,193,-1,0,194,-2,1,195,-3,1,196,-4,2,197,-5,2,198,-6,2,199,-7,2,200,-8,2,201,-8,2,202,-8,2,203,-8,2,204,-4,2,205,-5,2,206,-7,2,207,-7,2,208,-8,2,209,-8,2,210,-96,4,211,-8,2,212,-4,2,213,-96,4,214,-7,2,215,-7,2,216,-96,4,217,-1,0,218,-2,1,219,-3,1,220,-96,4,221,-5,2,222,-6,2,223,-7,2,224,-96,4,225,-1,0,226,-2,1,227,-3,1,228,-4,2,229,-5,2,230,-6,2,231,-7,2,232,-8,2,233,-8,2,234,-8,2,235,-8,2,236,-4,2,237,-5,2,238,-7,2,239,-7,2,240,-96,4,241,-96,4,242,-96,4,243,-96,4,244,-96,4,245,-96,4,246,-7,2,247,-96,4,248,-96,4,249,-96,4,250,-96,4,251,-96,4,252,-124,4,253,-96,4,254,-96,4,255,-96,4,256,-128,4,257,-1,0,258,-2,1,259,-3,1,260,-4,2,261,-5,2,262,-6,2,263,-7,2,264,-8,2,265,-8,2,266,-8,2,267,-96,4,268,-4,2,269,-5,2,270,-7,2,271,-96,4,272,-96,4,273,-128,4,274,-18,3,275,-128,4,276,-128,4,277,-128,4,278,-96,4,279,-128,4,280,-128,4,281,-128,4,282,-128,4,283,-128,4,284,-128,4,285,-128,4,286,-128,4,287,-128,4,288,-96,4,289,-96,4,290,-96,4,291,-96,4,292,-4,2,293,-96,4,294,-96,4,295,-96,4,296,-96,4,297,-96,4,298,-96,4,299,-96,4,300,-4,2,301,-96,4,302,-96,4,303,-96,4,304,-96,4,305,-96,4,306,-96,4,307,-96,4,308,-4,2,309,-96,4,310,-96,4,311,-96,4,312,-96,4,313,-96,4,314,-96,4,315,-96,4,316,-96,4,317,-96,4,318,-96,4,319,-96,4,320,-96,4,321,-96,4,322,-96,4,323,-96,4,324,-4,2,325,-96,4,326,-96,4,327,-96,4,328,-96,4,329,-96,4,330,-96,4,331,-96,4,332,-4,2,333,-96,4,334,-96,4,335,-96,4,336,-96,4,337,-96,4,338,-96,4,339,-96,4,340,-96,4,341,-96,4,342,-96,4,343,-96,4,344,-96,4,345,-96,4,346,-96,4,347,-96,4,348,-96,4,349,-96,4,350,-96,4,351,-96,4,352,-96,4,353,-96,4,354,-96,4,355,-96,4,356,-4,2,357,-96,4,358,-96,4,359,-96,4,360,-96,4,361,-96,4,362,-96,4,363,-96,4,364,-4,2,365,-96,4,366,-96,4,367,-96,4,368,-96,4,369,-96,4,370,-18,3,371,-96,4,372,-96,4,373,-96,4,374,-96,4,375,-96,4,376,-96,4,377,-96,4,378,-96,4,379,-96,4,380,-96,4,381,-96,4,382,-96,4,383,-96,4,384,-96,4,385,-96,4,386,-96,4,387,-96,4,388,-4,2,389,-96,4,390,-96,4,391,-96,4,392,-96,4,393,-96,4,394,-96,4,395,-96,4,396,-4,2,397,-96,4,398,-96,4,399,-96,4,400,-96,4,401,-96,4,402,-96,4,403,-96,4,404,-4,2,405,-96,4,406,-96,4,407,-96,4,408,-96,4,409,-96,4,410,-96,4,411,-96,4,412,-96,4,413,-96,4,414,-96,4,415,-96,4,416,-96,4,417,-96,4,418,-96,4,419,-96,4,420,-4,2,421,-96,4,422,-96,4,423,-96,4,424,-96,4,425,-96,4,426,-96,4,427,-96,4,428,-4,2,429,-96,4,430,-96,4,431,-96,4,432,-96,4,433,-96,4,434,-96,4,435,-96,4,436,-96,4,437,-96,4,438,-96,4,439,-96,4,440,-96,4,441,-96,4,442,-96,4,443,-96,4,444,-96,4,445,-96,4,446,-96,4,447,-96,4,448,-96,4,449,-96,4,450,-96,4,451,-96,4,452,-4,2,453,-96,4,454,-96,4,455,-96,4,456,-96,4,457,-96,4,458,-96,4,459,-96,4,460,-4,2,461,-96,4,462,-96,4,463,-96,4,464,-96,4,465,-96,4,466,-18,3,467,-96,4,468,-96,4,469,-96,4,470,-96,4,471,-96,4,472,-96,4,473,-96,4,474,-96,4,475,-96,4,476,-96,4,477,-96,4,478,-96,4,479,-96,4,480,-96,4,481,-96,4,482,-96,4,483,-96,4,484,-4,2,485,-96,4,486,-96,4,487,-96,4,488,-96,4,489,-96,4,490,-96,4,491,-96,4,492,-4,2,493,-96,4,494,-96,4,495,-96,4,496,-96,4,497,-96,4,498,-18,3,499,-96,4,500,-4,2,501,-96,4,502,-96,4,503,-96,4,504,-96,4,505,-96,4,506,-96,4,507,-96,4,508,-96,4,509,-96,4,510,-96,4,511,-96,4,512,-96,4,513,-96,4,514,-96,4,515,-96,4,516,-4,2,517,-96,4,518,-96,4,519,-96,4,520,-96,4,521,-96,4,522,-96,4,523,-96,4,524,-4,2,525,-96,4,526,-96,4,527,-96,4,528,-96,4,529,-96,4,530,-96,4,531,-96,4,532,-96,4,533,-96,4,534,-96,4,535,-96,4,536,-96,4,537,-96,4,538,-96,4,539,-96,4,540,-96,4,541,-96,4,542,-96,4,543,-96,4,544,-96,4,545,-96,4,546,-96,4,547,-96,4,548,-4,2,549,-96,4,550,-96,4,551,-96,4,552,-96,4,553,-96,4,554,-96,4,555,-96,4,556,-4,2,557,-96,4,558,-96,4,559,-96,4,560,-96,4,561,-96,4,562,-18,3,563,-96,4,564,-96,4,565,-96,4,566,-96,4,567,-96,4,568,-96,4,569,-96,4,570,-96,4,571,-96,4,572,-96,4,573,-96,4,574,-96,4,575,-96,4,576,-96,4,577,-96,4,578,-96,4,579,-96,4,580,-4,2,581,-96,4,582,-96,4,583,-96,4,584,-96,4,585,-96,4,586,-96,4,587,-96,4,588,-4,2,589,-96,4,590,-96,4,591,-96,4,592,-96,4,593,-96,4,594,-18,3,595,-96,4,596,-4,2,597,-96,4,598,-96,4,599,-96,4,600,-96,4,601,-96,4,602,-96,4,603,-96,4,604,-96,4,605,-96,4,606,-96,4,607,-96,4,608,-96,4,609,-96,4,610,-96,4,611,-96,4,612,-4,2,613,-96,4,614,-96,4,615,-96,4,616,-96,4,617,-96,4,618,-96,4,619,-96,4,620,-4,2,621,-96,4,622,-96,4,623,-96,4,624,-96,4,625,-96,4,626,-96,4,627,-96,4,628,-96,4,629,-96,4,630,-96,4,631,-96,4,632,-96,4,633,-96,4,634,-96,4,635,-96,4,636,-96,4,637,-96,4,638,-96,4,639,-96,4,640,-96,4,641,-96,4,642,-96,4,643,-96,4,644,-4,2,645,-96,4,646,-96,4,647,-96,4,648,-96,4,649,-96,4,650,-96,4,651,-96,4,652,-4,2,653,-96,4,654,-96,4,655,-96,4,656,-96,4,657,-96,4,658,-18,3,659,-96,4,660,-96,4,661,-96,4,662,-96,4,663,-96,4,664,-96,4,665,-96,4,666,-96,4,667,-96,4,668,-96,4,669,-96,4,670,-96,4,671,-96,4,672,-96,4,673,-96,4,674,-96,4,675,-96,4,676,-4,2,677,-96,4,678,-96,4,679,-96,4,680,-96,4,681,-96,4,682,-96,4,683,-96,4,684,-4,2,685,-96,4,686,-96,4,687,-96,4,688,-96,4,689,-96,4,690,-18,3,691,-96,4,692,-4,2,693,-96,4,694,-96,4,695,-96,4,696,-96,4,697,-96,4,698,-96,4,699,-96,4,700,-96,4,701,-96,4,702,-96,4,703,-96,4,704,-96,4,705,-96,4,706,-96,4,707,-96,4,708,-4,2,709,-96,4,710,-96,4,711,-96,4,712,-96,4,713,-96,4,714,-96,4,715,-96,4,716,-4,2,717,-96,4,718,-96,4,719,-96,4,720,-96,4,721,-96,4,722,-96,4,723,-96,4,724,-96,4,725,-96,4,726,-96,4,727,-96,4,728,-96,4,729,-96,4,730,-96,4,731,-96,4,732,-96,4,733,-96,4,734,-96,4,735,-96,4,736,-96,4,737,-96,4,738,-96,4,739,-96,4,740,-4,2,741,-96,4,742,-96,4,743,-96,4,744,-96,4,745,-96,4,746,-96,4,747,-96,4,748,-4,2,749,-96,4,750,-96,4,751,-96,4,752,-96,4,753,-96,4,754,-18,3,755,-96,4,756,-96,4,757,-96,4,758,-96,4,759,-96,4,760,-96,4,761,-96,4,762,-96,4,763,-96,4,764,-96,4,765,-96,4,766,-96,4,767,-96,4,768,-96,4,769,-96,4,770,-96,4,771,-96,4,772,-4,2,773,-96,4,774,-96,4,775,-96,4,776,-96,4,777,-96,4,778,-96,4,779,-96,4,780,-4,2,781,-96,4,782,-96,4,783,-96,4,784,-96,4,785,-96,4,786,-18,3,787,-96,4,788,-4,2,789,-96,4,790,-96,4,791,-96,4,792,-96,4,793,-96,4,794,-96,4,795,-96,4,796,-96,4,797,-96,4,798,-96,4,799,-96,4,800,-96,4,801,-96,4,802,-96,4,803,-96,4,804,-4,2,805,-96,4,806,-96,4,807,-96,4,808,-96,4,809,-96,4,810,-96,4,811,-96,4,812,-4,2,813,-96,4,814,-96,4,815,-96,4,816,-96,4,817,-96,4,818,-96,4,819,-96,4,820,-96,4,821,-96,4,822,-96,4,823,-96,4,824,-96,4,825,-96,4,826,-96,4,827,-96,4,828,-96,4,829,-96,4,830,-96,4,831,-96,4,832,-96,4,833,-96,4,834,-96,4,835,-96,4,836,-4,2,837,-96,4,838,-96,4,839,-96,4,840,-96,4,841,-96,4,842,-96,4,843,-96,4,844,-4,2,845,-96,4,846,-96,4,847,-96,4,848,-96,4,849,-96,4,850,-18,3,851,-96,4,852,-96,4,853,-96,4,854,-96,4,855,-96,4,856,-96,4,857,-96,4,858,-96,4,859,-96,4,860,-96,4,861,-96,4,862,-96,4,863,-96,4,864,-96,4,865,-96,4,866,-96,4,867,-96,4,868,-4,2,869,-96,4,870,-96,4,871,-96,4,872,-96,4,873,-96,4,874,-96,4,875,-96,4,876,-4,2,877,-96,4,878,-96,4,879,-96,4,880,-96,4,881,-96,4,882,-18,3,883,-96,4,884,-4,2,885,-96,4,886,-96,4,887,-96,4,888,-96,4,889,-96,4,890,-96,4,891,-96,4,892,-96,4,893,-96,4,894,-96,4,895,-96,4,896,-96,4,897,-96,4,898,-96,4,899,-96,4,900,-4,2,901,-96,4,902,-96,4,903,-96,4,904,-96,4,905,-96,4,906,-96,4,907,-96,4,908,-4,2,909,-96,4,910,-96,4,911,-96,4,912,-96,4,913,-96,4,914,-96,4,915,-96,4,916,-96,4,917,-96,4,918,-96,4,919,-96,4,920,-96,4,921,-96,4,922,-96,4,923,-96,4,924,-96,4,925,-96,4,926,-96,4,927,-96,4,928,-96,4,929,-96,4,930,-96,4,931,-96,4,932,-4,2,933,-96,4,934,-96,4,935,-96,4,936,-96,4,937,-96,4,938,-96,4,939,-96,4,940,-4,2,941,-96,4,942,-96,4,943,-96,4,944,-96,4,945,-96,4,946,-18,3,947,-96,4,948,-96,4,949,-96,4,950,-96,4,951,-96,4,952,-96,4,953,-96,4,954,-96,4,955,-96,4,956,-96,4,957,-96,4,958,-96,4,959,-96,4,960,-96,4,961,-96,4,962,-96,4,963,-96,4,964,-4,2,965,-96,4,966,-96,4,967,-96,4,968,-96,4,969,-96,4,970,-96,4,971,-96,4,972,-4,2,973,-96,4,974,-96,4,975,-96,4,976,-96,4,977,-96,4,978,-18,3,979,-96,4,980,-4,2,981,-96,4,982,-96,4,983,-96,4,984,-96,4,985,-96,4,986,-96,4,987,-96,4,988,-96,4,989,-96,4,990,-96,4,991,-96,4,992,-96,4,993,-96,4,994,-96,4,995,-96,4,996,-4,2,997,-96,4,998,-96,4,999,-96,4,1000,-96,4,1001,-96,4,1002,-96,4,1003,-96,4,1004,-4,2,1005,-96,4,1006,-96,4,1007,-96,4,1008,-96,4,1009,-96,4,1010,-96,4,1011,-96,4,1012,-96,4,1013,-96,4,1014,-96,4,1015,-96,4,1016,-96,4,1017,-96,4,1018,-96,4,1019,-96,4,1020,-96,4,1021,-96,4,1022,-96,4,1023,-96,4,1024,-96,4,1025,-96,4,1026,-96,4,1027,-96,4,1028,-4,2,1029,-96,4,1030,-96,4,1031,-96,4,1032,-96,4,1033,-96,4,1034,-96,4,1035,-96,4,1036,-4,2,1037,-96,4,1038,-96,4,1039,-96,4,1040,-96,4,1041,-96,4,1042,-18,3,1043,-96,4,1044,-96,4,1045,-96,4,1046,-96,4,1047,-96,4,1048,-96,4,1049,-96,4,1050,-96,4,1051,-96,4,1052,-96,4,1053,-96,4,1054,-96,4,1055,-96,4,1056,-96,4,1057,-96,4,1058,-96,4,1059,-96,4,1060,-4,2,1061,-96,4,1062,-96,4,1063,-96,4,1064,-96,4,1065,-96,4,1066,-96,4,1067,-96,4,1068,-4,2,1069,-96,4,1070,-96,4,1071,-96,4,1072,-96,4,1073,-96,4,1074,-18,3,1075,-96,4,1076,-4,2,1077,-96,4,1078,-96,4,1079,-96,4,1080,-96,4,1081,-96,4,1082,-96,4,1083,-96,4,1084,-96,4,1085,-96,4,1086,-96,4,1087,-96,4,1088,-96,4,1089,-96,4,1090,-96,4,1091,-96,4,1092,-4,2,1093,-96,4,1094,-96,4,1095,-96,4,1096,-96,4,1097,-96,4,1098,-96,4,1099,-96,4,1100,-4,2,1101,-96,4,1102,-96,4,1103,-96,4,1104,-96,4,1105,-96,4,1106,-96,4,1107,-96,4,1108,-96,4,1109,-96,4,1110,-96,4,1111,-96,4,1112,-96,4,1113,-96,4,1114,-96,4,1115,-96,4,1116,-96,4,1117,-96,4,1118,-96,4,1119,-96,4,1120,-96,4,1121,-96,4,1122,-96,4,1123,-96,4,1124,-4,2,1125,-96,4,1126,-96,4,1127,-96,4,1128,-96,4,1129,-96,4,1130,-96,4,1131,-96,4,1132,-4,2,1133,-96,4,1134,-96,4,1135,-96,4,1136,-96,4,1137,-96,4,1138,-18,3,1139,-96,4,1140,-96,4,1141,-96,4,1142,-96,4,1143,-96,4,1144,-96,4,1145,-96,4,1146,-96,4,1147,-96,4,1148,-96,4,1149,-96,4,1150,-96,4,1151,-96,4,1152,-96,4,1153,-96,4,1154,-96,4,1155,-96,4,1156,-4,2,1157,-96,4,1158,-96,4,1159,-96,4,1160,-96,4,1161,-96,4,1162,-96,4,1163,-96,4,1164,-4,2,1165,-96,4,1166,-96,4,1167,-96,4,1168,-96,4,1169,-96,4,1170,-18,3,1171,-96,4,1172,-4,2,1173,-96,4,1174,-96,4,1175,-96,4,1176,-96,4,1177,-96,4,1178,-96,4,1179,-96,4,1180,-96,4,1181,-96,4,1182,-96,4,1183,-96,4,1184,-96,4,1185,-96,4,1186,-96,4,1187,-96,4,1188,-4,2,1189,-96,4,1190,-96,4,1191,-96,4,1192,-96,4,1193,-96,4,1194,-96,4,1195,-96,4,1196,-4,2,1197,-96,4,1198,-96,4,1199,-96,4,1200,-96,4,1201,-96,4,1202,-96,4,1203,-96,4,1204,-96,4,1205,-96,4,1206,-96,4,1207,-96,4,1208,-96,4,1209,-96,4,1210,-96,4,1211,-96,4,1212,-96,4,1213,-96,4,1214,-96,4,1215,-96,4,1216,-96,4,1217,-96,4,1218,-96,4,1219,-96,4,1220,-4,2,1221,-96,4,1222,-96,4,1223,-96,4,1224,-96,4,1225,-96,4,1226,-96,4,1227,-96,4,1228,-4,2,1229,-96,4,1230,-96,4,1231,-96,4,1232,-96,4,1233,-96,4,1234,-18,3,1235,-96,4,1236,-96,4,1237,-96,4,1238,-96,4,1239,-96,4,1240,-96,4,1241,-96,4,1242,-96,4,1243,-96,4,1244,-96,4,1245,-96,4,1246,-96,4,1247,-96,4,1248,-96,4,1249,-96,4,1250,-96,4,1251,-96,4,1252,-4,2,1253,-96,4,1254,-96,4,1255,-96,4,1256,-96,4,1257,-96,4,1258,-96,4,1259,-96,4,1260,-4,2,1261,-96,4,1262,-96,4,1263,-96,4,1264,-96,4,1265,-96,4,1266,-18,3,1267,-96,4,1268,-4,2,1269,-96,4,1270,-96,4,1271,-96,4,1272,-96,4,1273,-96,4,1274,-96,4,1275,-96,4,1276,-96,4,1277,-96,4,1278,-96,4,1279,-96,4,1280,-96,4,1281,-96,4,1282,-96,4,1283,-96,4,1284,-4,2,1285,-96,4,1286,-96,4,1287,-96,4,1288,-96,4,1289,-96,4,1290,-96,4,1291,-96,4,1292,-4,2,1293,-96,4,1294,-96,4,1295,-96,4,1296,-96,4,1297,-96,4,1298,-96,4,1299,-96,4,1300,-96,4,1301,-96,4,1302,-96,4,1303,-96,4,1304,-96,4,1305,-96,4,1306,-96,4,1307,-96,4,1308,-96,4,1309,-96,4,1310,-96,4,1311,-96,4,1312,-96,4,1313,-96,4,1314,-96,4,1315,-96,4,1316,-4,2,1317,-96,4,1318,-96,4,1319,-96,4,1320,-96,4,1321,-96,4,1322,-96,4,1323,-96,4,1324,-4,2,1325,-96,4,1326,-96,4,1327,-96,4,1328,-96,4,1329,-96,4,1330,-18,3,1331,-96,4,1332,-96,4,1333,-96,4,1334,-96,4,1335,-96,4,1336,-96,4,1337,-96,4,1338,-96,4,1339,-96,4,1340,-96,4,1341,-96,4,1342,-96,4,1343,-96,4,1344,-96,4,1345,-96,4,1346,-96,4,1347,-96,4,1348,-4,2,1349,-96,4,1350,-96,4,1351,-96,4,1352,-96,4,1353,-96,4,1354,-96,4,1355,-96,4,1356,-4,2,1357,-96,4,1358,-96,4,1359,-96,4,1360,-96,4,1361,-96,4,1362,-18,3,1363,-96,4,1364,-4,2,1365,-96,4,1366,-96,4,1367,-96,4,1368,-96,4,1369,-96,4,1370,-96,4,1371,-96,4,1372,-96,4,1373,-96,4,1374,-96,4,1375,-96,4,1376,-96,4,1377,-96,4,1378,-96,4,1379,-96,4,1380,-4,2,1381,-96,4,1382,-96,4,1383,-96,4,1384,-96,4,1385,-96,4,1386,-96,4,1387,-96,4,1388,-4,2,1389,-96,4,1390,-96,4,1391,-96,4,1392,-96,4,1393,-96,4,1394,-96,4,1395,-96,4,1396,-96,4,1397,-96,4,1398,-96,4,1399,-96,4,1400,-96,4,1401,-96,4,1402,-96,4,1403,-96,4,1404,-96,4,1405,-96,4,1406,-96,4,1407,-96,4,1408,-96,4,1409,-96,4,1410,-96,4,1411,-96,4,1412,-4,2,1413,-96,4,1414,-96,4,1415,-96,4,1416,-96,4,1417,-96,4,1418,-96,4,1419,-96,4,1420,-4,2,1421,-96,4,1422,-96,4,1423,-96,4,1424,-96,4,1425,-96,4,1426,-18,3,1427,-96,4,1428,-96,4,1429,-96,4,1430,-96,4,1431,-96,4,1432,-96,4,1433,-96,4,1434,-96,4,1435,-96,4,1436,-96,4,1437,-96,4,1438,-96,4,1439,-96,4,1440,-96,4,1441,-96,4,1442,-96,4,1443,-96,4,1444,-4,2,1445,-96,4,1446,-96,4,1447,-96,4,1448,-96,4,1449,-96,4,1450,-96,4,1451,-96,4,1452,-4,2,1453,-96,4,1454,-96,4,1455,-96,4,1456,-96,4,1457,-96,4,1458,-18,3,1459,-96,4,1460,-4,2,1461,-96,4,1462,-96,4,1463,-96,4,1464,-96,4,1465,-96,4,1466,-96,4,1467,-96,4,1468,-96,4,1469,-96,4,1470,-96,4,1471,-96,4,1472,-96,4,1473,-96,4,1474,-96,4,1475,-96,4,1476,-4,2,1477,-96,4,1478,-96,4,1479,-96,4,1480,-96,4,1481,-96,4,1482,-96,4,1483,-96,4,1484,-4,2,1485,-96,4,1486,-96,4,1487,-96,4,1488,-96,4,1489,-96,4,1490,-96,4,1491,-96,4,1492,-96,4,1493,-96,4,1494,-96,4,1495,-96,4,1496,-96,4,1497,-96,4,1498,-96,4,1499,-96,4,1500,-96,4,1501,-96,4,1502,-96,4,1503,-96,4,1504,-96,4,1505,-96,4,1506,-96,4,1507,-96,4,1508,-4,2,1509,-96,4,1510,-96,4,1511,-96,4,1512,-96,4,1513,-96,4,1514,-96,4,1515,-96,4,1516,-4,2,1517,-96,4,1518,-96,4,1519,-96,4,1520,-96,4,1521,-96,4,1522,-18,3,1523,-96,4,1524,-96,4,1525,-96,4,1526,-96,4,1527,-96,4,1528,-96,4,1529,-96,4,1530,-96,4,1531,-96,4,1532,-96,4,1533,-96,4,1534,-96,4,1535,-96,4,1536,-96,4,1537,-96,4,1538,-96,4,1539,-96,4,1540,-4,2,1541,-96,4,1542,-96,4,1543,-96,4,1544,-96,4,1545,-96,4,1546,-96,4,1547,-96,4,1548,-4,2,1549,-96,4,1550,-96,4,1551,-96,4,1552,-96,4,1553,-96,4,1554,-18,3,1555,-96,4,1556,-4,2,1557,-96,4,1558,-96,4,1559,-96,4,1560,-96,4,1561,-96,4,1562,-96,4,1563,-96,4,1564,-96,4,1565,-96,4,1566,-96,4,1567,-96,4,1568,-96,4,1569,-96,4,1570,-96,4,1571,-96,4,1572,-4,2,1573,-96,4,1574,-96,4,1575,-96,4,1576,-96,4,1577,-96,4,1578,-96,4,1579,-96,4,1580,-4,2,1581,-96,4,1582,-96,4,1583,-96,4,1584,-96,4,1585,-96,4,1586,-18,3,1587,-96,4,1588,-96,4,1589,-96,4,1590,-96,4,1591,-96,4,1592,-96,4,1593,-96,4,1594,-96,4,1595,-96,4,1596,-96,4,1597,-96,4,1598,-96,4,1599,-96,4,1600,-96,4,1601,-96,4,1602,-96,4,1603,-96,4,1604,-4,2,1605,-96,4,1606,-96,4,1607,-96,4,1608,-96,4,1609,-96,4,1610,-96,4,1611,-96,4,1612,-4,2,1613,-96,4,1614,-96,4,1615,-96,4,1616,-96,4,1617,-96,4,1618,-18,3,1619,-96,4,1620,-96,4,1621,-96,4,1622,-96,4,1623,-96,4,1624,-96,4,1625,-96,4,1626,-96,4,1627,-96,4,1628,-96,4,1629,-96,4,1630,-96,4,1631,-96,4,1632,-96,4,1633,-96,4,1634,-96,4,1635,-96,4,1636,-4,2,1637,-96,4,1638,-96,4,1639,-96,4,1640,-96,4,1641,-96,4,1642,-96,4,1643,-96,4,1644,-4,2,1645,-96,4,1646,-96,4,1647,-96,4,1648,-96,4,1649,-96,4,1650,-18,3,1651,-96,4,1652,-4,2,1653,-96,4,1654,-96,4,1655,-96,4,1656,-96,4,1657,-96,4,1658,-96,4,1659,-96,4,1660,-96,4,1661,-96,4,1662,-96,4,1663,-96,4,1664,-96,4,1665,-96,4,1666,-96,4,1667,-96,4,1668,-4,2,1669,-96,4,1670,-96,4,1671,-96,4,1672,-96,4,1673,-96,4,1674,-96,4,1675,-96,4,1676,-4,2,1677,-96,4,1678,-96,4,1679,-96,4,1680,-96,4,1681,-96,4,1682,-18,3,1683,-96,4,1684,-96,4,1685,-96,4,1686,-96,4,1687,-96,4,1688,-96,4,1689,-96,4,1690,-96,4,1691,-96,4,1692,-96,4,1693,-96,4,1694,-96,4,1695,-96,4,1696,-96,4,1697,-96,4,1698,-96,4,1699,-96,4,1700,-4,2,1701,-96,4,1702,-96,4,1703,-96,4,1704,-96,4,1705,-96,4,1706,-96,4,1707,-96,4,1708,-4,2,1709,-96,4,1710,-96,4,1711,-96,4,1712,-96,4,1713,-96,4,1714,-18,3,1715,-96,4,1716,-96,4,1717,-96,4,1718,-96,4,1719,-96,4,1720,-96,4,1721,-96,4,1722,-96,4,1723,-96,4,1724,-96,4,1725,-96,4,1726,-96,4,1727,-96,4,1728,-96,4,1729,-96,4,1730,-96,4,1731,-96,4,1732,-4,2,1733,-96,4,1734,-96,4,1735,-96,4,1736,-96,4,1737,-96,4,1738,-96,4,1739,-96,4,1740,-4,2,1741,-96,4,1742,-96,4,1743,-96,4,1744,-96,4,1745,-96,4,1746,-18,3,1747,-96,4,1748,-4,2,1749,-96,4,1750,-96,4,1751,-96,4,1752,-96,4,1753,-96,4,1754,-96,4,1755,-96,4,1756,-96,4,1757,-96,4,1758,-96,4,1759,-96,4,1760,-96,4,1761,-96,4,1762,-96,4,1763,-96,4,1764,-4,2,1765,-96,4,1766,-96,4,1767,-96,4,1768,-96,4,1769,-96,4,1770,-96,4,1771,-96,4,1772,-4,2,1773,-96,4,1774,-96,4,1775,-96,4,1776,-96,4,1777,-96,4,1778,-18,3,1779,-96,4,1780,-96,4,1781,-96,4,1782,-96,4,1783,-96,4,1784,-96,4,1785,-96,4,1786,-96,4,1787,-96,4,1788,-96,4,1789,-96,4,1790,-96,4,1791,-96,4,1792,-96,4,1793,-96,4,1794,-96,4,1795,-96,4,1796,-4,2,1797,-96,4,1798,-96,4,1799,-96,4,1800,-96,4,1801,-96,4,1802,-96,4,1803,-96,4,1804,-4,2,1805,-96,4,1806,-96,4,1807,-96,4,1808,-96,4,1809,-96,4,1810,-18,3,1811,-96,4,1812,-96,4,1813,-96,4,1814,-96,4,1815,-96,4,1816,-96,4,1817,-96,4,1818,-96,4,1819,-96,4,1820,-96,4,1821,-96,4,1822,-96,4,1823,-96,4,1824,-96,4,1825,-96,4,1826,-96,4,1827,-96,4,1828,-4,2,1829,-96,4,1830,-96,4,1831,-96,4,1832,-96,4,1833,-96,4,1834,-96,4,1835,-96,4,1836,-4,2,1837,-96,4,1838,-96,4,1839,-96,4,1840,-96,4,1841,-96,4,1842,-18,3,1843,-96,4,1844,-4,2,1845,-96,4,1846,-96,4,1847,-96,4,1848,-96,4,1849,-96,4,1850,-96,4,1851,-96,4,1852,-96,4,1853,-96,4,1854,-96,4,1855,-96,4,1856,-96,4,1857,-96,4,1858,-96,4,1859,-96,4,1860,-4,2,1861,-96,4,1862,-96,4,1863,-96,4,1864,-96,4,1865,-96,4,1866,-96,4,1867,-96,4,1868,-4,2,1869,-96,4,1870,-96,4,1871,-96,4,1872,-96,4,1873,-96,4,1874,-18,3,1875,-96,4,1876,-96,4,1877,-96,4,1878,-96,4,1879,-96,4,1880,-96,4,1881,-96,4,1882,-96,4,1883,-96,4,1884,-96,4,1885,-96,4,1886,-96,4,1887,-96,4,1888,-96,4,1889,-96,4,1890,-96,4,1891,-96,4,1892,-4,2,1893,-96,4,1894,-96,4,1895,-96,4,1896,-96,4,1897,-96,4,1898,-96,4,1899,-96,4,1900,-4,2,1901,-96,4,1902,-96,4,1903,-96,4,1904,-96,4,1905,-96,4,1906,-18,3,1907,-96,4,1908,-96,4,1909,-96,4,1910,-96,4,1911,-96,4,1912,-96,4,1913,-96,4,1914,-96,4,1915,-96,4,1916,-96,4,1917,-96,4,1918,-96,4,1919,-96,4,1920,-96,4,1921,-96,4,1922,-96,4,1923,-96,4,1924,-4,2,1925,-96,4,1926,-96,4,1927,-96,4,1928,-96,4,1929,-96,4,1930,-96,4,1931,-96,4,1932,-4,2,1933,-96,4,1934,-96,4,1935,-96,4,1936,-96,4,1937,-96,4,1938,-18,3,1939,-96,4,1940,-4,2,1941,-96,4,1942,-96,4,1943,-96,4,1944,-96,4,1945,-96,4,1946,-96,4,1947,-96,4,1948,-96,4,1949,-96,4,1950,-96,4,1951,-96,4,1952,-96,4,1953,-96,4,1954,-96,4,1955,-96,4,1956,-4,2,1957,-96,4,1958,-96,4,1959,-96,4,1960,-96,4,1961,-96,4,1962,-96,4,1963,-96,4,1964,-4,2,1965,-96,4,1966,-96,4,1967,-96,4,1968,-96,4,1969,-96,4,1970,-18,3,1971,-96,4,1972,-96,4,1973,-96,4,1974,-96,4,1975,-96,4,1976,-96,4,1977,-96,4,1978,-96,4,1979,-96,4,1980,-96,4,1981,-96,4,1982,-96,4,1983,-96,4,1984,-96,4,1985,-96,4,1986,-96,4,1987,-96,4,1988,-4,2,1989,-96,4,1990,-96,4,1991,-96,4,1992,-96,4,1993,-96,4,1994,-96,4,1995,-96,4,1996,-4,2,1997,-5,2,1998,-96,4,1999,-96,4,2000,-96,4,2001,-96,4,2002,-18,3,2003,-96,4,2004,-96,4,2005,-96,4,2006,-96,4,2007,-96,4,2008,-96,4,2009,-96,4,2010,-96,4,2011,-96,4,2012,-96,4,2013,-96,4,2014,-96,4,2015,-96,4,2016,-96,4,2017,-96,4,2018,-96,4,2019,-96,4,2020,-4,2,2021,-96,4,2022,-96,4,2023,-96,4,2024,-96,4,2025,-96,4,2026,-96,4,2027,-96,4,2028,-4,2,2029,-96,4,2030,-96,4,2031,-96,4,2032,-96,4,2033,-96,4,2034,-18,3,2035,-96,4,2036,-4,2,2037,-96,4,2038,-96,4,2039,-96,4,2040,-96,4,2041,-96,4,2042,-96,4,2043,-96,4,2044,-96,4,2045,-96,4,2046,-96,4,2047,-96,4,0,0,0"), // leafSize 9 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,-2,1,11,-2,1,12,-3,1,13,-5,1,14,-7,2,15,-7,2,16,-8,2,17,-8,2,18,-9,2,19,-9,2,20,-9,2,21,3,4,22,-7,2,23,-7,2,24,3,4,25,-8,2,26,-8,2,27,3,4,28,4,7,29,-2,1,30,-6,2,31,-7,2,32,4,7,33,-9,2,34,-7,2,35,5,9,36,4,7,37,-1,1,38,-2,1,39,-7,2,40,-8,2,41,-9,2,42,-6,2,43,-7,2,44,-8,2,45,-9,2,46,-10,2,47,-9,2,48,-8,2,49,-8,2,50,-9,2,51,-7,2,52,-8,2,53,-8,2,54,-9,2,55,-27,3,56,-24,3,57,-21,3,58,-8,2,59,-27,3,60,-24,3,61,-8,2,62,-8,2,63,-27,3,64,-32,3,65,-32,3,66,-32,3,67,-32,3,68,-32,3,69,-33,3,70,-34,3,71,-35,3,72,-36,3,73,-32,3,74,-36,3,75,-36,3,76,-36,3,77,-36,3,78,-36,3,79,-36,3,80,-36,3,81,-36,3,82,-36,3,83,-38,3,84,-40,3,85,-41,3,86,-41,3,87,-43,3,88,-44,3,89,-44,3,90,-45,3,91,-27,3,92,-24,3,93,-21,3,94,-45,3,95,-27,3,96,3,6,97,-45,3,98,-45,3,99,-27,3,100,-32,3,101,-5,2,102,-6,2,103,-7,2,104,-32,3,105,-9,2,106,-27,3,107,-35,3,108,3,6,109,-1,1,110,-2,1,111,-3,1,112,-4,2,113,-5,2,114,-6,2,115,-7,2,116,-8,2,117,-9,2,118,-8,2,119,-9,2,120,-4,2,121,-5,2,122,-7,2,123,-7,2,124,-8,2,125,-8,2,126,-9,2,127,-8,2,128,-32,3,129,-21,3,130,-7,2,131,-7,2,132,-24,3,133,-8,2,134,-26,3,135,-27,3,136,-28,3,137,-29,3,138,-24,3,139,-31,3,140,-32,3,141,-33,3,142,-34,3,143,-35,3,144,-36,3,145,-1,0,146,-2,1,147,-7,2,148,-8,2,149,-9,2,150,-6,2,151,-7,2,152,-8,2,153,-9,2,154,-9,2,155,-2,1,156,-8,2,157,-8,2,158,-9,2,159,-7,2,160,-8,2,161,-8,2,162,-9,2,163,-27,3,164,-24,3,165,-21,3,166,-8,2,167,-27,3,168,-24,3,169,-8,2,170,-8,2,171,-27,3,172,-32,3,173,-27,3,174,-27,3,175,-32,3,176,-32,3,177,-33,3,178,-27,3,179,-35,3,180,-36,3,181,-36,3,182,-36,3,183,-32,3,184,-32,3,185,-32,3,186,-6,2,187,-7,2,188,-36,3,189,-36,3,190,-36,3,191,-36,3,192,-32,3,193,-32,3,194,-32,3,195,-7,2,196,-36,3,197,-36,3,198,-36,3,199,-27,3,200,-24,3,201,-21,3,202,-32,3,203,-27,3,204,-96,4,205,-36,3,206,-36,3,207,-27,3,208,-32,3,209,-5,2,210,-6,2,211,-7,2,212,-32,3,213,-105,4,214,-27,3,215,-35,3,216,-108,4,217,-1,0,218,-2,1,219,-3,1,220,-4,2,221,-5,2,222,-6,2,223,-7,2,224,-8,2,225,-9,2,226,-9,2,227,-2,1,228,-9,2,229,-5,2,230,-7,2,231,-7,2,232,-8,2,233,-8,2,234,-9,2,235,-9,2,236,-108,4,237,-108,4,238,-7,2,239,-27,3,240,-108,4,241,-8,2,242,-8,2,243,-108,4,244,-108,4,245,-2,1,246,-6,2,247,-7,2,248,-108,4,249,-108,4,250,-7,2,251,-108,4,252,-108,4,253,-108,4,254,-108,4,255,-108,4,256,-108,4,257,-108,4,258,-108,4,259,-108,4,260,-108,4,261,-108,4,262,-108,4,263,-108,4,264,-108,4,265,-108,4,266,-108,4,267,-108,4,268,-108,4,269,-108,4,270,-108,4,271,-108,4,272,-108,4,273,-108,4,274,-108,4,275,-135,4,276,-108,4,277,-108,4,278,-108,4,279,-135,4,280,-140,4,281,-135,4,282,-135,4,283,-140,4,284,-140,4,285,-141,4,286,-135,4,287,-143,4,288,-144,4,289,-144,4,290,-144,4,291,-144,4,292,-144,4,293,-144,4,294,-144,4,295,-144,4,296,-144,4,297,-144,4,298,-145,4,299,-146,4,300,-148,4,301,-149,4,302,-149,4,303,-151,4,304,-152,4,305,-152,4,306,-153,4,307,-140,4,308,-144,4,309,-144,4,310,-153,4,311,-144,4,312,-96,4,313,-153,4,314,-153,4,315,-144,4,316,-144,4,317,-96,4,318,-96,4,319,-96,4,320,-144,4,321,-96,4,322,-151,4,323,-144,4,324,3,8,325,-1,0,326,-2,1,327,-3,1,328,-4,2,329,-5,2,330,-6,2,331,-7,2,332,-8,2,333,-9,2,334,-9,2,335,-2,1,336,-9,2,337,-5,2,338,-7,2,339,-7,2,340,-8,2,341,-8,2,342,-9,2,343,-9,2,344,-2,1,345,-21,3,346,-7,2,347,-7,2,348,-24,3,349,-8,2,350,-8,2,351,-27,3,352,-28,3,353,-27,3,354,-24,3,355,-24,3,356,-32,3,357,-33,3,358,-27,3,359,-35,3,360,-36,3,361,-36,3,362,-2,1,363,-7,2,364,-32,3,365,-32,3,366,-6,2,367,-7,2,368,-8,2,369,-36,3,370,-36,3,371,-2,1,372,-32,3,373,-32,3,374,-32,3,375,-7,2,376,-8,2,377,-8,2,378,-36,3,379,-27,3,380,-24,3,381,-21,3,382,-32,3,383,-27,3,384,-24,3,385,-8,2,386,-8,2,387,-27,3,388,-32,3,389,-27,3,390,-27,3,391,-32,3,392,-32,3,393,-33,3,394,-27,3,395,-35,3,396,-36,3,397,-36,3,398,-2,1,399,-7,2,400,-32,3,401,-32,3,402,-6,2,403,-7,2,404,-8,2,405,-81,4,406,-81,4,407,-81,4,408,-32,3,409,-32,3,410,-86,4,411,-7,2,412,-88,4,413,-89,4,414,-90,4,415,-27,3,416,-24,3,417,-21,3,418,-94,4,419,-27,3,420,-96,4,421,-97,4,422,-98,4,423,-27,3,424,-32,3,425,-96,4,426,-96,4,427,-96,4,428,-104,4,429,-96,4,430,-27,3,431,-107,4,432,-108,4,433,-108,4,434,-108,4,435,-108,4,436,-4,2,437,-5,2,438,-108,4,439,-108,4,440,-108,4,441,-108,4,442,-108,4,443,-108,4,444,-108,4,445,-5,2,446,-108,4,447,-108,4,448,-108,4,449,-108,4,450,-108,4,451,-108,4,452,-128,4,453,-21,3,454,-108,4,455,-108,4,456,-108,4,457,-108,4,458,-108,4,459,-108,4,460,-108,4,461,-108,4,462,-108,4,463,-108,4,464,-140,4,465,-141,4,466,-108,4,467,-143,4,468,-144,4,469,-145,4,470,-146,4,471,-147,4,472,-148,4,473,-149,4,474,-6,2,475,-151,4,476,-152,4,477,-153,4,478,-154,4,479,-155,4,480,-156,4,481,-157,4,482,-158,4,483,-159,4,484,-160,4,485,-161,4,486,-162,4,487,-108,4,488,-24,3,489,-21,3,490,-166,4,491,-140,4,492,-24,3,493,-169,4,494,-170,4,495,-27,3,496,-172,4,497,-27,3,498,-27,3,499,-175,4,500,-176,4,501,-177,4,502,-27,3,503,-179,4,504,-180,4,505,-181,4,506,-182,4,507,-7,2,508,-152,4,509,-153,4,510,-6,2,511,-7,2,512,-188,4,513,-189,4,514,-190,4,515,-191,4,516,-160,4,517,-161,4,518,-162,4,519,-7,2,520,-196,4,521,-197,4,522,-90,4,523,-27,3,524,-24,3,525,-21,3,526,-170,4,527,-27,3,528,-96,4,529,-205,4,530,-98,4,531,-27,3,532,-144,4,533,-5,2,534,-96,4,535,-96,4,536,-180,4,537,-96,4,538,-27,3,539,-180,4,540,-108,4,541,-108,4,542,-108,4,543,-108,4,544,-4,2,545,-5,2,546,-108,4,547,-108,4,548,-108,4,549,-108,4,550,-108,4,551,-108,4,552,-108,4,553,-5,2,554,-108,4,555,-108,4,556,-108,4,557,-108,4,558,-108,4,559,-108,4,560,-108,4,561,-21,3,562,-108,4,563,-108,4,564,-108,4,565,-108,4,566,-108,4,567,-108,4,568,-108,4,569,-108,4,570,-108,4,571,-108,4,572,-108,4,573,-108,4,574,-108,4,575,-108,4,576,-108,4,577,-108,4,578,-108,4,579,-108,4,580,-108,4,581,-108,4,582,-108,4,583,-108,4,584,-108,4,585,-108,4,586,-108,4,587,-108,4,588,-108,4,589,-108,4,590,-108,4,591,-108,4,592,-108,4,593,-108,4,594,-108,4,595,-108,4,596,-108,4,597,-21,3,598,-108,4,599,-108,4,600,-108,4,601,-108,4,602,-108,4,603,-108,4,604,-140,4,605,-108,4,606,-108,4,607,-140,4,608,-140,4,609,-141,4,610,-108,4,611,-143,4,612,-144,4,613,-144,4,614,-144,4,615,-140,4,616,-140,4,617,-140,4,618,-6,2,619,-144,4,620,-144,4,621,-144,4,622,-144,4,623,-144,4,624,-140,4,625,-144,4,626,-144,4,627,-144,4,628,-144,4,629,-144,4,630,-144,4,631,-27,3,632,-24,3,633,-21,3,634,-144,4,635,-27,3,636,-96,4,637,-144,4,638,-144,4,639,-27,3,640,-144,4,641,-5,2,642,-96,4,643,-96,4,644,-144,4,645,-96,4,646,-27,3,647,-144,4,648,-324,4,649,-324,4,650,-2,1,651,-324,4,652,-4,2,653,-5,2,654,-6,2,655,-7,2,656,-8,2,657,-9,2,658,-9,2,659,-2,1,660,-4,2,661,-5,2,662,-7,2,663,-7,2,664,-8,2,665,-8,2,666,-9,2,667,-9,2,668,-2,1,669,-21,3,670,-7,2,671,-7,2,672,-24,3,673,-8,2,674,-8,2,675,-27,3,676,-28,3,677,-27,3,678,-24,3,679,-24,3,680,-32,3,681,-33,3,682,-27,3,683,-35,3,684,-36,3,685,-36,3,686,-2,1,687,-7,2,688,-32,3,689,-32,3,690,-6,2,691,-7,2,692,-8,2,693,-36,3,694,-36,3,695,-2,1,696,-32,3,697,-32,3,698,-32,3,699,-7,2,700,-8,2,701,-8,2,702,-36,3,703,-27,3,704,-24,3,705,-21,3,706,-32,3,707,-27,3,708,-24,3,709,-8,2,710,-8,2,711,-27,3,712,-32,3,713,-27,3,714,-27,3,715,-32,3,716,-32,3,717,-24,3,718,-27,3,719,-35,3,720,-36,3,721,-36,3,722,-2,1,723,-7,2,724,-32,3,725,-77,4,726,-6,2,727,-79,4,728,-80,4,729,-81,4,730,-81,4,731,-81,4,732,-84,4,733,-85,4,734,-86,4,735,-80,4,736,-88,4,737,-89,4,738,-90,4,739,-27,3,740,-24,3,741,-21,3,742,-94,4,743,-27,3,744,-96,4,745,-97,4,746,-98,4,747,-99,4,748,-100,4,749,-5,2,750,-96,4,751,-96,4,752,-104,4,753,-96,4,754,-99,4,755,-107,4,756,-108,4,757,-108,4,758,-108,4,759,-108,4,760,-4,2,761,-5,2,762,-108,4,763,-108,4,764,-108,4,765,-108,4,766,-108,4,767,-108,4,768,-4,2,769,-5,2,770,-108,4,771,-108,4,772,-108,4,773,-108,4,774,-108,4,775,-108,4,776,-128,4,777,-21,3,778,-108,4,779,-108,4,780,-24,3,781,-108,4,782,-108,4,783,-108,4,784,-108,4,785,-108,4,786,-24,3,787,-24,3,788,-140,4,789,-141,4,790,-108,4,791,-143,4,792,-144,4,793,-145,4,794,-146,4,795,-7,2,796,-148,4,797,-149,4,798,-6,2,799,-7,2,800,-152,4,801,-153,4,802,-154,4,803,-155,4,804,-156,4,805,-149,4,806,-324,5,807,-331,5,808,-332,5,809,-332,5,810,-324,5,811,-108,4,812,-24,3,813,-21,3,814,-332,5,815,-351,5,816,-348,5,817,-340,5,818,-332,5,819,-351,5,820,-356,5,821,-351,5,822,-358,5,823,-356,5,824,-356,5,825,-357,5,826,-358,5,827,-359,5,828,-360,5,829,-360,5,830,-360,5,831,-363,5,832,-356,5,833,-356,5,834,-366,5,835,-367,5,836,-360,5,837,-360,5,838,-360,5,839,-360,5,840,-356,5,841,-356,5,842,-356,5,843,-367,5,844,-360,5,845,-89,4,846,-90,4,847,-383,5,848,-380,5,849,-381,5,850,-364,5,851,-383,5,852,-96,4,853,-368,5,854,-98,4,855,-351,5,856,-388,5,857,-5,2,858,-96,4,859,-96,4,860,-356,5,861,-96,4,862,-358,5,863,-359,5,864,-108,4,865,-108,4,866,-108,4,867,-432,5,868,-4,2,869,-5,2,870,-432,5,871,-432,5,872,-432,5,873,-432,5,874,-433,5,875,-434,5,876,-435,5,877,-437,5,878,-439,5,879,-439,5,880,-440,5,881,-440,5,882,-441,5,883,-441,5,884,-432,5,885,-432,5,886,-440,5,887,-440,5,888,-432,5,889,-441,5,890,-441,5,891,-432,5,892,-432,5,893,-434,5,894,-438,5,895,-439,5,896,-432,5,897,-432,5,898,-439,5,899,-432,5,900,-432,5,901,-432,5,902,-432,5,903,-432,5,904,-432,5,905,-432,5,906,-438,5,907,-439,5,908,-432,5,909,-432,5,910,-432,5,911,-432,5,912,-432,5,913,-432,5,914,-432,5,915,-439,5,916,-432,5,917,-432,5,918,-432,5,919,-459,5,920,-456,5,921,-453,5,922,-432,5,923,-459,5,924,-456,5,925,-432,5,926,-440,5,927,-459,5,928,-464,5,929,-459,5,930,-459,5,931,-464,5,932,-464,5,933,-465,5,934,-459,5,935,-467,5,936,-468,5,937,-468,5,938,-468,5,939,-468,5,940,-468,5,941,-468,5,942,-468,5,943,-468,5,944,-468,5,945,-468,5,946,-469,5,947,-470,5,948,-472,5,949,-473,5,950,-473,5,951,-475,5,952,-476,5,953,-476,5,954,-477,5,955,-464,5,956,-468,5,957,-468,5,958,-477,5,959,-468,5,960,-432,5,961,-477,5,962,-477,5,963,-468,5,964,-468,5,965,-437,5,966,-438,5,967,-439,5,968,-468,5,969,-441,5,970,-475,5,971,-468,5,972,-324,5,973,-325,5,974,-326,5,975,-327,5,976,-328,5,977,-329,5,978,-330,5,979,-331,5,980,-332,5,981,-333,5,982,-334,5,983,-335,5,984,-336,5,985,-337,5,986,-338,5,987,-339,5,988,-340,5,989,-341,5,990,-342,5,991,-343,5,992,-344,5,993,-345,5,994,-346,5,995,-347,5,996,-348,5,997,-349,5,998,-350,5,999,-351,5,1000,-352,5,1001,-353,5,1002,-354,5,1003,-355,5,1004,-356,5,1005,-357,5,1006,-358,5,1007,-359,5,1008,-360,5,1009,-361,5,1010,-362,5,1011,-363,5,1012,-364,5,1013,-365,5,1014,-366,5,1015,-367,5,1016,-368,5,1017,-369,5,1018,-370,5,1019,-371,5,1020,-372,5,1021,-373,5,1022,-374,5,1023,-375,5,1024,-376,5,1025,-377,5,1026,-378,5,1027,-379,5,1028,-380,5,1029,-381,5,1030,-382,5,1031,-383,5,1032,-384,5,1033,-385,5,1034,-386,5,1035,-387,5,1036,-388,5,1037,-389,5,1038,-390,5,1039,-391,5,1040,-392,5,1041,-393,5,1042,-394,5,1043,-395,5,1044,-396,5,1045,-397,5,1046,-398,5,1047,-399,5,1048,-400,5,1049,-401,5,1050,-402,5,1051,-403,5,1052,-404,5,1053,-405,5,1054,-406,5,1055,-407,5,1056,-408,5,1057,-409,5,1058,-410,5,1059,-411,5,1060,-412,5,1061,-413,5,1062,-414,5,1063,-415,5,1064,-416,5,1065,-417,5,1066,-418,5,1067,-419,5,1068,-420,5,1069,-421,5,1070,-422,5,1071,-423,5,1072,-424,5,1073,-425,5,1074,-426,5,1075,-427,5,1076,-428,5,1077,-429,5,1078,-430,5,1079,-431,5,1080,-432,5,1081,-433,5,1082,-434,5,1083,-435,5,1084,-4,2,1085,-437,5,1086,-438,5,1087,-439,5,1088,-440,5,1089,-441,5,1090,-442,5,1091,-443,5,1092,-444,5,1093,-445,5,1094,-446,5,1095,-447,5,1096,-448,5,1097,-449,5,1098,-450,5,1099,-451,5,1100,-452,5,1101,-453,5,1102,-454,5,1103,-455,5,1104,-456,5,1105,-457,5,1106,-458,5,1107,-459,5,1108,-460,5,1109,-461,5,1110,-462,5,1111,-463,5,1112,-464,5,1113,-465,5,1114,-466,5,1115,-467,5,1116,-468,5,1117,-469,5,1118,-470,5,1119,-471,5,1120,-472,5,1121,-473,5,1122,-474,5,1123,-475,5,1124,-476,5,1125,-477,5,1126,-478,5,1127,-479,5,1128,-480,5,1129,-481,5,1130,-482,5,1131,-483,5,1132,-484,5,1133,-485,5,1134,-486,5,1135,-487,5,1136,-488,5,1137,-489,5,1138,-490,5,1139,-491,5,1140,-492,5,1141,-493,5,1142,-494,5,1143,-495,5,1144,-496,5,1145,-497,5,1146,-498,5,1147,-499,5,1148,-500,5,1149,-501,5,1150,-502,5,1151,-503,5,1152,-504,5,1153,-505,5,1154,-506,5,1155,-507,5,1156,-508,5,1157,-509,5,1158,-510,5,1159,-511,5,1160,-512,5,1161,-513,5,1162,-514,5,1163,-515,5,1164,-516,5,1165,-517,5,1166,-518,5,1167,-519,5,1168,-520,5,1169,-521,5,1170,-522,5,1171,-523,5,1172,-524,5,1173,-525,5,1174,-526,5,1175,-527,5,1176,-528,5,1177,-529,5,1178,-530,5,1179,-531,5,1180,-532,5,1181,-533,5,1182,-534,5,1183,-535,5,1184,-536,5,1185,-537,5,1186,-538,5,1187,-539,5,1188,-540,5,1189,-541,5,1190,-542,5,1191,-543,5,1192,-4,2,1193,-545,5,1194,-546,5,1195,-547,5,1196,-548,5,1197,-549,5,1198,-550,5,1199,-551,5,1200,-552,5,1201,-553,5,1202,-554,5,1203,-555,5,1204,-556,5,1205,-557,5,1206,-558,5,1207,-559,5,1208,-560,5,1209,-561,5,1210,-562,5,1211,-563,5,1212,-564,5,1213,-565,5,1214,-566,5,1215,-567,5,1216,-568,5,1217,-569,5,1218,-570,5,1219,-571,5,1220,-572,5,1221,-573,5,1222,-574,5,1223,-575,5,1224,-576,5,1225,-577,5,1226,-578,5,1227,-579,5,1228,-580,5,1229,-581,5,1230,-582,5,1231,-583,5,1232,-584,5,1233,-585,5,1234,-586,5,1235,-587,5,1236,-588,5,1237,-589,5,1238,-590,5,1239,-591,5,1240,-592,5,1241,-593,5,1242,-594,5,1243,-595,5,1244,-596,5,1245,-597,5,1246,-598,5,1247,-599,5,1248,-600,5,1249,-601,5,1250,-602,5,1251,-603,5,1252,-604,5,1253,-605,5,1254,-606,5,1255,-607,5,1256,-608,5,1257,-609,5,1258,-610,5,1259,-611,5,1260,-612,5,1261,-613,5,1262,-614,5,1263,-615,5,1264,-616,5,1265,-617,5,1266,-618,5,1267,-619,5,1268,-620,5,1269,-621,5,1270,-622,5,1271,-623,5,1272,-624,5,1273,-625,5,1274,-626,5,1275,-627,5,1276,-628,5,1277,-629,5,1278,-324,5,1279,-351,5,1280,-348,5,1281,-633,5,1282,-324,5,1283,-351,5,1284,-636,5,1285,-324,5,1286,-324,5,1287,-351,5,1288,-356,5,1289,-5,2,1290,-642,5,1291,-643,5,1292,-356,5,1293,-645,5,1294,-351,5,1295,-359,5,1296,-648,5,1297,-648,5,1298,-648,5,1299,-648,5,1300,-4,2,1301,-5,2,1302,-648,5,1303,-648,5,1304,-648,5,1305,-648,5,1306,-649,5,1307,-650,5,1308,-4,2,1309,-5,2,1310,-655,5,1311,-655,5,1312,-656,5,1313,-656,5,1314,-657,5,1315,-657,5,1316,-657,5,1317,-648,5,1318,-656,5,1319,-656,5,1320,-648,5,1321,-657,5,1322,-657,5,1323,-648,5,1324,-648,5,1325,-650,5,1326,-654,5,1327,-655,5,1328,-648,5,1329,-648,5,1330,-655,5,1331,-648,5,1332,-648,5,1333,-649,5,1334,-650,5,1335,-655,5,1336,-656,5,1337,-657,5,1338,-654,5,1339,-655,5,1340,-332,5,1341,-333,5,1342,-334,5,1343,-335,5,1344,-332,5,1345,-333,5,1346,-333,5,1347,-339,5,1348,-340,5,1349,-341,5,1350,-342,5,1351,-675,5,1352,-672,5,1353,-324,5,1354,-341,5,1355,-351,5,1356,-324,5,1357,-341,5,1358,-342,5,1359,-351,5,1360,-356,5,1361,-351,5,1362,-331,5,1363,-356,5,1364,-356,5,1365,-357,5,1366,-351,5,1367,-359,5,1368,-360,5,1369,-360,5,1370,-360,5,1371,-356,5,1372,-356,5,1373,-356,5,1374,-360,5,1375,-360,5,1376,-360,5,1377,-81,4,1378,-81,4,1379,-81,4,1380,-356,5,1381,-85,4,1382,-86,4,1383,-360,5,1384,-88,4,1385,-89,4,1386,-90,4,1387,-356,5,1388,-356,5,1389,-360,5,1390,-648,5,1391,-356,5,1392,-648,5,1393,-648,5,1394,-648,5,1395,-360,5,1396,-360,5,1397,-5,2,1398,-648,5,1399,-648,5,1400,-324,5,1401,-333,5,1402,-360,5,1403,-324,5,1404,-324,5,1405,-325,5,1406,-326,5,1407,-327,5,1408,-328,5,1409,-329,5,1410,-330,5,1411,-331,5,1412,-332,5,1413,-333,5,1414,-334,5,1415,-335,5,1416,-336,5,1417,-337,5,1418,-338,5,1419,-339,5,1420,-340,5,1421,-341,5,1422,-342,5,1423,-343,5,1424,-324,5,1425,-345,5,1426,-346,5,1427,-347,5,1428,-348,5,1429,-349,5,1430,-350,5,1431,-351,5,1432,-352,5,1433,-353,5,1434,-354,5,1435,-355,5,1436,-324,5,1437,-324,5,1438,-358,5,1439,-324,5,1440,-324,5,1441,-324,5,1442,-324,5,1443,-331,5,1444,-324,5,1445,-324,5,1446,-330,5,1447,-331,5,1448,-324,5,1449,-324,5,1450,-324,5,1451,-324,5,1452,-324,5,1453,-324,5,1454,-324,5,1455,-331,5,1456,-324,5,1457,-324,5,1458,-324,5,1459,-352,5,1460,-348,5,1461,-345,5,1462,-332,5,1463,-351,5,1464,-348,5,1465,-332,5,1466,-332,5,1467,-351,5,1468,-356,5,1469,-351,5,1470,-351,5,1471,-356,5,1472,-356,5,1473,-357,5,1474,-358,5,1475,-359,5,1476,-360,5,1477,-360,5,1478,-360,5,1479,-356,5,1480,-356,5,1481,-356,5,1482,-330,5,1483,-331,5,1484,-360,5,1485,-360,5,1486,-360,5,1487,-360,5,1488,-356,5,1489,-356,5,1490,-356,5,1491,-367,5,1492,-360,5,1493,-360,5,1494,-360,5,1495,-383,5,1496,-380,5,1497,-345,5,1498,-356,5,1499,-383,5,1500,-420,5,1501,-360,5,1502,-360,5,1503,-351,5,1504,-388,5,1505,-420,5,1506,-420,5,1507,-420,5,1508,-356,5,1509,-420,5,1510,-358,5,1511,-359,5,1512,-432,5,1513,-432,5,1514,-432,5,1515,-432,5,1516,-4,2,1517,-432,5,1518,-432,5,1519,-432,5,1520,-432,5,1521,-432,5,1522,-432,5,1523,-432,5,1524,-432,5,1525,-432,5,1526,-432,5,1527,-432,5,1528,-432,5,1529,-432,5,1530,-432,5,1531,-432,5,1532,-432,5,1533,-432,5,1534,-432,5,1535,-432,5,1536,-432,5,1537,-432,5,1538,-432,5,1539,-432,5,1540,-432,5,1541,-432,5,1542,-432,5,1543,-432,5,1544,-432,5,1545,-432,5,1546,-432,5,1547,-432,5,1548,-432,5,1549,-432,5,1550,-432,5,1551,-432,5,1552,-432,5,1553,-432,5,1554,-432,5,1555,-432,5,1556,-432,5,1557,-432,5,1558,-432,5,1559,-432,5,1560,-432,5,1561,-432,5,1562,-432,5,1563,-439,5,1564,-432,5,1565,-432,5,1566,-432,5,1567,-432,5,1568,-432,5,1569,-432,5,1570,-432,5,1571,-459,5,1572,-432,5,1573,-432,5,1574,-432,5,1575,-459,5,1576,-464,5,1577,-459,5,1578,-459,5,1579,-464,5,1580,-464,5,1581,-465,5,1582,-459,5,1583,-467,5,1584,-468,5,1585,-468,5,1586,-468,5,1587,-464,5,1588,-468,5,1589,-468,5,1590,-468,5,1591,-468,5,1592,-468,5,1593,-468,5,1594,-469,5,1595,-470,5,1596,-472,5,1597,-473,5,1598,-473,5,1599,-468,5,1600,-476,5,1601,-476,5,1602,-477,5,1603,-464,5,1604,-464,5,1605,-468,5,1606,-477,5,1607,-464,5,1608,-432,5,1609,-477,5,1610,-477,5,1611,-468,5,1612,-468,5,1613,-432,5,1614,-432,5,1615,-432,5,1616,-468,5,1617,-432,5,1618,-468,5,1619,-468,5,1620,-324,5,1621,-325,5,1622,-326,5,1623,-327,5,1624,-4,2,1625,-329,5,1626,-330,5,1627,-331,5,1628,-332,5,1629,-333,5,1630,-334,5,1631,-335,5,1632,-336,5,1633,-337,5,1634,-338,5,1635,-339,5,1636,-340,5,1637,-341,5,1638,-342,5,1639,-343,5,1640,-344,5,1641,-345,5,1642,-346,5,1643,-347,5,1644,-348,5,1645,-349,5,1646,-350,5,1647,-351,5,1648,-352,5,1649,-353,5,1650,-354,5,1651,-355,5,1652,-356,5,1653,-357,5,1654,-358,5,1655,-359,5,1656,-360,5,1657,-361,5,1658,-362,5,1659,-363,5,1660,-364,5,1661,-365,5,1662,-366,5,1663,-367,5,1664,-368,5,1665,-369,5,1666,-370,5,1667,-371,5,1668,-372,5,1669,-373,5,1670,-374,5,1671,-375,5,1672,-376,5,1673,-377,5,1674,-378,5,1675,-379,5,1676,-380,5,1677,-381,5,1678,-382,5,1679,-383,5,1680,-384,5,1681,-385,5,1682,-386,5,1683,-387,5,1684,-388,5,1685,-389,5,1686,-390,5,1687,-391,5,1688,-392,5,1689,-393,5,1690,-394,5,1691,-395,5,1692,-396,5,1693,-397,5,1694,-398,5,1695,-399,5,1696,-400,5,1697,-401,5,1698,-402,5,1699,-403,5,1700,-404,5,1701,-324,5,1702,-325,5,1703,-326,5,1704,-408,5,1705,-409,5,1706,-410,5,1707,-411,5,1708,-412,5,1709,-413,5,1710,-414,5,1711,-415,5,1712,-416,5,1713,-417,5,1714,-418,5,1715,-419,5,1716,-420,5,1717,-421,5,1718,-422,5,1719,-423,5,1720,-424,5,1721,-425,5,1722,-426,5,1723,-427,5,1724,-428,5,1725,-429,5,1726,-430,5,1727,-431,5,1728,-432,5,1729,-433,5,1730,-434,5,1731,-435,5,1732,-4,2,1733,-5,2,1734,-438,5,1735,-439,5,1736,-440,5,1737,-441,5,1738,-442,5,1739,-443,5,1740,-444,5,1741,-5,2,1742,-446,5,1743,-447,5,1744,-448,5,1745,-449,5,1746,-450,5,1747,-451,5,1748,-452,5,1749,-432,5,1750,-454,5,1751,-455,5,1752,-456,5,1753,-457,5,1754,-458,5,1755,-459,5,1756,-460,5,1757,-461,5,1758,-462,5,1759,-463,5,1760,-464,5,1761,-465,5,1762,-466,5,1763,-467,5,1764,-468,5,1765,-469,5,1766,-470,5,1767,-471,5,1768,-472,5,1769,-473,5,1770,-474,5,1771,-475,5,1772,-476,5,1773,-477,5,1774,-478,5,1775,-479,5,1776,-480,5,1777,-481,5,1778,-482,5,1779,-483,5,1780,-484,5,1781,-485,5,1782,-486,5,1783,-351,5,1784,-324,5,1785,-21,3,1786,-490,5,1787,-324,5,1788,-324,5,1789,-324,5,1790,-324,5,1791,-324,5,1792,-324,5,1793,-324,5,1794,-331,5,1795,-324,5,1796,-324,5,1797,-324,5,1798,-331,5,1799,-324,5,1800,-324,5,1801,-324,5,1802,-324,5,1803,-331,5,1804,-324,5,1805,-324,5,1806,-324,5,1807,-324,5,1808,-324,5,1809,-324,5,1810,-324,5,1811,-324,5,1812,-324,5,1813,-324,5,1814,-324,5,1815,-331,5,1816,-324,5,1817,-324,5,1818,-324,5,1819,-351,5,1820,-348,5,1821,-324,5,1822,-332,5,1823,-351,5,1824,-324,5,1825,-332,5,1826,-332,5,1827,-324,5,1828,-356,5,1829,-329,5,1830,-330,5,1831,-331,5,1832,-324,5,1833,-333,5,1834,-331,5,1835,-324,5,1836,-324,5,1837,-325,5,1838,-326,5,1839,-327,5,1840,-4,2,1841,-329,5,1842,-330,5,1843,-331,5,1844,-332,5,1845,-333,5,1846,-334,5,1847,-335,5,1848,-336,5,1849,-337,5,1850,-338,5,1851,-339,5,1852,-340,5,1853,-341,5,1854,-342,5,1855,-342,5,1856,-324,5,1857,-345,5,1858,-339,5,1859,-339,5,1860,-348,5,1861,-341,5,1862,-341,5,1863,-351,5,1864,-352,5,1865,-351,5,1866,-348,5,1867,-348,5,1868,-324,5,1869,-324,5,1870,-351,5,1871,-324,5,1872,-324,5,1873,-324,5,1874,-324,5,1875,-331,5,1876,-324,5,1877,-324,5,1878,-330,5,1879,-331,5,1880,-324,5,1881,-324,5,1882,-324,5,1883,-324,5,1884,-324,5,1885,-324,5,1886,-324,5,1887,-331,5,1888,-324,5,1889,-324,5,1890,-324,5,1891,-352,5,1892,-348,5,1893,-345,5,1894,-332,5,1895,-351,5,1896,-348,5,1897,-332,5,1898,-332,5,1899,-351,5,1900,-324,5,1901,-351,5,1902,-351,5,1903,-324,5,1904,-324,5,1905,-324,5,1906,-351,5,1907,-324,5,1908,-324,5,1909,-324,5,1910,-324,5,1911,-331,5,1912,-324,5,1913,-324,5,1914,-330,5,1915,-331,5,1916,-324,5,1917,-324,5,1918,-324,5,1919,-324,5,1920,-324,5,1921,-324,5,1922,-324,5,1923,-331,5,1924,-324,5,1925,-324,5,1926,-324,5,1927,-351,5,1928,-348,5,1929,-345,5,1930,-324,5,1931,-351,5,1932,-324,5,1933,-324,5,1934,-324,5,1935,-351,5,1936,-356,5,1937,-329,5,1938,-330,5,1939,-331,5,1940,-356,5,1941,-333,5,1942,-351,5,1943,-359,5,1944,-324,5,1945,-324,5,1946,-324,5,1947,-324,5,1948,-4,2,1949,-324,5,1950,-324,5,1951,-324,5,1952,-324,5,1953,-324,5,1954,-325,5,1955,-326,5,1956,-327,5,1957,-324,5,1958,-324,5,1959,-324,5,1960,-324,5,1961,-332,5,1962,-333,5,1963,-333,5,1964,-333,5,1965,-324,5,1966,-331,5,1967,-332,5,1968,-324,5,1969,-332,5,1970,-333,5,1971,-324,5,1972,-324,5,1973,-326,5,1974,-330,5,1975,-331,5,1976,-324,5,1977,-324,5,1978,-331,5,1979,-324,5,1980,-324,5,1981,-325,5,1982,-326,5,1983,-331,5,1984,-332,5,1985,-333,5,1986,-330,5,1987,-331,5,1988,-332,5,1989,-333,5,1990,-334,5,1991,-335,5,1992,-340,5,1993,-341,5,1994,-342,5,1995,-339,5,1996,-340,5,1997,-341,5,1998,-342,5,1999,-351,5,2000,-348,5,2001,-345,5,2002,-350,5,2003,-351,5,2004,-348,5,2005,-349,5,2006,-350,5,2007,-351,5,2008,-356,5,2009,-353,5,2010,-351,5,2011,-356,5,2012,-356,5,2013,-357,5,2014,-351,5,2015,-359,5,2016,-360,5,2017,-360,5,2018,-360,5,2019,-356,5,2020,-360,5,2021,-360,5,2022,-360,5,2023,-360,5,2024,-360,5,2025,-360,5,2026,-360,5,2027,-360,5,2028,-360,5,2029,-360,5,2030,-324,5,2031,-360,5,2032,-360,5,2033,-324,5,2034,-324,5,2035,-356,5,2036,-360,5,2037,-360,5,2038,-324,5,2039,-360,5,2040,-324,5,2041,-324,5,2042,-324,5,2043,-360,5,2044,-360,5,2045,-329,5,2046,-330,5,2047,-331,5,0,0,0"), // leafSize 10 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,-1,0,12,-2,1,13,-3,1,14,-4,1,15,-5,1,16,-8,2,17,-7,2,18,-8,2,19,-9,2,20,-10,2,21,3,4,22,-10,2,23,-10,2,24,3,4,25,-5,2,26,-8,2,27,3,4,28,-8,2,29,-9,2,30,3,5,31,-1,1,32,4,7,33,-3,1,34,-10,2,35,-5,2,36,4,7,37,-7,2,38,-8,2,39,-9,2,40,4,7,41,-1,1,42,-2,1,43,-3,1,44,-4,2,45,5,10,46,-6,2,47,-7,2,48,-8,2,49,-9,2,50,5,10,51,-1,1,52,-2,1,53,-3,1,54,-4,2,55,-5,2,56,-6,2,57,-7,2,58,-8,2,59,-9,2,60,-10,2,61,-1,1,62,-2,1,63,-13,2,64,-4,2,65,-5,2,66,-8,2,67,-7,2,68,-8,2,69,-9,2,70,-10,2,71,-21,3,72,-32,3,73,-23,3,74,-24,3,75,-30,3,76,-36,3,77,-27,3,78,-28,3,79,-29,3,80,-30,3,81,-36,3,82,-32,3,83,-33,3,84,-4,2,85,-40,3,86,-36,3,87,-37,3,88,-38,3,89,-39,3,90,-40,3,91,-1,1,92,-42,3,93,-43,3,94,-4,2,95,-45,3,96,-6,2,97,-47,3,98,-48,3,99,-49,3,100,-50,3,101,-1,1,102,-50,3,103,-50,3,104,-4,2,105,-5,2,106,-6,2,107,-7,2,108,-50,3,109,-50,3,110,-50,3,111,-1,1,112,-52,3,113,-50,3,114,-4,2,115,-5,2,116,-50,3,117,-7,2,118,-50,3,119,-59,3,120,3,7,121,-1,1,122,-2,1,123,-3,1,124,-24,3,125,-5,2,126,-6,2,127,-7,2,128,-8,2,129,-9,2,130,-30,3,131,-36,3,132,-32,3,133,-33,3,134,-4,2,135,-40,3,136,-36,3,137,-30,3,138,-38,3,139,-39,3,140,-40,3,141,-1,1,142,-40,3,143,-43,3,144,-4,2,145,-45,3,146,-6,2,147,-7,2,148,-8,2,149,-9,2,150,3,7,151,-1,0,152,-2,1,153,-3,1,154,-4,2,155,-5,2,156,-6,2,157,-7,2,158,-8,2,159,-9,2,160,-10,2,161,-10,2,162,-2,1,163,-10,2,164,-4,2,165,-5,2,166,-8,2,167,-7,2,168,-8,2,169,-9,2,170,-20,3,171,-21,3,172,-20,3,173,-23,3,174,-24,3,175,-20,3,176,-8,2,177,-27,3,178,-28,3,179,-29,3,180,-30,3,181,-30,3,182,-32,3,183,-30,3,184,-4,2,185,-5,2,186,-36,3,187,-30,3,188,-30,3,189,-30,3,190,-40,3,191,-40,3,192,-40,3,193,-40,3,194,-4,2,195,-45,3,196,-6,2,197,-7,2,198,-8,2,199,-40,3,200,-50,3,201,-50,3,202,-2,1,203,-50,3,204,-4,2,205,-5,2,206,-6,2,207,-7,2,208,-8,2,209,-9,2,210,-10,2,211,-10,2,212,-2,1,213,-10,2,214,-4,2,215,-5,2,216,-8,2,217,-7,2,218,-8,2,219,-9,2,220,-20,3,221,-21,3,222,-32,3,223,-23,3,224,-24,3,225,-30,3,226,-36,3,227,-27,3,228,-28,3,229,-29,3,230,-30,3,231,-30,3,232,-32,3,233,-30,3,234,-4,2,235,-40,3,236,-36,3,237,-30,3,238,-30,3,239,-30,3,240,-90,4,241,-90,4,242,-90,4,243,-90,4,244,-90,4,245,-95,4,246,-90,4,247,-90,4,248,-90,4,249,-99,4,250,-100,4,251,-100,4,252,-100,4,253,-100,4,254,-100,4,255,-100,4,256,-100,4,257,-100,4,258,-100,4,259,-100,4,260,-110,4,261,-110,4,262,-110,4,263,-110,4,264,-4,2,265,-110,4,266,-100,4,267,-110,4,268,-110,4,269,-110,4,270,-120,4,271,-120,4,272,-120,4,273,-120,4,274,-100,4,275,-120,4,276,-120,4,277,-120,4,278,-120,4,279,-120,4,280,-130,4,281,-130,4,282,-132,4,283,-130,4,284,-4,2,285,-135,4,286,-136,4,287,-130,4,288,-130,4,289,-139,4,290,-140,4,291,-140,4,292,-140,4,293,-140,4,294,-4,2,295,-145,4,296,-146,4,297,-147,4,298,-148,4,299,-149,4,300,-150,4,301,-150,4,302,-150,4,303,-150,4,304,-4,2,305,-150,4,306,-150,4,307,-150,4,308,-150,4,309,-150,4,310,-150,4,311,-151,4,312,-152,4,313,-153,4,314,-4,2,315,-5,2,316,-158,4,317,-157,4,318,-158,4,319,-159,4,320,-20,3,321,-21,3,322,-20,3,323,-150,4,324,-150,4,325,-20,3,326,-160,4,327,-150,4,328,-150,4,329,-150,4,330,-150,4,331,-151,4,332,-150,4,333,-153,4,334,-4,2,335,-5,2,336,-150,4,337,-157,4,338,-158,4,339,-159,4,340,-150,4,341,-151,4,342,-152,4,343,-153,4,344,-4,2,345,-150,4,346,-156,4,347,-157,4,348,-158,4,349,-159,4,350,-150,4,351,-151,4,352,-152,4,353,-153,4,354,-4,2,355,-5,2,356,-156,4,357,-157,4,358,-158,4,359,-159,4,360,-160,4,361,-161,4,362,-162,4,363,-163,4,364,-4,2,365,-5,2,366,-166,4,367,-167,4,368,-168,4,369,-169,4,370,-20,3,371,-21,3,372,-182,4,373,-23,3,374,-24,3,375,-180,4,376,-186,4,377,-177,4,378,-178,4,379,-179,4,380,-180,4,381,-181,4,382,-182,4,383,-183,4,384,-4,2,385,-190,4,386,-86,4,387,-187,4,388,-188,4,389,-89,4,390,-90,4,391,-90,4,392,-90,4,393,-90,4,394,-4,2,395,-95,4,396,-90,4,397,-90,4,398,-90,4,399,-99,4,400,-100,4,401,-100,4,402,-100,4,403,-100,4,404,-4,2,405,-100,4,406,-100,4,407,-100,4,408,-100,4,409,-100,4,410,-110,4,411,-110,4,412,-110,4,413,-110,4,414,-4,2,415,-110,4,416,-100,4,417,-110,4,418,-110,4,419,-110,4,420,-120,4,421,-120,4,422,-120,4,423,-120,4,424,-100,4,425,-5,2,426,-120,4,427,-120,4,428,-120,4,429,-120,4,430,-100,4,431,-100,4,432,-100,4,433,-100,4,434,-4,2,435,-135,4,436,-136,4,437,-100,4,438,-100,4,439,-100,4,440,-140,4,441,-140,4,442,-140,4,443,-140,4,444,-4,2,445,-145,4,446,-140,4,447,-140,4,448,-140,4,449,-140,4,450,3,9,451,-1,0,452,-2,1,453,-3,1,454,-4,2,455,-5,2,456,-6,2,457,-7,2,458,-8,2,459,-9,2,460,-10,2,461,-10,2,462,-2,1,463,-10,2,464,-4,2,465,-5,2,466,-8,2,467,-7,2,468,-8,2,469,-19,3,470,-20,3,471,-21,3,472,-20,3,473,-23,3,474,-24,3,475,-20,3,476,-8,2,477,-27,3,478,-28,3,479,-29,3,480,-30,3,481,-30,3,482,-32,3,483,-30,3,484,-4,2,485,-5,2,486,-36,3,487,-7,2,488,-30,3,489,-30,3,490,-40,3,491,-40,3,492,-2,1,493,-40,3,494,-4,2,495,-45,3,496,-6,2,497,-7,2,498,-8,2,499,-9,2,500,-50,3,501,-50,3,502,-2,1,503,-50,3,504,-4,2,505,-5,2,506,-6,2,507,-7,2,508,-58,4,509,-59,4,510,-60,4,511,-60,4,512,-60,4,513,-60,4,514,-60,4,515,-60,4,516,-66,4,517,-67,4,518,-68,4,519,-69,4,520,-70,4,521,-71,4,522,-32,3,523,-73,4,524,-74,4,525,-75,4,526,-76,4,527,-77,4,528,-78,4,529,-79,4,530,-80,4,531,-80,4,532,-82,4,533,-80,4,534,-4,2,535,-85,4,536,-86,4,537,-87,4,538,-88,4,539,-89,4,540,-90,4,541,-90,4,542,-90,4,543,-90,4,544,-4,2,545,-95,4,546,-90,4,547,-90,4,548,-98,4,549,-99,4,550,-100,4,551,-100,4,552,-100,4,553,-100,4,554,-4,2,555,-5,2,556,-100,4,557,-100,4,558,-100,4,559,-109,4,560,-110,4,561,-110,4,562,-110,4,563,-110,4,564,-4,2,565,-5,2,566,-100,4,567,-110,4,568,-110,4,569,-110,4,570,-120,4,571,-120,4,572,-120,4,573,-120,4,574,-100,4,575,-5,2,576,-6,2,577,-120,4,578,-120,4,579,-120,4,580,-130,4,581,-130,4,582,-132,4,583,-130,4,584,-4,2,585,-135,4,586,-136,4,587,-130,4,588,-130,4,589,-139,4,590,-140,4,591,-140,4,592,-140,4,593,-140,4,594,-4,2,595,-145,4,596,-6,2,597,-140,4,598,-148,4,599,-149,4,600,-150,4,601,-151,4,602,-152,4,603,-153,4,604,-4,2,605,-5,2,606,-6,2,607,-7,2,608,-158,4,609,-159,4,610,-160,4,611,-161,4,612,-162,4,613,-163,4,614,-4,2,615,-5,2,616,-166,4,617,-7,2,618,-168,4,619,-169,4,620,-20,3,621,-21,3,622,-20,3,623,-23,3,624,-24,3,625,-20,3,626,-168,4,627,-27,3,628,-28,3,629,-29,3,630,-30,3,631,-30,3,632,-150,4,633,-30,3,634,-4,2,635,-5,2,636,-186,4,637,-7,2,638,-30,3,639,-30,3,640,-190,4,641,-191,4,642,-2,1,643,-193,4,644,-4,2,645,-195,4,646,-6,2,647,-7,2,648,-8,2,649,-199,4,650,-200,4,651,-201,4,652,-2,1,653,-203,4,654,-4,2,655,-5,2,656,-6,2,657,-7,2,658,-8,2,659,-59,4,660,-60,4,661,-60,4,662,-60,4,663,-60,4,664,-60,4,665,-60,4,666,-66,4,667,-67,4,668,-68,4,669,-69,4,670,-70,4,671,-21,3,672,-32,3,673,-23,3,674,-74,4,675,-75,4,676,-76,4,677,-77,4,678,-78,4,679,-79,4,680,-80,4,681,-80,4,682,-82,4,683,-80,4,684,-4,2,685,-85,4,686,-86,4,687,-87,4,688,-88,4,689,-89,4,690,-90,4,691,-90,4,692,-90,4,693,-90,4,694,-4,2,695,-95,4,696,-90,4,697,-90,4,698,-98,4,699,-99,4,700,-100,4,701,-100,4,702,-100,4,703,-100,4,704,-4,2,705,-5,2,706,-100,4,707,-100,4,708,-100,4,709,-109,4,710,-110,4,711,-110,4,712,-110,4,713,-110,4,714,-4,2,715,-5,2,716,-100,4,717,-110,4,718,-110,4,719,-110,4,720,-120,4,721,-120,4,722,-120,4,723,-120,4,724,-100,4,725,-5,2,726,-6,2,727,-120,4,728,-120,4,729,-120,4,730,-130,4,731,-130,4,732,-132,4,733,-130,4,734,-4,2,735,-135,4,736,-136,4,737,-130,4,738,-130,4,739,-139,4,740,-140,4,741,-140,4,742,-140,4,743,-140,4,744,-4,2,745,-145,4,746,-6,2,747,-7,2,748,-140,4,749,-149,4,750,-150,4,751,-150,4,752,-150,4,753,-150,4,754,-4,2,755,-5,2,756,-6,2,757,-7,2,758,-150,4,759,-150,4,760,-150,4,761,-151,4,762,-150,4,763,-150,4,764,-4,2,765,-5,2,766,-150,4,767,-7,2,768,-150,4,769,-150,4,770,-20,3,771,-21,3,772,-20,3,773,-23,3,774,-24,3,775,-20,3,776,-150,4,777,-27,3,778,-28,3,779,-29,3,780,-30,3,781,-30,3,782,-150,4,783,-30,3,784,-4,2,785,-5,2,786,-150,4,787,-7,2,788,-30,3,789,-30,3,790,-150,4,791,-151,4,792,-152,4,793,-153,4,794,-4,2,795,-150,4,796,-6,2,797,-7,2,798,-150,4,799,-159,4,800,-150,4,801,-151,4,802,-152,4,803,-153,4,804,-4,2,805,-5,2,806,-6,2,807,-7,2,808,-150,4,809,-59,4,810,-60,4,811,-60,4,812,-60,4,813,-60,4,814,-60,4,815,-60,4,816,-66,4,817,-67,4,818,-68,4,819,-69,4,820,-70,4,821,-21,3,822,-150,4,823,-70,4,824,-74,4,825,-75,4,826,-76,4,827,-77,4,828,-78,4,829,-79,4,830,-80,4,831,-80,4,832,-82,4,833,-80,4,834,-4,2,835,-85,4,836,-86,4,837,-87,4,838,-88,4,839,-89,4,840,-90,4,841,-90,4,842,-90,4,843,-90,4,844,-4,2,845,-95,4,846,-90,4,847,-90,4,848,-98,4,849,-99,4,850,-100,4,851,-100,4,852,-100,4,853,-100,4,854,-4,2,855,-5,2,856,-100,4,857,-100,4,858,-100,4,859,-100,4,860,-110,4,861,-110,4,862,-110,4,863,-110,4,864,-4,2,865,-5,2,866,-100,4,867,-110,4,868,-110,4,869,-110,4,870,-120,4,871,-120,4,872,-120,4,873,-120,4,874,-100,4,875,-5,2,876,-6,2,877,-120,4,878,-120,4,879,-120,4,880,-100,4,881,-100,4,882,-132,4,883,-100,4,884,-4,2,885,-135,4,886,-136,4,887,-100,4,888,-100,4,889,-100,4,890,-140,4,891,-140,4,892,-140,4,893,-140,4,894,-4,2,895,-145,4,896,-6,2,897,-7,2,898,-140,4,899,-140,4,900,-450,5,901,-450,5,902,-450,5,903,-450,5,904,-450,5,905,-450,5,906,-450,5,907,-450,5,908,-450,5,909,-450,5,910,-450,5,911,-451,5,912,-452,5,913,-453,5,914,-454,5,915,-455,5,916,-458,5,917,-457,5,918,-458,5,919,-450,5,920,-450,5,921,-450,5,922,-452,5,923,-450,5,924,-450,5,925,-455,5,926,-460,5,927,-450,5,928,-450,5,929,-450,5,930,-450,5,931,-451,5,932,-450,5,933,-453,5,934,-454,5,935,-455,5,936,-450,5,937,-457,5,938,-458,5,939,-459,5,940,-450,5,941,-451,5,942,-452,5,943,-453,5,944,-454,5,945,-450,5,946,-456,5,947,-457,5,948,-458,5,949,-459,5,950,-450,5,951,-451,5,952,-452,5,953,-453,5,954,-454,5,955,-455,5,956,-456,5,957,-457,5,958,-58,4,959,-59,4,960,-60,4,961,-60,4,962,-60,4,963,-60,4,964,-60,4,965,-60,4,966,-450,5,967,-450,5,968,-450,5,969,-450,5,970,-450,5,971,-450,5,972,-482,5,973,-450,5,974,-450,5,975,-450,5,976,-450,5,977,-450,5,978,-450,5,979,-450,5,980,-450,5,981,-451,5,982,-450,5,983,-453,5,984,-454,5,985,-450,5,986,-450,5,987,-450,5,988,-450,5,989,-450,5,990,-450,5,991,-451,5,992,-452,5,993,-453,5,994,-454,5,995,-450,5,996,-456,5,997,-457,5,998,-450,5,999,-450,5,1000,-450,5,1001,-451,5,1002,-452,5,1003,-453,5,1004,-454,5,1005,-455,5,1006,-456,5,1007,-457,5,1008,-458,5,1009,-450,5,1010,-450,5,1011,-451,5,1012,-452,5,1013,-453,5,1014,-454,5,1015,-455,5,1016,-466,5,1017,-457,5,1018,-458,5,1019,-459,5,1020,-450,5,1021,-451,5,1022,-452,5,1023,-453,5,1024,-474,5,1025,-455,5,1026,-456,5,1027,-457,5,1028,-458,5,1029,-459,5,1030,-450,5,1031,-451,5,1032,-450,5,1033,-453,5,1034,-4,2,1035,-450,5,1036,-450,5,1037,-457,5,1038,-458,5,1039,-450,5,1040,-450,5,1041,-451,5,1042,-452,5,1043,-453,5,1044,-4,2,1045,-450,5,1046,-456,5,1047,-457,5,1048,-450,5,1049,-450,5,1050,-450,5,1051,-450,5,1052,-450,5,1053,-450,5,1054,-4,2,1055,-455,5,1056,-456,5,1057,-457,5,1058,-450,5,1059,-450,5,1060,-450,5,1061,-450,5,1062,-452,5,1063,-450,5,1064,-4,2,1065,-455,5,1066,-458,5,1067,-457,5,1068,-458,5,1069,-450,5,1070,-470,5,1071,-471,5,1072,-470,5,1073,-473,5,1074,-474,5,1075,-475,5,1076,-466,5,1077,-477,5,1078,-478,5,1079,-479,5,1080,-480,5,1081,-480,5,1082,-482,5,1083,-480,5,1084,-4,2,1085,-485,5,1086,-486,5,1087,-487,5,1088,-480,5,1089,-480,5,1090,-450,5,1091,-450,5,1092,-452,5,1093,-450,5,1094,-4,2,1095,-450,5,1096,-456,5,1097,-457,5,1098,-458,5,1099,-459,5,1100,-450,5,1101,-450,5,1102,-452,5,1103,-450,5,1104,-4,2,1105,-455,5,1106,-456,5,1107,-457,5,1108,-458,5,1109,-59,4,1110,-60,4,1111,-60,4,1112,-60,4,1113,-60,4,1114,-60,4,1115,-60,4,1116,-66,4,1117,-67,4,1118,-68,4,1119,-69,4,1120,-70,4,1121,-471,5,1122,-482,5,1123,-473,5,1124,-474,5,1125,-450,5,1126,-450,5,1127,-450,5,1128,-450,5,1129,-450,5,1130,-530,5,1131,-530,5,1132,-532,5,1133,-450,5,1134,-4,2,1135,-535,5,1136,-536,5,1137,-537,5,1138,-538,5,1139,-539,5,1140,-540,5,1141,-540,5,1142,-540,5,1143,-540,5,1144,-4,2,1145,-545,5,1146,-546,5,1147,-547,5,1148,-548,5,1149,-549,5,1150,-550,5,1151,-550,5,1152,-550,5,1153,-550,5,1154,-4,2,1155,-555,5,1156,-556,5,1157,-557,5,1158,-550,5,1159,-559,5,1160,-560,5,1161,-560,5,1162,-560,5,1163,-560,5,1164,-4,2,1165,-565,5,1166,-558,5,1167,-567,5,1168,-560,5,1169,-560,5,1170,-570,5,1171,-570,5,1172,-570,5,1173,-570,5,1174,-574,5,1175,-575,5,1176,-576,5,1177,-577,5,1178,-570,5,1179,-570,5,1180,-580,5,1181,-580,5,1182,-582,5,1183,-580,5,1184,-4,2,1185,-585,5,1186,-586,5,1187,-587,5,1188,-580,5,1189,-589,5,1190,-590,5,1191,-590,5,1192,-590,5,1193,-590,5,1194,-4,2,1195,-595,5,1196,-596,5,1197,-597,5,1198,-598,5,1199,-599,5,1200,-600,5,1201,-600,5,1202,-600,5,1203,-600,5,1204,-4,2,1205,-600,5,1206,-600,5,1207,-600,5,1208,-600,5,1209,-600,5,1210,-600,5,1211,-601,5,1212,-602,5,1213,-603,5,1214,-4,2,1215,-605,5,1216,-608,5,1217,-607,5,1218,-608,5,1219,-609,5,1220,-600,5,1221,-600,5,1222,-602,5,1223,-600,5,1224,-600,5,1225,-605,5,1226,-610,5,1227,-600,5,1228,-600,5,1229,-600,5,1230,-600,5,1231,-601,5,1232,-600,5,1233,-603,5,1234,-4,2,1235,-605,5,1236,-600,5,1237,-607,5,1238,-608,5,1239,-609,5,1240,-600,5,1241,-601,5,1242,-602,5,1243,-603,5,1244,-4,2,1245,-600,5,1246,-606,5,1247,-607,5,1248,-608,5,1249,-609,5,1250,-600,5,1251,-601,5,1252,-602,5,1253,-603,5,1254,-4,2,1255,-605,5,1256,-606,5,1257,-607,5,1258,-608,5,1259,-59,4,1260,-60,4,1261,-60,4,1262,-60,4,1263,-60,4,1264,-60,4,1265,-60,4,1266,-66,4,1267,-67,4,1268,-68,4,1269,-69,4,1270,-70,4,1271,-621,5,1272,-632,5,1273,-623,5,1274,-74,4,1275,-75,4,1276,-76,4,1277,-77,4,1278,-78,4,1279,-79,4,1280,-80,4,1281,-80,4,1282,-82,4,1283,-80,4,1284,-4,2,1285,-85,4,1286,-600,5,1287,-600,5,1288,-600,5,1289,-600,5,1290,-600,5,1291,-600,5,1292,-600,5,1293,-600,5,1294,-4,2,1295,-600,5,1296,-600,5,1297,-600,5,1298,-600,5,1299,-600,5,1300,-600,5,1301,-601,5,1302,-602,5,1303,-603,5,1304,-4,2,1305,-5,2,1306,-606,5,1307,-607,5,1308,-608,5,1309,-600,5,1310,-600,5,1311,-601,5,1312,-602,5,1313,-603,5,1314,-4,2,1315,-5,2,1316,-608,5,1317,-607,5,1318,-608,5,1319,-609,5,1320,-600,5,1321,-601,5,1322,-602,5,1323,-603,5,1324,-624,5,1325,-5,2,1326,-606,5,1327,-607,5,1328,-608,5,1329,-609,5,1330,-600,5,1331,-601,5,1332,-600,5,1333,-603,5,1334,-4,2,1335,-600,5,1336,-600,5,1337,-607,5,1338,-608,5,1339,-600,5,1340,-600,5,1341,-601,5,1342,-602,5,1343,-603,5,1344,-4,2,1345,-600,5,1346,-606,5,1347,-607,5,1348,-608,5,1349,-609,5,1350,3,10,1351,-1,0,1352,-2,1,1353,-3,1,1354,-4,2,1355,-5,2,1356,-6,2,1357,-7,2,1358,-8,2,1359,-9,2,1360,-10,2,1361,-10,2,1362,-2,1,1363,-10,2,1364,-4,2,1365,-5,2,1366,-8,2,1367,-7,2,1368,-8,2,1369,-19,3,1370,-20,3,1371,-21,3,1372,-20,3,1373,-23,3,1374,-24,3,1375,-20,3,1376,-8,2,1377,-27,3,1378,-28,3,1379,-29,3,1380,-30,3,1381,-30,3,1382,-32,3,1383,-30,3,1384,-4,2,1385,-5,2,1386,-36,3,1387,-7,2,1388,-30,3,1389,-30,3,1390,-40,3,1391,-40,3,1392,-2,1,1393,-40,3,1394,-4,2,1395,-45,3,1396,-6,2,1397,-7,2,1398,-8,2,1399,-9,2,1400,-50,4,1401,-50,4,1402,-50,4,1403,-50,4,1404,-50,4,1405,-50,4,1406,-56,4,1407,-57,4,1408,-58,4,1409,-59,4,1410,-60,4,1411,-60,4,1412,-60,4,1413,-60,4,1414,-60,4,1415,-60,4,1416,-66,4,1417,-67,4,1418,-68,4,1419,-69,4,1420,-70,4,1421,-71,4,1422,-72,4,1423,-73,4,1424,-74,4,1425,-75,4,1426,-76,4,1427,-77,4,1428,-78,4,1429,-79,4,1430,-80,4,1431,-80,4,1432,-82,4,1433,-80,4,1434,-4,2,1435,-85,4,1436,-86,4,1437,-87,4,1438,-88,4,1439,-89,4,1440,-90,4,1441,-90,4,1442,-90,4,1443,-90,4,1444,-4,2,1445,-95,4,1446,-90,4,1447,-90,4,1448,-98,4,1449,-99,4,1450,-100,4,1451,-100,4,1452,-100,4,1453,-100,4,1454,-4,2,1455,-5,2,1456,-100,4,1457,-100,4,1458,-100,4,1459,-109,4,1460,-110,4,1461,-110,4,1462,-110,4,1463,-110,4,1464,-4,2,1465,-5,2,1466,-100,4,1467,-110,4,1468,-110,4,1469,-110,4,1470,-120,4,1471,-120,4,1472,-120,4,1473,-120,4,1474,-100,4,1475,-5,2,1476,-6,2,1477,-120,4,1478,-120,4,1479,-120,4,1480,-100,4,1481,-100,4,1482,-100,4,1483,-100,4,1484,-4,2,1485,-135,4,1486,-136,4,1487,-100,4,1488,-100,4,1489,-109,4,1490,-140,4,1491,-140,4,1492,-140,4,1493,-140,4,1494,-4,2,1495,-145,4,1496,-6,2,1497,-7,2,1498,-140,4,1499,-149,4,1500,-150,4,1501,-151,4,1502,-2,1,1503,-153,4,1504,-4,2,1505,-5,2,1506,-6,2,1507,-7,2,1508,-8,2,1509,-159,4,1510,-160,4,1511,-161,4,1512,-2,1,1513,-163,4,1514,-4,2,1515,-5,2,1516,-8,2,1517,-7,2,1518,-8,2,1519,-19,3,1520,-20,3,1521,-21,3,1522,-20,3,1523,-23,3,1524,-24,3,1525,-20,3,1526,-8,2,1527,-27,3,1528,-28,3,1529,-29,3,1530,-30,3,1531,-30,3,1532,-32,3,1533,-30,3,1534,-4,2,1535,-5,2,1536,-150,4,1537,-7,2,1538,-30,3,1539,-30,3,1540,-150,4,1541,-151,4,1542,-2,1,1543,-153,4,1544,-4,2,1545,-195,4,1546,-6,2,1547,-7,2,1548,-8,2,1549,-159,4,1550,-50,4,1551,-50,4,1552,-50,4,1553,-50,4,1554,-50,4,1555,-50,4,1556,-56,4,1557,-57,4,1558,-58,4,1559,-59,4,1560,-60,4,1561,-60,4,1562,-60,4,1563,-60,4,1564,-60,4,1565,-60,4,1566,-66,4,1567,-67,4,1568,-68,4,1569,-69,4,1570,-70,4,1571,-71,4,1572,-72,4,1573,-70,4,1574,-74,4,1575,-75,4,1576,-76,4,1577,-77,4,1578,-78,4,1579,-79,4,1580,-80,4,1581,-80,4,1582,-82,4,1583,-80,4,1584,-4,2,1585,-85,4,1586,-86,4,1587,-87,4,1588,-88,4,1589,-89,4,1590,-90,4,1591,-90,4,1592,-90,4,1593,-90,4,1594,-4,2,1595,-95,4,1596,-90,4,1597,-90,4,1598,-98,4,1599,-99,4,1600,-100,4,1601,-100,4,1602,-100,4,1603,-100,4,1604,-4,2,1605,-5,2,1606,-100,4,1607,-100,4,1608,-100,4,1609,-109,4,1610,-110,4,1611,-110,4,1612,-110,4,1613,-110,4,1614,-4,2,1615,-5,2,1616,-100,4,1617,-110,4,1618,-110,4,1619,-110,4,1620,-120,4,1621,-120,4,1622,-120,4,1623,-120,4,1624,-100,4,1625,-5,2,1626,-6,2,1627,-7,2,1628,-120,4,1629,-120,4,1630,-100,4,1631,-100,4,1632,-100,4,1633,-100,4,1634,-4,2,1635,-135,4,1636,-136,4,1637,-100,4,1638,-100,4,1639,-109,4,1640,-140,4,1641,-140,4,1642,-2,1,1643,-293,5,1644,-4,2,1645,-295,5,1646,-296,5,1647,-297,5,1648,-298,5,1649,-299,5,1650,-300,5,1651,-301,5,1652,-302,5,1653,-303,5,1654,-304,5,1655,-305,5,1656,-306,5,1657,-307,5,1658,-308,5,1659,-309,5,1660,-310,5,1661,-311,5,1662,-312,5,1663,-313,5,1664,-4,2,1665,-315,5,1666,-316,5,1667,-317,5,1668,-318,5,1669,-319,5,1670,-320,5,1671,-321,5,1672,-322,5,1673,-323,5,1674,-324,5,1675,-325,5,1676,-326,5,1677,-327,5,1678,-328,5,1679,-329,5,1680,-330,5,1681,-331,5,1682,-332,5,1683,-333,5,1684,-4,2,1685,-335,5,1686,-336,5,1687,-337,5,1688,-338,5,1689,-339,5,1690,-340,5,1691,-341,5,1692,-342,5,1693,-343,5,1694,-4,2,1695,-345,5,1696,-346,5,1697,-347,5,1698,-348,5,1699,-349,5,1700,-350,5,1701,-351,5,1702,-352,5,1703,-353,5,1704,-4,2,1705,-355,5,1706,-356,5,1707,-357,5,1708,-358,5,1709,-359,5,1710,-360,5,1711,-361,5,1712,-362,5,1713,-363,5,1714,-4,2,1715,-365,5,1716,-366,5,1717,-367,5,1718,-368,5,1719,-369,5,1720,-370,5,1721,-371,5,1722,-372,5,1723,-373,5,1724,-374,5,1725,-375,5,1726,-376,5,1727,-377,5,1728,-378,5,1729,-379,5,1730,-380,5,1731,-381,5,1732,-382,5,1733,-383,5,1734,-4,2,1735,-385,5,1736,-386,5,1737,-387,5,1738,-388,5,1739,-389,5,1740,-390,5,1741,-391,5,1742,-392,5,1743,-393,5,1744,-4,2,1745,-395,5,1746,-396,5,1747,-397,5,1748,-398,5,1749,-399,5,1750,-400,5,1751,-401,5,1752,-402,5,1753,-403,5,1754,-4,2,1755,-405,5,1756,-406,5,1757,-407,5,1758,-408,5,1759,-409,5,1760,-410,5,1761,-411,5,1762,-412,5,1763,-413,5,1764,-4,2,1765,-415,5,1766,-416,5,1767,-417,5,1768,-418,5,1769,-419,5,1770,-420,5,1771,-421,5,1772,-422,5,1773,-423,5,1774,-424,5,1775,-5,2,1776,-426,5,1777,-427,5,1778,-428,5,1779,-429,5,1780,-430,5,1781,-431,5,1782,-432,5,1783,-433,5,1784,-4,2,1785,-435,5,1786,-436,5,1787,-437,5,1788,-438,5,1789,-439,5,1790,-440,5,1791,-441,5,1792,-442,5,1793,-443,5,1794,-4,2,1795,-445,5,1796,-446,5,1797,-447,5,1798,-448,5,1799,-449,5,1800,-450,5,1801,-451,5,1802,-452,5,1803,-453,5,1804,-4,2,1805,-5,2,1806,-456,5,1807,-457,5,1808,-458,5,1809,-459,5,1810,-460,5,1811,-461,5,1812,-462,5,1813,-463,5,1814,-4,2,1815,-5,2,1816,-466,5,1817,-467,5,1818,-468,5,1819,-19,3,1820,-20,3,1821,-21,3,1822,-20,3,1823,-473,5,1824,-474,5,1825,-20,3,1826,-476,5,1827,-477,5,1828,-478,5,1829,-479,5,1830,-480,5,1831,-481,5,1832,-482,5,1833,-483,5,1834,-4,2,1835,-5,2,1836,-486,5,1837,-487,5,1838,-488,5,1839,-489,5,1840,-490,5,1841,-491,5,1842,-492,5,1843,-493,5,1844,-4,2,1845,-495,5,1846,-496,5,1847,-497,5,1848,-498,5,1849,-499,5,1850,-500,5,1851,-501,5,1852,-502,5,1853,-503,5,1854,-4,2,1855,-5,2,1856,-506,5,1857,-57,4,1858,-58,4,1859,-59,4,1860,-60,4,1861,-60,4,1862,-60,4,1863,-60,4,1864,-60,4,1865,-60,4,1866,-66,4,1867,-67,4,1868,-68,4,1869,-69,4,1870,-70,4,1871,-71,4,1872,-522,5,1873,-73,4,1874,-74,4,1875,-75,4,1876,-76,4,1877,-77,4,1878,-78,4,1879,-79,4,1880,-80,4,1881,-80,4,1882,-82,4,1883,-80,4,1884,-4,2,1885,-85,4,1886,-86,4,1887,-80,4,1888,-88,4,1889,-89,4,1890,-90,4,1891,-90,4,1892,-90,4,1893,-90,4,1894,-4,2,1895,-450,5,1896,-90,4,1897,-90,4,1898,-90,4,1899,-450,5,1900,-450,5,1901,-451,5,1902,-452,5,1903,-453,5,1904,-4,2,1905,-5,2,1906,-456,5,1907,-457,5,1908,-458,5,1909,-559,5,1910,-560,5,1911,-561,5,1912,-562,5,1913,-563,5,1914,-4,2,1915,-5,2,1916,-466,5,1917,-567,5,1918,-568,5,1919,-569,5,1920,-570,5,1921,-571,5,1922,-572,5,1923,-573,5,1924,-474,5,1925,-5,2,1926,-6,2,1927,-577,5,1928,-578,5,1929,-579,5,1930,-580,5,1931,-581,5,1932,-582,5,1933,-483,5,1934,-4,2,1935,-585,5,1936,-586,5,1937,-487,5,1938,-488,5,1939,-589,5,1940,-590,5,1941,-591,5,1942,-592,5,1943,-593,5,1944,-4,2,1945,-595,5,1946,-6,2,1947,-597,5,1948,-598,5,1949,-599,5,1950,-600,5,1951,-601,5,1952,-602,5,1953,-603,5,1954,-4,2,1955,-5,2,1956,-6,2,1957,-607,5,1958,-608,5,1959,-609,5,1960,-610,5,1961,-611,5,1962,-612,5,1963,-613,5,1964,-4,2,1965,-5,2,1966,-616,5,1967,-617,5,1968,-618,5,1969,-619,5,1970,-20,3,1971,-21,3,1972,-20,3,1973,-23,3,1974,-24,3,1975,-20,3,1976,-626,5,1977,-600,5,1978,-628,5,1979,-629,5,1980,-630,5,1981,-631,5,1982,-632,5,1983,-633,5,1984,-4,2,1985,-5,2,1986,-636,5,1987,-637,5,1988,-638,5,1989,-639,5,1990,-640,5,1991,-641,5,1992,-642,5,1993,-643,5,1994,-4,2,1995,-645,5,1996,-6,2,1997,-647,5,1998,-648,5,1999,-649,5,2000,-650,5,2001,-651,5,2002,-652,5,2003,-653,5,2004,-4,2,2005,-5,2,2006,-6,2,2007,-657,5,2008,-58,4,2009,-59,4,2010,-60,4,2011,-60,4,2012,-60,4,2013,-60,4,2014,-60,4,2015,-60,4,2016,-66,4,2017,-67,4,2018,-68,4,2019,-69,4,2020,-70,4,2021,-21,3,2022,-72,4,2023,-70,4,2024,-74,4,2025,-75,4,2026,-76,4,2027,-77,4,2028,-78,4,2029,-79,4,2030,-80,4,2031,-80,4,2032,-82,4,2033,-80,4,2034,-4,2,2035,-85,4,2036,-86,4,2037,-87,4,2038,-88,4,2039,-89,4,2040,-90,4,2041,-90,4,2042,-90,4,2043,-90,4,2044,-4,2,2045,-95,4,2046,-90,4,2047,-90,4,0,0,0"), // leafSize 11 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,-2,1,13,-2,1,14,-3,1,15,-4,1,16,-8,2,17,-7,2,18,-8,2,19,-8,2,20,-10,2,21,-10,2,22,-11,2,23,-11,2,24,3,4,25,-11,2,26,-11,2,27,3,4,28,-8,2,29,-8,2,30,3,5,31,-10,2,32,-10,2,33,3,5,34,-1,1,35,-2,1,36,4,7,37,-4,2,38,-5,2,39,-6,2,40,4,7,41,-8,2,42,-9,2,43,-10,2,44,4,8,45,5,10,46,-2,1,47,-3,1,48,-4,2,49,-5,2,50,5,10,51,-7,2,52,-8,2,53,-9,2,54,-10,2,55,5,11,56,-1,1,57,-2,1,58,-3,1,59,-4,2,60,-10,2,61,-11,2,62,-7,2,63,-8,2,64,-9,2,65,-10,2,66,-11,2,67,-2,1,68,-2,1,69,-14,2,70,-10,2,71,-10,2,72,-22,3,73,-8,2,74,-8,2,75,-10,2,76,-10,2,77,-22,3,78,-33,3,79,-24,3,80,-30,3,81,-31,3,82,-27,3,83,-33,3,84,-40,3,85,-30,3,86,-31,3,87,-32,3,88,-33,3,89,-44,3,90,-40,3,91,-36,3,92,-4,2,93,-43,3,94,-44,3,95,-40,3,96,-41,3,97,-42,3,98,-43,3,99,-44,3,100,-50,3,101,-46,3,102,-47,3,103,-4,2,104,-50,3,105,-50,3,106,-44,3,107,-52,3,108,-53,3,109,-54,3,110,-55,3,111,-1,1,112,-55,3,113,-55,3,114,-4,2,115,-55,3,116,-55,3,117,-7,2,118,-55,3,119,-55,3,120,-55,3,121,-55,3,122,-57,3,123,-57,3,124,-55,3,125,-60,3,126,-21,3,127,-22,3,128,-8,2,129,-8,2,130,-20,3,131,-21,3,132,3,7,133,-1,1,134,-2,1,135,-3,1,136,-4,2,137,-5,2,138,-33,3,139,-40,3,140,-30,3,141,-9,2,142,-10,2,143,-33,3,144,-44,3,145,-40,3,146,-36,3,147,-4,2,148,-43,3,149,-44,3,150,3,7,151,-8,2,152,-20,3,153,-43,3,154,-44,3,155,-50,3,156,-2,1,157,-3,1,158,-8,2,159,-10,2,160,-50,3,161,-11,2,162,-8,2,163,-9,2,164,-10,2,165,3,7,166,-1,0,167,-2,1,168,-3,1,169,-4,2,170,-5,2,171,-6,2,172,-7,2,173,-8,2,174,-9,2,175,-10,2,176,-11,2,177,-11,2,178,-2,1,179,-11,2,180,-5,2,181,-8,2,182,-7,2,183,-8,2,184,-8,2,185,-20,3,186,-21,3,187,-22,3,188,-22,3,189,-24,3,190,-25,3,191,-26,3,192,-27,3,193,-28,3,194,-29,3,195,-30,3,196,-31,3,197,-32,3,198,-33,3,199,-33,3,200,4,11,201,-36,3,202,-4,2,203,-5,2,204,-44,3,205,-40,3,206,-33,3,207,-33,3,208,-33,3,209,-44,3,210,-45,3,211,-2,1,212,-44,3,213,-4,2,214,-5,2,215,-50,3,216,-7,2,217,-8,2,218,-9,2,219,-44,3,220,4,11,221,-1,0,222,-2,1,223,-3,1,224,-4,2,225,-5,2,226,-6,2,227,-7,2,228,-8,2,229,-9,2,230,-10,2,231,-11,2,232,-11,2,233,-2,1,234,-11,2,235,-5,2,236,-8,2,237,-7,2,238,-8,2,239,-8,2,240,-20,3,241,-21,3,242,-22,3,243,-22,3,244,-24,3,245,-25,3,246,-26,3,247,-27,3,248,-28,3,249,-29,3,250,-30,3,251,-31,3,252,-32,3,253,-33,3,254,-33,3,255,-33,3,256,-36,3,257,-4,2,258,-5,2,259,-6,2,260,-40,3,261,-33,3,262,-33,3,263,-33,3,264,-44,3,265,-45,3,266,-2,1,267,-44,3,268,-4,2,269,-5,2,270,-50,3,271,-7,2,272,-8,2,273,-9,2,274,-44,3,275,-55,3,276,-55,3,277,-2,1,278,-55,3,279,-4,2,280,-10,2,281,-50,3,282,-7,2,283,-8,2,284,-9,2,285,-65,4,286,-66,4,287,-66,4,288,-66,4,289,-66,4,290,-20,3,291,-21,3,292,-22,3,293,-73,4,294,-74,4,295,-75,4,296,-21,3,297,-22,3,298,-33,3,299,-79,4,300,-30,3,301,-81,4,302,-82,4,303,-83,4,304,-84,4,305,-85,4,306,-86,4,307,-87,4,308,-88,4,309,-89,4,310,-90,4,311,-91,4,312,-4,2,313,-93,4,314,-94,4,315,-95,4,316,-96,4,317,-97,4,318,-98,4,319,-99,4,320,-100,4,321,-99,4,322,-99,4,323,-4,2,324,-104,4,325,-105,4,326,-99,4,327,-99,4,328,-108,4,329,-109,4,330,-110,4,331,-110,4,332,-110,4,333,-110,4,334,-4,2,335,-115,4,336,-116,4,337,-110,4,338,-110,4,339,-110,4,340,-120,4,341,-121,4,342,-121,4,343,-121,4,344,-121,4,345,-105,4,346,-105,4,347,-105,4,348,-120,4,349,-121,4,350,-20,3,351,-110,4,352,-132,4,353,-132,4,354,-132,4,355,-132,4,356,-4,2,357,-5,2,358,-138,4,359,-132,4,360,-132,4,361,-132,4,362,-132,4,363,-143,4,364,-144,4,365,-165,4,366,-146,4,367,-4,2,368,-148,4,369,-149,4,370,-150,4,371,-151,4,372,-20,3,373,-153,4,374,-154,4,375,-155,4,376,-156,4,377,-157,4,378,-158,4,379,-159,4,380,-160,4,381,-161,4,382,-162,4,383,-163,4,384,-164,4,385,-165,4,386,-166,4,387,-167,4,388,-168,4,389,-4,2,390,-5,2,391,-6,2,392,-172,4,393,-173,4,394,-174,4,395,-175,4,396,-176,4,397,-177,4,398,-178,4,399,-179,4,400,-5,2,401,-181,4,402,-182,4,403,-183,4,404,-184,4,405,-20,3,406,-21,3,407,-22,3,408,-22,3,409,-24,3,410,-25,3,411,-4,2,412,-165,4,413,-165,4,414,-165,4,415,-195,4,416,-196,4,417,-197,4,418,-198,4,419,-199,4,420,-200,4,421,-201,4,422,-4,2,423,-5,2,424,-99,4,425,-205,4,426,-206,4,427,-207,4,428,-208,4,429,-209,4,430,-210,4,431,-211,4,432,-212,4,433,-4,2,434,-5,2,435,-215,4,436,-209,4,437,-217,4,438,-218,4,439,-219,4,440,-220,4,441,-220,4,442,-220,4,443,-220,4,444,-4,2,445,-5,2,446,-6,2,447,-7,2,448,-220,4,449,-220,4,450,-220,4,451,-220,4,452,-221,4,453,-222,4,454,-223,4,455,-5,2,456,-228,4,457,-7,2,458,-228,4,459,-228,4,460,-20,3,461,-21,3,462,-22,3,463,-22,3,464,-24,3,465,-25,3,466,-4,2,467,-27,3,468,-28,3,469,-21,3,470,-30,3,471,-31,3,472,-220,4,473,-220,4,474,-220,4,475,-220,4,476,-220,4,477,-4,2,478,-5,2,479,-94,4,480,-220,4,481,-220,4,482,-220,4,483,-220,4,484,-220,4,485,-220,4,486,-222,4,487,-223,4,488,-4,2,489,-5,2,490,-220,4,491,-7,2,492,-228,4,493,-229,4,494,-230,4,495,-55,4,496,-55,4,497,-55,4,498,-55,4,499,-55,4,500,-60,4,501,-61,4,502,-62,4,503,-63,4,504,-64,4,505,-65,4,506,-66,4,507,-66,4,508,-66,4,509,-66,4,510,-70,4,511,-21,3,512,-22,3,513,-73,4,514,-74,4,515,-75,4,516,-76,4,517,-77,4,518,-78,4,519,-79,4,520,-80,4,521,-81,4,522,-82,4,523,-83,4,524,-84,4,525,-85,4,526,-86,4,527,-87,4,528,-88,4,529,-89,4,530,-90,4,531,-91,4,532,-4,2,533,-93,4,534,-94,4,535,-95,4,536,-96,4,537,-97,4,538,-98,4,539,-99,4,540,-100,4,541,-99,4,542,-99,4,543,-4,2,544,-104,4,545,-105,4,546,-99,4,547,-99,4,548,-108,4,549,-109,4,550,-110,4,551,-110,4,552,-110,4,553,-110,4,554,-4,2,555,-115,4,556,-116,4,557,-110,4,558,-110,4,559,-110,4,560,-120,4,561,-121,4,562,-121,4,563,-121,4,564,-121,4,565,-20,3,566,-21,3,567,-22,3,568,-120,4,569,-121,4,570,-20,3,571,-21,3,572,-132,4,573,-132,4,574,-132,4,575,-132,4,576,-4,2,577,-5,2,578,-138,4,579,-132,4,580,-132,4,581,-132,4,582,-132,4,583,-143,4,584,-144,4,585,-165,4,586,-146,4,587,-4,2,588,-148,4,589,-149,4,590,-150,4,591,-151,4,592,-20,3,593,-153,4,594,-154,4,595,-155,4,596,-156,4,597,-157,4,598,-158,4,599,-159,4,600,-160,4,601,-161,4,602,-154,4,603,-163,4,604,-164,4,605,-165,4,606,-166,4,607,-165,4,608,-168,4,609,-4,2,610,-5,2,611,-6,2,612,-7,2,613,-165,4,614,-165,4,615,-175,4,616,-176,4,617,-177,4,618,-176,4,619,-179,4,620,-5,2,621,-165,4,622,-7,2,623,-175,4,624,-19,3,625,-20,3,626,-21,3,627,-22,3,628,-22,3,629,-24,3,630,-25,3,631,-4,2,632,-27,3,633,-28,3,634,-21,3,635,-30,3,636,-31,3,637,-165,4,638,-165,4,639,-166,4,640,-200,4,641,-165,4,642,-4,2,643,-5,2,644,-99,4,645,-165,4,646,-165,4,647,-165,4,648,-175,4,649,-165,4,650,-165,4,651,-165,4,652,-168,4,653,-4,2,654,-5,2,655,-165,4,656,-7,2,657,-165,4,658,-174,4,659,-175,4,660,3,9,661,-1,0,662,-2,1,663,-3,1,664,-4,2,665,-5,2,666,-6,2,667,-7,2,668,-8,2,669,-9,2,670,-10,2,671,-11,2,672,-11,2,673,-2,1,674,-11,2,675,-5,2,676,-8,2,677,-7,2,678,-8,2,679,-19,3,680,-20,3,681,-21,3,682,-22,3,683,-22,3,684,-24,3,685,-25,3,686,-4,2,687,-27,3,688,-28,3,689,-21,3,690,-30,3,691,-31,3,692,-32,3,693,-33,3,694,-33,3,695,-33,3,696,-36,3,697,-4,2,698,-5,2,699,-6,2,700,-40,3,701,-33,3,702,-33,3,703,-33,3,704,-44,3,705,-45,3,706,-2,1,707,-44,3,708,-4,2,709,-5,2,710,-50,3,711,-7,2,712,-8,2,713,-9,2,714,-10,2,715,-55,4,716,-55,4,717,-55,4,718,-55,4,719,-55,4,720,-60,4,721,-61,4,722,-62,4,723,-63,4,724,-64,4,725,-65,4,726,-66,4,727,-66,4,728,-66,4,729,-66,4,730,-70,4,731,-71,4,732,-72,4,733,-73,4,734,-74,4,735,-75,4,736,-76,4,737,-77,4,738,-78,4,739,-79,4,740,-80,4,741,-81,4,742,-82,4,743,-83,4,744,-84,4,745,-85,4,746,-86,4,747,-87,4,748,-88,4,749,-89,4,750,-90,4,751,-91,4,752,-4,2,753,-93,4,754,-94,4,755,-95,4,756,-96,4,757,-97,4,758,-98,4,759,-99,4,760,-100,4,761,-99,4,762,-99,4,763,-4,2,764,-104,4,765,-105,4,766,-99,4,767,-99,4,768,-108,4,769,-109,4,770,-110,4,771,-110,4,772,-110,4,773,-110,4,774,-4,2,775,-115,4,776,-116,4,777,-110,4,778,-110,4,779,-110,4,780,-120,4,781,-121,4,782,-121,4,783,-121,4,784,-121,4,785,-20,3,786,-21,3,787,-22,3,788,-120,4,789,-121,4,790,-20,3,791,-21,3,792,-132,4,793,-132,4,794,-132,4,795,-132,4,796,-4,2,797,-5,2,798,-138,4,799,-132,4,800,-132,4,801,-132,4,802,-132,4,803,-143,4,804,-144,4,805,-145,4,806,-146,4,807,-4,2,808,-148,4,809,-149,4,810,-150,4,811,-151,4,812,-20,3,813,-153,4,814,-154,4,815,-155,4,816,-156,4,817,-157,4,818,-150,4,819,-159,4,820,-160,4,821,-161,4,822,-154,4,823,-163,4,824,-164,4,825,-165,4,826,-166,4,827,-2,1,828,-168,4,829,-4,2,830,-5,2,831,-6,2,832,-7,2,833,-8,2,834,-174,4,835,-175,4,836,-176,4,837,-177,4,838,-2,1,839,-179,4,840,-5,2,841,-8,2,842,-7,2,843,-8,2,844,-19,3,845,-20,3,846,-21,3,847,-22,3,848,-22,3,849,-24,3,850,-25,3,851,-4,2,852,-27,3,853,-28,3,854,-21,3,855,-30,3,856,-31,3,857,-32,3,858,-33,3,859,-33,3,860,-200,4,861,-165,4,862,-4,2,863,-5,2,864,-99,4,865,-165,4,866,-8,2,867,-33,3,868,-33,3,869,-209,4,870,-210,4,871,-2,1,872,-212,4,873,-4,2,874,-5,2,875,-215,4,876,-7,2,877,-8,2,878,-9,2,879,-54,4,880,-220,4,881,-221,4,882,-2,1,883,-223,4,884,-4,2,885,-5,2,886,-6,2,887,-7,2,888,-8,2,889,-9,2,890,-10,2,891,-231,4,892,-232,4,893,-2,1,894,-234,4,895,-5,2,896,-8,2,897,-7,2,898,-8,2,899,-19,3,900,-20,3,901,-21,3,902,-22,3,903,-22,3,904,-24,3,905,-25,3,906,-4,2,907,-27,3,908,-28,3,909,-21,3,910,-30,3,911,-31,3,912,-32,3,913,-33,3,914,-33,3,915,-33,3,916,-36,3,917,-4,2,918,-5,2,919,-94,4,920,-260,5,921,-8,2,922,-33,3,923,-263,5,924,-264,5,925,-265,5,926,-266,5,927,-267,5,928,-268,5,929,-269,5,930,-270,5,931,-271,5,932,-272,5,933,-273,5,934,-274,5,935,-55,4,936,-55,4,937,-55,4,938,-55,4,939,-55,4,940,-60,4,941,-61,4,942,-62,4,943,-63,4,944,-64,4,945,-65,4,946,-66,4,947,-66,4,948,-66,4,949,-66,4,950,-70,4,951,-71,4,952,-292,5,953,-73,4,954,-74,4,955,-75,4,956,-76,4,957,-77,4,958,-78,4,959,-79,4,960,-80,4,961,-81,4,962,-82,4,963,-83,4,964,-84,4,965,-85,4,966,-86,4,967,-87,4,968,-88,4,969,-89,4,970,-90,4,971,-91,4,972,-4,2,973,-93,4,974,-94,4,975,-95,4,976,-96,4,977,-97,4,978,-98,4,979,-99,4,980,-100,4,981,-99,4,982,-99,4,983,-4,2,984,-104,4,985,-105,4,986,-99,4,987,-327,5,988,-328,5,989,-329,5,990,-330,5,991,-331,5,992,-332,5,993,-333,5,994,-334,5,995,-335,5,996,-336,5,997,-337,5,998,-338,5,999,-339,5,1000,-340,5,1001,-341,5,1002,-342,5,1003,-343,5,1004,-344,5,1005,-345,5,1006,-346,5,1007,-347,5,1008,-348,5,1009,-349,5,1010,-350,5,1011,-351,5,1012,-352,5,1013,-353,5,1014,-354,5,1015,-355,5,1016,-356,5,1017,-357,5,1018,-358,5,1019,-359,5,1020,-360,5,1021,-361,5,1022,-362,5,1023,-363,5,1024,-364,5,1025,-365,5,1026,-366,5,1027,-367,5,1028,-368,5,1029,-369,5,1030,-370,5,1031,-371,5,1032,-372,5,1033,-373,5,1034,-374,5,1035,-375,5,1036,-376,5,1037,-377,5,1038,-378,5,1039,-379,5,1040,-380,5,1041,-381,5,1042,-382,5,1043,-383,5,1044,-384,5,1045,-385,5,1046,-386,5,1047,-387,5,1048,-388,5,1049,-389,5,1050,-390,5,1051,-391,5,1052,-392,5,1053,-393,5,1054,-394,5,1055,-395,5,1056,-396,5,1057,-397,5,1058,-398,5,1059,-399,5,1060,-400,5,1061,-401,5,1062,-402,5,1063,-403,5,1064,-404,5,1065,-405,5,1066,-406,5,1067,-407,5,1068,-408,5,1069,-409,5,1070,-410,5,1071,-4,2,1072,-412,5,1073,-413,5,1074,-414,5,1075,-415,5,1076,-416,5,1077,-417,5,1078,-418,5,1079,-419,5,1080,-420,5,1081,-421,5,1082,-4,2,1083,-423,5,1084,-424,5,1085,-425,5,1086,-426,5,1087,-427,5,1088,-428,5,1089,-429,5,1090,-430,5,1091,-431,5,1092,-432,5,1093,-4,2,1094,-434,5,1095,-435,5,1096,-436,5,1097,-437,5,1098,-438,5,1099,-439,5,1100,-440,5,1101,-441,5,1102,-442,5,1103,-443,5,1104,-4,2,1105,-445,5,1106,-446,5,1107,-447,5,1108,-448,5,1109,-449,5,1110,-450,5,1111,-451,5,1112,-452,5,1113,-453,5,1114,-454,5,1115,-455,5,1116,-456,5,1117,-457,5,1118,-458,5,1119,-459,5,1120,-460,5,1121,-461,5,1122,-462,5,1123,-463,5,1124,-464,5,1125,-465,5,1126,-4,2,1127,-467,5,1128,-468,5,1129,-469,5,1130,-470,5,1131,-471,5,1132,-472,5,1133,-473,5,1134,-474,5,1135,-475,5,1136,-476,5,1137,-4,2,1138,-478,5,1139,-479,5,1140,-480,5,1141,-481,5,1142,-482,5,1143,-483,5,1144,-484,5,1145,-485,5,1146,-486,5,1147,-487,5,1148,-4,2,1149,-489,5,1150,-490,5,1151,-491,5,1152,-492,5,1153,-493,5,1154,-494,5,1155,-55,4,1156,-55,4,1157,-55,4,1158,-55,4,1159,-55,4,1160,-60,4,1161,-61,4,1162,-62,4,1163,-63,4,1164,-64,4,1165,-65,4,1166,-66,4,1167,-66,4,1168,-66,4,1169,-66,4,1170,-70,4,1171,-511,5,1172,-512,5,1173,-73,4,1174,-440,5,1175,-440,5,1176,-440,5,1177,-440,5,1178,-440,5,1179,-440,5,1180,-440,5,1181,-440,5,1182,-440,5,1183,-523,5,1184,-524,5,1185,-525,5,1186,-526,5,1187,-527,5,1188,-528,5,1189,-529,5,1190,-530,5,1191,-531,5,1192,-4,2,1193,-533,5,1194,-534,5,1195,-535,5,1196,-536,5,1197,-537,5,1198,-538,5,1199,-539,5,1200,-540,5,1201,-541,5,1202,-542,5,1203,-4,2,1204,-544,5,1205,-545,5,1206,-546,5,1207,-547,5,1208,-548,5,1209,-549,5,1210,-550,5,1211,-551,5,1212,-552,5,1213,-553,5,1214,-4,2,1215,-555,5,1216,-556,5,1217,-557,5,1218,-558,5,1219,-559,5,1220,-560,5,1221,-561,5,1222,-562,5,1223,-563,5,1224,-564,5,1225,-565,5,1226,-566,5,1227,-567,5,1228,-568,5,1229,-569,5,1230,-570,5,1231,-571,5,1232,-572,5,1233,-573,5,1234,-574,5,1235,-575,5,1236,-4,2,1237,-577,5,1238,-578,5,1239,-579,5,1240,-580,5,1241,-581,5,1242,-582,5,1243,-583,5,1244,-584,5,1245,-585,5,1246,-586,5,1247,-4,2,1248,-588,5,1249,-589,5,1250,-590,5,1251,-591,5,1252,-572,5,1253,-593,5,1254,-594,5,1255,-595,5,1256,-596,5,1257,-597,5,1258,-598,5,1259,-599,5,1260,-600,5,1261,-601,5,1262,-602,5,1263,-603,5,1264,-604,5,1265,-605,5,1266,-606,5,1267,-607,5,1268,-608,5,1269,-4,2,1270,-5,2,1271,-611,5,1272,-612,5,1273,-613,5,1274,-614,5,1275,-615,5,1276,-616,5,1277,-617,5,1278,-618,5,1279,-619,5,1280,-5,2,1281,-621,5,1282,-622,5,1283,-623,5,1284,-19,3,1285,-20,3,1286,-626,5,1287,-627,5,1288,-628,5,1289,-629,5,1290,-630,5,1291,-4,2,1292,-632,5,1293,-633,5,1294,-634,5,1295,-635,5,1296,-636,5,1297,-637,5,1298,-638,5,1299,-639,5,1300,-640,5,1301,-641,5,1302,-4,2,1303,-5,2,1304,-644,5,1305,-645,5,1306,-646,5,1307,-647,5,1308,-648,5,1309,-649,5,1310,-650,5,1311,-651,5,1312,-652,5,1313,-4,2,1314,-5,2,1315,-655,5,1316,-656,5,1317,-657,5,1318,-658,5,1319,-659,5,1320,-660,5,1321,-660,5,1322,-660,5,1323,-660,5,1324,-4,2,1325,-5,2,1326,-660,5,1327,-660,5,1328,-660,5,1329,-660,5,1330,-660,5,1331,-660,5,1332,-661,5,1333,-662,5,1334,-663,5,1335,-5,2,1336,-668,5,1337,-667,5,1338,-668,5,1339,-19,3,1340,-20,3,1341,-21,3,1342,-660,5,1343,-661,5,1344,-660,5,1345,-660,5,1346,-4,2,1347,-660,5,1348,-660,5,1349,-21,3,1350,-660,5,1351,-660,5,1352,-660,5,1353,-660,5,1354,-661,5,1355,-662,5,1356,-660,5,1357,-4,2,1358,-5,2,1359,-666,5,1360,-660,5,1361,-668,5,1362,-669,5,1363,-670,5,1364,-660,5,1365,-660,5,1366,-662,5,1367,-663,5,1368,-4,2,1369,-5,2,1370,-50,4,1371,-667,5,1372,-668,5,1373,-669,5,1374,-54,4,1375,-55,4,1376,-55,4,1377,-55,4,1378,-55,4,1379,-55,4,1380,-60,4,1381,-61,4,1382,-62,4,1383,-63,4,1384,-64,4,1385,-65,4,1386,-66,4,1387,-66,4,1388,-66,4,1389,-66,4,1390,-70,4,1391,-71,4,1392,-72,4,1393,-73,4,1394,-74,4,1395,-75,4,1396,-76,4,1397,-77,4,1398,-78,4,1399,-79,4,1400,-80,4,1401,-81,4,1402,-82,4,1403,-83,4,1404,-84,4,1405,-85,4,1406,-86,4,1407,-87,4,1408,-88,4,1409,-89,4,1410,-90,4,1411,-91,4,1412,-4,2,1413,-93,4,1414,-94,4,1415,-660,5,1416,-660,5,1417,-660,5,1418,-660,5,1419,-660,5,1420,-660,5,1421,-660,5,1422,-660,5,1423,-4,2,1424,-660,5,1425,-660,5,1426,-660,5,1427,-660,5,1428,-660,5,1429,-660,5,1430,-660,5,1431,-661,5,1432,-662,5,1433,-663,5,1434,-4,2,1435,-660,5,1436,-660,5,1437,-667,5,1438,-668,5,1439,-669,5,1440,-660,5,1441,-660,5,1442,-661,5,1443,-662,5,1444,-663,5,1445,-20,3,1446,-21,3,1447,-22,3,1448,-668,5,1449,-668,5,1450,-20,3,1451,-21,3,1452,-660,5,1453,-661,5,1454,-662,5,1455,-663,5,1456,-4,2,1457,-5,2,1458,-660,5,1459,-667,5,1460,-668,5,1461,-669,5,1462,-670,5,1463,-660,5,1464,-660,5,1465,-660,5,1466,-660,5,1467,-4,2,1468,-660,5,1469,-660,5,1470,-660,5,1471,-660,5,1472,-20,3,1473,-660,5,1474,-660,5,1475,-660,5,1476,-662,5,1477,-660,5,1478,-668,5,1479,-660,5,1480,-660,5,1481,-660,5,1482,-668,5,1483,-660,5,1484,-660,5,1485,-660,5,1486,-660,5,1487,-662,5,1488,-660,5,1489,-4,2,1490,-5,2,1491,-666,5,1492,-667,5,1493,-668,5,1494,-660,5,1495,-660,5,1496,-660,5,1497,-660,5,1498,-662,5,1499,-660,5,1500,-5,2,1501,-676,5,1502,-667,5,1503,-668,5,1504,-19,3,1505,-20,3,1506,-21,3,1507,-22,3,1508,-22,3,1509,-660,5,1510,-685,5,1511,-4,2,1512,-687,5,1513,-688,5,1514,-21,3,1515,-690,5,1516,-691,5,1517,-692,5,1518,-693,5,1519,-693,5,1520,-660,5,1521,-696,5,1522,-4,2,1523,-5,2,1524,-99,4,1525,-700,5,1526,-701,5,1527,-693,5,1528,-693,5,1529,-660,5,1530,-660,5,1531,-662,5,1532,-660,5,1533,-4,2,1534,-5,2,1535,-660,5,1536,-667,5,1537,-668,5,1538,-669,5,1539,-54,4,1540,-660,5,1541,-660,5,1542,-662,5,1543,-660,5,1544,-4,2,1545,-5,2,1546,-660,5,1547,-667,5,1548,-668,5,1549,-669,5,1550,-670,5,1551,-671,5,1552,-671,5,1553,-233,5,1554,-234,5,1555,-5,2,1556,-676,5,1557,-237,5,1558,-238,5,1559,-19,3,1560,-20,3,1561,-21,3,1562,-22,3,1563,-22,3,1564,-24,3,1565,-685,5,1566,-4,2,1567,-247,5,1568,-248,5,1569,-249,5,1570,-250,5,1571,-251,5,1572,-252,5,1573,-253,5,1574,-254,5,1575,-255,5,1576,-256,5,1577,-257,5,1578,-258,5,1579,-94,4,1580,-260,5,1581,-261,5,1582,-262,5,1583,-263,5,1584,-264,5,1585,-265,5,1586,-266,5,1587,-267,5,1588,-268,5,1589,-269,5,1590,-270,5,1591,-271,5,1592,-272,5,1593,-273,5,1594,-274,5,1595,-275,5,1596,-276,5,1597,-277,5,1598,-278,5,1599,-279,5,1600,-280,5,1601,-61,4,1602,-282,5,1603,-63,4,1604,-64,4,1605,-65,4,1606,-66,4,1607,-66,4,1608,-66,4,1609,-66,4,1610,-290,5,1611,-291,5,1612,-292,5,1613,-73,4,1614,-74,4,1615,-75,4,1616,-296,5,1617,-297,5,1618,-298,5,1619,-79,4,1620,-300,5,1621,-81,4,1622,-82,4,1623,-303,5,1624,-304,5,1625,-305,5,1626,-306,5,1627,-307,5,1628,-308,5,1629,-309,5,1630,-310,5,1631,-311,5,1632,-308,5,1633,-313,5,1634,-314,5,1635,-315,5,1636,-316,5,1637,-317,5,1638,-318,5,1639,-319,5,1640,-320,5,1641,-321,5,1642,-322,5,1643,-4,2,1644,-324,5,1645,-325,5,1646,-326,5,1647,-327,5,1648,-328,5,1649,-329,5,1650,-330,5,1651,-331,5,1652,-332,5,1653,-333,5,1654,-4,2,1655,-335,5,1656,-336,5,1657,-337,5,1658,-338,5,1659,-339,5,1660,-340,5,1661,-341,5,1662,-342,5,1663,-343,5,1664,-344,5,1665,-345,5,1666,-346,5,1667,-347,5,1668,-348,5,1669,-349,5,1670,-350,5,1671,-351,5,1672,-352,5,1673,-353,5,1674,-354,5,1675,-355,5,1676,-4,2,1677,-357,5,1678,-358,5,1679,-359,5,1680,-360,5,1681,-361,5,1682,-362,5,1683,-363,5,1684,-364,5,1685,-365,5,1686,-366,5,1687,-4,2,1688,-368,5,1689,-369,5,1690,-370,5,1691,-371,5,1692,-372,5,1693,-373,5,1694,-374,5,1695,-375,5,1696,-376,5,1697,-377,5,1698,-378,5,1699,-379,5,1700,-380,5,1701,-381,5,1702,-382,5,1703,-383,5,1704,-384,5,1705,-385,5,1706,-386,5,1707,-387,5,1708,-388,5,1709,-4,2,1710,-390,5,1711,-391,5,1712,-392,5,1713,-393,5,1714,-394,5,1715,-395,5,1716,-396,5,1717,-397,5,1718,-398,5,1719,-399,5,1720,-400,5,1721,-401,5,1722,-402,5,1723,-403,5,1724,-404,5,1725,-405,5,1726,-406,5,1727,-407,5,1728,-408,5,1729,-409,5,1730,-410,5,1731,-4,2,1732,-412,5,1733,-413,5,1734,-414,5,1735,-415,5,1736,-416,5,1737,-417,5,1738,-418,5,1739,-419,5,1740,-420,5,1741,-421,5,1742,-4,2,1743,-5,2,1744,-424,5,1745,-425,5,1746,-426,5,1747,-427,5,1748,-428,5,1749,-429,5,1750,-430,5,1751,-431,5,1752,-432,5,1753,-4,2,1754,-5,2,1755,-435,5,1756,-436,5,1757,-437,5,1758,-438,5,1759,-439,5,1760,-440,5,1761,-441,5,1762,-442,5,1763,-443,5,1764,-4,2,1765,-5,2,1766,-446,5,1767,-447,5,1768,-448,5,1769,-449,5,1770,-450,5,1771,-451,5,1772,-452,5,1773,-453,5,1774,-454,5,1775,-5,2,1776,-456,5,1777,-457,5,1778,-458,5,1779,-19,3,1780,-20,3,1781,-21,3,1782,-440,5,1783,-441,5,1784,-464,5,1785,-465,5,1786,-4,2,1787,-467,5,1788,-468,5,1789,-21,3,1790,-470,5,1791,-471,5,1792,-472,5,1793,-473,5,1794,-474,5,1795,-475,5,1796,-476,5,1797,-4,2,1798,-5,2,1799,-385,5,1800,-480,5,1801,-481,5,1802,-482,5,1803,-483,5,1804,-484,5,1805,-485,5,1806,-486,5,1807,-487,5,1808,-4,2,1809,-5,2,1810,-490,5,1811,-491,5,1812,-492,5,1813,-493,5,1814,-494,5,1815,-55,4,1816,-55,4,1817,-55,4,1818,-55,4,1819,-55,4,1820,-60,4,1821,-61,4,1822,-62,4,1823,-63,4,1824,-64,4,1825,-65,4,1826,-66,4,1827,-66,4,1828,-66,4,1829,-66,4,1830,-70,4,1831,-71,4,1832,-490,5,1833,-73,4,1834,-74,4,1835,-75,4,1836,-76,4,1837,-77,4,1838,-78,4,1839,-79,4,1840,-80,4,1841,-81,4,1842,-82,4,1843,-83,4,1844,-84,4,1845,-85,4,1846,-86,4,1847,-87,4,1848,-88,4,1849,-440,5,1850,-440,5,1851,-440,5,1852,-4,2,1853,-440,5,1854,-440,5,1855,-440,5,1856,-440,5,1857,-440,5,1858,-440,5,1859,-440,5,1860,-440,5,1861,-442,5,1862,-443,5,1863,-4,2,1864,-440,5,1865,-440,5,1866,-447,5,1867,-448,5,1868,-440,5,1869,-440,5,1870,-440,5,1871,-441,5,1872,-442,5,1873,-443,5,1874,-4,2,1875,-440,5,1876,-440,5,1877,-447,5,1878,-448,5,1879,-449,5,1880,-560,5,1881,-561,5,1882,-562,5,1883,-563,5,1884,-564,5,1885,-20,3,1886,-21,3,1887,-440,5,1888,-448,5,1889,-448,5,1890,-20,3,1891,-21,3,1892,-572,5,1893,-573,5,1894,-574,5,1895,-575,5,1896,-4,2,1897,-5,2,1898,-578,5,1899,-572,5,1900,-580,5,1901,-581,5,1902,-582,5,1903,-583,5,1904,-584,5,1905,-585,5,1906,-586,5,1907,-4,2,1908,-588,5,1909,-589,5,1910,-590,5,1911,-591,5,1912,-20,3,1913,-593,5,1914,-594,5,1915,-595,5,1916,-596,5,1917,-597,5,1918,-598,5,1919,-599,5,1920,-600,5,1921,-601,5,1922,-602,5,1923,-603,5,1924,-604,5,1925,-605,5,1926,-606,5,1927,-607,5,1928,-608,5,1929,-4,2,1930,-5,2,1931,-605,5,1932,-605,5,1933,-613,5,1934,-614,5,1935,-615,5,1936,-616,5,1937,-617,5,1938,-618,5,1939,-619,5,1940,-5,2,1941,-621,5,1942,-615,5,1943,-623,5,1944,-19,3,1945,-20,3,1946,-21,3,1947,-22,3,1948,-22,3,1949,-605,5,1950,-605,5,1951,-4,2,1952,-605,5,1953,-605,5,1954,-21,3,1955,-605,5,1956,-605,5,1957,-605,5,1958,-605,5,1959,-606,5,1960,-640,5,1961,-641,5,1962,-4,2,1963,-5,2,1964,-440,5,1965,-645,5,1966,-613,5,1967,-614,5,1968,-615,5,1969,-649,5,1970,-650,5,1971,-649,5,1972,-652,5,1973,-4,2,1974,-5,2,1975,-655,5,1976,-649,5,1977,-649,5,1978,-658,5,1979,-659,5,1980,3,11,1981,-1,0,1982,-2,1,1983,-3,1,1984,-4,2,1985,-5,2,1986,-6,2,1987,-7,2,1988,-8,2,1989,-9,2,1990,-10,2,1991,-11,2,1992,-11,2,1993,-2,1,1994,-11,2,1995,-5,2,1996,-8,2,1997,-7,2,1998,-8,2,1999,-19,3,2000,-20,3,2001,-21,3,2002,-22,3,2003,-22,3,2004,-24,3,2005,-25,3,2006,-4,2,2007,-27,3,2008,-28,3,2009,-21,3,2010,-30,3,2011,-31,3,2012,-32,3,2013,-33,3,2014,-33,3,2015,-2,1,2016,-36,3,2017,-4,2,2018,-5,2,2019,-6,2,2020,-40,3,2021,-8,2,2022,-33,3,2023,-33,3,2024,-44,3,2025,-45,3,2026,-2,1,2027,-44,3,2028,-4,2,2029,-5,2,2030,-50,4,2031,-7,2,2032,-8,2,2033,-53,4,2034,-54,4,2035,-55,4,2036,-55,4,2037,-55,4,2038,-55,4,2039,-55,4,2040,-60,4,2041,-61,4,2042,-62,4,2043,-63,4,2044,-64,4,2045,-65,4,2046,-66,4,2047,-66,4,0,0,0"), // leafSize 12 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,-2,1,14,-2,1,15,-3,1,16,-4,1,17,-7,2,18,-8,2,19,-8,2,20,-10,2,21,-10,2,22,-11,2,23,-11,2,24,-12,2,25,-12,2,26,-12,2,27,3,4,28,-8,2,29,-8,2,30,3,5,31,-10,2,32,-10,2,33,3,5,34,-11,2,35,-11,2,36,3,5,37,-1,1,38,-2,1,39,-3,1,40,4,7,41,-8,2,42,-12,2,43,-10,2,44,4,8,45,-12,2,46,-10,2,47,-11,2,48,4,8,49,-1,1,50,5,10,51,-3,1,52,-8,2,53,-5,2,54,-10,2,55,5,11,56,-12,2,57,-9,2,58,-10,2,59,-11,2,60,5,11,61,-1,1,62,-2,1,63,-3,1,64,-4,2,65,-10,2,66,6,13,67,-12,2,68,-8,2,69,-9,2,70,-10,2,71,-11,2,72,-12,2,73,-7,2,74,-8,2,75,-9,2,76,-10,2,77,-11,2,78,-12,2,79,-24,3,80,-10,2,81,-21,3,82,-22,3,83,-23,3,84,-24,3,85,-8,2,86,-10,2,87,-21,3,88,-22,3,89,-23,3,90,-24,3,91,-36,3,92,-44,3,93,-33,3,94,-34,3,95,-35,3,96,-36,3,97,-1,1,98,-48,3,99,-33,3,100,-40,3,101,-35,3,102,-36,3,103,-48,3,104,-44,3,105,-50,3,106,-40,3,107,-47,3,108,-48,3,109,-1,1,110,-44,3,111,-45,3,112,-46,3,113,-47,3,114,-48,3,115,-55,3,116,-50,3,117,-57,3,118,-58,3,119,-59,3,120,-60,3,121,-55,3,122,-60,3,123,-60,3,124,-58,3,125,-59,3,126,-60,3,127,-60,3,128,-8,2,129,-60,3,130,-60,3,131,-60,3,132,-66,3,133,-66,3,134,-8,2,135,-60,3,136,-66,3,137,-66,3,138,-66,3,139,-7,2,140,-8,2,141,-9,2,142,-66,3,143,-66,3,144,3,7,145,-1,0,146,-2,1,147,-21,3,148,-22,3,149,-23,3,150,-24,3,151,-36,3,152,-44,3,153,-33,3,154,-10,2,155,-11,2,156,-36,3,157,-36,3,158,-44,3,159,-33,3,160,-40,3,161,-35,3,162,-36,3,163,-48,3,164,-44,3,165,3,7,166,-40,3,167,-23,3,168,-48,3,169,-48,3,170,-44,3,171,-50,3,172,-7,2,173,-8,2,174,-48,3,175,-55,3,176,-44,3,177,-12,2,178,-10,2,179,-11,2,180,3,7,181,-1,0,182,-2,1,183,-3,1,184,-4,2,185,-5,2,186,-60,3,187,-7,2,188,-8,2,189,-9,2,190,-10,2,191,-11,2,192,-12,2,193,-12,2,194,-2,1,195,-12,2,196,-8,2,197,-7,2,198,3,7,199,-8,2,200,-20,3,201,-21,3,202,-22,3,203,-23,3,204,-24,3,205,-7,2,206,-8,2,207,-9,2,208,-10,2,209,-11,2,210,-12,2,211,-31,3,212,-32,3,213,-33,3,214,-34,3,215,-35,3,216,-36,3,217,-36,3,218,-36,3,219,-21,3,220,4,11,221,-23,3,222,-24,3,223,-33,3,224,-44,3,225,-60,3,226,-36,3,227,-36,3,228,-48,3,229,-48,3,230,-50,3,231,-11,2,232,-12,2,233,-5,2,234,-36,3,235,-55,3,236,-8,2,237,-9,2,238,-10,2,239,-48,3,240,4,11,241,-1,0,242,-2,1,243,-3,1,244,-4,2,245,-5,2,246,-6,2,247,-7,2,248,-8,2,249,-9,2,250,-10,2,251,-11,2,252,-12,2,253,-12,2,254,-2,1,255,-15,3,256,-8,2,257,-7,2,258,-60,3,259,-8,2,260,-20,3,261,-21,3,262,-22,3,263,-23,3,264,4,11,265,-1,0,266,-2,1,267,-27,3,268,-4,2,269,-5,2,270,-30,3,271,-7,2,272,-8,2,273,-33,3,274,-10,2,275,-11,2,276,-36,3,277,-36,3,278,-36,3,279,-36,3,280,-40,3,281,-33,3,282,-102,4,283,-33,3,284,-44,3,285,-45,3,286,-36,3,287,-36,3,288,-48,3,289,-48,3,290,-50,3,291,-48,3,292,-8,2,293,-5,2,294,-44,3,295,-55,3,296,-8,2,297,-9,2,298,-10,2,299,-48,3,300,-60,4,301,-60,4,302,-60,4,303,-60,4,304,-60,4,305,-65,4,306,-66,4,307,-67,4,308,-68,4,309,-69,4,310,-70,4,311,-71,4,312,-72,4,313,-66,4,314,-74,4,315,-75,4,316,-76,4,317,-77,4,318,-78,4,319,-55,3,320,-80,4,321,-81,4,322,-82,4,323,-83,4,324,-60,4,325,-77,4,326,-86,4,327,-87,4,328,-88,4,329,-89,4,330,-66,4,331,-91,4,332,-92,4,333,-93,4,334,-70,4,335,-71,4,336,-96,4,337,-96,4,338,-98,4,339,-99,4,340,-100,4,341,-77,4,342,-102,4,343,-103,4,344,-104,4,345,-105,4,346,-106,4,347,-107,4,348,-108,4,349,-108,4,350,-110,4,351,-108,4,352,-112,4,353,-113,4,354,-114,4,355,-115,4,356,-116,4,357,-108,4,358,-118,4,359,-119,4,360,-120,4,361,-121,4,362,-120,4,363,-120,4,364,-124,4,365,-125,4,366,-126,4,367,-120,4,368,-120,4,369,-120,4,370,-120,4,371,-131,4,372,-132,4,373,-126,4,374,-126,4,375,-126,4,376,-126,4,377,-137,4,378,-180,4,379,-115,4,380,-132,4,381,-132,4,382,-132,4,383,-143,4,384,-144,4,385,-145,4,386,-146,4,387,-21,3,388,-190,4,389,-191,4,390,-126,4,391,-151,4,392,-152,4,393,-153,4,394,-154,4,395,-155,4,396,-156,4,397,-157,4,398,-158,4,399,-159,4,400,-180,4,401,-137,4,402,-162,4,403,-163,4,404,-164,4,405,-165,4,406,-166,4,407,-23,3,408,-168,4,409,-169,4,410,-170,4,411,-191,4,412,-172,4,413,-173,4,414,-174,4,415,-175,4,416,-176,4,417,-177,4,418,-178,4,419,-179,4,420,-180,4,421,-181,4,422,-182,4,423,-183,4,424,-4,2,425,-5,2,426,-66,4,427,-187,4,428,-188,4,429,-189,4,430,-190,4,431,-191,4,432,-192,4,433,-193,4,434,-194,4,435,-15,3,436,-196,4,437,-77,4,438,-198,4,439,-199,4,440,-20,3,441,-21,3,442,-22,3,443,-23,3,444,-180,4,445,-7,2,446,-206,4,447,-207,4,448,-208,4,449,-209,4,450,-210,4,451,-180,4,452,-188,4,453,-213,4,454,-190,4,455,-191,4,456,-216,4,457,-217,4,458,-218,4,459,-21,3,460,-220,4,461,-23,3,462,-198,4,463,-223,4,464,-224,4,465,-60,4,466,-226,4,467,-227,4,468,-228,4,469,-229,4,470,-230,4,471,-231,4,472,-232,4,473,-5,2,474,-234,4,475,-235,4,476,-236,4,477,-237,4,478,-238,4,479,-239,4,480,-240,4,481,-240,4,482,-240,4,483,-240,4,484,-4,2,485,-5,2,486,-66,4,487,-7,2,488,-240,4,489,-240,4,490,-240,4,491,-240,4,492,-240,4,493,-241,4,494,-242,4,495,-15,3,496,-240,4,497,-7,2,498,-60,4,499,-19,3,500,-20,3,501,-21,3,502,-22,3,503,-23,3,504,-240,4,505,-241,4,506,-242,4,507,-27,3,508,-4,2,509,-5,2,510,-30,3,511,-7,2,512,-8,2,513,-33,3,514,-250,4,515,-251,4,516,-240,4,517,-240,4,518,-2,1,519,-240,4,520,-240,4,521,-33,3,522,-102,4,523,-33,3,524,-240,4,525,-240,4,526,-240,4,527,-240,4,528,-240,4,529,-241,4,530,-240,4,531,-243,4,532,-8,2,533,-5,2,534,-114,4,535,-55,4,536,-8,2,537,-249,4,538,-58,4,539,-59,4,540,3,9,541,-1,0,542,-2,1,543,-3,1,544,-4,2,545,-65,4,546,-66,4,547,-67,4,548,-68,4,549,-69,4,550,-70,4,551,-71,4,552,-72,4,553,-66,4,554,-74,4,555,-75,4,556,-76,4,557,-77,4,558,-78,4,559,-55,4,560,-80,4,561,-21,3,562,-82,4,563,-83,4,564,-60,4,565,-85,4,566,-86,4,567,-87,4,568,-88,4,569,-89,4,570,-66,4,571,-91,4,572,-92,4,573,-93,4,574,-70,4,575,-71,4,576,-96,4,577,-96,4,578,-98,4,579,-99,4,580,-100,4,581,-77,4,582,-102,4,583,-103,4,584,-104,4,585,-105,4,586,-106,4,587,-107,4,588,-108,4,589,-108,4,590,-110,4,591,-108,4,592,-112,4,593,-113,4,594,-114,4,595,-115,4,596,-116,4,597,-108,4,598,-118,4,599,-119,4,600,-120,4,601,-121,4,602,-120,4,603,-120,4,604,-124,4,605,-125,4,606,-126,4,607,-120,4,608,-120,4,609,-120,4,610,-120,4,611,-131,4,612,-132,4,613,-126,4,614,-126,4,615,-126,4,616,-126,4,617,-137,4,618,-180,4,619,-115,4,620,-132,4,621,-132,4,622,-132,4,623,-132,4,624,-144,4,625,-145,4,626,-146,4,627,-21,3,628,-180,4,629,-180,4,630,-126,4,631,-151,4,632,-152,4,633,-153,4,634,-154,4,635,-155,4,636,-156,4,637,-157,4,638,-158,4,639,-159,4,640,-180,4,641,-137,4,642,-162,4,643,-163,4,644,-164,4,645,-165,4,646,-166,4,647,-23,3,648,-168,4,649,-169,4,650,-170,4,651,-180,4,652,-7,2,653,-165,4,654,-66,4,655,-115,4,656,-66,4,657,-177,4,658,-70,4,659,-71,4,660,-180,4,661,-181,4,662,-180,4,663,-183,4,664,-4,2,665,-5,2,666,-66,4,667,-7,2,668,-180,4,669,-180,4,670,-180,4,671,-191,4,672,-192,4,673,-193,4,674,-192,4,675,-15,3,676,-76,4,677,-77,4,678,-198,4,679,-19,3,680,-20,3,681,-21,3,682,-22,3,683,-23,3,684,-180,4,685,-7,2,686,-8,2,687,-198,4,688,-198,4,689,-198,4,690,-198,4,691,-7,2,692,-8,2,693,-33,3,694,-190,4,695,-191,4,696,-180,4,697,-181,4,698,-2,1,699,-21,3,700,-220,4,701,-23,3,702,-198,4,703,-108,4,704,-180,4,705,-165,4,706,-190,4,707,-191,4,708,-180,4,709,-181,4,710,-180,4,711,-220,4,712,-220,4,713,-5,2,714,-198,4,715,-55,4,716,-8,2,717,-57,4,718,-58,4,719,-59,4,720,3,9,721,-1,0,722,-2,1,723,-3,1,724,-4,2,725,-5,2,726,-6,2,727,-7,2,728,-8,2,729,-9,2,730,-10,2,731,-11,2,732,-12,2,733,-12,2,734,-2,1,735,-15,3,736,-8,2,737,-7,2,738,-8,2,739,-19,3,740,-20,3,741,-21,3,742,-22,3,743,-23,3,744,-24,3,745,-24,3,746,-24,3,747,-27,3,748,-5,2,749,-21,3,750,-30,3,751,-31,3,752,-32,3,753,-33,3,754,-34,3,755,-35,3,756,-36,3,757,-36,3,758,-2,1,759,-36,3,760,-40,3,761,-33,3,762,-33,3,763,-33,3,764,-44,3,765,-45,3,766,-36,3,767,-36,3,768,-48,3,769,-48,3,770,-50,4,771,-48,3,772,-8,2,773,-5,2,774,-54,4,775,-55,4,776,-56,4,777,-57,4,778,-58,4,779,-59,4,780,-60,4,781,-60,4,782,-60,4,783,-60,4,784,-60,4,785,-65,4,786,-66,4,787,-67,4,788,-68,4,789,-69,4,790,-70,4,791,-71,4,792,-72,4,793,-66,4,794,-74,4,795,-75,4,796,-76,4,797,-77,4,798,-78,4,799,-79,4,800,-80,4,801,-81,4,802,-82,4,803,-83,4,804,-84,4,805,-85,4,806,-86,4,807,-66,4,808,-88,4,809,-89,4,810,-90,4,811,-91,4,812,-92,4,813,-93,4,814,-94,4,815,-95,4,816,-96,4,817,-96,4,818,-98,4,819,-99,4,820,-100,4,821,-101,4,822,-102,4,823,-103,4,824,-104,4,825,-105,4,826,-106,4,827,-107,4,828,-108,4,829,-108,4,830,-110,4,831,-108,4,832,-112,4,833,-113,4,834,-114,4,835,-115,4,836,-116,4,837,-108,4,838,-118,4,839,-119,4,840,-120,4,841,-121,4,842,-120,4,843,-120,4,844,-124,4,845,-125,4,846,-126,4,847,-120,4,848,-120,4,849,-120,4,850,-130,4,851,-131,4,852,-132,4,853,-126,4,854,-126,4,855,-126,4,856,-126,4,857,-137,4,858,-138,4,859,-7,2,860,-132,4,861,-21,3,862,-132,4,863,-143,4,864,-144,4,865,-145,4,866,-146,4,867,-21,3,868,-22,3,869,-23,3,870,-24,3,871,-151,4,872,-152,4,873,-120,4,874,-154,4,875,-155,4,876,-156,4,877,-157,4,878,-158,4,879,-126,4,880,-160,4,881,-161,4,882,-162,4,883,-163,4,884,-164,4,885,-165,4,886,-66,4,887,-23,3,888,-60,4,889,-66,4,890,-66,4,891,-66,4,892,-7,2,893,-8,2,894,-66,4,895,-60,4,896,-66,4,897,-177,4,898,-70,4,899,-71,4,900,-180,4,901,-181,4,902,-2,1,903,-183,4,904,-4,2,905,-5,2,906,-66,4,907,-7,2,908,-8,2,909,-9,2,910,-190,4,911,-191,4,912,-192,4,913,-193,4,914,-2,1,915,-15,3,916,-76,4,917,-77,4,918,-198,4,919,-19,3,920,-20,3,921,-21,3,922,-22,3,923,-23,3,924,-24,3,925,-7,2,926,-8,2,927,-9,2,928,-198,4,929,-209,4,930,-210,4,931,-31,3,932,-32,3,933,-33,3,934,-34,3,935,-35,3,936,-36,3,937,-36,3,938,-2,1,939,-21,3,940,-220,4,941,-23,3,942,-24,3,943,-108,4,944,-180,4,945,-60,4,946,-36,3,947,-36,3,948,-228,4,949,-229,4,950,-230,4,951,-33,3,952,-232,4,953,-5,2,954,-36,3,955,-55,4,956,-56,4,957,-57,4,958,-58,4,959,-59,4,960,-240,4,961,-241,4,962,-2,1,963,-243,5,964,-4,2,965,-5,2,966,-66,4,967,-247,5,968,-248,5,969,-249,5,970,-250,5,971,-251,5,972,-252,5,973,-253,5,974,-254,5,975,-15,3,976,-76,4,977,-77,4,978,-60,4,979,-259,5,980,-260,5,981,-261,5,982,-262,5,983,-263,5,984,-264,5,985,-265,5,986,-266,5,987,-267,5,988,-268,5,989,-269,5,990,-270,5,991,-271,5,992,-272,5,993,-273,5,994,-274,5,995,-275,5,996,-276,5,997,-277,5,998,-278,5,999,-279,5,1000,-280,5,1001,-281,5,1002,-102,4,1003,-283,5,1004,-284,5,1005,-285,5,1006,-286,5,1007,-287,5,1008,-288,5,1009,-289,5,1010,-290,5,1011,-291,5,1012,-292,5,1013,-293,5,1014,-114,4,1015,-295,5,1016,-296,5,1017,-297,5,1018,-298,5,1019,-299,5,1020,-60,4,1021,-301,5,1022,-302,5,1023,-303,5,1024,-60,4,1025,-305,5,1026,-306,5,1027,-307,5,1028,-308,5,1029,-309,5,1030,-310,5,1031,-311,5,1032,-312,5,1033,-313,5,1034,-314,5,1035,-315,5,1036,-316,5,1037,-317,5,1038,-318,5,1039,-319,5,1040,-320,5,1041,-321,5,1042,-322,5,1043,-323,5,1044,-264,5,1045,-325,5,1046,-326,5,1047,-327,5,1048,-328,5,1049,-329,5,1050,-330,5,1051,-331,5,1052,-332,5,1053,-333,5,1054,-334,5,1055,-335,5,1056,-336,5,1057,-337,5,1058,-338,5,1059,-339,5,1060,-340,5,1061,-341,5,1062,-342,5,1063,-343,5,1064,-344,5,1065,-345,5,1066,-346,5,1067,-347,5,1068,-348,5,1069,-349,5,1070,-350,5,1071,-351,5,1072,-352,5,1073,-353,5,1074,-354,5,1075,-355,5,1076,-356,5,1077,-357,5,1078,-358,5,1079,-359,5,1080,-360,5,1081,-361,5,1082,-362,5,1083,-363,5,1084,-364,5,1085,-365,5,1086,-366,5,1087,-367,5,1088,-368,5,1089,-369,5,1090,-370,5,1091,-371,5,1092,-372,5,1093,-373,5,1094,-374,5,1095,-375,5,1096,-376,5,1097,-377,5,1098,-378,5,1099,-379,5,1100,-380,5,1101,-381,5,1102,-382,5,1103,-383,5,1104,-384,5,1105,-385,5,1106,-386,5,1107,-387,5,1108,-388,5,1109,-389,5,1110,-390,5,1111,-391,5,1112,-392,5,1113,-393,5,1114,-394,5,1115,-395,5,1116,-396,5,1117,-397,5,1118,-398,5,1119,-399,5,1120,-400,5,1121,-401,5,1122,-402,5,1123,-403,5,1124,-404,5,1125,-405,5,1126,-406,5,1127,-407,5,1128,-408,5,1129,-409,5,1130,-410,5,1131,-411,5,1132,-412,5,1133,-413,5,1134,-414,5,1135,-415,5,1136,-416,5,1137,-417,5,1138,-418,5,1139,-419,5,1140,-420,5,1141,-421,5,1142,-422,5,1143,-423,5,1144,-4,2,1145,-425,5,1146,-360,5,1147,-427,5,1148,-428,5,1149,-429,5,1150,-430,5,1151,-431,5,1152,-432,5,1153,-433,5,1154,-434,5,1155,-15,3,1156,-436,5,1157,-437,5,1158,-438,5,1159,-439,5,1160,-440,5,1161,-441,5,1162,-442,5,1163,-443,5,1164,-444,5,1165,-445,5,1166,-446,5,1167,-447,5,1168,-448,5,1169,-449,5,1170,-450,5,1171,-451,5,1172,-452,5,1173,-453,5,1174,-454,5,1175,-455,5,1176,-456,5,1177,-457,5,1178,-458,5,1179,-459,5,1180,-460,5,1181,-461,5,1182,-462,5,1183,-463,5,1184,-464,5,1185,-60,4,1186,-466,5,1187,-467,5,1188,-468,5,1189,-469,5,1190,-470,5,1191,-471,5,1192,-472,5,1193,-473,5,1194,-474,5,1195,-475,5,1196,-476,5,1197,-477,5,1198,-478,5,1199,-59,4,1200,-480,5,1201,-481,5,1202,-482,5,1203,-483,5,1204,-4,2,1205,-485,5,1206,-66,4,1207,-487,5,1208,-488,5,1209,-489,5,1210,-490,5,1211,-491,5,1212,-492,5,1213,-493,5,1214,-494,5,1215,-15,3,1216,-496,5,1217,-497,5,1218,-60,4,1219,-480,5,1220,-500,5,1221,-501,5,1222,-502,5,1223,-503,5,1224,-504,5,1225,-505,5,1226,-506,5,1227,-507,5,1228,-4,2,1229,-509,5,1230,-510,5,1231,-511,5,1232,-512,5,1233,-513,5,1234,-514,5,1235,-515,5,1236,-516,5,1237,-517,5,1238,-518,5,1239,-519,5,1240,-520,5,1241,-521,5,1242,-522,5,1243,-523,5,1244,-524,5,1245,-525,5,1246,-526,5,1247,-527,5,1248,-528,5,1249,-529,5,1250,-530,5,1251,-531,5,1252,-532,5,1253,-533,5,1254,-534,5,1255,-55,4,1256,-536,5,1257,-537,5,1258,-58,4,1259,-59,4,1260,-540,5,1261,-541,5,1262,-542,5,1263,-543,5,1264,-4,2,1265,-65,4,1266,-66,4,1267,-67,4,1268,-68,4,1269,-69,4,1270,-70,4,1271,-71,4,1272,-72,4,1273,-66,4,1274,-74,4,1275,-75,4,1276,-76,4,1277,-77,4,1278,-78,4,1279,-55,4,1280,-80,4,1281,-561,5,1282,-480,5,1283,-480,5,1284,-60,4,1285,-77,4,1286,-480,5,1287,-66,4,1288,-66,4,1289,-480,5,1290,-66,4,1291,-571,5,1292,-572,5,1293,-573,5,1294,-70,4,1295,-71,4,1296,-576,5,1297,-577,5,1298,-578,5,1299,-579,5,1300,-580,5,1301,-77,4,1302,-582,5,1303,-583,5,1304,-584,5,1305,-585,5,1306,-586,5,1307,-587,5,1308,-588,5,1309,-589,5,1310,-590,5,1311,-591,5,1312,-592,5,1313,-593,5,1314,-594,5,1315,-595,5,1316,-596,5,1317,-597,5,1318,-598,5,1319,-599,5,1320,-600,5,1321,-601,5,1322,-602,5,1323,-603,5,1324,-604,5,1325,-605,5,1326,-606,5,1327,-607,5,1328,-608,5,1329,-609,5,1330,-610,5,1331,-611,5,1332,-612,5,1333,-613,5,1334,-614,5,1335,-615,5,1336,-616,5,1337,-617,5,1338,-618,5,1339,-619,5,1340,-620,5,1341,-621,5,1342,-622,5,1343,-623,5,1344,-624,5,1345,-625,5,1346,-626,5,1347,-21,3,1348,-628,5,1349,-629,5,1350,-630,5,1351,-631,5,1352,-632,5,1353,-633,5,1354,-634,5,1355,-635,5,1356,-636,5,1357,-637,5,1358,-638,5,1359,-639,5,1360,-640,5,1361,-641,5,1362,-642,5,1363,-643,5,1364,-644,5,1365,-645,5,1366,-646,5,1367,-647,5,1368,-648,5,1369,-66,4,1370,-66,4,1371,-651,5,1372,-652,5,1373,-653,5,1374,-66,4,1375,-60,4,1376,-66,4,1377,-657,5,1378,-70,4,1379,-71,4,1380,-660,5,1381,-661,5,1382,-662,5,1383,-663,5,1384,-4,2,1385,-5,2,1386,-66,4,1387,-667,5,1388,-668,5,1389,-669,5,1390,-670,5,1391,-671,5,1392,-672,5,1393,-673,5,1394,-674,5,1395,-15,3,1396,-76,4,1397,-77,4,1398,-678,5,1399,-19,3,1400,-20,3,1401,-21,3,1402,-22,3,1403,-683,5,1404,-684,5,1405,-685,5,1406,-686,5,1407,-687,5,1408,-688,5,1409,-689,5,1410,-690,5,1411,-691,5,1412,-692,5,1413,-693,5,1414,-694,5,1415,-695,5,1416,-696,5,1417,-697,5,1418,-698,5,1419,-21,3,1420,-700,5,1421,-701,5,1422,-702,5,1423,-703,5,1424,-704,5,1425,-60,4,1426,-706,5,1427,-707,5,1428,-708,5,1429,-709,5,1430,-710,5,1431,-711,5,1432,-712,5,1433,-5,2,1434,-714,5,1435,-55,4,1436,-56,4,1437,-57,4,1438,-58,4,1439,-59,4,1440,-720,5,1441,-720,5,1442,-720,5,1443,-720,5,1444,-4,2,1445,-5,2,1446,-720,5,1447,-720,5,1448,-720,5,1449,-720,5,1450,-720,5,1451,-720,5,1452,-720,5,1453,-721,5,1454,-722,5,1455,-15,3,1456,-728,5,1457,-727,5,1458,-728,5,1459,-19,3,1460,-20,3,1461,-21,3,1462,-22,3,1463,-23,3,1464,-720,5,1465,-721,5,1466,-722,5,1467,-720,5,1468,-5,2,1469,-21,3,1470,-720,5,1471,-720,5,1472,-720,5,1473,-720,5,1474,-720,5,1475,-720,5,1476,-720,5,1477,-721,5,1478,-722,5,1479,-723,5,1480,-720,5,1481,-728,5,1482,-729,5,1483,-730,5,1484,-720,5,1485,-720,5,1486,-730,5,1487,-731,5,1488,-720,5,1489,-721,5,1490,-50,4,1491,-723,5,1492,-728,5,1493,-5,2,1494,-54,4,1495,-55,4,1496,-56,4,1497,-57,4,1498,-58,4,1499,-59,4,1500,-60,4,1501,-60,4,1502,-60,4,1503,-60,4,1504,-60,4,1505,-65,4,1506,-66,4,1507,-67,4,1508,-68,4,1509,-69,4,1510,-70,4,1511,-71,4,1512,-72,4,1513,-66,4,1514,-74,4,1515,-75,4,1516,-76,4,1517,-77,4,1518,-78,4,1519,-79,4,1520,-80,4,1521,-81,4,1522,-82,4,1523,-83,4,1524,-84,4,1525,-77,4,1526,-86,4,1527,-66,4,1528,-66,4,1529,-89,4,1530,-90,4,1531,-91,4,1532,-92,4,1533,-93,4,1534,-94,4,1535,-95,4,1536,-96,4,1537,-96,4,1538,-98,4,1539,-99,4,1540,-100,4,1541,-101,4,1542,-102,4,1543,-103,4,1544,-720,5,1545,-720,5,1546,-720,5,1547,-720,5,1548,-720,5,1549,-720,5,1550,-720,5,1551,-720,5,1552,-720,5,1553,-720,5,1554,-720,5,1555,-720,5,1556,-720,5,1557,-720,5,1558,-720,5,1559,-720,5,1560,-720,5,1561,-720,5,1562,-722,5,1563,-723,5,1564,-720,5,1565,-720,5,1566,-720,5,1567,-727,5,1568,-728,5,1569,-729,5,1570,-720,5,1571,-720,5,1572,-720,5,1573,-727,5,1574,-728,5,1575,-729,5,1576,-730,5,1577,-720,5,1578,-720,5,1579,-727,5,1580,-728,5,1581,-21,3,1582,-730,5,1583,-720,5,1584,-720,5,1585,-720,5,1586,-720,5,1587,-21,3,1588,-22,3,1589,-23,3,1590,-24,3,1591,-720,5,1592,-720,5,1593,-753,5,1594,-720,5,1595,-720,5,1596,-720,5,1597,-720,5,1598,-720,5,1599,-753,5,1600,-720,5,1601,-720,5,1602,-66,4,1603,-720,5,1604,-720,5,1605,-720,5,1606,-66,4,1607,-23,3,1608,-60,4,1609,-66,4,1610,-66,4,1611,-66,4,1612,-727,5,1613,-728,5,1614,-66,4,1615,-60,4,1616,-66,4,1617,-720,5,1618,-70,4,1619,-71,4,1620,-720,5,1621,-720,5,1622,-722,5,1623,-720,5,1624,-4,2,1625,-5,2,1626,-66,4,1627,-727,5,1628,-728,5,1629,-729,5,1630,-720,5,1631,-720,5,1632,-720,5,1633,-720,5,1634,-722,5,1635,-15,3,1636,-76,4,1637,-77,4,1638,-720,5,1639,-19,3,1640,-20,3,1641,-21,3,1642,-22,3,1643,-23,3,1644,-24,3,1645,-727,5,1646,-728,5,1647,-729,5,1648,-730,5,1649,-720,5,1650,-720,5,1651,-751,5,1652,-752,5,1653,-753,5,1654,-754,5,1655,-755,5,1656,-756,5,1657,-756,5,1658,-758,5,1659,-21,3,1660,-720,5,1661,-23,3,1662,-24,3,1663,-108,4,1664,-764,5,1665,-60,4,1666,-756,5,1667,-756,5,1668,-228,5,1669,-229,5,1670,-230,5,1671,-231,5,1672,-232,5,1673,-233,5,1674,-234,5,1675,-55,4,1676,-56,4,1677,-57,4,1678,-58,4,1679,-59,4,1680,-240,5,1681,-241,5,1682,-242,5,1683,-243,5,1684,-244,5,1685,-245,5,1686,-66,4,1687,-247,5,1688,-248,5,1689,-249,5,1690,-250,5,1691,-251,5,1692,-252,5,1693,-253,5,1694,-254,5,1695,-255,5,1696,-256,5,1697,-77,4,1698,-60,4,1699,-259,5,1700,-260,5,1701,-261,5,1702,-262,5,1703,-263,5,1704,-264,5,1705,-265,5,1706,-266,5,1707,-267,5,1708,-268,5,1709,-269,5,1710,-270,5,1711,-271,5,1712,-272,5,1713,-273,5,1714,-274,5,1715,-275,5,1716,-276,5,1717,-277,5,1718,-278,5,1719,-279,5,1720,-280,5,1721,-281,5,1722,-102,4,1723,-283,5,1724,-284,5,1725,-285,5,1726,-286,5,1727,-287,5,1728,-288,5,1729,-289,5,1730,-290,5,1731,-291,5,1732,-292,5,1733,-293,5,1734,-294,5,1735,-295,5,1736,-296,5,1737,-297,5,1738,-298,5,1739,-299,5,1740,-240,5,1741,-241,5,1742,-242,5,1743,-243,5,1744,-244,5,1745,-240,5,1746,-240,5,1747,-307,5,1748,-308,5,1749,-309,5,1750,-310,5,1751,-311,5,1752,-312,5,1753,-247,5,1754,-314,5,1755,-315,5,1756,-316,5,1757,-317,5,1758,-318,5,1759,-319,5,1760,-320,5,1761,-321,5,1762,-322,5,1763,-323,5,1764,-264,5,1765,-325,5,1766,-326,5,1767,-327,5,1768,-328,5,1769,-329,5,1770,-264,5,1771,-331,5,1772,-332,5,1773,-333,5,1774,-264,5,1775,-264,5,1776,-336,5,1777,-337,5,1778,-338,5,1779,-339,5,1780,-340,5,1781,-341,5,1782,-342,5,1783,-343,5,1784,-344,5,1785,-345,5,1786,-346,5,1787,-347,5,1788,-348,5,1789,-349,5,1790,-350,5,1791,-351,5,1792,-352,5,1793,-353,5,1794,-354,5,1795,-355,5,1796,-356,5,1797,-357,5,1798,-358,5,1799,-359,5,1800,-360,5,1801,-361,5,1802,-362,5,1803,-363,5,1804,-364,5,1805,-365,5,1806,-366,5,1807,-367,5,1808,-368,5,1809,-369,5,1810,-370,5,1811,-371,5,1812,-372,5,1813,-373,5,1814,-374,5,1815,-375,5,1816,-376,5,1817,-377,5,1818,-378,5,1819,-379,5,1820,-380,5,1821,-381,5,1822,-382,5,1823,-383,5,1824,-384,5,1825,-385,5,1826,-386,5,1827,-387,5,1828,-388,5,1829,-389,5,1830,-390,5,1831,-391,5,1832,-392,5,1833,-393,5,1834,-394,5,1835,-395,5,1836,-396,5,1837,-397,5,1838,-398,5,1839,-399,5,1840,-400,5,1841,-401,5,1842,-402,5,1843,-403,5,1844,-404,5,1845,-405,5,1846,-406,5,1847,-407,5,1848,-408,5,1849,-409,5,1850,-410,5,1851,-411,5,1852,-412,5,1853,-413,5,1854,-414,5,1855,-415,5,1856,-416,5,1857,-417,5,1858,-418,5,1859,-419,5,1860,-420,5,1861,-421,5,1862,-422,5,1863,-423,5,1864,-4,2,1865,-5,2,1866,-66,4,1867,-427,5,1868,-428,5,1869,-429,5,1870,-430,5,1871,-431,5,1872,-432,5,1873,-433,5,1874,-434,5,1875,-15,3,1876,-436,5,1877,-360,5,1878,-438,5,1879,-439,5,1880,-20,3,1881,-420,5,1882,-442,5,1883,-443,5,1884,-444,5,1885,-445,5,1886,-446,5,1887,-447,5,1888,-448,5,1889,-449,5,1890,-450,5,1891,-451,5,1892,-452,5,1893,-453,5,1894,-454,5,1895,-455,5,1896,-456,5,1897,-457,5,1898,-458,5,1899,-21,3,1900,-460,5,1901,-461,5,1902,-462,5,1903,-463,5,1904,-464,5,1905,-60,4,1906,-466,5,1907,-467,5,1908,-468,5,1909,-469,5,1910,-470,5,1911,-471,5,1912,-472,5,1913,-5,2,1914,-474,5,1915,-55,4,1916,-476,5,1917,-477,5,1918,-58,4,1919,-59,4,1920,-480,5,1921,-481,5,1922,-482,5,1923,-483,5,1924,-4,2,1925,-5,2,1926,-66,4,1927,-487,5,1928,-488,5,1929,-489,5,1930,-490,5,1931,-491,5,1932,-492,5,1933,-493,5,1934,-494,5,1935,-15,3,1936,-496,5,1937,-497,5,1938,-60,4,1939,-19,3,1940,-20,3,1941,-21,3,1942,-22,3,1943,-480,5,1944,-504,5,1945,-505,5,1946,-506,5,1947,-507,5,1948,-4,2,1949,-5,2,1950,-510,5,1951,-511,5,1952,-512,5,1953,-513,5,1954,-514,5,1955,-515,5,1956,-516,5,1957,-517,5,1958,-518,5,1959,-519,5,1960,-520,5,1961,-521,5,1962,-420,5,1963,-523,5,1964,-524,5,1965,-525,5,1966,-526,5,1967,-527,5,1968,-528,5,1969,-529,5,1970,-530,5,1971,-531,5,1972,-532,5,1973,-5,2,1974,-534,5,1975,-55,4,1976,-56,4,1977,-57,4,1978,-58,4,1979,-59,4,1980,-60,4,1981,-60,4,1982,-60,4,1983,-60,4,1984,-60,4,1985,-65,4,1986,-66,4,1987,-67,4,1988,-68,4,1989,-69,4,1990,-70,4,1991,-71,4,1992,-72,4,1993,-66,4,1994,-74,4,1995,-75,4,1996,-76,4,1997,-77,4,1998,-78,4,1999,-55,4,2000,-80,4,2001,-81,4,2002,-82,4,2003,-83,4,2004,-60,4,2005,-77,4,2006,-86,4,2007,-66,4,2008,-66,4,2009,-89,4,2010,-66,4,2011,-91,4,2012,-92,4,2013,-93,4,2014,-70,4,2015,-71,4,2016,-96,4,2017,-96,4,2018,-480,5,2019,-480,5,2020,-480,5,2021,-77,4,2022,-480,5,2023,-480,5,2024,-480,5,2025,-480,5,2026,-480,5,2027,-480,5,2028,-480,5,2029,-481,5,2030,-480,5,2031,-483,5,2032,-480,5,2033,-480,5,2034,-480,5,2035,-480,5,2036,-480,5,2037,-489,5,2038,-480,5,2039,-480,5,2040,-480,5,2041,-480,5,2042,-482,5,2043,-483,5,2044,-480,5,2045,-480,5,2046,-480,5,2047,-487,5,0,0,0"), // leafSize 13 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,-1,1,15,-2,1,16,-3,1,17,-4,1,18,-5,2,19,-8,2,20,-10,2,21,-8,2,22,-11,2,23,-10,2,24,-11,2,25,-12,2,26,-13,2,27,3,4,28,-13,2,29,-13,2,30,3,5,31,-5,2,32,-8,2,33,3,5,34,-8,2,35,-11,2,36,3,5,37,-11,2,38,-12,2,39,3,5,40,4,7,41,-2,1,42,-3,1,43,-4,2,44,4,8,45,-6,2,46,-7,2,47,-8,2,48,4,8,49,-10,2,50,-11,2,51,-12,2,52,4,8,53,-1,1,54,-2,1,55,5,11,56,-4,2,57,-5,2,58,-6,2,59,-7,2,60,5,11,61,-9,2,62,-10,2,63,-11,2,64,-12,2,65,5,11,66,6,13,67,-2,1,68,-3,1,69,-4,2,70,-5,2,71,-6,2,72,6,14,73,-8,2,74,-9,2,75,-10,2,76,-11,2,77,-12,2,78,6,14,79,-1,1,80,-2,1,81,-3,1,82,-4,2,83,-5,2,84,-6,2,85,-7,2,86,-8,2,87,-9,2,88,-10,2,89,-11,2,90,-12,2,91,-13,2,92,-1,1,93,-2,1,94,-16,3,95,-17,3,96,-8,2,97,-8,2,98,-10,2,99,-21,3,100,-22,3,101,-23,3,102,-24,3,103,-25,3,104,-26,3,105,-40,3,106,-28,3,107,-52,3,108,-30,3,109,-44,3,110,-32,3,111,-33,3,112,-52,3,113,-48,3,114,-36,3,115,-37,3,116,-38,3,117,-39,3,118,-40,3,119,-41,3,120,-55,3,121,-4,2,122,-44,3,123,-6,2,124,-52,3,125,-60,3,126,-48,3,127,-49,3,128,-50,3,129,-51,3,130,-52,3,131,-65,3,132,-60,3,133,-55,3,134,-4,2,135,-5,2,136,-6,2,137,-65,3,138,-60,3,139,-52,3,140,-62,3,141,-63,3,142,-64,3,143,-65,3,144,-66,3,145,-2,1,146,-3,1,147,-4,2,148,-5,2,149,-6,2,150,-72,3,151,-8,2,152,-9,2,153,-10,2,154,-11,2,155,-12,2,156,-78,3,157,-1,0,158,-2,1,159,-3,1,160,-4,2,161,-5,2,162,-6,2,163,-7,2,164,-8,2,165,-9,2,166,-10,2,167,-11,2,168,-12,2,169,-13,2,170,-13,2,171,-2,1,172,-16,3,173,-17,3,174,-8,2,175,-8,2,176,-20,3,177,-21,3,178,-22,3,179,-23,3,180,-24,3,181,-25,3,182,-26,3,183,-40,3,184,-26,3,185,-52,3,186,-30,3,187,-44,3,188,-24,3,189,-33,3,190,-52,3,191,-48,3,192,-36,3,193,-37,3,194,-38,3,195,3,7,196,-1,0,197,-2,1,198,-3,1,199,-4,2,200,-5,2,201,-6,2,202,-7,2,203,-8,2,204,-9,2,205,-10,2,206,-11,2,207,-12,2,208,-52,3,209,-78,4,210,-78,4,211,-78,4,212,-4,2,213,-5,2,214,-8,2,215,-78,4,216,-78,4,217,-22,3,218,-23,3,219,-24,3,220,-25,3,221,-78,4,222,-78,4,223,-78,4,224,-78,4,225,-78,4,226,-78,4,227,-78,4,228,-78,4,229,-78,4,230,-87,4,231,-88,4,232,-89,4,233,-90,4,234,3,8,235,-1,0,236,-2,1,237,-3,1,238,-4,2,239,-5,2,240,-6,2,241,-7,2,242,-8,2,243,-9,2,244,-10,2,245,-11,2,246,-12,2,247,-13,2,248,-13,2,249,-2,1,250,-16,3,251,-17,3,252,-8,2,253,-8,2,254,-20,3,255,-21,3,256,-22,3,257,-23,3,258,-24,3,259,-25,3,260,4,11,261,-1,0,262,-2,1,263,-3,1,264,-4,2,265,-5,2,266,-6,2,267,-7,2,268,-8,2,269,-9,2,270,-10,2,271,-11,2,272,-12,2,273,-39,3,274,-40,3,275,-2,1,276,-39,3,277,-4,2,278,-44,3,279,-8,2,280,-7,2,281,-21,3,282,-48,3,283,-23,3,284,-24,3,285,-25,3,286,-52,3,287,-52,3,288,-2,1,289,-55,3,290,-4,2,291,-5,2,292,-6,2,293,-7,2,294,-60,4,295,-9,2,296,-10,2,297,-63,4,298,-64,4,299,-65,4,300,-66,4,301,-65,4,302,-65,4,303,-65,4,304,-65,4,305,-65,4,306,-72,4,307,-73,4,308,-74,4,309,-75,4,310,-76,4,311,-77,4,312,4,12,313,-1,0,314,-2,1,315,-3,1,316,-4,2,317,-5,2,318,-6,2,319,-7,2,320,-8,2,321,-9,2,322,-10,2,323,-11,2,324,-12,2,325,-13,3,326,-13,3,327,-13,3,328,-16,3,329,-17,3,330,-8,2,331,-8,2,332,-20,3,333,-21,3,334,-22,3,335,-23,3,336,-24,3,337,-25,3,338,-26,3,339,-27,3,340,-26,3,341,-26,3,342,-30,3,343,-5,2,344,-24,3,345,-33,3,346,-26,3,347,-35,3,348,-36,3,349,-37,3,350,-38,3,351,-39,3,352,-40,3,353,-2,1,354,-39,3,355,-4,2,356,-44,3,357,-6,2,358,-7,2,359,-8,2,360,-48,3,361,-39,3,362,-39,3,363,-39,3,364,-52,3,365,-52,3,366,-2,1,367,-55,4,368,-4,2,369,-5,2,370,-6,2,371,-7,2,372,-60,4,373,-61,4,374,-62,4,375,-63,4,376,-64,4,377,-65,4,378,-66,4,379,-65,4,380,-65,4,381,-65,4,382,-65,4,383,-65,4,384,-72,4,385,-73,4,386,-74,4,387,-75,4,388,-76,4,389,-77,4,390,-78,4,391,-78,4,392,-78,4,393,-78,4,394,-78,4,395,-78,4,396,-78,4,397,-78,4,398,-78,4,399,-87,4,400,-88,4,401,-89,4,402,-90,4,403,-91,4,404,-91,4,405,-91,4,406,-16,3,407,-4,2,408,-91,4,409,-89,4,410,-78,4,411,-78,4,412,-78,4,413,-101,4,414,-102,4,415,-103,4,416,-104,4,417,-105,4,418,-104,4,419,-107,4,420,-108,4,421,-109,4,422,-110,4,423,-111,4,424,-112,4,425,-113,4,426,-114,4,427,-115,4,428,-116,4,429,-117,4,430,-118,4,431,-117,4,432,-120,4,433,-4,2,434,-122,4,435,-117,4,436,-124,4,437,-125,4,438,-126,4,439,-127,4,440,-128,4,441,-129,4,442,-130,4,443,-131,4,444,-132,4,445,-133,4,446,-4,2,447,-5,2,448,-130,4,449,-137,4,450,-138,4,451,-130,4,452,-130,4,453,-141,4,454,-142,4,455,-143,4,456,-144,4,457,-145,4,458,-146,4,459,-4,2,460,-5,2,461,-6,2,462,-150,4,463,-151,4,464,-152,4,465,-153,4,466,-154,4,467,-155,4,468,-156,4,469,-157,4,470,-158,4,471,-159,4,472,-4,2,473,-5,2,474,-6,2,475,-163,4,476,-164,4,477,-165,4,478,-166,4,479,-78,4,480,-78,4,481,-78,4,482,-78,4,483,-78,4,484,-16,3,485,-17,3,486,-78,4,487,-78,4,488,-20,3,489,-21,3,490,-22,3,491,-23,3,492,-24,3,493,-25,3,494,-234,4,495,-235,4,496,-236,4,497,-237,4,498,-4,2,499,-5,2,500,-6,2,501,-78,4,502,-234,4,503,-243,4,504,-244,4,505,-245,4,506,-246,4,507,-195,4,508,-196,4,509,-197,4,510,-198,4,511,-4,2,512,-5,2,513,-6,2,514,-7,2,515,-203,4,516,-204,4,517,-205,4,518,-206,4,519,-207,4,520,-78,4,521,-78,4,522,-78,4,523,-78,4,524,-4,2,525,-78,4,526,-78,4,527,-78,4,528,-78,4,529,-22,3,530,-88,4,531,-24,3,532,-25,3,533,-78,4,534,-78,4,535,-78,4,536,-78,4,537,-4,2,538,-78,4,539,-78,4,540,-78,4,541,-78,4,542,-87,4,543,-88,4,544,-89,4,545,-90,4,546,-234,4,547,-235,4,548,-2,1,549,-237,4,550,-4,2,551,-5,2,552,-6,2,553,-7,2,554,-8,2,555,-243,4,556,-244,4,557,-245,4,558,-246,4,559,-13,3,560,-13,3,561,-13,3,562,-16,3,563,-17,3,564,-8,2,565,-19,3,566,-20,3,567,-21,3,568,-22,3,569,-23,3,570,-24,3,571,-25,3,572,-260,4,573,-261,4,574,-2,1,575,-263,4,576,-4,2,577,-5,2,578,-6,2,579,-7,2,580,-8,2,581,-9,2,582,-270,4,583,-271,4,584,-272,4,585,-78,4,586,-234,4,587,-78,4,588,-78,4,589,-4,2,590,-278,4,591,-78,4,592,-78,4,593,-78,4,594,-282,4,595,-23,3,596,-24,3,597,-25,3,598,-286,4,599,-287,4,600,-2,1,601,-55,4,602,-4,2,603,-5,2,604,-6,2,605,-59,4,606,-60,4,607,-61,4,608,-62,4,609,-63,4,610,-64,4,611,-65,4,612,-66,4,613,-65,4,614,-65,4,615,-65,4,616,-65,4,617,-65,4,618,-72,4,619,-73,4,620,-74,4,621,-75,4,622,-76,4,623,-77,4,624,-312,4,625,-312,4,626,-2,1,627,-312,4,628,-4,2,629,-5,2,630,-6,2,631,-7,2,632,-8,2,633,-9,2,634,-10,2,635,-312,4,636,-312,4,637,-13,3,638,-13,3,639,-13,3,640,-16,3,641,-17,3,642,-8,2,643,-19,3,644,-20,3,645,-21,3,646,-22,3,647,-23,3,648,-24,3,649,-25,3,650,-26,3,651,-27,3,652,-26,3,653,-26,3,654,-30,3,655,-5,2,656,-24,3,657,-33,3,658,-26,3,659,-35,3,660,-36,3,661,-37,3,662,-38,3,663,-39,3,664,-40,3,665,-2,1,666,-39,3,667,-4,2,668,-44,3,669,-6,2,670,-7,2,671,-8,2,672,-312,4,673,-39,3,674,-39,3,675,-39,3,676,-52,4,677,-52,4,678,-52,4,679,-55,4,680,-52,4,681,-52,4,682,-52,4,683,-59,4,684,-60,4,685,-61,4,686,-62,4,687,-63,4,688,-64,4,689,-65,4,690,-66,4,691,-65,4,692,-65,4,693,-65,4,694,-65,4,695,-65,4,696,-72,4,697,-73,4,698,-74,4,699,-75,4,700,-76,4,701,-77,4,702,-78,4,703,-78,4,704,-78,4,705,-78,4,706,-4,2,707,-78,4,708,-78,4,709,-78,4,710,-78,4,711,-87,4,712,-88,4,713,-89,4,714,-90,4,715,-91,4,716,-91,4,717,-91,4,718,-16,3,719,-4,2,720,-5,2,721,-89,4,722,-78,4,723,-78,4,724,-78,4,725,-78,4,726,-78,4,727,-103,4,728,-104,4,729,-105,4,730,-104,4,731,-107,4,732,-108,4,733,-109,4,734,-110,4,735,-111,4,736,-112,4,737,-113,4,738,-114,4,739,-115,4,740,-116,4,741,-117,4,742,-118,4,743,-117,4,744,-120,4,745,-4,2,746,-122,4,747,-6,2,748,-124,4,749,-125,4,750,-126,4,751,-127,4,752,-128,4,753,-129,4,754,-130,4,755,-131,4,756,-132,4,757,-133,4,758,-4,2,759,-5,2,760,-6,2,761,-137,4,762,-138,4,763,-130,4,764,-130,4,765,-141,4,766,-142,4,767,-143,4,768,-144,4,769,-145,4,770,-146,4,771,-4,2,772,-5,2,773,-6,2,774,-72,4,775,-151,4,776,-152,4,777,-153,4,778,-76,4,779,-77,4,780,-78,4,781,-78,4,782,-78,4,783,-78,4,784,-4,2,785,-78,4,786,-78,4,787,-78,4,788,-78,4,789,-78,4,790,-78,4,791,-78,4,792,-78,4,793,-78,4,794,-78,4,795,-78,4,796,-16,3,797,-4,2,798,-78,4,799,-78,4,800,-78,4,801,-78,4,802,-78,4,803,-78,4,804,-78,4,805,-78,4,806,-312,5,807,-312,5,808,-314,5,809,-315,5,810,-78,4,811,-317,5,812,-318,5,813,-78,4,814,-320,5,815,-78,4,816,-78,4,817,-323,5,818,-324,5,819,-312,5,820,-312,5,821,-314,5,822,-312,5,823,-316,5,824,-317,5,825,-318,5,826,-319,5,827,-320,5,828,-321,5,829,-322,5,830,-323,5,831,-324,5,832,-78,4,833,-78,4,834,-78,4,835,-78,4,836,-4,2,837,-78,4,838,-78,4,839,-78,4,840,-78,4,841,-334,5,842,-88,4,843,-89,4,844,-337,5,845,-78,4,846,-78,4,847,-78,4,848,-78,4,849,-4,2,850,-78,4,851,-78,4,852,-72,4,853,-78,4,854,-87,4,855,-88,4,856,-89,4,857,-90,4,858,-312,5,859,-313,5,860,-314,5,861,-315,5,862,-316,5,863,-317,5,864,-318,5,865,-319,5,866,-320,5,867,-321,5,868,-322,5,869,-323,5,870,-324,5,871,-312,5,872,-313,5,873,-314,5,874,-328,5,875,-329,5,876,-330,5,877,-331,5,878,-332,5,879,-333,5,880,-334,5,881,-335,5,882,-336,5,883,-337,5,884,-312,5,885,-313,5,886,-314,5,887,-315,5,888,-316,5,889,-317,5,890,-318,5,891,-319,5,892,-320,5,893,-321,5,894,-322,5,895,-323,5,896,-324,5,897,-390,5,898,-352,5,899,-392,5,900,-390,5,901,-394,5,902,-356,5,903,-396,5,904,-397,5,905,-398,5,906,-360,5,907,-335,5,908,-336,5,909,-337,5,910,-364,5,911,-364,5,912,-366,5,913,-312,5,914,-368,5,915,-369,5,916,-370,5,917,-371,5,918,-312,5,919,-312,5,920,-374,5,921,-375,5,922,-376,5,923,-377,5,924,-378,5,925,-379,5,926,-380,5,927,-381,5,928,-382,5,929,-383,5,930,-384,5,931,-385,5,932,-386,5,933,-387,5,934,-388,5,935,-389,5,936,3,10,937,-1,0,938,-2,1,939,-3,1,940,-4,2,941,-5,2,942,-6,2,943,-7,2,944,-8,2,945,-9,2,946,-10,2,947,-11,2,948,-12,2,949,-13,3,950,-13,3,951,-13,3,952,-16,3,953,-17,3,954,-8,2,955,-19,3,956,-20,3,957,-21,3,958,-22,3,959,-23,3,960,-24,3,961,-25,3,962,-26,3,963,-27,3,964,-26,3,965,-26,3,966,-30,3,967,-5,2,968,-24,3,969,-33,3,970,-26,3,971,-35,3,972,-36,3,973,-37,3,974,-38,3,975,-39,3,976,-40,3,977,-2,1,978,-39,3,979,-4,2,980,-44,3,981,-6,2,982,-7,2,983,-8,2,984,-48,3,985,-39,3,986,-39,3,987,-51,4,988,-52,4,989,-52,4,990,-52,4,991,-55,4,992,-52,4,993,-52,4,994,-52,4,995,-59,4,996,-60,4,997,-61,4,998,-62,4,999,-63,4,1000,-64,4,1001,-65,4,1002,-66,4,1003,-65,4,1004,-65,4,1005,-65,4,1006,-65,4,1007,-65,4,1008,-72,4,1009,-73,4,1010,-74,4,1011,-75,4,1012,-76,4,1013,-77,4,1014,-78,4,1015,-78,4,1016,-78,4,1017,-78,4,1018,-4,2,1019,-78,4,1020,-78,4,1021,-78,4,1022,-78,4,1023,-87,4,1024,-88,4,1025,-89,4,1026,-90,4,1027,-91,4,1028,-91,4,1029,-91,4,1030,-16,3,1031,-4,2,1032,-5,2,1033,-89,4,1034,-78,4,1035,-78,4,1036,-78,4,1037,-78,4,1038,-78,4,1039,-78,4,1040,-104,4,1041,-105,4,1042,-104,4,1043,-107,4,1044,-108,4,1045,-109,4,1046,-110,4,1047,-111,4,1048,-112,4,1049,-113,4,1050,-114,4,1051,-115,4,1052,-116,4,1053,-117,4,1054,-118,4,1055,-117,4,1056,-120,4,1057,-4,2,1058,-122,4,1059,-6,2,1060,-124,4,1061,-125,4,1062,-126,4,1063,-127,4,1064,-128,4,1065,-129,4,1066,-130,4,1067,-131,4,1068,-132,4,1069,-133,4,1070,-4,2,1071,-5,2,1072,-6,2,1073,-137,4,1074,-138,4,1075,-130,4,1076,-130,4,1077,-141,4,1078,-142,4,1079,-143,4,1080,-144,4,1081,-145,4,1082,-146,4,1083,-4,2,1084,-5,2,1085,-6,2,1086,-72,4,1087,-143,4,1088,-74,4,1089,-75,4,1090,-76,4,1091,-77,4,1092,-78,4,1093,-78,4,1094,-78,4,1095,-78,4,1096,-4,2,1097,-78,4,1098,-78,4,1099,-78,4,1100,-78,4,1101,-78,4,1102,-78,4,1103,-78,4,1104,-78,4,1105,-78,4,1106,-78,4,1107,-78,4,1108,-16,3,1109,-4,2,1110,-78,4,1111,-78,4,1112,-78,4,1113,-78,4,1114,-78,4,1115,-78,4,1116,-78,4,1117,-78,4,1118,-78,4,1119,-78,4,1120,-78,4,1121,-78,4,1122,-78,4,1123,-78,4,1124,-78,4,1125,-78,4,1126,-78,4,1127,-78,4,1128,-78,4,1129,-78,4,1130,-78,4,1131,-195,4,1132,-196,4,1133,-2,1,1134,-198,4,1135,-4,2,1136,-5,2,1137,-6,2,1138,-7,2,1139,-8,2,1140,-9,2,1141,-10,2,1142,-206,4,1143,-207,4,1144,-78,4,1145,-78,4,1146,-78,4,1147,-78,4,1148,-4,2,1149,-78,4,1150,-78,4,1151,-78,4,1152,-78,4,1153,-87,4,1154,-88,4,1155,-89,4,1156,-90,4,1157,-78,4,1158,-78,4,1159,-78,4,1160,-78,4,1161,-4,2,1162,-78,4,1163,-78,4,1164,-72,4,1165,-78,4,1166,-74,4,1167,-75,4,1168,-76,4,1169,-77,4,1170,-234,5,1171,-235,5,1172,-236,5,1173,-237,5,1174,-238,5,1175,-239,5,1176,-240,5,1177,-241,5,1178,-242,5,1179,-243,5,1180,-244,5,1181,-245,5,1182,-246,5,1183,-247,5,1184,-248,5,1185,-249,5,1186,-250,5,1187,-251,5,1188,-252,5,1189,-253,5,1190,-254,5,1191,-255,5,1192,-256,5,1193,-257,5,1194,-258,5,1195,-259,5,1196,-260,5,1197,-261,5,1198,-262,5,1199,-263,5,1200,-264,5,1201,-265,5,1202,-266,5,1203,-267,5,1204,-268,5,1205,-269,5,1206,-270,5,1207,-271,5,1208,-272,5,1209,-78,4,1210,-274,5,1211,-78,4,1212,-78,4,1213,-4,2,1214,-278,5,1215,-279,5,1216,-78,4,1217,-281,5,1218,-282,5,1219,-283,5,1220,-284,5,1221,-285,5,1222,-286,5,1223,-287,5,1224,-288,5,1225,-289,5,1226,-290,5,1227,-291,5,1228,-292,5,1229,-293,5,1230,-294,5,1231,-295,5,1232,-296,5,1233,-297,5,1234,-298,5,1235,-299,5,1236,-300,5,1237,-301,5,1238,-302,5,1239,-303,5,1240,-304,5,1241,-305,5,1242,-306,5,1243,-307,5,1244,-308,5,1245,-309,5,1246,-310,5,1247,-311,5,1248,4,15,1249,-1,0,1250,-2,1,1251,-3,1,1252,-4,2,1253,-5,2,1254,-6,2,1255,-7,2,1256,-8,2,1257,-9,2,1258,-10,2,1259,-11,2,1260,-12,2,1261,-13,3,1262,-13,3,1263,-13,3,1264,-16,3,1265,-17,3,1266,-13,3,1267,-19,3,1268,-20,3,1269,-21,3,1270,-22,3,1271,-23,3,1272,-24,3,1273,-25,3,1274,-26,3,1275,-27,3,1276,-26,3,1277,-26,3,1278,-30,3,1279,-5,2,1280,-24,3,1281,-33,3,1282,-26,3,1283,-35,3,1284,-36,3,1285,-37,3,1286,-38,3,1287,-39,3,1288,-40,3,1289,-2,1,1290,-39,3,1291,-4,2,1292,-44,3,1293,-6,2,1294,-7,2,1295,-8,2,1296,-48,3,1297,-39,3,1298,-39,3,1299,-51,4,1300,-52,4,1301,-52,4,1302,-52,4,1303,-55,4,1304,-52,4,1305,-52,4,1306,-52,4,1307,-59,4,1308,-60,4,1309,-61,4,1310,-62,4,1311,-63,4,1312,-64,4,1313,-65,4,1314,-66,4,1315,-65,4,1316,-65,4,1317,-65,4,1318,-65,4,1319,-65,4,1320,-72,4,1321,-73,4,1322,-74,4,1323,-75,4,1324,-76,4,1325,-77,4,1326,-78,4,1327,-78,4,1328,-78,4,1329,-78,4,1330,-4,2,1331,-78,4,1332,-78,4,1333,-78,4,1334,-78,4,1335,-87,4,1336,-88,4,1337,-89,4,1338,-90,4,1339,-91,4,1340,-91,4,1341,-91,4,1342,-16,3,1343,-17,3,1344,-5,2,1345,-89,4,1346,-78,4,1347,-78,4,1348,-78,4,1349,-78,4,1350,-78,4,1351,-78,4,1352,-104,4,1353,-105,4,1354,-104,4,1355,-107,4,1356,-108,4,1357,-109,4,1358,-110,4,1359,-111,4,1360,-112,4,1361,-113,4,1362,-114,4,1363,-115,4,1364,-116,4,1365,-117,4,1366,-118,4,1367,-117,4,1368,-120,4,1369,-4,2,1370,-122,4,1371,-6,2,1372,-124,4,1373,-125,4,1374,-126,4,1375,-127,4,1376,-128,4,1377,-129,4,1378,-130,4,1379,-131,4,1380,-132,4,1381,-133,4,1382,-4,2,1383,-5,2,1384,-6,2,1385,-137,4,1386,-138,4,1387,-130,4,1388,-130,4,1389,-141,4,1390,-142,4,1391,-143,4,1392,-144,4,1393,-2,1,1394,-146,4,1395,-4,2,1396,-5,2,1397,-6,2,1398,-72,4,1399,-73,4,1400,-74,4,1401,-75,4,1402,-76,4,1403,-77,4,1404,-78,4,1405,-78,4,1406,-78,4,1407,-78,4,1408,-4,2,1409,-78,4,1410,-78,4,1411,-78,4,1412,-78,4,1413,-78,4,1414,-78,4,1415,-78,4,1416,-78,4,1417,-78,4,1418,-78,4,1419,-78,4,1420,-16,3,1421,-17,3,1422,-78,4,1423,-78,4,1424,-78,4,1425,-78,4,1426,-78,4,1427,-78,4,1428,-78,4,1429,-78,4,1430,-78,4,1431,-78,4,1432,-78,4,1433,-78,4,1434,-78,4,1435,-78,4,1436,-78,4,1437,-78,4,1438,-78,4,1439,-78,4,1440,-78,4,1441,-78,4,1442,-78,4,1443,-195,4,1444,-196,4,1445,-2,1,1446,-198,4,1447,-4,2,1448,-5,2,1449,-6,2,1450,-7,2,1451,-8,2,1452,-9,2,1453,-10,2,1454,-195,4,1455,-207,4,1456,-78,4,1457,-78,4,1458,-78,4,1459,-78,4,1460,-4,2,1461,-78,4,1462,-78,4,1463,-78,4,1464,-78,4,1465,-87,4,1466,-88,4,1467,-89,4,1468,-90,4,1469,-78,4,1470,-78,4,1471,-78,4,1472,-78,4,1473,-4,2,1474,-78,4,1475,-78,4,1476,-72,4,1477,-73,4,1478,-74,4,1479,-75,4,1480,-76,4,1481,-77,4,1482,-234,5,1483,-235,5,1484,-236,5,1485,-237,5,1486,-238,5,1487,-239,5,1488,-240,5,1489,-241,5,1490,-242,5,1491,-243,5,1492,-244,5,1493,-245,5,1494,-246,5,1495,-247,5,1496,-248,5,1497,-249,5,1498,-250,5,1499,-251,5,1500,-252,5,1501,-253,5,1502,-254,5,1503,-255,5,1504,-256,5,1505,-257,5,1506,-258,5,1507,-259,5,1508,-260,5,1509,-261,5,1510,-262,5,1511,-263,5,1512,-264,5,1513,-265,5,1514,-266,5,1515,-267,5,1516,-268,5,1517,-269,5,1518,-270,5,1519,-271,5,1520,-272,5,1521,-273,5,1522,-274,5,1523,-275,5,1524,-276,5,1525,-277,5,1526,-278,5,1527,-279,5,1528,-280,5,1529,-281,5,1530,-282,5,1531,-283,5,1532,-284,5,1533,-285,5,1534,-286,5,1535,-287,5,1536,-288,5,1537,-289,5,1538,-290,5,1539,-291,5,1540,-292,5,1541,-293,5,1542,-234,5,1543,-295,5,1544,-296,5,1545,-234,5,1546,-298,5,1547,-299,5,1548,-300,5,1549,-301,5,1550,-302,5,1551,-303,5,1552,-304,5,1553,-305,5,1554,-306,5,1555,-307,5,1556,-308,5,1557,-309,5,1558,-310,5,1559,-311,5,1560,-312,5,1561,-313,5,1562,-314,5,1563,-315,5,1564,-312,5,1565,-317,5,1566,-318,5,1567,-319,5,1568,-320,5,1569,-321,5,1570,-322,5,1571,-323,5,1572,-324,5,1573,-13,3,1574,-13,3,1575,-13,3,1576,-312,5,1577,-329,5,1578,-330,5,1579,-331,5,1580,-332,5,1581,-333,5,1582,-334,5,1583,-335,5,1584,-336,5,1585,-337,5,1586,-338,5,1587,-339,5,1588,-340,5,1589,-341,5,1590,-342,5,1591,-343,5,1592,-344,5,1593,-345,5,1594,-346,5,1595,-347,5,1596,-348,5,1597,-349,5,1598,-350,5,1599,-351,5,1600,-352,5,1601,-353,5,1602,-354,5,1603,-4,2,1604,-356,5,1605,-357,5,1606,-358,5,1607,-359,5,1608,-360,5,1609,-361,5,1610,-362,5,1611,-363,5,1612,-364,5,1613,-365,5,1614,-366,5,1615,-55,4,1616,-4,2,1617,-369,5,1618,-370,5,1619,-371,5,1620,-60,4,1621,-61,4,1622,-62,4,1623,-63,4,1624,-64,4,1625,-65,4,1626,-312,5,1627,-65,4,1628,-65,4,1629,-65,4,1630,-65,4,1631,-65,4,1632,-312,5,1633,-312,5,1634,-312,5,1635,-312,5,1636,-312,5,1637,-312,5,1638,-312,5,1639,-313,5,1640,-314,5,1641,-315,5,1642,-4,2,1643,-317,5,1644,-318,5,1645,-319,5,1646,-320,5,1647,-399,5,1648,-400,5,1649,-401,5,1650,-402,5,1651,-403,5,1652,-404,5,1653,-405,5,1654,-16,3,1655,-4,2,1656,-408,5,1657,-409,5,1658,-332,5,1659,-333,5,1660,-334,5,1661,-335,5,1662,-414,5,1663,-415,5,1664,-416,5,1665,-417,5,1666,-418,5,1667,-419,5,1668,-420,5,1669,-421,5,1670,-422,5,1671,-423,5,1672,-424,5,1673,-425,5,1674,-426,5,1675,-427,5,1676,-428,5,1677,-429,5,1678,-430,5,1679,-431,5,1680,-432,5,1681,-4,2,1682,-434,5,1683,-435,5,1684,-436,5,1685,-437,5,1686,-438,5,1687,-439,5,1688,-440,5,1689,-441,5,1690,-442,5,1691,-443,5,1692,-444,5,1693,-445,5,1694,-4,2,1695,-5,2,1696,-448,5,1697,-449,5,1698,-450,5,1699,-451,5,1700,-452,5,1701,-453,5,1702,-454,5,1703,-455,5,1704,-456,5,1705,-457,5,1706,-458,5,1707,-4,2,1708,-5,2,1709,-461,5,1710,-462,5,1711,-463,5,1712,-464,5,1713,-465,5,1714,-466,5,1715,-467,5,1716,-468,5,1717,-469,5,1718,-470,5,1719,-471,5,1720,-4,2,1721,-5,2,1722,-474,5,1723,-475,5,1724,-476,5,1725,-477,5,1726,-78,4,1727,-78,4,1728,-78,4,1729,-78,4,1730,-78,4,1731,-78,4,1732,-16,3,1733,-17,3,1734,-78,4,1735,-78,4,1736,-20,3,1737,-21,3,1738,-468,5,1739,-491,5,1740,-492,5,1741,-78,4,1742,-494,5,1743,-495,5,1744,-496,5,1745,-497,5,1746,-4,2,1747,-5,2,1748,-500,5,1749,-78,4,1750,-502,5,1751,-503,5,1752,-78,4,1753,-505,5,1754,-506,5,1755,-507,5,1756,-508,5,1757,-509,5,1758,-510,5,1759,-4,2,1760,-5,2,1761,-513,5,1762,-514,5,1763,-515,5,1764,-516,5,1765,-517,5,1766,-518,5,1767,-519,5,1768,-78,4,1769,-78,4,1770,-78,4,1771,-78,4,1772,-4,2,1773,-78,4,1774,-78,4,1775,-78,4,1776,-78,4,1777,-22,3,1778,-442,5,1779,-531,5,1780,-532,5,1781,-78,4,1782,-78,4,1783,-78,4,1784,-78,4,1785,-4,2,1786,-78,4,1787,-78,4,1788,-78,4,1789,-78,4,1790,-78,4,1791,-88,4,1792,-89,4,1793,-455,5,1794,-546,5,1795,-547,5,1796,-548,5,1797,-549,5,1798,-4,2,1799,-5,2,1800,-6,2,1801,-553,5,1802,-554,5,1803,-555,5,1804,-556,5,1805,-557,5,1806,-558,5,1807,-13,3,1808,-13,3,1809,-13,3,1810,-16,3,1811,-17,3,1812,-564,5,1813,-19,3,1814,-20,3,1815,-21,3,1816,-22,3,1817,-23,3,1818,-24,3,1819,-546,5,1820,-572,5,1821,-573,5,1822,-574,5,1823,-575,5,1824,-4,2,1825,-5,2,1826,-6,2,1827,-579,5,1828,-580,5,1829,-581,5,1830,-582,5,1831,-583,5,1832,-584,5,1833,-78,4,1834,-586,5,1835,-78,4,1836,-78,4,1837,-4,2,1838,-590,5,1839,-78,4,1840,-78,4,1841,-78,4,1842,-594,5,1843,-23,3,1844,-24,3,1845,-25,3,1846,-52,4,1847,-52,4,1848,-52,4,1849,-55,4,1850,-52,4,1851,-52,4,1852,-52,4,1853,-59,4,1854,-60,4,1855,-61,4,1856,-62,4,1857,-63,4,1858,-64,4,1859,-65,4,1860,-66,4,1861,-65,4,1862,-65,4,1863,-65,4,1864,-65,4,1865,-65,4,1866,-72,4,1867,-73,4,1868,-74,4,1869,-75,4,1870,-76,4,1871,-77,4,1872,-624,5,1873,-312,5,1874,-312,5,1875,-312,5,1876,-4,2,1877,-312,5,1878,-312,5,1879,-312,5,1880,-312,5,1881,-312,5,1882,-312,5,1883,-312,5,1884,-312,5,1885,-13,3,1886,-13,3,1887,-13,3,1888,-16,3,1889,-312,5,1890,-320,5,1891,-320,5,1892,-312,5,1893,-312,5,1894,-312,5,1895,-312,5,1896,-312,5,1897,-312,5,1898,-312,5,1899,-312,5,1900,-314,5,1901,-315,5,1902,-312,5,1903,-317,5,1904,-320,5,1905,-312,5,1906,-320,5,1907,-312,5,1908,-312,5,1909,-312,5,1910,-312,5,1911,-312,5,1912,-312,5,1913,-314,5,1914,-315,5,1915,-4,2,1916,-312,5,1917,-318,5,1918,-319,5,1919,-320,5,1920,-312,5,1921,-322,5,1922,-323,5,1923,-324,5,1924,-312,5,1925,-313,5,1926,-314,5,1927,-55,4,1928,-4,2,1929,-317,5,1930,-318,5,1931,-319,5,1932,-60,4,1933,-61,4,1934,-62,4,1935,-63,4,1936,-64,4,1937,-65,4,1938,-66,4,1939,-65,4,1940,-65,4,1941,-65,4,1942,-65,4,1943,-65,4,1944,-312,5,1945,-312,5,1946,-312,5,1947,-312,5,1948,-312,5,1949,-312,5,1950,-312,5,1951,-312,5,1952,-312,5,1953,-312,5,1954,-4,2,1955,-312,5,1956,-312,5,1957,-312,5,1958,-312,5,1959,-312,5,1960,-312,5,1961,-312,5,1962,-312,5,1963,-312,5,1964,-313,5,1965,-314,5,1966,-16,3,1967,-4,2,1968,-317,5,1969,-320,5,1970,-312,5,1971,-312,5,1972,-312,5,1973,-312,5,1974,-312,5,1975,-312,5,1976,-312,5,1977,-312,5,1978,-314,5,1979,-312,5,1980,-312,5,1981,-312,5,1982,-312,5,1983,-312,5,1984,-312,5,1985,-312,5,1986,-312,5,1987,-312,5,1988,-312,5,1989,-312,5,1990,-312,5,1991,-314,5,1992,-312,5,1993,-4,2,1994,-312,5,1995,-318,5,1996,-312,5,1997,-312,5,1998,-312,5,1999,-312,5,2000,-312,5,2001,-312,5,2002,-312,5,2003,-312,5,2004,-312,5,2005,-312,5,2006,-4,2,2007,-317,5,2008,-318,5,2009,-312,5,2010,-312,5,2011,-321,5,2012,-322,5,2013,-312,5,2014,-312,5,2015,-312,5,2016,-312,5,2017,-312,5,2018,-312,5,2019,-4,2,2020,-317,5,2021,-318,5,2022,-312,5,2023,-312,5,2024,-312,5,2025,-312,5,2026,-312,5,2027,-312,5,2028,-312,5,2029,-312,5,2030,-314,5,2031,-312,5,2032,-4,2,2033,-317,5,2034,-318,5,2035,-319,5,2036,-320,5,2037,-312,5,2038,-312,5,2039,-312,5,2040,-312,5,2041,-312,5,2042,-313,5,2043,-314,5,2044,-16,3,2045,-312,5,2046,-317,5,2047,-320,5,0,0,0"), // leafSize 14 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,14,16,15,-2,1,16,-2,1,17,-3,1,18,-5,2,19,-8,2,20,-10,2,21,-8,2,22,-11,2,23,-10,2,24,-11,2,25,-12,2,26,-13,2,27,-13,2,28,-14,2,29,-14,2,30,3,5,31,-14,2,32,-8,2,33,3,5,34,-8,2,35,-11,2,36,3,5,37,-11,2,38,-12,2,39,3,5,40,-13,2,41,-13,2,42,3,5,43,-1,1,44,4,8,45,-3,1,46,-7,2,47,-8,2,48,4,8,49,-10,2,50,-11,2,51,-12,2,52,4,8,53,-14,2,54,-12,2,55,-13,2,56,4,8,57,-1,1,58,-2,1,59,-7,2,60,5,11,61,-9,2,62,-10,2,63,-11,2,64,-12,2,65,5,11,66,-14,2,67,-11,2,68,-12,2,69,-13,2,70,5,11,71,-1,1,72,6,14,73,-8,2,74,-9,2,75,-10,2,76,-11,2,77,-12,2,78,6,14,79,-14,2,80,-10,2,81,-11,2,82,-12,2,83,-13,2,84,6,14,85,-1,1,86,-2,1,87,-9,2,88,-10,2,89,-11,2,90,-12,2,91,-13,2,92,-14,2,93,-9,2,94,-10,2,95,-11,2,96,-12,2,97,-13,2,98,-14,2,99,-2,1,100,-16,3,101,-23,3,102,-24,3,103,-25,3,104,-26,3,105,-27,3,106,-28,3,107,-42,3,108,-52,3,109,-39,3,110,-26,3,111,-27,3,112,-28,3,113,-48,3,114,-36,3,115,-37,3,116,-56,3,117,-39,3,118,-48,3,119,-41,3,120,-42,3,121,-56,3,122,-52,3,123,-39,3,124,-40,3,125,-60,3,126,-42,3,127,-1,1,128,-44,3,129,-51,3,130,-52,3,131,-53,3,132,-48,3,133,-55,3,134,-56,3,135,-65,3,136,-52,3,137,-65,3,138,-60,3,139,-55,3,140,-70,3,141,-1,1,142,-70,3,143,-65,3,144,-60,3,145,-11,2,146,-12,2,147,-69,3,148,-70,3,149,-65,3,150,-72,3,151,-11,2,152,-12,2,153,-13,2,154,-70,3,155,-1,0,156,-78,3,157,-14,3,158,-10,2,159,-11,2,160,-12,2,161,-13,2,162,-78,3,163,-14,3,164,-10,2,165,-11,2,166,-12,2,167,-13,2,168,-84,3,169,-1,0,170,-2,1,171,-9,2,172,-10,2,173,-11,2,174,-12,2,175,-13,2,176,-14,3,177,-9,2,178,-10,2,179,-11,2,180,-12,2,181,-13,2,182,-14,3,183,-27,3,184,-28,3,185,-42,3,186,-52,3,187,-39,3,188,-26,3,189,-27,3,190,-28,3,191,-42,3,192,-52,3,193,-39,3,194,-26,3,195,3,7,196,-28,3,197,-48,3,198,-42,3,199,-56,3,200,-52,3,201,-39,3,202,-48,3,203,-8,2,204,-42,3,205,-84,4,206,-52,3,207,-39,3,208,-52,3,209,-14,3,210,3,7,211,-1,0,212,-2,1,213,-78,4,214,-84,4,215,-5,2,216,-6,2,217,-7,2,218,-84,4,219,-84,4,220,-84,4,221,-11,2,222,-12,2,223,-13,2,224,4,11,225,-1,0,226,-78,4,227,-84,4,228,-84,4,229,-95,4,230,-96,4,231,-97,4,232,-84,4,233,-84,4,234,3,8,235,-1,0,236,-2,1,237,-27,3,238,-84,4,239,-5,2,240,-84,4,241,-7,2,242,-8,2,243,-9,2,244,-10,2,245,-11,2,246,-84,4,247,-13,2,248,-14,3,249,-39,3,250,-96,4,251,-97,4,252,3,8,253,-1,0,254,-2,1,255,-3,1,256,-4,2,257,-5,2,258,-6,2,259,-7,2,260,-8,2,261,-9,2,262,-10,2,263,-11,2,264,-12,2,265,-13,2,266,-14,3,267,-14,3,268,-14,3,269,-17,3,270,-8,2,271,-8,2,272,-20,3,273,-21,3,274,-22,3,275,-23,3,276,-24,3,277,-25,3,278,-26,3,279,-27,3,280,4,12,281,-1,0,282,-2,1,283,-3,1,284,-4,2,285,-5,2,286,-6,2,287,-7,2,288,-78,4,289,-9,2,290,-10,2,291,-39,3,292,-12,2,293,-13,3,294,-14,3,295,-14,3,296,-44,3,297,-148,4,298,-149,4,299,-65,4,300,-48,3,301,-91,4,302,-148,4,303,-149,4,304,-52,3,305,-25,3,306,-26,3,307,-27,3,308,-56,3,309,-56,3,310,-154,4,311,-149,4,312,4,12,313,-1,0,314,-2,1,315,-63,4,316,-154,4,317,-65,4,318,-84,4,319,-39,3,320,-8,2,321,-69,4,322,-70,4,323,-11,2,324,-12,2,325,-13,3,326,-14,3,327,-75,4,328,-76,4,329,-77,4,330,-78,4,331,-79,4,332,-52,3,333,-81,4,334,-82,4,335,-83,4,336,4,12,337,-1,0,338,-2,1,339,-3,1,340,-4,2,341,-5,2,342,-6,2,343,-7,2,344,-8,2,345,-9,2,346,-10,2,347,-11,2,348,-12,2,349,-13,3,350,5,16,351,-1,0,352,-2,1,353,-3,1,354,-4,2,355,-5,2,356,-20,3,357,-21,3,358,-78,4,359,-23,3,360,-24,3,361,-25,3,362,-26,3,363,-27,3,364,-28,3,365,-28,3,366,-30,3,367,-5,2,368,-56,4,369,-33,3,370,-26,3,371,-91,4,372,-36,3,373,-37,3,374,-38,3,375,-39,3,376,-26,3,377,-27,3,378,-42,3,379,-42,3,380,-44,3,381,-42,3,382,-70,4,383,-8,2,384,-48,3,385,-39,3,386,-134,4,387,-135,4,388,-52,3,389,-39,3,390,5,16,391,-13,3,392,-56,4,393,-56,4,394,-56,4,395,-143,4,396,-84,4,397,-7,2,398,-8,2,399,-9,2,400,-10,2,401,-65,4,402,-12,2,403,-13,3,404,-14,3,405,-69,4,406,-70,4,407,-70,4,408,-72,4,409,-73,4,410,-74,4,411,-75,4,412,-76,4,413,-77,4,414,-78,4,415,-65,4,416,-80,4,417,-81,4,418,-82,4,419,-83,4,420,-84,4,421,-84,4,422,-84,4,423,-87,4,424,-88,4,425,-89,4,426,-90,4,427,-91,4,428,-78,4,429,-93,4,430,-94,4,431,-95,4,432,-96,4,433,-97,4,434,-84,4,435,-84,4,436,-84,4,437,-101,4,438,-102,4,439,-103,4,440,-104,4,441,-105,4,442,-106,4,443,-107,4,444,-108,4,445,-109,4,446,-110,4,447,-111,4,448,-112,4,449,-113,4,450,-114,4,451,-115,4,452,-116,4,453,-117,4,454,-118,4,455,-65,4,456,-120,4,457,-121,4,458,-122,4,459,-123,4,460,-70,4,461,-125,4,462,-126,4,463,-126,4,464,-128,4,465,-75,4,466,-130,4,467,-117,4,468,-78,4,469,-79,4,470,-134,4,471,-135,4,472,-136,4,473,-83,4,474,-84,4,475,-139,4,476,-140,4,477,-140,4,478,-142,4,479,-143,4,480,-144,4,481,-91,4,482,-14,3,483,-147,4,484,-148,4,485,-149,4,486,-234,4,487,-97,4,488,-14,3,489,-153,4,490,-154,4,491,-155,4,492,-156,4,493,-143,4,494,-158,4,495,-159,4,496,-160,4,497,-161,4,498,-78,4,499,-149,4,500,-164,4,501,-81,4,502,-82,4,503,-83,4,504,-84,4,505,-84,4,506,-156,4,507,-84,4,508,-84,4,509,-84,4,510,-84,4,511,-84,4,512,-78,4,513,-84,4,514,-84,4,515,-84,4,516,-84,4,517,-84,4,518,-84,4,519,-78,4,520,-130,4,521,-78,4,522,-210,4,523,-78,4,524,-134,4,525,-135,4,526,-136,4,527,-84,4,528,-84,4,529,-84,4,530,-140,4,531,-195,4,532,-252,4,533,-143,4,534,-78,4,535,-78,4,536,-78,4,537,-84,4,538,-148,4,539,-149,4,540,-84,4,541,-84,4,542,-84,4,543,-84,4,544,-154,4,545,-13,3,546,-210,4,547,-211,4,548,-78,4,549,-78,4,550,-84,4,551,-5,2,552,-78,4,553,-7,2,554,-84,4,555,-84,4,556,-84,4,557,-221,4,558,-84,4,559,-13,3,560,-84,4,561,-84,4,562,-78,4,563,-84,4,564,-252,4,565,-84,4,566,-14,3,567,-97,4,568,-84,4,569,-84,4,570,-234,4,571,-84,4,572,-14,3,573,-27,3,574,-84,4,575,-263,4,576,-84,4,577,-13,3,578,-14,3,579,-234,4,580,-244,4,581,-245,4,582,-78,4,583,-13,3,584,-234,4,585,-210,4,586,-96,4,587,-97,4,588,-252,4,589,-253,4,590,-2,1,591,-255,4,592,-4,2,593,-5,2,594,-6,2,595,-7,2,596,-8,2,597,-9,2,598,-262,4,599,-263,4,600,-264,4,601,-13,3,602,-252,4,603,-253,4,604,-2,1,605,-255,4,606,-4,2,607,-5,2,608,-20,3,609,-21,3,610,-78,4,611,-65,4,612,-24,3,613,-25,3,614,-26,3,615,-27,3,616,-280,4,617,-281,4,618,-2,1,619,-283,4,620,-4,2,621,-5,2,622,-6,2,623,-7,2,624,-78,4,625,-9,2,626,-280,4,627,-39,3,628,-292,4,629,-13,3,630,-280,4,631,-281,4,632,-252,4,633,-78,4,634,-70,4,635,-65,4,636,-300,4,637,-91,4,638,-84,4,639,-84,4,640,-52,4,641,-39,3,642,-252,4,643,-27,3,644,-56,4,645,-56,4,646,-78,4,647,-84,4,648,-312,4,649,-313,4,650,-2,1,651,-9,2,652,-84,4,653,-65,4,654,-84,4,655,-13,3,656,-14,3,657,-69,4,658,-70,4,659,-323,4,660,-72,4,661,-13,3,662,-312,4,663,-75,4,664,-76,4,665,-77,4,666,-78,4,667,-65,4,668,-52,4,669,-81,4,670,-280,4,671,-83,4,672,-336,4,673,-336,4,674,-2,1,675,-336,4,676,-4,2,677,-5,2,678,-6,2,679,-7,2,680,-8,2,681,-9,2,682,-10,2,683,-336,4,684,-336,4,685,-13,3,686,-336,4,687,-337,4,688,-2,1,689,-339,4,690,-4,2,691,-5,2,692,-20,3,693,-21,3,694,-78,4,695,-23,3,696,-24,3,697,-25,3,698,-26,3,699,-27,3,700,-28,3,701,-28,3,702,-312,4,703,-5,2,704,-56,4,705,-33,3,706,-90,4,707,-65,4,708,-36,3,709,-37,3,710,-38,3,711,-39,3,712,-26,3,713,-27,3,714,-42,3,715,-42,3,716,-44,3,717,-42,3,718,-70,4,719,-8,2,720,-48,3,721,-39,3,722,-134,4,723,-135,4,724,-52,4,725,-39,3,726,-336,4,727,-55,4,728,-56,4,729,-56,4,730,-56,4,731,-143,4,732,-84,4,733,-343,5,734,-344,5,735,-345,5,736,-346,5,737,-65,4,738,-348,5,739,-13,3,740,-350,5,741,-69,4,742,-70,4,743,-70,4,744,-72,4,745,-73,4,746,-74,4,747,-75,4,748,-76,4,749,-77,4,750,-78,4,751,-65,4,752,-80,4,753,-81,4,754,-82,4,755,-83,4,756,-84,4,757,-84,4,758,-84,4,759,-87,4,760,-88,4,761,-89,4,762,-90,4,763,-91,4,764,-78,4,765,-93,4,766,-94,4,767,-95,4,768,-96,4,769,-97,4,770,-84,4,771,-84,4,772,-84,4,773,-78,4,774,-78,4,775,-103,4,776,-104,4,777,-105,4,778,-106,4,779,-107,4,780,-390,5,781,-109,4,782,-110,4,783,-111,4,784,-112,4,785,-113,4,786,-114,4,787,-115,4,788,-116,4,789,-117,4,790,-118,4,791,-390,5,792,-120,4,793,-121,4,794,-122,4,795,-123,4,796,-390,5,797,-125,4,798,-126,4,799,-126,4,800,-128,4,801,-78,4,802,-336,5,803,-350,5,804,-390,5,805,-390,5,806,-336,5,807,-336,5,808,-336,5,809,-390,5,810,-390,5,811,-336,5,812,-336,5,813,-337,5,814,-336,5,815,-336,5,816,-336,5,817,-390,5,818,-390,5,819,-336,5,820,-336,5,821,-336,5,822,-336,5,823,-390,5,824,-390,5,825,-336,5,826,-336,5,827,-336,5,828,-78,4,829,-350,5,830,-336,5,831,-78,4,832,-78,4,833,-78,4,834,-78,4,835,-350,5,836,-80,4,837,-81,4,838,-82,4,839,-83,4,840,-84,4,841,-84,4,842,-350,5,843,-84,4,844,-84,4,845,-84,4,846,-84,4,847,-84,4,848,-78,4,849,-84,4,850,-84,4,851,-84,4,852,-84,4,853,-84,4,854,-84,4,855,-78,4,856,-390,5,857,-78,4,858,-336,5,859,-78,4,860,-390,5,861,-390,5,862,-390,5,863,-84,4,864,-390,5,865,-420,5,866,-390,5,867,-336,5,868,-336,5,869,-390,5,870,-414,5,871,-414,5,872,-414,5,873,-420,5,874,-390,5,875,-390,5,876,-420,5,877,-420,5,878,-420,5,879,-420,5,880,-390,5,881,-13,3,882,-336,5,883,-336,5,884,-414,5,885,-414,5,886,-420,5,887,-341,5,888,-420,5,889,-343,5,890,-420,5,891,-420,5,892,-420,5,893,-347,5,894,-420,5,895,-13,3,896,-420,5,897,-421,5,898,-414,5,899,-420,5,900,-336,5,901,-427,5,902,-434,5,903,-433,5,904,-420,5,905,-420,5,906,-336,5,907,-433,5,908,-434,5,909,-363,5,910,-420,5,911,-347,5,912,-420,5,913,-13,3,914,-350,5,915,-345,5,916,-346,5,917,-347,5,918,-78,4,919,-13,3,920,-350,5,921,-375,5,922,-390,5,923,-433,5,924,-336,5,925,-337,5,926,-338,5,927,-339,5,928,-340,5,929,-341,5,930,-342,5,931,-343,5,932,-344,5,933,-345,5,934,-346,5,935,-347,5,936,-348,5,937,-13,3,938,-350,5,939,-351,5,940,-352,5,941,-353,5,942,-354,5,943,-355,5,944,-356,5,945,-357,5,946,-414,5,947,-401,5,948,-360,5,949,-361,5,950,-362,5,951,-363,5,952,-336,5,953,-337,5,954,-338,5,955,-339,5,956,-340,5,957,-341,5,958,-342,5,959,-343,5,960,-414,5,961,-345,5,962,-346,5,963,-375,5,964,-348,5,965,-13,3,966,-350,5,967,-351,5,968,-380,5,969,-484,5,970,-406,5,971,-336,5,972,-384,5,973,-427,5,974,-470,5,975,-471,5,976,-388,5,977,-389,5,978,-390,5,979,-363,5,980,-56,4,981,-56,4,982,-484,5,983,-479,5,984,-336,5,985,-337,5,986,-338,5,987,-399,5,988,-484,5,989,-336,5,990,-420,5,991,-13,3,992,-390,5,993,-405,5,994,-406,5,995,-347,5,996,-408,5,997,-13,3,998,-350,5,999,-411,5,1000,-412,5,1001,-413,5,1002,-414,5,1003,-350,5,1004,-388,5,1005,-417,5,1006,-390,5,1007,-419,5,1008,3,10,1009,-1,0,1010,-2,1,1011,-3,1,1012,-4,2,1013,-5,2,1014,-6,2,1015,-7,2,1016,-8,2,1017,-9,2,1018,-10,2,1019,-11,2,1020,-12,2,1021,-13,3,1022,-14,3,1023,-14,3,1024,-14,3,1025,-17,3,1026,-13,3,1027,-19,3,1028,-20,3,1029,-21,3,1030,-22,3,1031,-23,3,1032,-24,3,1033,-25,3,1034,-26,3,1035,-27,3,1036,-28,3,1037,-28,3,1038,-30,3,1039,-5,2,1040,-5,2,1041,-33,3,1042,-26,3,1043,-27,3,1044,-36,3,1045,-37,3,1046,-38,3,1047,-39,3,1048,-40,3,1049,-14,3,1050,-42,3,1051,-42,3,1052,-44,3,1053,-42,3,1054,-7,2,1055,-8,2,1056,-48,3,1057,-39,3,1058,-39,3,1059,-51,4,1060,-52,4,1061,-14,3,1062,-390,5,1063,-55,4,1064,-56,4,1065,-56,4,1066,-56,4,1067,-59,4,1068,-60,4,1069,-61,4,1070,-62,4,1071,-63,4,1072,-64,4,1073,-65,4,1074,-66,4,1075,-67,4,1076,-68,4,1077,-69,4,1078,-70,4,1079,-70,4,1080,-72,4,1081,-73,4,1082,-74,4,1083,-75,4,1084,-76,4,1085,-77,4,1086,-78,4,1087,-79,4,1088,-80,4,1089,-81,4,1090,-82,4,1091,-83,4,1092,-84,4,1093,-84,4,1094,-84,4,1095,-87,4,1096,-88,4,1097,-89,4,1098,-90,4,1099,-91,4,1100,-14,3,1101,-93,4,1102,-94,4,1103,-95,4,1104,-96,4,1105,-97,4,1106,-14,3,1107,-14,3,1108,-14,3,1109,-78,4,1110,-78,4,1111,-78,4,1112,-104,4,1113,-105,4,1114,-106,4,1115,-107,4,1116,-108,4,1117,-109,4,1118,-84,4,1119,-111,4,1120,-112,4,1121,-113,4,1122,-114,4,1123,-115,4,1124,-116,4,1125,-117,4,1126,-118,4,1127,-119,4,1128,-120,4,1129,-121,4,1130,-122,4,1131,-123,4,1132,-124,4,1133,-125,4,1134,-126,4,1135,-126,4,1136,-128,4,1137,-129,4,1138,-130,4,1139,-14,3,1140,-132,4,1141,-133,4,1142,-134,4,1143,-135,4,1144,-136,4,1145,-137,4,1146,-138,4,1147,-139,4,1148,-140,4,1149,-140,4,1150,-142,4,1151,-143,4,1152,-144,4,1153,-145,4,1154,-68,4,1155,-69,4,1156,-70,4,1157,-65,4,1158,-72,4,1159,-70,4,1160,-70,4,1161,-70,4,1162,-70,4,1163,-70,4,1164,-78,4,1165,-78,4,1166,-78,4,1167,-78,4,1168,-78,4,1169,-78,4,1170,-78,4,1171,-79,4,1172,-80,4,1173,-81,4,1174,-82,4,1175,-83,4,1176,-84,4,1177,-84,4,1178,-84,4,1179,-84,4,1180,-84,4,1181,-84,4,1182,-84,4,1183,-84,4,1184,-14,3,1185,-84,4,1186,-84,4,1187,-84,4,1188,-84,4,1189,-84,4,1190,-14,3,1191,-78,4,1192,-78,4,1193,-78,4,1194,-78,4,1195,-78,4,1196,-84,4,1197,-84,4,1198,-84,4,1199,-84,4,1200,-84,4,1201,-84,4,1202,-84,4,1203,-195,4,1204,-84,4,1205,-84,4,1206,-78,4,1207,-78,4,1208,-78,4,1209,-84,4,1210,-84,4,1211,-8,2,1212,-84,4,1213,-84,4,1214,-84,4,1215,-84,4,1216,-78,4,1217,-14,3,1218,-210,4,1219,-211,4,1220,-78,4,1221,-78,4,1222,-84,4,1223,-5,2,1224,-84,4,1225,-7,2,1226,-84,4,1227,-84,4,1228,-84,4,1229,-210,4,1230,-222,5,1231,-13,3,1232,-84,4,1233,-84,4,1234,-70,4,1235,-84,4,1236,-84,4,1237,-95,4,1238,-96,4,1239,-97,4,1240,-70,4,1241,-65,4,1242,-234,5,1243,-235,5,1244,-236,5,1245,-70,4,1246,-70,4,1247,-239,5,1248,-78,4,1249,-241,5,1250,-242,5,1251,-243,5,1252,-244,5,1253,-245,5,1254,-78,4,1255,-247,5,1256,-248,5,1257,-81,4,1258,-82,4,1259,-83,4,1260,-252,5,1261,-253,5,1262,-254,5,1263,-255,5,1264,-256,5,1265,-257,5,1266,-258,5,1267,-259,5,1268,-260,5,1269,-261,5,1270,-262,5,1271,-263,5,1272,-264,5,1273,-265,5,1274,-266,5,1275,-267,5,1276,-268,5,1277,-269,5,1278,-270,5,1279,-271,5,1280,-272,5,1281,-273,5,1282,-274,5,1283,-65,4,1284,-276,5,1285,-277,5,1286,-278,5,1287,-279,5,1288,-280,5,1289,-281,5,1290,-282,5,1291,-283,5,1292,-284,5,1293,-285,5,1294,-286,5,1295,-287,5,1296,-78,4,1297,-289,5,1298,-290,5,1299,-291,5,1300,-292,5,1301,-13,3,1302,-280,5,1303,-14,3,1304,-296,5,1305,-78,4,1306,-84,4,1307,-299,5,1308,-300,5,1309,-301,5,1310,-84,4,1311,-84,4,1312,-304,5,1313,-305,5,1314,-306,5,1315,-307,5,1316,-308,5,1317,-309,5,1318,-70,4,1319,-84,4,1320,-312,5,1321,-313,5,1322,-314,5,1323,-252,5,1324,-70,4,1325,-317,5,1326,-318,5,1327,-319,5,1328,-320,5,1329,-321,5,1330,-322,5,1331,-323,5,1332,-324,5,1333,-13,3,1334,-14,3,1335,-327,5,1336,-328,5,1337,-329,5,1338,-330,5,1339,-331,5,1340,-332,5,1341,-333,5,1342,-334,5,1343,-335,5,1344,4,15,1345,-1,0,1346,-2,1,1347,-3,1,1348,-4,2,1349,-5,2,1350,-6,2,1351,-7,2,1352,-8,2,1353,-9,2,1354,-10,2,1355,-11,2,1356,-12,2,1357,-13,3,1358,-14,3,1359,-14,3,1360,-14,3,1361,-17,3,1362,-13,3,1363,-19,3,1364,-20,3,1365,-21,3,1366,-22,3,1367,-23,3,1368,-24,3,1369,-25,3,1370,-26,3,1371,-27,3,1372,-28,3,1373,-28,3,1374,-30,3,1375,-5,2,1376,-5,2,1377,-33,3,1378,-26,3,1379,-27,3,1380,-36,3,1381,-37,3,1382,-38,3,1383,-39,3,1384,-14,3,1385,-14,3,1386,-42,3,1387,-42,3,1388,-44,3,1389,-42,3,1390,-7,2,1391,-8,2,1392,-48,3,1393,-39,3,1394,-39,3,1395,-51,4,1396,-52,4,1397,-14,3,1398,-390,5,1399,-55,4,1400,-56,4,1401,-56,4,1402,-56,4,1403,-59,4,1404,-60,4,1405,-61,4,1406,-62,4,1407,-63,4,1408,-64,4,1409,-65,4,1410,-66,4,1411,-67,4,1412,-68,4,1413,-69,4,1414,-70,4,1415,-70,4,1416,-72,4,1417,-73,4,1418,-74,4,1419,-75,4,1420,-76,4,1421,-77,4,1422,-78,4,1423,-79,4,1424,-80,4,1425,-81,4,1426,-82,4,1427,-83,4,1428,-84,4,1429,-84,4,1430,-84,4,1431,-87,4,1432,-88,4,1433,-89,4,1434,-90,4,1435,-91,4,1436,-14,3,1437,-93,4,1438,-94,4,1439,-95,4,1440,-96,4,1441,-97,4,1442,-14,3,1443,-14,3,1444,-14,3,1445,-78,4,1446,-78,4,1447,-78,4,1448,-104,4,1449,-105,4,1450,-106,4,1451,-107,4,1452,-108,4,1453,-109,4,1454,-84,4,1455,-84,4,1456,-112,4,1457,-113,4,1458,-114,4,1459,-115,4,1460,-116,4,1461,-117,4,1462,-118,4,1463,-119,4,1464,-120,4,1465,-121,4,1466,-122,4,1467,-123,4,1468,-124,4,1469,-125,4,1470,-126,4,1471,-126,4,1472,-128,4,1473,-129,4,1474,-130,4,1475,-14,3,1476,-132,4,1477,-133,4,1478,-134,4,1479,-135,4,1480,-136,4,1481,-137,4,1482,-138,4,1483,-139,4,1484,-140,4,1485,-140,4,1486,-70,4,1487,-65,4,1488,-144,4,1489,-67,4,1490,-68,4,1491,-69,4,1492,-70,4,1493,-65,4,1494,-72,4,1495,-70,4,1496,-70,4,1497,-70,4,1498,-70,4,1499,-70,4,1500,-78,4,1501,-78,4,1502,-78,4,1503,-78,4,1504,-78,4,1505,-78,4,1506,-78,4,1507,-79,4,1508,-80,4,1509,-81,4,1510,-82,4,1511,-83,4,1512,-84,4,1513,-84,4,1514,-84,4,1515,-84,4,1516,-84,4,1517,-84,4,1518,-84,4,1519,-84,4,1520,-14,3,1521,-84,4,1522,-84,4,1523,-84,4,1524,-84,4,1525,-84,4,1526,-14,3,1527,-78,4,1528,-78,4,1529,-78,4,1530,-78,4,1531,-78,4,1532,-84,4,1533,-84,4,1534,-84,4,1535,-84,4,1536,-84,4,1537,-84,4,1538,-84,4,1539,-195,4,1540,-84,4,1541,-84,4,1542,-78,4,1543,-78,4,1544,-78,4,1545,-84,4,1546,-84,4,1547,-8,2,1548,-84,4,1549,-84,4,1550,-84,4,1551,-84,4,1552,-78,4,1553,-14,3,1554,-210,5,1555,-211,5,1556,-78,4,1557,-78,4,1558,-84,4,1559,-215,5,1560,-84,4,1561,-217,5,1562,-84,4,1563,-84,4,1564,-84,4,1565,-221,5,1566,-222,5,1567,-223,5,1568,-84,4,1569,-84,4,1570,-70,4,1571,-65,4,1572,-84,4,1573,-95,4,1574,-70,4,1575,-70,4,1576,-70,4,1577,-65,4,1578,-234,5,1579,-235,5,1580,-236,5,1581,-70,4,1582,-70,4,1583,-239,5,1584,-78,4,1585,-241,5,1586,-242,5,1587,-243,5,1588,-244,5,1589,-245,5,1590,-78,4,1591,-247,5,1592,-248,5,1593,-81,4,1594,-82,4,1595,-83,4,1596,-252,5,1597,-253,5,1598,-254,5,1599,-255,5,1600,-256,5,1601,-257,5,1602,-258,5,1603,-259,5,1604,-260,5,1605,-261,5,1606,-262,5,1607,-263,5,1608,-264,5,1609,-265,5,1610,-252,5,1611,-253,5,1612,-254,5,1613,-269,5,1614,-270,5,1615,-271,5,1616,-272,5,1617,-273,5,1618,-274,5,1619,-275,5,1620,-276,5,1621,-277,5,1622,-278,5,1623,-279,5,1624,-280,5,1625,-281,5,1626,-282,5,1627,-283,5,1628,-284,5,1629,-285,5,1630,-286,5,1631,-287,5,1632,-288,5,1633,-289,5,1634,-290,5,1635,-291,5,1636,-292,5,1637,-13,3,1638,-14,3,1639,-14,3,1640,-296,5,1641,-78,4,1642,-84,4,1643,-299,5,1644,-300,5,1645,-301,5,1646,-84,4,1647,-84,4,1648,-304,5,1649,-305,5,1650,-306,5,1651,-307,5,1652,-308,5,1653,-309,5,1654,-70,4,1655,-65,4,1656,-312,5,1657,-313,5,1658,-314,5,1659,-252,5,1660,-70,4,1661,-252,5,1662,-318,5,1663,-319,5,1664,-320,5,1665,-321,5,1666,-322,5,1667,-323,5,1668,-324,5,1669,-13,3,1670,-14,3,1671,-327,5,1672,-328,5,1673,-329,5,1674,-330,5,1675,-331,5,1676,-332,5,1677,-333,5,1678,-334,5,1679,-335,5,1680,-336,5,1681,-337,5,1682,-338,5,1683,-339,5,1684,-4,2,1685,-341,5,1686,-342,5,1687,-343,5,1688,-344,5,1689,-345,5,1690,-346,5,1691,-347,5,1692,-348,5,1693,-13,3,1694,-350,5,1695,-351,5,1696,-352,5,1697,-353,5,1698,-4,2,1699,-355,5,1700,-356,5,1701,-357,5,1702,-358,5,1703,-359,5,1704,-360,5,1705,-361,5,1706,-362,5,1707,-363,5,1708,-364,5,1709,-365,5,1710,-366,5,1711,-367,5,1712,-56,4,1713,-369,5,1714,-370,5,1715,-371,5,1716,-372,5,1717,-373,5,1718,-374,5,1719,-375,5,1720,-376,5,1721,-377,5,1722,-378,5,1723,-379,5,1724,-380,5,1725,-381,5,1726,-312,5,1727,-383,5,1728,-384,5,1729,-385,5,1730,-386,5,1731,-387,5,1732,-388,5,1733,-389,5,1734,-390,5,1735,-55,4,1736,-56,4,1737,-56,4,1738,-56,4,1739,-395,5,1740,-60,4,1741,-397,5,1742,-398,5,1743,-399,5,1744,-400,5,1745,-65,4,1746,-402,5,1747,-13,3,1748,-14,3,1749,-69,4,1750,-70,4,1751,-70,4,1752,-336,5,1753,-336,5,1754,-336,5,1755,-336,5,1756,-336,5,1757,-336,5,1758,-336,5,1759,-65,4,1760,-336,5,1761,-336,5,1762,-336,5,1763,-336,5,1764,-336,5,1765,-337,5,1766,-338,5,1767,-423,5,1768,-424,5,1769,-425,5,1770,-426,5,1771,-427,5,1772,-350,5,1773,-429,5,1774,-430,5,1775,-431,5,1776,-432,5,1777,-433,5,1778,-350,5,1779,-351,5,1780,-352,5,1781,-359,5,1782,-360,5,1783,-439,5,1784,-440,5,1785,-441,5,1786,-442,5,1787,-443,5,1788,-444,5,1789,-445,5,1790,-446,5,1791,-447,5,1792,-448,5,1793,-449,5,1794,-450,5,1795,-451,5,1796,-452,5,1797,-453,5,1798,-454,5,1799,-65,4,1800,-456,5,1801,-457,5,1802,-458,5,1803,-459,5,1804,-70,4,1805,-461,5,1806,-462,5,1807,-463,5,1808,-464,5,1809,-75,4,1810,-466,5,1811,-467,5,1812,-78,4,1813,-79,4,1814,-470,5,1815,-471,5,1816,-472,5,1817,-390,5,1818,-390,5,1819,-475,5,1820,-476,5,1821,-477,5,1822,-478,5,1823,-479,5,1824,-480,5,1825,-390,5,1826,-14,3,1827,-483,5,1828,-484,5,1829,-485,5,1830,-486,5,1831,-487,5,1832,-14,3,1833,-489,5,1834,-490,5,1835,-491,5,1836,-336,5,1837,-493,5,1838,-494,5,1839,-495,5,1840,-336,5,1841,-336,5,1842,-336,5,1843,-499,5,1844,-336,5,1845,-336,5,1846,-336,5,1847,-336,5,1848,-336,5,1849,-337,5,1850,-350,5,1851,-84,4,1852,-84,4,1853,-84,4,1854,-84,4,1855,-84,4,1856,-350,5,1857,-84,4,1858,-84,4,1859,-84,4,1860,-84,4,1861,-84,4,1862,-350,5,1863,-78,4,1864,-520,5,1865,-78,4,1866,-522,5,1867,-78,4,1868,-524,5,1869,-525,5,1870,-526,5,1871,-84,4,1872,-84,4,1873,-84,4,1874,-530,5,1875,-531,5,1876,-532,5,1877,-533,5,1878,-78,4,1879,-78,4,1880,-78,4,1881,-84,4,1882,-538,5,1883,-539,5,1884,-84,4,1885,-84,4,1886,-84,4,1887,-84,4,1888,-70,4,1889,-13,3,1890,-546,5,1891,-547,5,1892,-78,4,1893,-78,4,1894,-84,4,1895,-5,2,1896,-78,4,1897,-553,5,1898,-84,4,1899,-84,4,1900,-84,4,1901,-557,5,1902,-390,5,1903,-13,3,1904,-84,4,1905,-84,4,1906,-78,4,1907,-84,4,1908,-564,5,1909,-390,5,1910,-14,3,1911,-91,4,1912,-84,4,1913,-84,4,1914,-570,5,1915,-84,4,1916,-14,3,1917,-573,5,1918,-84,4,1919,-575,5,1920,-336,5,1921,-13,3,1922,-14,3,1923,-579,5,1924,-580,5,1925,-581,5,1926,-336,5,1927,-13,3,1928,-584,5,1929,-585,5,1930,-336,5,1931,-336,5,1932,-588,5,1933,-589,5,1934,-590,5,1935,-591,5,1936,-4,2,1937,-5,2,1938,-6,2,1939,-595,5,1940,-596,5,1941,-597,5,1942,-598,5,1943,-599,5,1944,-600,5,1945,-13,3,1946,-602,5,1947,-603,5,1948,-604,5,1949,-605,5,1950,-4,2,1951,-5,2,1952,-20,3,1953,-21,3,1954,-78,4,1955,-65,4,1956,-24,3,1957,-25,3,1958,-26,3,1959,-615,5,1960,-616,5,1961,-617,5,1962,-618,5,1963,-619,5,1964,-4,2,1965,-5,2,1966,-6,2,1967,-623,5,1968,-78,4,1969,-289,5,1970,-290,5,1971,-291,5,1972,-292,5,1973,-13,3,1974,-280,5,1975,-281,5,1976,-296,5,1977,-78,4,1978,-70,4,1979,-65,4,1980,-300,5,1981,-91,4,1982,-84,4,1983,-84,4,1984,-52,4,1985,-291,5,1986,-642,5,1987,-307,5,1988,-56,4,1989,-56,4,1990,-78,4,1991,-84,4,1992,-312,5,1993,-313,5,1994,-314,5,1995,-63,4,1996,-84,4,1997,-65,4,1998,-84,4,1999,-13,3,2000,-14,3,2001,-69,4,2002,-70,4,2003,-323,5,2004,-324,5,2005,-13,3,2006,-312,5,2007,-75,4,2008,-76,4,2009,-77,4,2010,-78,4,2011,-65,4,2012,-332,5,2013,-81,4,2014,-280,5,2015,-83,4,2016,-336,5,2017,-336,5,2018,-336,5,2019,-336,5,2020,-4,2,2021,-336,5,2022,-336,5,2023,-336,5,2024,-336,5,2025,-336,5,2026,-336,5,2027,-336,5,2028,-336,5,2029,-13,3,2030,-336,5,2031,-337,5,2032,-338,5,2033,-339,5,2034,-4,2,2035,-336,5,2036,-336,5,2037,-336,5,2038,-78,4,2039,-336,5,2040,-336,5,2041,-336,5,2042,-336,5,2043,-336,5,2044,-336,5,2045,-337,5,2046,-312,5,2047,-341,5,0,0,0"), // leafSize 15 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,14,16,15,15,18,16,-1,1,17,-2,1,18,-3,1,19,-4,1,20,-10,2,21,-8,2,22,-11,2,23,-10,2,24,-11,2,25,-10,2,26,-13,2,27,-13,2,28,-13,2,29,-14,2,30,-15,2,31,-1,1,32,-15,2,33,3,5,34,-8,2,35,-5,2,36,3,5,37,-11,2,38,-10,2,39,3,5,40,-10,2,41,-13,2,42,3,5,43,-13,2,44,-14,2,45,3,5,46,-1,1,47,-2,1,48,4,8,49,-10,2,50,-11,2,51,-12,2,52,4,8,53,-8,2,54,-15,2,55,-10,2,56,4,8,57,-12,2,58,-13,2,59,-14,2,60,4,8,61,-1,1,62,-2,1,63,-3,1,64,-4,2,65,5,11,66,-14,2,67,-7,2,68,-8,2,69,-9,2,70,5,11,71,-11,2,72,-12,2,73,-13,2,74,-14,2,75,5,11,76,-1,1,77,-2,1,78,6,14,79,-4,2,80,-5,2,81,-6,2,82,-7,2,83,-8,2,84,6,14,85,-10,2,86,-11,2,87,-12,2,88,-13,2,89,-14,2,90,6,15,91,7,17,92,-2,1,93,-3,1,94,-4,2,95,-5,2,96,-6,2,97,-7,2,98,7,17,99,-9,2,100,-10,2,101,-11,2,102,-12,2,103,-13,2,104,-14,2,105,-15,3,106,-15,3,107,-9,2,108,-10,2,109,-11,2,110,-12,2,111,-13,2,112,-14,3,113,-15,3,114,-24,3,115,-25,3,116,-26,3,117,-27,3,118,-28,3,119,-29,3,120,-30,3,121,-30,3,122,-52,3,123,-45,3,124,-26,3,125,-60,3,126,-36,3,127,-52,3,128,-30,3,129,-39,3,130,-60,3,131,-56,3,132,-42,3,133,-43,3,134,-44,3,135,-45,3,136,-45,3,137,-39,3,138,-60,3,139,-49,3,140,-65,3,141,-51,3,142,-52,3,143,-45,3,144,-60,3,145,-70,3,146,-56,3,147,-57,3,148,-58,3,149,-59,3,150,-60,3,151,-60,3,152,-2,1,153,-75,3,154,-70,3,155,-65,3,156,-65,3,157,-7,2,158,-60,3,159,-75,3,160,-70,3,161,-11,2,162,-12,2,163,-13,2,164,-14,3,165,-75,3,166,-75,3,167,-2,1,168,-78,3,169,-78,3,170,-15,3,171,-6,2,172,-7,2,173,-75,3,174,-84,3,175,-10,2,176,-11,2,177,-12,2,178,-13,2,179,-14,3,180,-90,3,181,-90,3,182,-84,3,183,-3,1,184,-4,2,185,-5,2,186,-6,2,187,-7,2,188,-90,4,189,-9,2,190,-10,2,191,-11,2,192,-12,2,193,-90,4,194,-14,3,195,-15,3,196,-98,4,197,-98,4,198,-98,4,199,-98,4,200,-98,4,201,-98,4,202,-98,4,203,-15,3,204,-24,3,205,-25,3,206,-26,3,207,-90,4,208,-90,4,209,-90,4,210,-90,4,211,-15,3,212,-90,4,213,-90,4,214,-98,4,215,-90,4,216,-90,4,217,-90,4,218,-98,4,219,-90,4,220,-90,4,221,-90,4,222,-90,4,223,-90,4,224,-90,4,225,3,8,226,-1,0,227,-2,1,228,-90,4,229,-4,2,230,-90,4,231,-6,2,232,-90,4,233,-8,2,234,-9,2,235,-10,2,236,-11,2,237,-12,2,238,-13,2,239,-14,3,240,-90,4,241,-91,4,242,-98,4,243,-90,4,244,-90,4,245,-90,4,246,-91,4,247,-90,4,248,-98,4,249,-90,4,250,-90,4,251,-26,3,252,3,8,253,-28,3,254,-29,3,255,-90,4,256,-91,4,257,-98,4,258,-90,4,259,-91,4,260,-15,3,261,-90,4,262,-90,4,263,-98,4,264,-90,4,265,-100,4,266,-101,4,267,-102,4,268,-103,4,269,-14,3,270,3,8,271,-1,0,272,-2,1,273,-3,1,274,-4,2,275,-5,2,276,-6,2,277,-7,2,278,-8,2,279,-9,2,280,-10,2,281,-11,2,282,-12,2,283,-13,3,284,-14,3,285,-15,3,286,-15,3,287,-15,3,288,-18,3,289,-19,3,290,-15,3,291,-21,3,292,-22,3,293,-23,3,294,3,8,295,-25,3,296,-26,3,297,-27,3,298,-28,3,299,-29,3,300,4,12,301,-1,0,302,-2,1,303,-3,1,304,-4,2,305,-5,2,306,-6,2,307,-7,2,308,-8,2,309,-39,3,310,-10,2,311,-11,2,312,-42,3,313,-13,3,314,-14,3,315,-45,3,316,-91,4,317,-2,1,318,-48,3,319,-4,2,320,-5,2,321,-21,3,322,-52,3,323,-98,4,324,-15,3,325,-25,3,326,-56,3,327,-27,3,328,-28,3,329,-29,3,330,-60,4,331,-60,4,332,-60,4,333,-60,4,334,-60,4,335,-65,4,336,4,12,337,-15,3,338,-68,4,339,-39,3,340,-70,4,341,-71,4,342,-72,4,343,-73,4,344,-74,4,345,-75,4,346,-75,4,347,-75,4,348,-78,4,349,-75,4,350,-75,4,351,-75,4,352,-75,4,353,-83,4,354,-84,4,355,-85,4,356,-86,4,357,-87,4,358,-88,4,359,-89,4,360,4,12,361,-1,0,362,-2,1,363,-3,1,364,-4,2,365,-5,2,366,-6,2,367,-7,2,368,-8,2,369,-9,2,370,-10,2,371,-11,2,372,-12,2,373,-13,3,374,-14,3,375,-15,3,376,-15,3,377,-15,3,378,-18,3,379,-15,3,380,-15,3,381,-21,3,382,-22,3,383,-23,3,384,-24,3,385,-25,3,386,-26,3,387,-27,3,388,-28,3,389,-29,3,390,-30,3,391,-91,4,392,4,12,393,-33,3,394,-4,2,395,-5,2,396,-36,3,397,-7,2,398,-98,4,399,-39,3,400,-30,3,401,-15,3,402,-42,3,403,-15,3,404,-15,3,405,-45,3,406,-45,3,407,-2,1,408,-48,3,409,-4,2,410,-5,2,411,-6,2,412,-52,3,413,-8,2,414,-15,3,415,-10,2,416,-56,4,417,-57,4,418,-58,4,419,-14,3,420,-60,4,421,-60,4,422,-60,4,423,-60,4,424,-60,4,425,-65,4,426,-66,4,427,-67,4,428,-68,4,429,-69,4,430,-70,4,431,-71,4,432,-72,4,433,-73,4,434,-74,4,435,-75,4,436,-75,4,437,-75,4,438,-78,4,439,-75,4,440,-75,4,441,-75,4,442,-75,4,443,-83,4,444,-84,4,445,-85,4,446,-86,4,447,-87,4,448,-88,4,449,-89,4,450,5,17,451,-1,0,452,-2,1,453,-3,1,454,-4,2,455,-5,2,456,-6,2,457,-7,2,458,-8,2,459,-9,2,460,-10,2,461,-11,2,462,-12,2,463,-13,3,464,-14,3,465,-15,3,466,-15,3,467,-15,3,468,-18,3,469,-15,3,470,-15,3,471,-21,3,472,-22,3,473,-23,3,474,-24,3,475,-25,3,476,-26,3,477,-27,3,478,-28,3,479,-29,3,480,-30,3,481,-30,3,482,-30,3,483,-33,3,484,-4,2,485,-5,2,486,-36,3,487,-7,2,488,-30,3,489,-39,3,490,-30,3,491,-15,3,492,-42,3,493,-15,3,494,-15,3,495,-45,3,496,-45,3,497,-2,1,498,-48,3,499,-4,2,500,-140,4,501,-6,2,502,-52,3,503,-8,2,504,-15,3,505,-10,2,506,-56,4,507,-57,4,508,-58,4,509,-59,4,510,-60,4,511,-60,4,512,-60,4,513,-60,4,514,-60,4,515,-65,4,516,-66,4,517,-67,4,518,-68,4,519,-69,4,520,-70,4,521,-71,4,522,-72,4,523,-73,4,524,-74,4,525,-75,4,526,-75,4,527,-75,4,528,-78,4,529,-4,2,530,-75,4,531,-75,4,532,-75,4,533,-83,4,534,-84,4,535,-85,4,536,-86,4,537,-87,4,538,-88,4,539,-89,4,540,-90,4,541,-91,4,542,-90,4,543,-90,4,544,-4,2,545,-90,4,546,-90,4,547,-90,4,548,-98,4,549,-99,4,550,-100,4,551,-101,4,552,-102,4,553,-103,4,554,-14,3,555,-15,3,556,-15,3,557,-98,4,558,-108,4,559,-109,4,560,-110,4,561,-111,4,562,-14,3,563,-15,3,564,-90,4,565,-90,4,566,-116,4,567,-117,4,568,-118,4,569,-119,4,570,-120,4,571,-121,4,572,-122,4,573,-123,4,574,-98,4,575,-125,4,576,-126,4,577,-127,4,578,-128,4,579,-129,4,580,-130,4,581,-131,4,582,-132,4,583,-133,4,584,-134,4,585,-135,4,586,-136,4,587,-137,4,588,-138,4,589,-4,2,590,-140,4,591,-6,2,592,-142,4,593,-143,4,594,-144,4,595,-145,4,596,-146,4,597,-147,4,598,-148,4,599,-14,3,600,-150,4,601,-151,4,602,-152,4,603,-153,4,604,-154,4,605,-155,4,606,-78,4,607,-15,3,608,-158,4,609,-75,4,610,-70,4,611,-161,4,612,-72,4,613,-73,4,614,-14,3,615,-75,4,616,-75,4,617,-75,4,618,-78,4,619,-78,4,620,-15,3,621,-75,4,622,-75,4,623,-75,4,624,-84,4,625,-85,4,626,-86,4,627,-87,4,628,-88,4,629,-14,3,630,-90,4,631,-90,4,632,-84,4,633,-90,4,634,-4,2,635,-90,4,636,-90,4,637,-90,4,638,-90,4,639,-90,4,640,-90,4,641,-90,4,642,-90,4,643,-90,4,644,-14,3,645,-15,3,646,-98,4,647,-98,4,648,-98,4,649,-98,4,650,-98,4,651,-98,4,652,-14,3,653,-15,3,654,-294,4,655,-90,4,656,-90,4,657,-90,4,658,-90,4,659,-90,4,660,-300,4,661,-15,3,662,-2,1,663,-90,4,664,-4,2,665,-90,4,666,-90,4,667,-90,4,668,-8,2,669,-90,4,670,-10,2,671,-311,4,672,-90,4,673,-13,3,674,-14,3,675,-225,4,676,-226,4,677,-2,1,678,-90,4,679,-4,2,680,-90,4,681,-6,2,682,-90,4,683,-8,2,684,-9,2,685,-235,4,686,-90,4,687,-237,4,688,-13,3,689,-14,3,690,-90,4,691,-91,4,692,-98,4,693,-90,4,694,-90,4,695,-90,4,696,-336,4,697,-15,3,698,-98,4,699,-75,4,700,-70,4,701,-101,4,702,-252,4,703,-73,4,704,-14,3,705,-75,4,706,-75,4,707,-75,4,708,-78,4,709,-78,4,710,-15,3,711,-75,4,712,-75,4,713,-75,4,714,-84,4,715,-85,4,716,-86,4,717,-87,4,718,-88,4,719,-14,3,720,-360,4,721,-360,4,722,-2,1,723,-360,4,724,-4,2,725,-5,2,726,-6,2,727,-7,2,728,-360,5,729,-360,5,730,-360,5,731,-360,5,732,-360,5,733,-13,3,734,-14,3,735,-15,3,736,-90,4,737,-15,3,738,-18,3,739,-15,3,740,-15,3,741,-360,5,742,-360,5,743,-360,5,744,-294,4,745,-360,5,746,-360,5,747,-360,5,748,-360,5,749,-360,5,750,-300,4,751,-301,4,752,-2,1,753,-303,4,754,-4,2,755,-5,2,756,-360,5,757,-307,5,758,-308,5,759,-360,5,760,-310,5,761,-311,5,762,-360,5,763,-13,3,764,-14,3,765,-360,5,766,-91,4,767,-362,5,768,-360,5,769,-364,5,770,-90,4,771,-366,5,772,-360,5,773,-98,4,774,-375,5,775,-370,5,776,-56,4,777,-57,4,778,-58,4,779,-374,5,780,-60,4,781,-60,4,782,-60,4,783,-363,5,784,-60,4,785,-360,5,786,-336,5,787,-360,5,788,-360,5,789,-360,5,790,-360,5,791,-360,5,792,-360,5,793,-360,5,794,-360,5,795,-360,5,796,-361,5,797,-362,5,798,-360,5,799,-364,5,800,-365,5,801,-366,5,802,-367,5,803,-360,5,804,-360,5,805,-360,5,806,-360,5,807,-360,5,808,-360,5,809,-360,5,810,-360,5,811,-361,5,812,-362,5,813,-363,5,814,-364,5,815,-365,5,816,-366,5,817,-367,5,818,-368,5,819,-369,5,820,-370,5,821,-371,5,822,-372,5,823,-360,5,824,-374,5,825,-375,5,826,-376,5,827,-377,5,828,-378,5,829,-379,5,830,-380,5,831,-381,5,832,-382,5,833,-383,5,834,-384,5,835,-385,5,836,-386,5,837,-387,5,838,-388,5,839,-389,5,840,-390,5,841,-391,5,842,-392,5,843,-393,5,844,-394,5,845,-395,5,846,-396,5,847,-397,5,848,-398,5,849,-399,5,850,-400,5,851,-401,5,852,-402,5,853,-403,5,854,-404,5,855,-405,5,856,-406,5,857,-407,5,858,-408,5,859,-409,5,860,-360,5,861,-411,5,862,-412,5,863,-413,5,864,-414,5,865,-415,5,866,-56,4,867,-360,5,868,-360,5,869,-405,5,870,-360,5,871,-361,5,872,-362,5,873,-363,5,874,-364,5,875,-425,5,876,-426,5,877,-427,5,878,-428,5,879,-429,5,880,-430,5,881,-431,5,882,-432,5,883,-433,5,884,-434,5,885,-435,5,886,-436,5,887,-437,5,888,-438,5,889,-439,5,890,-440,5,891,-441,5,892,-442,5,893,-443,5,894,-444,5,895,-445,5,896,-446,5,897,-447,5,898,-448,5,899,-449,5,900,-450,5,901,-450,5,902,-450,5,903,-450,5,904,-450,5,905,-450,5,906,-450,5,907,-450,5,908,-450,5,909,-450,5,910,-450,5,911,-450,5,912,-450,5,913,-13,3,914,-14,3,915,-450,5,916,-451,5,917,-452,5,918,-450,5,919,-454,5,920,-455,5,921,-450,5,922,-450,5,923,-450,5,924,-450,5,925,-450,5,926,-450,5,927,-450,5,928,-450,5,929,-450,5,930,-450,5,931,-451,5,932,-452,5,933,-450,5,934,-454,5,935,-455,5,936,-450,5,937,-457,5,938,-458,5,939,-450,5,940,-460,5,941,-15,3,942,-450,5,943,-15,3,944,-15,3,945,-450,5,946,-451,5,947,-452,5,948,-450,5,949,-454,5,950,-450,5,951,-456,5,952,-52,4,953,-458,5,954,-15,3,955,-460,5,956,-56,4,957,-57,4,958,-58,4,959,-59,4,960,-60,4,961,-60,4,962,-60,4,963,-60,4,964,-60,4,965,-450,5,966,-450,5,967,-450,5,968,-450,5,969,-450,5,970,-450,5,971,-450,5,972,-450,5,973,-450,5,974,-450,5,975,-450,5,976,-451,5,977,-452,5,978,-450,5,979,-454,5,980,-455,5,981,-456,5,982,-457,5,983,-450,5,984,-450,5,985,-450,5,986,-450,5,987,-450,5,988,-450,5,989,-14,3,990,-450,5,991,-450,5,992,-452,5,993,-453,5,994,-454,5,995,-455,5,996,-456,5,997,-457,5,998,-450,5,999,-450,5,1000,-450,5,1001,-450,5,1002,-450,5,1003,-450,5,1004,-14,3,1005,-15,3,1006,-15,3,1007,-459,5,1008,-450,5,1009,-450,5,1010,-450,5,1011,-450,5,1012,-14,3,1013,-15,3,1014,-474,5,1015,-475,5,1016,-476,5,1017,-450,5,1018,-450,5,1019,-450,5,1020,-450,5,1021,-450,5,1022,-450,5,1023,-450,5,1024,-476,5,1025,-450,5,1026,-450,5,1027,-450,5,1028,-450,5,1029,-450,5,1030,-450,5,1031,-450,5,1032,-450,5,1033,-450,5,1034,-450,5,1035,-450,5,1036,-450,5,1037,-450,5,1038,-450,5,1039,-4,2,1040,-450,5,1041,-456,5,1042,-450,5,1043,-450,5,1044,-450,5,1045,-450,5,1046,-450,5,1047,-450,5,1048,-450,5,1049,-14,3,1050,-450,5,1051,-450,5,1052,-450,5,1053,-450,5,1054,-450,5,1055,-450,5,1056,-528,5,1057,-15,3,1058,-450,5,1059,-450,5,1060,-450,5,1061,-450,5,1062,-450,5,1063,-450,5,1064,-14,3,1065,-450,5,1066,-450,5,1067,-452,5,1068,-528,5,1069,-528,5,1070,-15,3,1071,-456,5,1072,-457,5,1073,-450,5,1074,-534,5,1075,-535,5,1076,-536,5,1077,-537,5,1078,-538,5,1079,-14,3,1080,3,10,1081,-1,0,1082,-2,1,1083,-3,1,1084,-4,2,1085,-5,2,1086,-6,2,1087,-7,2,1088,-8,2,1089,-9,2,1090,-10,2,1091,-11,2,1092,-12,2,1093,-13,3,1094,-14,3,1095,-15,3,1096,-15,3,1097,-15,3,1098,-18,3,1099,-15,3,1100,-15,3,1101,-21,3,1102,-22,3,1103,-23,3,1104,-24,3,1105,-25,3,1106,-26,3,1107,-27,3,1108,-28,3,1109,-29,3,1110,-30,3,1111,-30,3,1112,-30,3,1113,-33,3,1114,-4,2,1115,-5,2,1116,-36,3,1117,-7,2,1118,-30,3,1119,-39,3,1120,-30,3,1121,-15,3,1122,-42,3,1123,-15,3,1124,-15,3,1125,-45,3,1126,-45,3,1127,-2,1,1128,-48,3,1129,-4,2,1130,-5,2,1131,-6,2,1132,-52,4,1133,-8,2,1134,-15,3,1135,-55,4,1136,-56,4,1137,-57,4,1138,-58,4,1139,-59,4,1140,-60,4,1141,-60,4,1142,-60,4,1143,-60,4,1144,-60,4,1145,-65,4,1146,-66,4,1147,-67,4,1148,-68,4,1149,-69,4,1150,-70,4,1151,-71,4,1152,-72,4,1153,-73,4,1154,-74,4,1155,-75,4,1156,-75,4,1157,-75,4,1158,-78,4,1159,-4,2,1160,-75,4,1161,-75,4,1162,-75,4,1163,-83,4,1164,-84,4,1165,-85,4,1166,-86,4,1167,-87,4,1168,-88,4,1169,-14,3,1170,-90,4,1171,-91,4,1172,-90,4,1173,-90,4,1174,-4,2,1175,-5,2,1176,-90,4,1177,-90,4,1178,-98,4,1179,-99,4,1180,-100,4,1181,-101,4,1182,-102,4,1183,-103,4,1184,-14,3,1185,-15,3,1186,-15,3,1187,-98,4,1188,-108,4,1189,-109,4,1190,-110,4,1191,-111,4,1192,-14,3,1193,-15,3,1194,-294,5,1195,-90,4,1196,-90,4,1197,-90,4,1198,-90,4,1199,-119,4,1200,-300,5,1201,-301,5,1202,-302,5,1203,-123,4,1204,-98,4,1205,-125,4,1206,-126,4,1207,-127,4,1208,-98,4,1209,-129,4,1210,-130,4,1211,-131,4,1212,-132,4,1213,-13,3,1214,-14,3,1215,-135,4,1216,-136,4,1217,-137,4,1218,-138,4,1219,-4,2,1220,-140,4,1221,-6,2,1222,-142,4,1223,-143,4,1224,-144,4,1225,-70,4,1226,-146,4,1227,-147,4,1228,-148,4,1229,-14,3,1230,-150,4,1231,-151,4,1232,-2,1,1233,-75,4,1234,-70,4,1235,-65,4,1236,-336,5,1237,-15,3,1238,-60,4,1239,-75,4,1240,-70,4,1241,-71,4,1242,-72,4,1243,-73,4,1244,-74,4,1245,-75,4,1246,-75,4,1247,-75,4,1248,-78,4,1249,-78,4,1250,-75,4,1251,-75,4,1252,-75,4,1253,-75,4,1254,-84,4,1255,-85,4,1256,-86,4,1257,-87,4,1258,-88,4,1259,-14,3,1260,-360,5,1261,-361,5,1262,-362,5,1263,-363,5,1264,-360,5,1265,-360,5,1266,-366,5,1267,-367,5,1268,-368,5,1269,-369,5,1270,-370,5,1271,-371,5,1272,-372,5,1273,-13,3,1274,-14,3,1275,-15,3,1276,-98,4,1277,-15,3,1278,-360,5,1279,-15,3,1280,-15,3,1281,-360,5,1282,-360,5,1283,-383,5,1284,-384,5,1285,-385,5,1286,-386,5,1287,-387,5,1288,-388,5,1289,-389,5,1290,-390,5,1291,-391,5,1292,-392,5,1293,-393,5,1294,-390,5,1295,-390,5,1296,-396,5,1297,-390,5,1298,-398,5,1299,-399,5,1300,-400,5,1301,-15,3,1302,-402,5,1303,-15,3,1304,-15,3,1305,-405,5,1306,-406,5,1307,-405,5,1308,-408,5,1309,-405,5,1310,-90,4,1311,-405,5,1312,-412,5,1313,-405,5,1314,-15,3,1315,-415,5,1316,-56,4,1317,-57,4,1318,-58,4,1319,-14,3,1320,-60,4,1321,-60,4,1322,-60,4,1323,-75,4,1324,-60,4,1325,-65,4,1326,-360,5,1327,-360,5,1328,-360,5,1329,-360,5,1330,-360,5,1331,-360,5,1332,-360,5,1333,-360,5,1334,-360,5,1335,-360,5,1336,-361,5,1337,-362,5,1338,-360,5,1339,-360,5,1340,-360,5,1341,-366,5,1342,-367,5,1343,-360,5,1344,-360,5,1345,-360,5,1346,-360,5,1347,-360,5,1348,-360,5,1349,-14,3,1350,3,10,1351,-1,0,1352,-2,1,1353,-3,1,1354,-4,2,1355,-5,2,1356,-6,2,1357,-7,2,1358,-8,2,1359,-9,2,1360,-10,2,1361,-11,2,1362,-12,2,1363,-13,3,1364,-14,3,1365,-15,3,1366,-15,3,1367,-15,3,1368,-18,3,1369,-15,3,1370,-15,3,1371,-21,3,1372,-22,3,1373,-23,3,1374,-24,3,1375,-25,3,1376,-26,3,1377,-27,3,1378,-28,3,1379,-29,3,1380,-30,3,1381,-30,3,1382,-30,3,1383,-33,3,1384,-4,2,1385,-5,2,1386,-36,3,1387,-7,2,1388,-30,3,1389,-39,3,1390,-30,3,1391,-15,3,1392,-42,3,1393,-15,3,1394,-15,3,1395,-45,3,1396,-45,3,1397,-2,1,1398,-48,3,1399,-4,2,1400,-5,2,1401,-51,4,1402,-52,4,1403,-8,2,1404,-15,3,1405,-55,4,1406,-56,4,1407,-57,4,1408,-58,4,1409,-59,4,1410,-60,4,1411,-60,4,1412,-60,4,1413,-60,4,1414,-60,4,1415,-65,4,1416,-66,4,1417,-67,4,1418,-68,4,1419,-69,4,1420,-70,4,1421,-71,4,1422,-72,4,1423,-73,4,1424,-74,4,1425,-75,4,1426,-75,4,1427,-75,4,1428,-78,4,1429,-4,2,1430,-75,4,1431,-75,4,1432,-75,4,1433,-83,4,1434,-84,4,1435,-85,4,1436,-86,4,1437,-87,4,1438,-88,4,1439,-14,3,1440,4,15,1441,-91,4,1442,-2,1,1443,-3,1,1444,-4,2,1445,-5,2,1446,-6,2,1447,-7,2,1448,-98,4,1449,-9,2,1450,-10,2,1451,-11,2,1452,-12,2,1453,-13,3,1454,-14,3,1455,-15,3,1456,-15,3,1457,-98,4,1458,-108,4,1459,-109,4,1460,-110,4,1461,-111,4,1462,-14,3,1463,-15,3,1464,-24,3,1465,-25,3,1466,-26,3,1467,-27,3,1468,-28,3,1469,-29,3,1470,-30,3,1471,-121,4,1472,-122,4,1473,-123,4,1474,-98,4,1475,-125,4,1476,-36,3,1477,-127,4,1478,-98,4,1479,-39,3,1480,-130,4,1481,-131,4,1482,-42,3,1483,-15,3,1484,-15,3,1485,-45,3,1486,-136,4,1487,-137,4,1488,-138,4,1489,-4,2,1490,-140,4,1491,-51,4,1492,-52,4,1493,-143,4,1494,-144,4,1495,-70,4,1496,-56,4,1497,-57,4,1498,-58,4,1499,-59,4,1500,-60,4,1501,-60,4,1502,-60,4,1503,-75,4,1504,-70,4,1505,-65,4,1506,-78,4,1507,-67,4,1508,-60,4,1509,-75,4,1510,-70,4,1511,-71,4,1512,-72,4,1513,-73,4,1514,-74,4,1515,-75,4,1516,-75,4,1517,-75,4,1518,-78,4,1519,-78,4,1520,-75,4,1521,-75,4,1522,-75,4,1523,-75,4,1524,-84,4,1525,-85,4,1526,-86,4,1527,-87,4,1528,-88,4,1529,-14,3,1530,-90,4,1531,-91,4,1532,-84,4,1533,-90,4,1534,-4,2,1535,-5,2,1536,-90,4,1537,-90,4,1538,-98,4,1539,-90,4,1540,-100,4,1541,-101,4,1542,-102,4,1543,-103,4,1544,-14,3,1545,-15,3,1546,-98,4,1547,-98,4,1548,-108,4,1549,-109,4,1550,-110,4,1551,-111,4,1552,-14,3,1553,-15,3,1554,-90,4,1555,-90,4,1556,-90,4,1557,-90,4,1558,-90,4,1559,-90,4,1560,-120,4,1561,-15,3,1562,-122,4,1563,-123,4,1564,-98,4,1565,-125,4,1566,-126,4,1567,-127,4,1568,-98,4,1569,-129,4,1570,-130,4,1571,-131,4,1572,-132,4,1573,-133,4,1574,-134,4,1575,-225,5,1576,-226,5,1577,-227,5,1578,-138,4,1579,-229,5,1580,-140,4,1581,-231,5,1582,-142,4,1583,-143,4,1584,-144,4,1585,-70,4,1586,-146,4,1587,-237,5,1588,-238,5,1589,-239,5,1590,-60,4,1591,-60,4,1592,-98,4,1593,-75,4,1594,-70,4,1595,-65,4,1596,-78,4,1597,-15,3,1598,-60,4,1599,-75,4,1600,-70,4,1601,-71,4,1602,-252,5,1603,-73,4,1604,-74,4,1605,-75,4,1606,-75,4,1607,-75,4,1608,-78,4,1609,-78,4,1610,-75,4,1611,-75,4,1612,-75,4,1613,-75,4,1614,-84,4,1615,-85,4,1616,-86,4,1617,-87,4,1618,-88,4,1619,-14,3,1620,-270,5,1621,-271,5,1622,-272,5,1623,-273,5,1624,-274,5,1625,-275,5,1626,-276,5,1627,-277,5,1628,-278,5,1629,-279,5,1630,-280,5,1631,-281,5,1632,-282,5,1633,-13,3,1634,-14,3,1635,-285,5,1636,-98,4,1637,-272,5,1638,-288,5,1639,-274,5,1640,-275,5,1641,-291,5,1642,-292,5,1643,-293,5,1644,-294,5,1645,-295,5,1646,-296,5,1647,-297,5,1648,-298,5,1649,-299,5,1650,-300,5,1651,-301,5,1652,-302,5,1653,-303,5,1654,-304,5,1655,-305,5,1656,-306,5,1657,-307,5,1658,-308,5,1659,-309,5,1660,-310,5,1661,-311,5,1662,-312,5,1663,-13,3,1664,-14,3,1665,-315,5,1666,-316,5,1667,-317,5,1668,-318,5,1669,-4,2,1670,-90,4,1671,-321,5,1672,-322,5,1673,-323,5,1674,-15,3,1675,-325,5,1676,-56,4,1677,-327,5,1678,-328,5,1679,-329,5,1680,-270,5,1681,-271,5,1682,-272,5,1683,-75,4,1684,-274,5,1685,-270,5,1686,-336,5,1687,-60,4,1688,-270,5,1689,-339,5,1690,-270,5,1691,-270,5,1692,-270,5,1693,-343,5,1694,-344,5,1695,-345,5,1696,-346,5,1697,-347,5,1698,-348,5,1699,-4,2,1700,-350,5,1701,-351,5,1702,-352,5,1703,-353,5,1704,-354,5,1705,-355,5,1706,-356,5,1707,-357,5,1708,-358,5,1709,-359,5,1710,-360,5,1711,-361,5,1712,-362,5,1713,-363,5,1714,-4,2,1715,-365,5,1716,-366,5,1717,-367,5,1718,-368,5,1719,-369,5,1720,-370,5,1721,-371,5,1722,-372,5,1723,-13,3,1724,-14,3,1725,-15,3,1726,-15,3,1727,-15,3,1728,-18,3,1729,-15,3,1730,-15,3,1731,-381,5,1732,-382,5,1733,-383,5,1734,-384,5,1735,-385,5,1736,-386,5,1737,-387,5,1738,-388,5,1739,-389,5,1740,-390,5,1741,-391,5,1742,-392,5,1743,-393,5,1744,-4,2,1745,-395,5,1746,-396,5,1747,-397,5,1748,-398,5,1749,-399,5,1750,-400,5,1751,-15,3,1752,-402,5,1753,-15,3,1754,-15,3,1755,-405,5,1756,-406,5,1757,-407,5,1758,-408,5,1759,-4,2,1760,-410,5,1761,-411,5,1762,-52,4,1763,-413,5,1764,-15,3,1765,-415,5,1766,-56,4,1767,-57,4,1768,-58,4,1769,-59,4,1770,-60,4,1771,-60,4,1772,-60,4,1773,-60,4,1774,-60,4,1775,-65,4,1776,-66,4,1777,-67,4,1778,-68,4,1779,-69,4,1780,-70,4,1781,-71,4,1782,-72,4,1783,-73,4,1784,-74,4,1785,-75,4,1786,-75,4,1787,-75,4,1788,-360,5,1789,-4,2,1790,-75,4,1791,-75,4,1792,-75,4,1793,-75,4,1794,-360,5,1795,-360,5,1796,-360,5,1797,-360,5,1798,-360,5,1799,-14,3,1800,4,16,1801,-1,0,1802,-2,1,1803,-3,1,1804,-4,2,1805,-5,2,1806,-6,2,1807,-7,2,1808,-8,2,1809,-9,2,1810,-10,2,1811,-11,2,1812,-12,2,1813,-13,3,1814,-14,3,1815,-15,3,1816,-15,3,1817,-15,3,1818,-18,3,1819,-15,3,1820,-15,3,1821,-21,3,1822,-22,3,1823,-23,3,1824,-24,3,1825,-25,3,1826,-26,3,1827,-27,3,1828,-28,3,1829,-29,3,1830,-30,3,1831,-30,3,1832,-30,3,1833,-33,3,1834,-4,2,1835,-5,2,1836,-36,3,1837,-7,2,1838,-30,3,1839,-39,3,1840,-30,3,1841,-15,3,1842,-42,3,1843,-15,3,1844,-15,3,1845,-45,3,1846,-45,3,1847,-2,1,1848,-48,4,1849,-4,2,1850,-5,2,1851,-51,4,1852,-52,4,1853,-53,4,1854,-15,3,1855,-55,4,1856,-56,4,1857,-57,4,1858,-58,4,1859,-59,4,1860,-60,4,1861,-60,4,1862,-60,4,1863,-60,4,1864,-60,4,1865,-65,4,1866,-66,4,1867,-67,4,1868,-68,4,1869,-69,4,1870,-70,4,1871,-71,4,1872,-72,4,1873,-73,4,1874,-74,4,1875,-75,4,1876,-75,4,1877,-75,4,1878,-78,4,1879,-4,2,1880,-75,4,1881,-75,4,1882,-75,4,1883,-83,4,1884,-84,4,1885,-85,4,1886,-86,4,1887,-87,4,1888,-88,4,1889,-14,3,1890,-90,4,1891,-91,4,1892,-90,4,1893,-90,4,1894,-4,2,1895,-5,2,1896,-90,4,1897,-90,4,1898,-98,4,1899,-90,4,1900,-100,4,1901,-101,4,1902,-102,4,1903,-103,4,1904,-14,3,1905,-15,3,1906,-15,3,1907,-98,4,1908,-108,4,1909,-109,4,1910,-110,4,1911,-111,4,1912,-14,3,1913,-15,3,1914,-90,4,1915,-90,4,1916,-90,4,1917,-90,4,1918,-90,4,1919,-90,4,1920,-120,4,1921,-91,4,1922,-122,4,1923,-123,4,1924,-98,4,1925,-125,4,1926,-126,4,1927,-127,4,1928,-98,4,1929,-129,4,1930,-130,4,1931,-131,4,1932,-132,4,1933,-133,4,1934,-134,4,1935,-135,4,1936,-136,4,1937,-137,4,1938,-138,4,1939,-4,2,1940,-140,4,1941,-6,2,1942,-142,4,1943,-143,4,1944,-144,4,1945,-70,4,1946,-146,4,1947,-147,4,1948,-148,4,1949,-14,3,1950,-60,4,1951,-60,4,1952,-60,4,1953,-75,4,1954,-70,4,1955,-65,4,1956,-78,4,1957,-15,3,1958,-60,4,1959,-75,4,1960,-70,4,1961,-71,4,1962,-72,4,1963,-73,4,1964,-74,4,1965,-75,4,1966,-75,4,1967,-75,4,1968,-78,4,1969,-78,4,1970,-75,4,1971,-75,4,1972,-75,4,1973,-75,4,1974,-84,4,1975,-85,4,1976,-86,4,1977,-87,4,1978,-88,4,1979,-14,3,1980,-90,4,1981,-90,4,1982,-84,4,1983,-90,4,1984,-4,2,1985,-5,2,1986,-90,4,1987,-90,4,1988,-90,4,1989,-90,4,1990,-90,4,1991,-90,4,1992,-90,4,1993,-90,4,1994,-14,3,1995,-15,3,1996,-98,4,1997,-90,4,1998,-98,4,1999,-98,4,2000,-98,4,2001,-98,4,2002,-14,3,2003,-15,3,2004,-90,4,2005,-90,4,2006,-90,4,2007,-90,4,2008,-90,4,2009,-90,4,2010,-90,4,2011,-15,3,2012,-90,4,2013,-90,4,2014,-90,4,2015,-90,4,2016,-90,4,2017,-90,4,2018,-98,4,2019,-90,4,2020,-90,4,2021,-90,4,2022,-90,4,2023,-90,4,2024,-90,4,2025,-225,5,2026,-226,5,2027,-227,5,2028,-90,4,2029,-229,5,2030,-90,4,2031,-231,5,2032,-90,4,2033,-233,5,2034,-234,5,2035,-235,5,2036,-90,4,2037,-237,5,2038,-238,5,2039,-239,5,2040,-60,4,2041,-60,4,2042,-98,4,2043,-75,4,2044,-70,4,2045,-65,4,2046,-78,4,2047,-15,3,0,0,0"), // leafSize 16 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,14,16,15,15,18,16,16,19,17,-1,1,18,-2,1,19,-3,1,20,-4,1,21,-8,2,22,-11,2,23,-10,2,24,-8,2,25,-10,2,26,-13,2,27,-11,2,28,-13,2,29,-13,2,30,-15,2,31,-15,2,32,-16,2,33,3,5,34,-2,1,35,-16,2,36,3,5,37,-8,2,38,-10,2,39,3,5,40,-8,2,41,-13,2,42,3,5,43,-11,2,44,-13,2,45,3,5,46,-15,2,47,-15,2,48,3,5,49,-1,1,50,-2,1,51,-3,1,52,4,8,53,-5,2,54,-15,2,55,-7,2,56,4,8,57,-9,2,58,-10,2,59,-11,2,60,4,8,61,-13,2,62,-14,2,63,-15,2,64,4,8,65,5,11,66,-2,1,67,-3,1,68,-4,2,69,-5,2,70,5,11,71,-7,2,72,-8,2,73,-13,2,74,-10,2,75,5,11,76,-16,3,77,-13,2,78,6,14,79,-15,2,80,5,12,81,-1,1,82,-2,1,83,-3,1,84,6,14,85,-5,2,86,-6,2,87,-7,2,88,-8,2,89,-9,2,90,6,15,91,7,17,92,-12,2,93,-13,2,94,-14,2,95,-15,3,96,6,15,97,-1,1,98,7,17,99,-3,1,100,-4,2,101,-5,2,102,-6,2,103,-7,2,104,-8,2,105,7,18,106,-10,2,107,-11,2,108,-12,2,109,-13,2,110,-14,2,111,-15,3,112,7,18,113,-1,1,114,-2,1,115,-3,1,116,-4,2,117,-5,2,118,-6,2,119,-7,2,120,-8,2,121,-9,2,122,-10,2,123,-11,2,124,-12,2,125,-13,2,126,-14,3,127,-15,3,128,-16,3,129,-17,3,130,-18,3,131,-19,3,132,-20,3,133,-21,3,134,-22,3,135,-23,3,136,-24,3,137,-25,3,138,-26,3,139,-27,3,140,-28,3,141,-29,3,142,-30,3,143,-31,3,144,-32,3,145,-33,3,146,-56,3,147,-42,3,148,-52,3,149,-5,2,150,-45,3,151,-39,3,152,-56,3,153,-48,3,154,-42,3,155,-75,3,156,-60,3,157,-45,3,158,-16,3,159,-16,3,160,-48,3,161,-65,3,162,-64,3,163,-3,1,164,-52,3,165,-60,3,166,-70,3,167,-7,2,168,-56,3,169,-64,3,170,-80,3,171,-75,3,172,-60,3,173,-16,3,174,-78,3,175,-15,3,176,-64,3,177,-65,3,178,-80,3,179,-3,1,180,-75,3,181,-5,2,182,-70,3,183,-7,2,184,-8,2,185,-80,3,186,-90,3,187,-75,3,188,-16,3,189,-13,2,190,-78,3,191,-15,3,192,-96,4,193,-1,0,194,-96,4,195,-90,4,196,-84,4,197,-5,2,198,-6,2,199,-7,2,200,-8,2,201,-96,4,202,-90,4,203,-91,4,204,-96,4,205,-96,4,206,-14,3,207,-15,3,208,-96,4,209,-1,0,210,-105,4,211,-3,1,212,-4,2,213,-5,2,214,-6,2,215,-7,2,216,-8,2,217,-105,4,218,-106,4,219,-107,4,220,-108,4,221,-109,4,222,-14,3,223,-15,3,224,-112,4,225,-1,0,226,-2,1,227,-3,1,228,-4,2,229,-5,2,230,-6,2,231,-7,2,232,-8,2,233,-9,2,234,-112,4,235,-112,4,236,-112,4,237,-112,4,238,-14,3,239,-15,3,240,-16,3,241,-16,3,242,-16,3,243,-19,3,244,-16,3,245,-16,3,246,-112,4,247,-112,4,248,-112,4,249,-112,4,250,-112,4,251,-112,4,252,-112,4,253,-112,4,254,-112,4,255,-112,4,256,-112,4,257,-112,4,258,-105,4,259,-112,4,260,-112,4,261,-105,4,262,-112,4,263,-112,4,264,-112,4,265,-112,4,266,-112,4,267,-112,4,268,-112,4,269,-112,4,270,3,8,271,-16,3,272,-112,4,273,-112,4,274,-105,4,275,-105,4,276,-112,4,277,-112,4,278,-112,4,279,-112,4,280,-112,4,281,-112,4,282,-112,4,283,-112,4,284,-112,4,285,-16,3,286,-112,4,287,-15,3,288,3,8,289,-1,0,290,-2,1,291,-3,1,292,-4,2,293,-5,2,294,-6,2,295,-7,2,296,-8,2,297,-9,2,298,-10,2,299,-11,2,300,-12,2,301,-13,3,302,-14,3,303,-15,3,304,-16,3,305,-16,3,306,-96,4,307,-90,4,308,-84,4,309,-16,3,310,-22,3,311,-23,3,312,-24,3,313,-96,4,314,-90,4,315,3,8,316,-28,3,317,-29,3,318,-30,3,319,-31,3,320,-96,4,321,-96,4,322,-105,4,323,-96,4,324,-4,2,325,-96,4,326,-96,4,327,-96,4,328,-96,4,329,-105,4,330,-106,4,331,-107,4,332,-108,4,333,-109,4,334,-14,3,335,-15,3,336,3,8,337,-1,0,338,-2,1,339,-3,1,340,-4,2,341,-5,2,342,-6,2,343,-7,2,344,-8,2,345,-9,2,346,-10,2,347,-11,2,348,-12,2,349,-13,3,350,-14,3,351,-15,3,352,-16,3,353,-16,3,354,-16,3,355,-19,3,356,-16,3,357,-16,3,358,-22,3,359,-23,3,360,-24,3,361,-25,3,362,-26,3,363,-27,3,364,-28,3,365,-29,3,366,-30,3,367,-31,3,368,-32,3,369,-33,3,370,-32,3,371,-32,3,372,-36,3,373,-5,2,374,-7,2,375,-39,3,376,-32,3,377,-31,3,378,-42,3,379,-16,3,380,-16,3,381,-45,3,382,-16,3,383,-16,3,384,4,12,385,-1,0,386,-2,1,387,-3,1,388,-4,2,389,-5,2,390,-6,2,391,-7,2,392,-8,2,393,-9,2,394,-10,2,395,-11,2,396,-60,4,397,-13,3,398,-14,3,399,-15,3,400,-112,4,401,-65,4,402,-112,4,403,-112,4,404,-4,2,405,-5,2,406,-70,4,407,-23,3,408,-24,3,409,-25,3,410,-26,3,411,-75,4,412,-28,3,413,-29,3,414,-30,3,415,-31,3,416,-80,4,417,-80,4,418,-80,4,419,-80,4,420,-84,4,421,-80,4,422,-80,4,423,-80,4,424,-80,4,425,-89,4,426,-90,4,427,-91,4,428,-92,4,429,-93,4,430,-14,3,431,-15,3,432,-96,4,433,-96,4,434,-98,4,435,-96,4,436,-4,2,437,-96,4,438,-96,4,439,-96,4,440,-96,4,441,-105,4,442,-106,4,443,-107,4,444,-108,4,445,-109,4,446,-14,3,447,-15,3,448,4,13,449,-1,0,450,-2,1,451,-3,1,452,-4,2,453,-5,2,454,-6,2,455,-7,2,456,-8,2,457,-9,2,458,-10,2,459,-11,2,460,-12,2,461,-13,3,462,-14,3,463,-15,3,464,-16,3,465,-16,3,466,-16,3,467,-19,3,468,-16,3,469,-16,3,470,-22,3,471,-23,3,472,-24,3,473,-25,3,474,-26,3,475,-27,3,476,-28,3,477,-29,3,478,-30,3,479,-31,3,480,5,17,481,-1,0,482,-2,1,483,-3,1,484,-4,2,485,-5,2,486,-6,2,487,-7,2,488,-8,2,489,-9,2,490,-10,2,491,-11,2,492,-12,2,493,-13,3,494,-14,3,495,-15,3,496,-16,3,497,-16,3,498,-16,3,499,-19,3,500,-52,3,501,-16,3,502,-22,3,503,-23,3,504,-56,4,505,-25,3,506,-26,3,507,-27,3,508,-60,4,509,-29,3,510,-30,3,511,-31,3,512,-64,4,513,-65,4,514,-64,4,515,-64,4,516,-64,4,517,-64,4,518,-70,4,519,-71,4,520,-72,4,521,-73,4,522,-74,4,523,-75,4,524,-60,4,525,-77,4,526,-78,4,527,-64,4,528,-80,4,529,-80,4,530,-80,4,531,-80,4,532,-84,4,533,-80,4,534,-80,4,535,-80,4,536,-80,4,537,-96,4,538,-90,4,539,-91,4,540,-92,4,541,-93,4,542,-14,3,543,-15,3,544,-96,4,545,-96,4,546,-98,4,547,-96,4,548,-4,2,549,-5,2,550,-96,4,551,-96,4,552,-96,4,553,-105,4,554,-106,4,555,-107,4,556,-108,4,557,-109,4,558,-14,3,559,-15,3,560,5,17,561,-1,0,562,-2,1,563,-3,1,564,-4,2,565,-5,2,566,-6,2,567,-7,2,568,-8,2,569,-9,2,570,-10,2,571,-11,2,572,-12,2,573,-13,3,574,-14,3,575,-15,3,576,-16,3,577,-16,3,578,-16,3,579,-19,3,580,-16,3,581,-16,3,582,-22,3,583,-23,3,584,-24,3,585,-25,3,586,-26,3,587,-27,3,588,-28,3,589,-29,3,590,-30,3,591,-31,3,592,-32,3,593,-33,3,594,-32,3,595,-32,3,596,-36,3,597,-5,2,598,-7,2,599,-39,3,600,-32,3,601,-15,3,602,-42,3,603,-16,3,604,-16,3,605,-45,3,606,-16,3,607,-16,3,608,-48,3,609,-48,3,610,-2,1,611,-48,3,612,-52,4,613,-5,2,614,-15,3,615,-16,3,616,-56,4,617,-57,4,618,-58,4,619,-59,4,620,-60,4,621,-61,4,622,-62,4,623,-63,4,624,-64,4,625,-65,4,626,-64,4,627,-64,4,628,-64,4,629,-64,4,630,-70,4,631,-71,4,632,-72,4,633,-73,4,634,-74,4,635,-75,4,636,-60,4,637,-77,4,638,-78,4,639,-64,4,640,-80,4,641,-80,4,642,-80,4,643,-80,4,644,-84,4,645,-80,4,646,-80,4,647,-80,4,648,-80,4,649,-89,4,650,-90,4,651,-91,4,652,-92,4,653,-93,4,654,-14,3,655,-15,3,656,-96,4,657,-96,4,658,-98,4,659,-96,4,660,-4,2,661,-5,2,662,-96,4,663,-96,4,664,-96,4,665,-105,4,666,-106,4,667,-107,4,668,-108,4,669,-109,4,670,-14,3,671,-15,3,672,-112,4,673,-112,4,674,-112,4,675,-112,4,676,-4,2,677,-5,2,678,-112,4,679,-112,4,680,-112,4,681,-112,4,682,-112,4,683,-123,4,684,-124,4,685,-13,3,686,-14,3,687,-15,3,688,-16,3,689,-16,3,690,-16,3,691,-19,3,692,-16,3,693,-16,3,694,-22,3,695,-23,3,696,-24,3,697,-112,4,698,-112,4,699,-112,4,700,-112,4,701,-112,4,702,-112,4,703,-112,4,704,-144,4,705,-145,4,706,-146,4,707,-147,4,708,-148,4,709,-5,2,710,-150,4,711,-151,4,712,-152,4,713,-153,4,714,-154,4,715,-75,4,716,-156,4,717,-157,4,718,-16,3,719,-16,3,720,-160,4,721,-65,4,722,-64,4,723,-163,4,724,-164,4,725,-165,4,726,-70,4,727,-16,3,728,-168,4,729,-64,4,730,-80,4,731,-75,4,732,-60,4,733,-16,3,734,-78,4,735,-15,3,736,-80,4,737,-65,4,738,-80,4,739,-80,4,740,-75,4,741,-80,4,742,-70,4,743,-80,4,744,-80,4,745,-80,4,746,-90,4,747,-75,4,748,-60,4,749,-93,4,750,-78,4,751,-15,3,752,-96,4,753,-96,4,754,-96,4,755,-90,4,756,-84,4,757,-5,2,758,-96,4,759,-96,4,760,-96,4,761,-96,4,762,-90,4,763,-91,4,764,-96,4,765,-96,4,766,-14,3,767,-15,3,768,-96,4,769,-96,4,770,-105,4,771,-96,4,772,-4,2,773,-5,2,774,-96,4,775,-96,4,776,-96,4,777,-105,4,778,-106,4,779,-107,4,780,-108,4,781,-109,4,782,-14,3,783,-15,3,784,-112,4,785,-112,4,786,-112,4,787,-112,4,788,-4,2,789,-5,2,790,-112,4,791,-112,4,792,-112,4,793,-112,4,794,-112,4,795,-112,4,796,-112,4,797,-13,3,798,-14,3,799,-15,3,800,-16,3,801,-16,3,802,-16,3,803,-19,3,804,-16,3,805,-16,3,806,-22,3,807,-23,3,808,-24,3,809,-112,4,810,-112,4,811,-112,4,812,-112,4,813,-112,4,814,-112,4,815,-112,4,816,-336,5,817,-337,5,818,-338,5,819,-339,5,820,-112,4,821,-341,5,822,-112,4,823,-112,4,824,-344,5,825,-112,4,826,-112,4,827,-75,4,828,-112,4,829,-112,4,830,-350,5,831,-351,5,832,-384,5,833,-385,5,834,-386,5,835,-387,5,836,-112,4,837,-389,5,838,-390,5,839,-391,5,840,-392,5,841,-80,4,842,-90,4,843,-75,4,844,-60,4,845,-13,3,846,-398,5,847,-399,5,848,-288,5,849,-289,5,850,-290,5,851,-291,5,852,-292,5,853,-293,5,854,-294,5,855,-295,5,856,-296,5,857,-96,4,858,-298,5,859,-75,4,860,-300,5,861,-301,5,862,-302,5,863,-303,5,864,-96,4,865,-96,4,866,-96,4,867,-90,4,868,-84,4,869,-5,2,870,-96,4,871,-96,4,872,-96,4,873,-96,4,874,-90,4,875,-315,5,876,-316,5,877,-96,4,878,-318,5,879,-15,3,880,-96,4,881,-96,4,882,-105,4,883,-96,4,884,-4,2,885,-5,2,886,-96,4,887,-96,4,888,-96,4,889,-105,4,890,-106,4,891,-107,4,892,-108,4,893,-109,4,894,-14,3,895,-15,3,896,-336,5,897,-337,5,898,-338,5,899,-339,5,900,-340,5,901,-341,5,902,-342,5,903,-343,5,904,-344,5,905,-345,5,906,-346,5,907,-347,5,908,-348,5,909,-13,3,910,-350,5,911,-351,5,912,-352,5,913,-353,5,914,-354,5,915,-355,5,916,-356,5,917,-357,5,918,-358,5,919,-359,5,920,-360,5,921,-361,5,922,-362,5,923,-363,5,924,-364,5,925,-365,5,926,-366,5,927,-367,5,928,-448,5,929,-449,5,930,-450,5,931,-451,5,932,-452,5,933,-453,5,934,-454,5,935,-375,5,936,-456,5,937,-457,5,938,-458,5,939,-459,5,940,-460,5,941,-381,5,942,-14,3,943,-15,3,944,-384,5,945,-385,5,946,-386,5,947,-387,5,948,-388,5,949,-389,5,950,-390,5,951,-391,5,952,-392,5,953,-393,5,954,-394,5,955,-395,5,956,-336,5,957,-13,3,958,-14,3,959,-384,5,960,-400,5,961,-336,5,962,-402,5,963,-403,5,964,-404,5,965,-405,5,966,-406,5,967,-407,5,968,-408,5,969,-409,5,970,-410,5,971,-411,5,972,-412,5,973,-413,5,974,-414,5,975,-415,5,976,-416,5,977,-417,5,978,-418,5,979,-419,5,980,-420,5,981,-421,5,982,-422,5,983,-423,5,984,-424,5,985,-96,4,986,-426,5,987,-427,5,988,-428,5,989,-429,5,990,-14,3,991,-15,3,992,-432,5,993,-433,5,994,-434,5,995,-435,5,996,-436,5,997,-437,5,998,-438,5,999,-439,5,1000,-440,5,1001,-441,5,1002,-442,5,1003,-443,5,1004,-444,5,1005,-445,5,1006,-14,3,1007,-15,3,1008,-448,5,1009,-449,5,1010,-450,5,1011,-451,5,1012,-452,5,1013,-453,5,1014,-454,5,1015,-455,5,1016,-456,5,1017,-457,5,1018,-458,5,1019,-459,5,1020,-460,5,1021,-13,3,1022,-14,3,1023,-15,3,1024,-16,3,1025,-16,3,1026,-16,3,1027,-467,5,1028,-16,3,1029,-16,3,1030,-470,5,1031,-471,5,1032,-472,5,1033,-473,5,1034,-474,5,1035,-475,5,1036,-476,5,1037,-477,5,1038,-478,5,1039,-479,5,1040,-480,5,1041,-481,5,1042,-482,5,1043,-483,5,1044,-4,2,1045,-485,5,1046,-486,5,1047,-487,5,1048,-488,5,1049,-489,5,1050,-490,5,1051,-491,5,1052,-492,5,1053,-13,3,1054,-14,3,1055,-15,3,1056,-16,3,1057,-16,3,1058,-16,3,1059,-499,5,1060,-52,4,1061,-16,3,1062,-502,5,1063,-503,5,1064,-56,4,1065,-505,5,1066,-506,5,1067,-507,5,1068,-60,4,1069,-509,5,1070,-510,5,1071,-511,5,1072,-64,4,1073,-65,4,1074,-64,4,1075,-64,4,1076,-64,4,1077,-64,4,1078,-70,4,1079,-64,4,1080,-448,5,1081,-448,5,1082,-448,5,1083,-448,5,1084,-60,4,1085,-448,5,1086,-526,5,1087,-15,3,1088,-528,5,1089,-529,5,1090,-530,5,1091,-531,5,1092,-532,5,1093,-533,5,1094,-534,5,1095,-535,5,1096,-536,5,1097,-537,5,1098,-538,5,1099,-539,5,1100,-540,5,1101,-541,5,1102,-14,3,1103,-15,3,1104,-544,5,1105,-545,5,1106,-546,5,1107,-547,5,1108,-4,2,1109,-549,5,1110,-550,5,1111,-551,5,1112,-552,5,1113,-553,5,1114,-554,5,1115,-555,5,1116,-556,5,1117,-557,5,1118,-14,3,1119,-15,3,1120,-560,5,1121,-560,5,1122,-560,5,1123,-560,5,1124,-4,2,1125,-560,5,1126,-560,5,1127,-560,5,1128,-560,5,1129,-560,5,1130,-560,5,1131,-560,5,1132,-560,5,1133,-13,3,1134,-14,3,1135,-15,3,1136,-16,3,1137,-16,3,1138,-16,3,1139,-560,5,1140,-16,3,1141,-16,3,1142,-560,5,1143,-560,5,1144,-560,5,1145,-560,5,1146,-560,5,1147,-560,5,1148,-560,5,1149,-560,5,1150,-560,5,1151,-560,5,1152,-560,5,1153,-560,5,1154,-562,5,1155,-563,5,1156,-560,5,1157,-565,5,1158,-567,5,1159,-560,5,1160,-568,5,1161,-15,3,1162,-560,5,1163,-16,3,1164,-16,3,1165,-560,5,1166,-16,3,1167,-16,3,1168,-560,5,1169,-561,5,1170,-562,5,1171,-563,5,1172,-52,4,1173,-565,5,1174,-15,3,1175,-16,3,1176,-56,4,1177,-57,4,1178,-58,4,1179,-59,4,1180,-60,4,1181,-61,4,1182,-62,4,1183,-63,4,1184,-64,4,1185,-65,4,1186,-64,4,1187,-64,4,1188,-64,4,1189,-64,4,1190,-70,4,1191,-64,4,1192,-72,4,1193,-73,4,1194,-74,4,1195,-75,4,1196,-60,4,1197,-77,4,1198,-78,4,1199,-15,3,1200,-80,4,1201,-80,4,1202,-80,4,1203,-80,4,1204,-560,5,1205,-80,4,1206,-80,4,1207,-80,4,1208,-80,4,1209,-560,5,1210,-560,5,1211,-560,5,1212,-560,5,1213,-560,5,1214,-14,3,1215,-15,3,1216,-560,5,1217,-561,5,1218,-560,5,1219,-563,5,1220,-4,2,1221,-565,5,1222,-566,5,1223,-567,5,1224,-568,5,1225,-560,5,1226,-560,5,1227,-560,5,1228,-560,5,1229,-560,5,1230,-14,3,1231,-15,3,1232,-560,5,1233,-561,5,1234,-562,5,1235,-563,5,1236,-4,2,1237,-565,5,1238,-566,5,1239,-567,5,1240,-568,5,1241,-569,5,1242,-570,5,1243,-560,5,1244,-560,5,1245,-13,3,1246,-14,3,1247,-15,3,1248,-16,3,1249,-16,3,1250,-16,3,1251,-19,3,1252,-16,3,1253,-16,3,1254,-582,5,1255,-583,5,1256,-584,5,1257,-585,5,1258,-586,5,1259,-587,5,1260,-588,5,1261,-589,5,1262,-590,5,1263,-591,5,1264,-560,5,1265,-560,5,1266,-560,5,1267,-560,5,1268,-560,5,1269,-565,5,1270,-560,5,1271,-560,5,1272,-560,5,1273,-560,5,1274,-560,5,1275,-75,4,1276,-560,5,1277,-560,5,1278,-16,3,1279,-16,3,1280,-560,5,1281,-65,4,1282,-64,4,1283,-560,5,1284,-560,5,1285,-60,4,1286,-70,4,1287,-16,3,1288,-560,5,1289,-64,4,1290,-80,4,1291,-75,4,1292,-60,4,1293,-16,3,1294,-78,4,1295,-15,3,1296,-80,4,1297,-65,4,1298,-80,4,1299,-80,4,1300,-75,4,1301,-80,4,1302,-70,4,1303,-80,4,1304,-80,4,1305,-80,4,1306,-560,5,1307,-75,4,1308,-60,4,1309,-77,4,1310,-78,4,1311,-15,3,1312,-656,5,1313,-656,5,1314,-656,5,1315,-560,5,1316,-84,4,1317,-5,2,1318,-656,5,1319,-656,5,1320,-656,5,1321,-656,5,1322,-560,5,1323,-560,5,1324,-656,5,1325,-656,5,1326,-14,3,1327,-15,3,1328,-656,5,1329,-657,5,1330,-665,5,1331,-659,5,1332,-4,2,1333,-5,2,1334,-662,5,1335,-663,5,1336,-664,5,1337,-665,5,1338,-666,5,1339,-667,5,1340,-668,5,1341,-669,5,1342,-14,3,1343,-15,3,1344,3,10,1345,-1,0,1346,-2,1,1347,-3,1,1348,-4,2,1349,-5,2,1350,-6,2,1351,-7,2,1352,-8,2,1353,-9,2,1354,-10,2,1355,-11,2,1356,-12,2,1357,-13,3,1358,-14,3,1359,-15,3,1360,-16,3,1361,-16,3,1362,-16,3,1363,-19,3,1364,-16,3,1365,-16,3,1366,-22,3,1367,-23,3,1368,-24,3,1369,-25,3,1370,-26,3,1371,-27,3,1372,-28,3,1373,-29,3,1374,-30,3,1375,-31,3,1376,-32,3,1377,-33,3,1378,-32,3,1379,-32,3,1380,-36,3,1381,-5,2,1382,-7,2,1383,-39,3,1384,-32,3,1385,-15,3,1386,-42,3,1387,-16,3,1388,-16,3,1389,-45,3,1390,-16,3,1391,-16,3,1392,-384,5,1393,-385,5,1394,-386,5,1395,-387,5,1396,-52,4,1397,-384,5,1398,-384,5,1399,-391,5,1400,-56,4,1401,-80,4,1402,-90,4,1403,-75,4,1404,-60,4,1405,-61,4,1406,-14,3,1407,-15,3,1408,-288,5,1409,-65,4,1410,-290,5,1411,-291,5,1412,-292,5,1413,-293,5,1414,-70,4,1415,-295,5,1416,-296,5,1417,-560,5,1418,-298,5,1419,-75,4,1420,-300,5,1421,-13,3,1422,-78,4,1423,-288,5,1424,-80,4,1425,-80,4,1426,-80,4,1427,-80,4,1428,-84,4,1429,-80,4,1430,-80,4,1431,-80,4,1432,-80,4,1433,-665,5,1434,-90,4,1435,-91,4,1436,-92,4,1437,-93,4,1438,-14,3,1439,-15,3,1440,-96,4,1441,-96,4,1442,-98,4,1443,-96,4,1444,-4,2,1445,-5,2,1446,-96,4,1447,-96,4,1448,-96,4,1449,-105,4,1450,-106,4,1451,-107,4,1452,-108,4,1453,-109,4,1454,-14,3,1455,-15,3,1456,-336,5,1457,-337,5,1458,-338,5,1459,-339,5,1460,-4,2,1461,-341,5,1462,-342,5,1463,-343,5,1464,-344,5,1465,-345,5,1466,-346,5,1467,-347,5,1468,-348,5,1469,-13,3,1470,-14,3,1471,-15,3,1472,-16,3,1473,-16,3,1474,-16,3,1475,-355,5,1476,-16,3,1477,-16,3,1478,-358,5,1479,-359,5,1480,-360,5,1481,-361,5,1482,-362,5,1483,-363,5,1484,-364,5,1485,-365,5,1486,-366,5,1487,-367,5,1488,-448,5,1489,-449,5,1490,-448,5,1491,-448,5,1492,-4,2,1493,-448,5,1494,-448,5,1495,-375,5,1496,-448,5,1497,-448,5,1498,-448,5,1499,-448,5,1500,-448,5,1501,-381,5,1502,-14,3,1503,-15,3,1504,-384,5,1505,-385,5,1506,-386,5,1507,-387,5,1508,-4,2,1509,-389,5,1510,-390,5,1511,-391,5,1512,-392,5,1513,-393,5,1514,-394,5,1515,-395,5,1516,-60,4,1517,-13,3,1518,-14,3,1519,-15,3,1520,-400,5,1521,-65,4,1522,-80,4,1523,-403,5,1524,-75,4,1525,-400,5,1526,-336,5,1527,-407,5,1528,-408,5,1529,-80,4,1530,-90,4,1531,-336,5,1532,-412,5,1533,-413,5,1534,-414,5,1535,-415,5,1536,-336,5,1537,-337,5,1538,-96,4,1539,-90,4,1540,-420,5,1541,-341,5,1542,-342,5,1543,-343,5,1544,-344,5,1545,-96,4,1546,-426,5,1547,-427,5,1548,-428,5,1549,-429,5,1550,-14,3,1551,-15,3,1552,-432,5,1553,-433,5,1554,-105,4,1555,-435,5,1556,-4,2,1557,-432,5,1558,-438,5,1559,-439,5,1560,-440,5,1561,-441,5,1562,-442,5,1563,-443,5,1564,-444,5,1565,-445,5,1566,-14,3,1567,-15,3,1568,-448,5,1569,-449,5,1570,-450,5,1571,-451,5,1572,-4,2,1573,-448,5,1574,-448,5,1575,-455,5,1576,-456,5,1577,-457,5,1578,-458,5,1579,-459,5,1580,-460,5,1581,-13,3,1582,-14,3,1583,-15,3,1584,-16,3,1585,-16,3,1586,-16,3,1587,-448,5,1588,-16,3,1589,-16,3,1590,-448,5,1591,-448,5,1592,-448,5,1593,-448,5,1594,-448,5,1595,-448,5,1596,-448,5,1597,-477,5,1598,-478,5,1599,-479,5,1600,-480,5,1601,-481,5,1602,-480,5,1603,-483,5,1604,-4,2,1605,-480,5,1606,-480,5,1607,-480,5,1608,-480,5,1609,-489,5,1610,-490,5,1611,-491,5,1612,-492,5,1613,-13,3,1614,-14,3,1615,-15,3,1616,-16,3,1617,-16,3,1618,-16,3,1619,-480,5,1620,-52,4,1621,-16,3,1622,-480,5,1623,-480,5,1624,-56,4,1625,-480,5,1626,-480,5,1627,-480,5,1628,-60,4,1629,-480,5,1630,-480,5,1631,-480,5,1632,-64,4,1633,-65,4,1634,-64,4,1635,-64,4,1636,-64,4,1637,-64,4,1638,-70,4,1639,-64,4,1640,-72,4,1641,-73,4,1642,-74,4,1643,-75,4,1644,-60,4,1645,-77,4,1646,-78,4,1647,-15,3,1648,-80,4,1649,-80,4,1650,-80,4,1651,-80,4,1652,-448,5,1653,-80,4,1654,-80,4,1655,-80,4,1656,-80,4,1657,-441,5,1658,-448,5,1659,-448,5,1660,-448,5,1661,-448,5,1662,-14,3,1663,-15,3,1664,-448,5,1665,-449,5,1666,-448,5,1667,-451,5,1668,-4,2,1669,-448,5,1670,-448,5,1671,-455,5,1672,-456,5,1673,-448,5,1674,-448,5,1675,-448,5,1676,-448,5,1677,-448,5,1678,-14,3,1679,-15,3,1680,3,10,1681,-1,0,1682,-2,1,1683,-3,1,1684,-4,2,1685,-5,2,1686,-6,2,1687,-7,2,1688,-8,2,1689,-9,2,1690,-10,2,1691,-11,2,1692,-12,2,1693,-13,3,1694,-14,3,1695,-15,3,1696,-16,3,1697,-16,3,1698,-16,3,1699,-19,3,1700,-16,3,1701,-16,3,1702,-22,3,1703,-23,3,1704,-24,3,1705,-25,3,1706,-26,3,1707,-27,3,1708,-28,3,1709,-29,3,1710,-30,3,1711,-31,3,1712,-32,3,1713,-33,3,1714,-32,3,1715,-32,3,1716,-36,3,1717,-5,2,1718,-7,2,1719,-39,3,1720,-32,3,1721,-15,3,1722,-42,3,1723,-16,3,1724,-16,3,1725,-45,3,1726,-16,3,1727,-16,3,1728,-48,3,1729,-48,3,1730,-2,1,1731,-48,3,1732,-52,4,1733,-5,2,1734,-15,3,1735,-16,3,1736,-56,4,1737,-57,4,1738,-58,4,1739,-59,4,1740,-60,4,1741,-61,4,1742,-62,4,1743,-63,4,1744,-64,4,1745,-65,4,1746,-64,4,1747,-64,4,1748,-64,4,1749,-64,4,1750,-70,4,1751,-64,4,1752,-72,4,1753,-73,4,1754,-74,4,1755,-75,4,1756,-60,4,1757,-77,4,1758,-78,4,1759,-15,3,1760,-80,4,1761,-80,4,1762,-80,4,1763,-80,4,1764,-84,4,1765,-80,4,1766,-80,4,1767,-80,4,1768,-80,4,1769,-89,4,1770,-90,4,1771,-91,4,1772,-92,4,1773,-93,4,1774,-14,3,1775,-15,3,1776,-96,4,1777,-96,4,1778,-98,4,1779,-96,4,1780,-4,2,1781,-5,2,1782,-96,4,1783,-96,4,1784,-96,4,1785,-105,4,1786,-106,4,1787,-107,4,1788,-108,4,1789,-109,4,1790,-14,3,1791,-15,3,1792,-112,4,1793,-112,4,1794,-112,4,1795,-112,4,1796,-4,2,1797,-5,2,1798,-6,2,1799,-112,4,1800,-112,4,1801,-112,4,1802,-112,4,1803,-123,4,1804,-124,4,1805,-13,3,1806,-14,3,1807,-15,3,1808,-16,3,1809,-16,3,1810,-16,3,1811,-19,3,1812,-4,2,1813,-16,3,1814,-22,3,1815,-23,3,1816,-24,3,1817,-25,3,1818,-26,3,1819,-112,4,1820,-112,4,1821,-112,4,1822,-112,4,1823,-112,4,1824,-112,4,1825,-112,4,1826,-146,4,1827,-147,4,1828,-148,4,1829,-5,2,1830,-150,4,1831,-151,4,1832,-152,4,1833,-153,4,1834,-154,4,1835,-75,4,1836,-60,4,1837,-157,4,1838,-16,3,1839,-16,3,1840,-160,4,1841,-65,4,1842,-64,4,1843,-163,4,1844,-164,4,1845,-60,4,1846,-70,4,1847,-16,3,1848,-56,4,1849,-64,4,1850,-80,4,1851,-75,4,1852,-60,4,1853,-61,4,1854,-78,4,1855,-15,3,1856,-80,4,1857,-65,4,1858,-80,4,1859,-80,4,1860,-75,4,1861,-80,4,1862,-70,4,1863,-80,4,1864,-80,4,1865,-80,4,1866,-90,4,1867,-75,4,1868,-60,4,1869,-93,4,1870,-78,4,1871,-15,3,1872,-96,4,1873,-96,4,1874,-96,4,1875,-90,4,1876,-84,4,1877,-5,2,1878,-96,4,1879,-96,4,1880,-96,4,1881,-96,4,1882,-90,4,1883,-91,4,1884,-96,4,1885,-96,4,1886,-14,3,1887,-15,3,1888,-96,4,1889,-96,4,1890,-105,4,1891,-96,4,1892,-4,2,1893,-5,2,1894,-96,4,1895,-96,4,1896,-96,4,1897,-105,4,1898,-106,4,1899,-107,4,1900,-108,4,1901,-109,4,1902,-14,3,1903,-15,3,1904,-112,4,1905,-112,4,1906,-112,4,1907,-112,4,1908,-4,2,1909,-5,2,1910,-6,2,1911,-112,4,1912,-112,4,1913,-112,4,1914,-112,4,1915,-112,4,1916,-112,4,1917,-13,3,1918,-14,3,1919,-15,3,1920,-16,3,1921,-16,3,1922,-16,3,1923,-19,3,1924,-4,2,1925,-16,3,1926,-22,3,1927,-23,3,1928,-24,3,1929,-25,3,1930,-26,3,1931,-112,4,1932,-112,4,1933,-112,4,1934,-112,4,1935,-112,4,1936,-112,4,1937,-65,4,1938,-105,4,1939,-112,4,1940,-112,4,1941,-60,4,1942,-112,4,1943,-112,4,1944,-112,4,1945,-112,4,1946,-112,4,1947,-75,4,1948,-60,4,1949,-112,4,1950,-270,5,1951,-16,3,1952,-112,4,1953,-65,4,1954,-64,4,1955,-80,4,1956,-112,4,1957,-60,4,1958,-70,4,1959,-16,3,1960,-56,4,1961,-80,4,1962,-90,4,1963,-75,4,1964,-60,4,1965,-61,4,1966,-78,4,1967,-15,3,1968,-288,5,1969,-289,5,1970,-290,5,1971,-291,5,1972,-292,5,1973,-293,5,1974,-294,5,1975,-295,5,1976,-296,5,1977,-96,4,1978,-298,5,1979,-299,5,1980,-300,5,1981,-13,3,1982,-14,3,1983,-15,3,1984,-96,4,1985,-96,4,1986,-96,4,1987,-90,4,1988,-84,4,1989,-5,2,1990,-96,4,1991,-96,4,1992,-96,4,1993,-96,4,1994,-90,4,1995,-315,5,1996,-316,5,1997,-317,5,1998,-318,5,1999,-319,5,2000,-96,4,2001,-96,4,2002,-105,4,2003,-96,4,2004,-4,2,2005,-5,2,2006,-96,4,2007,-96,4,2008,-96,4,2009,-105,4,2010,-106,4,2011,-107,4,2012,-108,4,2013,-109,4,2014,-14,3,2015,-15,3,2016,-336,5,2017,-337,5,2018,-338,5,2019,-339,5,2020,-4,2,2021,-341,5,2022,-342,5,2023,-343,5,2024,-344,5,2025,-345,5,2026,-346,5,2027,-347,5,2028,-348,5,2029,-13,3,2030,-14,3,2031,-15,3,2032,-16,3,2033,-16,3,2034,-16,3,2035,-355,5,2036,-4,2,2037,-16,3,2038,-358,5,2039,-359,5,2040,-360,5,2041,-361,5,2042,-362,5,2043,-363,5,2044,-364,5,2045,-365,5,2046,-366,5,2047,-367,5,0,0,0"), // leafSize 17 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,14,16,15,15,18,16,16,19,17,17,21,18,-2,1,19,-2,1,20,-3,1,21,-8,2,22,-11,2,23,-10,2,24,-8,2,25,-10,2,26,-13,2,27,-11,2,28,-13,2,29,-13,2,30,-15,2,31,-15,2,32,-16,2,33,-16,2,34,-17,2,35,-2,1,36,3,5,37,-17,2,38,-10,2,39,3,5,40,-8,2,41,-13,2,42,3,5,43,-11,2,44,-13,2,45,3,5,46,-15,2,47,-15,2,48,3,5,49,-16,2,50,-16,2,51,3,5,52,4,8,53,-2,1,54,-15,2,55,-7,2,56,4,8,57,-9,2,58,-10,2,59,-11,2,60,4,8,61,-13,2,62,-14,2,63,-15,2,64,4,8,65,5,11,66,-15,2,67,-16,2,68,4,9,69,-1,1,70,5,11,71,-7,2,72,-8,2,73,-13,2,74,-10,2,75,5,11,76,-16,3,77,-13,2,78,6,14,79,-15,2,80,5,12,81,-17,3,82,-14,2,83,-15,3,84,-16,3,85,5,12,86,-1,1,87,-2,1,88,-8,2,89,-9,2,90,6,15,91,7,17,92,-12,2,93,-13,2,94,-14,2,95,-15,3,96,6,15,97,-17,3,98,-13,2,99,-14,2,100,-15,3,101,-16,3,102,6,15,103,-1,1,104,-2,1,105,7,18,106,-10,2,107,-11,2,108,-12,2,109,-13,2,110,-14,2,111,-15,3,112,7,18,113,-17,3,114,-12,2,115,-13,2,116,-14,3,117,-15,3,118,-16,3,119,7,18,120,-1,1,121,-2,1,122,-10,2,123,-11,2,124,-12,2,125,-13,2,126,-14,3,127,-15,3,128,-16,3,129,-17,3,130,-11,2,131,-12,2,132,-13,2,133,-14,3,134,-15,3,135,-16,3,136,-17,3,137,-18,3,138,-19,3,139,-27,3,140,-28,3,141,-29,3,142,-30,3,143,-31,3,144,-32,3,145,-33,3,146,-34,3,147,-51,3,148,-29,3,149,-30,3,150,-31,3,151,-32,3,152,-33,3,153,-34,3,154,-42,3,155,-75,3,156,-60,3,157,-45,3,158,-39,3,159,-16,3,160,-48,3,161,-42,3,162,-60,3,163,-51,3,164,-68,3,165,-80,3,166,-64,3,167,-48,3,168,-56,3,169,-64,3,170,-51,3,171,-75,3,172,-60,3,173,-68,3,174,-78,3,175,-85,3,176,-64,3,177,-75,3,178,-15,3,179,-60,3,180,-68,3,181,-85,3,182,-80,3,183,-64,3,184,-65,3,185,-80,3,186,-90,3,187,-85,3,188,-1,0,189,-70,3,190,-85,4,191,-15,3,192,-96,4,193,-17,3,194,-75,3,195,-90,4,196,-16,3,197,-85,4,198,-96,4,199,-80,4,200,-17,3,201,-96,4,202,-90,4,203,-16,3,204,-102,4,205,-1,0,206,-2,1,207,-102,4,208,-96,4,209,-90,4,210,-105,4,211,-102,4,212,-15,3,213,-16,3,214,-102,4,215,-96,4,216,-17,3,217,-105,4,218,-14,3,219,-15,3,220,-16,3,221,-102,4,222,-1,0,223,-2,1,224,-112,4,225,-17,3,226,-112,4,227,-112,4,228,-14,3,229,-15,3,230,-16,3,231,-112,4,232,-17,3,233,-114,4,234,-115,4,235,-14,3,236,-15,3,237,-16,3,238,-119,4,239,-1,0,240,-16,3,241,-17,3,242,-119,4,243,-119,4,244,-119,4,245,-14,3,246,-15,3,247,-16,3,248,-17,3,249,-119,4,250,-119,4,251,-119,4,252,-14,3,253,-15,3,254,-16,3,255,-17,3,256,-112,4,257,-112,4,258,-112,4,259,-112,4,260,-112,4,261,-112,4,262,-112,4,263,-119,4,264,-119,4,265,-119,4,266,-119,4,267,-119,4,268,-119,4,269,-119,4,270,-119,4,271,-119,4,272,-119,4,273,-102,4,274,-112,4,275,-112,4,276,-112,4,277,-112,4,278,-112,4,279,-119,4,280,-119,4,281,-119,4,282,-119,4,283,-102,4,284,-102,4,285,-119,4,286,-119,4,287,-112,4,288,3,8,289,-85,4,290,-119,4,291,-119,4,292,-112,4,293,-112,4,294,-96,4,295,-119,4,296,-119,4,297,-9,2,298,-119,4,299,-85,4,300,-96,4,301,-119,4,302,-119,4,303,-15,3,304,-16,3,305,-17,3,306,3,8,307,-1,0,308,-2,1,309,-85,4,310,-96,4,311,-96,4,312,-6,2,313,-7,2,314,-8,2,315,-9,2,316,-102,4,317,-96,4,318,-80,4,319,-13,3,320,-14,3,321,-15,3,322,-16,3,323,-17,3,324,-17,3,325,-17,3,326,-102,4,327,-96,4,328,-90,4,329,-105,4,330,-14,3,331,-15,3,332,-16,3,333,-102,4,334,-96,4,335,-29,3,336,3,8,337,-1,0,338,-2,1,339,-33,3,340,-102,4,341,-5,2,342,-6,2,343,-112,4,344,-8,2,345,-9,2,346,-10,2,347,-11,2,348,-12,2,349,-13,3,350,-112,4,351,-15,3,352,-16,3,353,-17,3,354,-48,3,355,-15,3,356,-16,3,357,3,8,358,-1,0,359,-2,1,360,-3,1,361,-4,2,362,-5,2,363,-6,2,364,-7,2,365,-8,2,366,-9,2,367,-10,2,368,-11,2,369,-12,2,370,-13,3,371,-14,3,372,-15,3,373,-16,3,374,-17,3,375,-17,3,376,-17,3,377,-20,3,378,-16,3,379,-17,3,380,-23,3,381,-24,3,382,-25,3,383,-26,3,384,4,12,385,-28,3,386,-80,4,387,-30,3,388,-31,3,389,-32,3,390,-33,3,391,-34,3,392,-8,2,393,-36,3,394,-10,2,395,-11,2,396,-90,4,397,-13,3,398,-14,3,399,-42,3,400,-112,4,401,-17,3,402,-96,4,403,-80,4,404,-68,4,405,-48,3,406,-17,3,407,-119,4,408,4,12,409,-1,0,410,-2,1,411,-105,4,412,-4,2,413,-5,2,414,-6,2,415,-7,2,416,-8,2,417,-60,4,418,-112,4,419,-11,2,420,-12,2,421,-64,4,422,-14,3,423,-15,3,424,-16,3,425,-119,4,426,-90,4,427,-70,4,428,-102,4,429,-96,4,430,-96,4,431,-125,4,432,-75,4,433,-15,3,434,-16,3,435,-17,3,436,-96,4,437,-80,4,438,-102,4,439,-31,3,440,-32,3,441,-33,3,442,-85,4,443,-85,4,444,-85,4,445,-102,4,446,-96,4,447,-90,4,448,4,13,449,-1,0,450,-2,1,451,-3,1,452,-102,4,453,-96,4,454,-17,3,455,-119,4,456,-48,3,457,-15,3,458,-16,3,459,-102,4,460,-12,2,461,-13,3,462,-105,4,463,-15,3,464,-16,3,465,-17,3,466,-109,4,467,-14,3,468,-15,3,469,-112,4,470,-17,3,471,-114,4,472,-64,4,473,-14,3,474,-15,3,475,-16,3,476,4,13,477,-1,0,478,-2,1,479,-3,1,480,-4,2,481,-5,2,482,-6,2,483,-7,2,484,-8,2,485,-9,2,486,-10,2,487,-11,2,488,-12,2,489,-13,3,490,-14,3,491,-15,3,492,-16,3,493,-17,3,494,-17,3,495,-17,3,496,-20,3,497,-16,3,498,-90,4,499,-23,3,500,-24,3,501,-25,3,502,-26,3,503,-27,3,504,-96,4,505,-29,3,506,-30,3,507,-31,3,508,-32,3,509,-33,3,510,5,17,511,-1,0,512,-2,1,513,-3,1,514,-4,2,515,-5,2,516,-6,2,517,-7,2,518,-8,2,519,-9,2,520,-112,4,521,-11,2,522,-12,2,523,-13,3,524,-14,3,525,-15,3,526,-16,3,527,-17,3,528,-80,4,529,-17,3,530,-112,4,531,-123,4,532,-56,4,533,-85,4,534,-14,3,535,-15,3,536,-60,4,537,-112,4,538,-28,3,539,-29,3,540,-64,4,541,-31,3,542,-32,3,543,-33,3,544,-68,4,545,-68,4,546,-70,4,547,-85,4,548,-72,4,549,-96,4,550,-102,4,551,-75,4,552,-60,4,553,-105,4,554,-85,4,555,-96,4,556,-80,4,557,-64,4,558,-48,3,559,-15,3,560,5,17,561,-85,4,562,-2,1,563,-85,4,564,-4,2,565,-96,4,566,-90,4,567,-119,4,568,-8,2,569,-9,2,570,-10,2,571,-11,2,572,-96,4,573,-13,3,574,-14,3,575,-15,3,576,-16,3,577,-17,3,578,-102,4,579,-102,4,580,-102,4,581,-105,4,582,-106,4,583,-107,4,584,-108,4,585,-109,4,586,-14,3,587,-15,3,588,-112,4,589,-17,3,590,-80,4,591,-115,4,592,-14,3,593,-15,3,594,-16,3,595,5,17,596,-1,0,597,-2,1,598,-3,1,599,-4,2,600,-5,2,601,-6,2,602,-7,2,603,-8,2,604,-9,2,605,-10,2,606,-11,2,607,-12,2,608,-13,3,609,-14,3,610,-15,3,611,-16,3,612,-17,3,613,-17,3,614,-17,3,615,-105,4,616,-16,3,617,-17,3,618,-23,3,619,-24,3,620,-25,3,621,-26,3,622,-112,4,623,-28,3,624,-29,3,625,-30,3,626,-31,3,627,-32,3,628,-33,3,629,-119,4,630,-119,4,631,-36,3,632,-112,4,633,-123,4,634,-39,3,635,-125,4,636,-14,3,637,-42,3,638,-16,3,639,-17,3,640,-45,3,641,-16,3,642,-16,3,643,-48,3,644,-17,3,645,-85,4,646,-51,4,647,-52,4,648,-51,4,649,-68,4,650,-90,4,651,-56,4,652,-80,4,653,-58,4,654,-59,4,655,-60,4,656,-96,4,657,-85,4,658,-80,4,659,-64,4,660,-65,4,661,-66,4,662,-102,4,663,-68,4,664,-68,4,665,-70,4,666,-85,4,667,-72,4,668,-96,4,669,-74,4,670,-75,4,671,-60,4,672,-112,4,673,-85,4,674,-96,4,675,-80,4,676,-64,4,677,-82,4,678,-15,3,679,-119,4,680,-85,4,681,-85,4,682,-85,4,683,-102,4,684,-96,4,685,-90,4,686,-91,4,687,-92,4,688,-93,4,689,-14,3,690,-102,4,691,-96,4,692,-17,3,693,-98,4,694,-14,3,695,-15,3,696,-16,3,697,-102,4,698,-102,4,699,-102,4,700,-105,4,701,-106,4,702,-107,4,703,-108,4,704,-109,4,705,-14,3,706,-15,3,707,-112,4,708,-17,3,709,-114,4,710,-115,4,711,-14,3,712,-15,3,713,-16,3,714,-119,4,715,-119,4,716,-119,4,717,-112,4,718,-123,4,719,-124,4,720,-13,3,721,-14,3,722,-15,3,723,-16,3,724,-17,3,725,-130,4,726,-131,4,727,-13,3,728,-14,3,729,-15,3,730,-16,3,731,-17,3,732,-17,3,733,-17,3,734,-112,4,735,-112,4,736,-112,4,737,-112,4,738,-112,4,739,-144,4,740,-145,4,741,-112,4,742,-147,4,743,-119,4,744,-119,4,745,-119,4,746,-151,4,747,-152,4,748,-119,4,749,-154,4,750,-75,4,751,-156,4,752,-157,4,753,-158,4,754,-306,5,755,-160,4,756,-161,4,757,-162,4,758,-163,4,759,-68,4,760,-80,4,761,-64,4,762,-167,4,763,-168,4,764,-288,4,765,-85,4,766,-75,4,767,-60,4,768,-68,4,769,-85,4,770,-85,4,771,-80,4,772,-75,4,773,-15,3,774,-60,4,775,-68,4,776,-85,4,777,-80,4,778,-64,4,779,-65,4,780,-80,4,781,-90,4,782,-85,4,783,-85,4,784,-70,4,785,-85,4,786,-96,4,787,-96,4,788,-17,3,789,-75,4,790,-90,4,791,-112,4,792,-85,4,793,-96,4,794,-80,4,795,-64,4,796,-96,4,797,-90,4,798,-119,4,799,-102,4,800,-102,4,801,-102,4,802,-102,4,803,-96,4,804,-90,4,805,-357,5,806,-102,4,807,-15,3,808,-16,3,809,-102,4,810,-96,4,811,-17,3,812,-336,5,813,-14,3,814,-15,3,815,-16,3,816,-102,4,817,-102,4,818,-102,4,819,-112,4,820,-17,3,821,-112,4,822,-112,4,823,-14,3,824,-15,3,825,-16,3,826,-112,4,827,-17,3,828,-114,4,829,-115,4,830,-14,3,831,-15,3,832,-16,3,833,-119,4,834,-119,4,835,-16,3,836,-17,3,837,-119,4,838,-119,4,839,-13,3,840,-14,3,841,-15,3,842,-16,3,843,-17,3,844,-119,4,845,-119,4,846,-13,3,847,-14,3,848,-15,3,849,-16,3,850,-17,3,851,-112,4,852,-112,4,853,-112,4,854,-112,4,855,-112,4,856,-408,5,857,-112,4,858,-119,4,859,-119,4,860,-384,5,861,-85,4,862,-119,4,863,-119,4,864,-119,4,865,-119,4,866,-306,5,867,-357,5,868,-358,5,869,-359,5,870,-112,4,871,-68,4,872,-85,4,873,-80,4,874,-119,4,875,-365,5,876,-60,4,877,-85,4,878,-85,4,879,-80,4,880,-64,4,881,-119,4,882,-372,5,883,-288,5,884,-408,5,885,-409,5,886,-410,5,887,-68,4,888,-85,4,889,-96,4,890,-80,4,891,-75,4,892,-416,5,893,-60,4,894,-85,4,895,-96,4,896,-80,4,897,-64,4,898,-14,3,899,-423,5,900,-424,5,901,-306,5,902,-307,5,903,-308,5,904,-85,4,905,-96,4,906,-96,4,907,-312,5,908,-75,4,909,-314,5,910,-315,5,911,-102,4,912,-96,4,913,-80,4,914,-13,3,915,-320,5,916,-321,5,917,-357,5,918,-408,5,919,-359,5,920,-410,5,921,-102,4,922,-96,4,923,-90,4,924,-448,5,925,-449,5,926,-450,5,927,-16,3,928,-102,4,929,-96,4,930,-13,3,931,-336,5,932,-337,5,933,-373,5,934,-374,5,935,-102,4,936,-460,5,937,-13,3,938,-112,4,939,-448,5,940,-464,5,941,-465,5,942,-347,5,943,-348,5,944,-13,3,945,-112,4,946,-351,5,947,-352,5,948,-353,5,949,-354,5,950,-15,3,951,-16,3,952,-476,5,953,-476,5,954,-476,5,955,-476,5,956,-476,5,957,-476,5,958,-448,5,959,-476,5,960,-476,5,961,-476,5,962,-476,5,963,-476,5,964,-476,5,965,-13,3,966,-14,3,967,-15,3,968,-476,5,969,-476,5,970,-477,5,971,-478,5,972,-462,5,973,-481,5,974,-476,5,975,-476,5,976,-476,5,977,-476,5,978,-476,5,979,-384,5,980,-476,5,981,-386,5,982,-476,5,983,-476,5,984,-476,5,985,-476,5,986,-476,5,987,-477,5,988,-478,5,989,-479,5,990,-480,5,991,-481,5,992,-482,5,993,-483,5,994,-484,5,995,-485,5,996,-476,5,997,-402,5,998,-488,5,999,-13,3,1000,-14,3,1001,-15,3,1002,-476,5,1003,-408,5,1004,-409,5,1005,-410,5,1006,-411,5,1007,-412,5,1008,-448,5,1009,-414,5,1010,-415,5,1011,-416,5,1012,-60,4,1013,-418,5,1014,-419,5,1015,-420,5,1016,-64,4,1017,-14,3,1018,-15,3,1019,-424,5,1020,-510,5,1021,-426,5,1022,-476,5,1023,-102,4,1024,-96,4,1025,-96,4,1026,-476,5,1027,-476,5,1028,-15,3,1029,-469,5,1030,-510,5,1031,-96,4,1032,-476,5,1033,-438,5,1034,-14,3,1035,-15,3,1036,-476,5,1037,-476,5,1038,-478,5,1039,-478,5,1040,-102,4,1041,-96,4,1042,-476,5,1043,-448,5,1044,-449,5,1045,-450,5,1046,-486,5,1047,-102,4,1048,-476,5,1049,-13,3,1050,-455,5,1051,-15,3,1052,-16,3,1053,-493,5,1054,-476,5,1055,-460,5,1056,-13,3,1057,-476,5,1058,-15,3,1059,-16,3,1060,-465,5,1061,-476,5,1062,-14,3,1063,-15,3,1064,-476,5,1065,-476,5,1066,-510,5,1067,-64,4,1068,-14,3,1069,-15,3,1070,-510,5,1071,-476,5,1072,-477,5,1073,-478,5,1074,-479,5,1075,-4,2,1076,-481,5,1077,-482,5,1078,-483,5,1079,-484,5,1080,-485,5,1081,-486,5,1082,-487,5,1083,-488,5,1084,-13,3,1085,-14,3,1086,-15,3,1087,-16,3,1088,-17,3,1089,-17,3,1090,-17,3,1091,-510,5,1092,-16,3,1093,-498,5,1094,-499,5,1095,-500,5,1096,-501,5,1097,-502,5,1098,-510,5,1099,-504,5,1100,-505,5,1101,-506,5,1102,-507,5,1103,-508,5,1104,-509,5,1105,-510,5,1106,-511,5,1107,-512,5,1108,-513,5,1109,-4,2,1110,-515,5,1111,-516,5,1112,-517,5,1113,-518,5,1114,-519,5,1115,-520,5,1116,-521,5,1117,-522,5,1118,-13,3,1119,-14,3,1120,-15,3,1121,-16,3,1122,-17,3,1123,-528,5,1124,-17,3,1125,-530,5,1126,-560,5,1127,-56,4,1128,-533,5,1129,-14,3,1130,-15,3,1131,-60,4,1132,-560,5,1133,-538,5,1134,-539,5,1135,-64,4,1136,-541,5,1137,-542,5,1138,-560,5,1139,-68,4,1140,-68,4,1141,-70,4,1142,-561,5,1143,-72,4,1144,-572,5,1145,-550,5,1146,-476,5,1147,-60,4,1148,-560,5,1149,-561,5,1150,-572,5,1151,-476,5,1152,-64,4,1153,-558,5,1154,-15,3,1155,-560,5,1156,-561,5,1157,-562,5,1158,-563,5,1159,-4,2,1160,-572,5,1161,-566,5,1162,-567,5,1163,-568,5,1164,-569,5,1165,-570,5,1166,-578,5,1167,-572,5,1168,-13,3,1169,-14,3,1170,-15,3,1171,-16,3,1172,-17,3,1173,-578,5,1174,-579,5,1175,-580,5,1176,-581,5,1177,-582,5,1178,-583,5,1179,-584,5,1180,-585,5,1181,-14,3,1182,-15,3,1183,-588,5,1184,-17,3,1185,-510,5,1186,-591,5,1187,-14,3,1188,-15,3,1189,-16,3,1190,-595,5,1191,-595,5,1192,-595,5,1193,-595,5,1194,-4,2,1195,-595,5,1196,-595,5,1197,-595,5,1198,-595,5,1199,-595,5,1200,-595,5,1201,-595,5,1202,-595,5,1203,-13,3,1204,-14,3,1205,-15,3,1206,-16,3,1207,-17,3,1208,-17,3,1209,-17,3,1210,-595,5,1211,-16,3,1212,-17,3,1213,-595,5,1214,-595,5,1215,-595,5,1216,-595,5,1217,-595,5,1218,-595,5,1219,-595,5,1220,-595,5,1221,-595,5,1222,-595,5,1223,-595,5,1224,-595,5,1225,-595,5,1226,-595,5,1227,-595,5,1228,-595,5,1229,-595,5,1230,-13,3,1231,-14,3,1232,-595,5,1233,-16,3,1234,-17,3,1235,-595,5,1236,-16,3,1237,-16,3,1238,-595,5,1239,-17,3,1240,-560,5,1241,-51,4,1242,-52,4,1243,-51,4,1244,-68,4,1245,-595,5,1246,-56,4,1247,-80,4,1248,-58,4,1249,-59,4,1250,-60,4,1251,-595,5,1252,-572,5,1253,-80,4,1254,-64,4,1255,-65,4,1256,-66,4,1257,-595,5,1258,-68,4,1259,-68,4,1260,-70,4,1261,-581,5,1262,-72,4,1263,-595,5,1264,-74,4,1265,-75,4,1266,-60,4,1267,-595,5,1268,-588,5,1269,-595,5,1270,-80,4,1271,-64,4,1272,-82,4,1273,-15,3,1274,-595,5,1275,-85,4,1276,-85,4,1277,-85,4,1278,-595,5,1279,-595,5,1280,-595,5,1281,-595,5,1282,-595,5,1283,-595,5,1284,-14,3,1285,-595,5,1286,-595,5,1287,-17,3,1288,-595,5,1289,-14,3,1290,-15,3,1291,-16,3,1292,-595,5,1293,-596,5,1294,-597,5,1295,-595,5,1296,-595,5,1297,-595,5,1298,-595,5,1299,-595,5,1300,-14,3,1301,-15,3,1302,-595,5,1303,-17,3,1304,-595,5,1305,-595,5,1306,-14,3,1307,-15,3,1308,-16,3,1309,-595,5,1310,-596,5,1311,-597,5,1312,-605,5,1313,-595,5,1314,-595,5,1315,-13,3,1316,-14,3,1317,-15,3,1318,-16,3,1319,-17,3,1320,-595,5,1321,-595,5,1322,-13,3,1323,-14,3,1324,-15,3,1325,-16,3,1326,-17,3,1327,-17,3,1328,-17,3,1329,-622,5,1330,-623,5,1331,-624,5,1332,-625,5,1333,-626,5,1334,-595,5,1335,-595,5,1336,-629,5,1337,-595,5,1338,-624,5,1339,-625,5,1340,-626,5,1341,-627,5,1342,-595,5,1343,-629,5,1344,-595,5,1345,-75,4,1346,-595,5,1347,-595,5,1348,-595,5,1349,-306,5,1350,-595,5,1351,-595,5,1352,-60,4,1353,-595,5,1354,-68,4,1355,-80,4,1356,-64,4,1357,-595,5,1358,-595,5,1359,-288,5,1360,-408,5,1361,-75,4,1362,-60,4,1363,-68,4,1364,-85,4,1365,-85,4,1366,-80,4,1367,-75,4,1368,-15,3,1369,-60,4,1370,-68,4,1371,-85,4,1372,-80,4,1373,-64,4,1374,-65,4,1375,-80,4,1376,-90,4,1377,-85,4,1378,-85,4,1379,-70,4,1380,-85,4,1381,-595,5,1382,-595,5,1383,-17,3,1384,-75,4,1385,-90,4,1386,-679,5,1387,-85,4,1388,-595,5,1389,-80,4,1390,-64,4,1391,-595,5,1392,-90,4,1393,-679,5,1394,-697,5,1395,-697,5,1396,-697,5,1397,-697,5,1398,-595,5,1399,-90,4,1400,-448,5,1401,-697,5,1402,-15,3,1403,-16,3,1404,-697,5,1405,-595,5,1406,-17,3,1407,-336,5,1408,-14,3,1409,-15,3,1410,-16,3,1411,-697,5,1412,-698,5,1413,-699,5,1414,-707,5,1415,-17,3,1416,-707,5,1417,-707,5,1418,-14,3,1419,-15,3,1420,-16,3,1421,-707,5,1422,-17,3,1423,-709,5,1424,-710,5,1425,-14,3,1426,-15,3,1427,-16,3,1428,3,10,1429,-1,0,1430,-2,1,1431,-3,1,1432,-4,2,1433,-5,2,1434,-6,2,1435,-7,2,1436,-8,2,1437,-9,2,1438,-10,2,1439,-11,2,1440,-12,2,1441,-13,3,1442,-14,3,1443,-15,3,1444,-16,3,1445,-17,3,1446,-17,3,1447,-17,3,1448,-20,3,1449,-16,3,1450,-17,3,1451,-23,3,1452,-24,3,1453,-25,3,1454,-26,3,1455,-27,3,1456,-28,3,1457,-29,3,1458,-30,3,1459,-31,3,1460,-32,3,1461,-33,3,1462,-476,5,1463,-476,5,1464,-36,3,1465,-707,5,1466,-68,4,1467,-39,3,1468,-80,4,1469,-714,5,1470,-42,3,1471,-16,3,1472,-408,5,1473,-45,3,1474,-80,4,1475,-16,3,1476,-48,3,1477,-17,3,1478,-288,5,1479,-408,5,1480,-52,4,1481,-410,5,1482,-68,4,1483,-85,4,1484,-56,4,1485,-80,4,1486,-58,4,1487,-59,4,1488,-60,4,1489,-418,5,1490,-96,4,1491,-80,4,1492,-64,4,1493,-65,4,1494,-15,3,1495,-16,3,1496,-306,5,1497,-307,5,1498,-70,4,1499,-85,4,1500,-96,4,1501,-96,4,1502,-74,4,1503,-75,4,1504,-60,4,1505,-77,4,1506,-595,5,1507,-96,4,1508,-80,4,1509,-64,4,1510,-14,3,1511,-15,3,1512,-476,5,1513,-85,4,1514,-85,4,1515,-85,4,1516,-707,5,1517,-96,4,1518,-90,4,1519,-448,5,1520,-92,4,1521,-93,4,1522,-14,3,1523,-707,5,1524,-96,4,1525,-17,3,1526,-336,5,1527,-14,3,1528,-15,3,1529,-16,3,1530,-102,4,1531,-102,4,1532,-102,4,1533,-105,4,1534,-106,4,1535,-107,4,1536,-108,4,1537,-109,4,1538,-14,3,1539,-15,3,1540,-112,4,1541,-17,3,1542,-114,4,1543,-13,3,1544,-14,3,1545,-15,3,1546,-16,3,1547,-119,4,1548,-119,4,1549,-119,4,1550,-112,4,1551,-123,4,1552,-124,4,1553,-13,3,1554,-14,3,1555,-15,3,1556,-16,3,1557,-17,3,1558,-119,4,1559,-131,4,1560,-13,3,1561,-14,3,1562,-15,3,1563,-16,3,1564,-17,3,1565,-17,3,1566,-17,3,1567,-112,4,1568,-112,4,1569,-112,4,1570,-112,4,1571,-112,4,1572,-112,4,1573,-112,4,1574,-384,5,1575,-408,5,1576,-119,4,1577,-119,4,1578,-119,4,1579,-119,4,1580,-119,4,1581,-476,5,1582,-477,5,1583,-476,5,1584,-60,4,1585,-157,4,1586,-476,5,1587,-476,5,1588,-160,4,1589,-476,5,1590,-476,5,1591,-408,5,1592,-402,5,1593,-80,4,1594,-13,3,1595,-14,3,1596,-15,3,1597,-16,3,1598,-408,5,1599,-409,5,1600,-410,5,1601,-411,5,1602,-4,2,1603,-448,5,1604,-80,4,1605,-75,4,1606,-416,5,1607,-60,4,1608,-418,5,1609,-85,4,1610,-80,4,1611,-64,4,1612,-14,3,1613,-15,3,1614,-16,3,1615,-510,5,1616,-426,5,1617,-70,4,1618,-85,4,1619,-96,4,1620,-96,4,1621,-448,5,1622,-75,4,1623,-15,3,1624,-476,5,1625,-85,4,1626,-96,4,1627,-80,4,1628,-438,5,1629,-96,4,1630,-90,4,1631,-476,5,1632,-102,4,1633,-478,5,1634,-102,4,1635,-102,4,1636,-96,4,1637,-90,4,1638,-448,5,1639,-449,5,1640,-450,5,1641,-16,3,1642,-102,4,1643,-96,4,1644,-13,3,1645,-455,5,1646,-15,3,1647,-16,3,1648,-17,3,1649,-102,4,1650,-460,5,1651,-13,3,1652,-112,4,1653,-15,3,1654,-16,3,1655,-17,3,1656,-14,3,1657,-15,3,1658,-16,3,1659,-112,4,1660,-17,3,1661,-80,4,1662,-64,4,1663,-14,3,1664,-15,3,1665,-510,5,1666,-476,5,1667,-477,5,1668,-478,5,1669,-479,5,1670,-4,2,1671,-476,5,1672,-476,5,1673,-483,5,1674,-484,5,1675,-485,5,1676,-486,5,1677,-487,5,1678,-488,5,1679,-13,3,1680,-14,3,1681,-15,3,1682,-16,3,1683,-17,3,1684,-17,3,1685,-17,3,1686,-476,5,1687,-16,3,1688,-408,5,1689,-476,5,1690,-476,5,1691,-476,5,1692,-476,5,1693,-476,5,1694,-408,5,1695,-476,5,1696,-476,5,1697,-507,5,1698,-508,5,1699,-509,5,1700,-510,5,1701,-511,5,1702,-510,5,1703,-513,5,1704,-4,2,1705,-510,5,1706,-510,5,1707,-510,5,1708,-510,5,1709,-510,5,1710,-408,5,1711,-521,5,1712,-522,5,1713,-13,3,1714,-14,3,1715,-15,3,1716,-16,3,1717,-17,3,1718,-80,4,1719,-17,3,1720,-418,5,1721,-85,4,1722,-56,4,1723,-85,4,1724,-14,3,1725,-15,3,1726,-60,4,1727,-476,5,1728,-96,4,1729,-510,5,1730,-64,4,1731,-510,5,1732,-510,5,1733,-476,5,1734,-306,5,1735,-307,5,1736,-70,4,1737,-85,4,1738,-96,4,1739,-96,4,1740,-448,5,1741,-75,4,1742,-314,5,1743,-476,5,1744,-102,4,1745,-96,4,1746,-80,4,1747,-64,4,1748,-558,5,1749,-15,3,1750,-560,5,1751,-85,4,1752,-560,5,1753,-85,4,1754,-102,4,1755,-96,4,1756,-476,5,1757,-448,5,1758,-560,5,1759,-560,5,1760,-560,5,1761,-102,4,1762,-476,5,1763,-13,3,1764,-336,5,1765,-15,3,1766,-16,3,1767,-17,3,1768,-476,5,1769,-477,5,1770,-478,5,1771,-476,5,1772,-476,5,1773,-476,5,1774,-476,5,1775,-476,5,1776,-14,3,1777,-15,3,1778,-476,5,1779,-17,3,1780,-80,4,1781,-476,5,1782,-14,3,1783,-15,3,1784,-16,3,1785,3,11,1786,-1,0,1787,-2,1,1788,-3,1,1789,-4,2,1790,-5,2,1791,-6,2,1792,-7,2,1793,-8,2,1794,-9,2,1795,-10,2,1796,-11,2,1797,-12,2,1798,-13,3,1799,-14,3,1800,-15,3,1801,-16,3,1802,-17,3,1803,-17,3,1804,-17,3,1805,-20,3,1806,-16,3,1807,-17,3,1808,-23,3,1809,-24,3,1810,-25,3,1811,-26,3,1812,-27,3,1813,-28,3,1814,-29,3,1815,-30,3,1816,-31,3,1817,-32,3,1818,-33,3,1819,-34,3,1820,-34,3,1821,-36,3,1822,-5,2,1823,-7,2,1824,-39,3,1825,-32,3,1826,-15,3,1827,-42,3,1828,-16,3,1829,-16,3,1830,-45,3,1831,-16,3,1832,-16,3,1833,-48,4,1834,-17,3,1835,-17,3,1836,-51,4,1837,-52,4,1838,-51,4,1839,-15,3,1840,-16,3,1841,-56,4,1842,-57,4,1843,-58,4,1844,-59,4,1845,-60,4,1846,-61,4,1847,-62,4,1848,-63,4,1849,-64,4,1850,-65,4,1851,-66,4,1852,-51,4,1853,-68,4,1854,-68,4,1855,-70,4,1856,-64,4,1857,-72,4,1858,-73,4,1859,-74,4,1860,-75,4,1861,-60,4,1862,-77,4,1863,-78,4,1864,-15,3,1865,-80,4,1866,-64,4,1867,-82,4,1868,-15,3,1869,-16,3,1870,-85,4,1871,-85,4,1872,-85,4,1873,-80,4,1874,-89,4,1875,-90,4,1876,-91,4,1877,-92,4,1878,-93,4,1879,-14,3,1880,-15,3,1881,-96,4,1882,-17,3,1883,-98,4,1884,-14,3,1885,-15,3,1886,-16,3,1887,-102,4,1888,-102,4,1889,-102,4,1890,-105,4,1891,-106,4,1892,-107,4,1893,-108,4,1894,-109,4,1895,-14,3,1896,-15,3,1897,-112,4,1898,-17,3,1899,-114,4,1900,-13,3,1901,-14,3,1902,-15,3,1903,-16,3,1904,4,16,1905,-1,0,1906,-2,1,1907,-112,4,1908,-123,4,1909,-124,4,1910,-13,3,1911,-14,3,1912,-15,3,1913,-16,3,1914,-17,3,1915,-11,2,1916,-12,2,1917,-13,3,1918,-14,3,1919,-15,3,1920,-16,3,1921,-17,3,1922,-17,3,1923,-17,3,1924,-112,4,1925,-112,4,1926,-112,4,1927,-112,4,1928,-112,4,1929,-112,4,1930,-112,4,1931,-112,4,1932,-28,3,1933,-29,3,1934,-30,3,1935,-31,3,1936,-32,3,1937,-33,3,1938,-510,5,1939,-154,4,1940,-75,4,1941,-60,4,1942,-157,4,1943,-39,3,1944,-16,3,1945,-160,4,1946,-42,3,1947,-60,4,1948,-163,4,1949,-68,4,1950,-80,4,1951,-64,4,1952,-48,4,1953,-17,3,1954,-64,4,1955,-85,4,1956,-75,4,1957,-60,4,1958,-68,4,1959,-78,4,1960,-85,4,1961,-80,4,1962,-75,4,1963,-66,4,1964,-60,4,1965,-68,4,1966,-85,4,1967,-80,4,1968,-64,4,1969,-65,4,1970,-80,4,1971,-90,4,1972,-85,4,1973,-85,4,1974,-70,4,1975,-85,4,1976,-15,3,1977,-96,4,1978,-17,3,1979,-75,4,1980,-90,4,1981,-16,3,1982,-85,4,1983,-96,4,1984,-80,4,1985,-64,4,1986,-96,4,1987,-90,4,1988,-560,5,1989,-85,4,1990,-85,4,1991,-85,4,1992,-102,4,1993,-96,4,1994,-90,4,1995,-105,4,1996,-102,4,1997,-15,3,1998,-16,3,1999,-102,4,2000,-96,4,2001,-17,3,2002,-105,4,2003,-14,3,2004,-15,3,2005,-16,3,2006,-102,4,2007,-102,4,2008,-102,4,2009,-112,4,2010,-17,3,2011,-107,4,2012,-13,3,2013,-109,4,2014,-15,3,2015,-16,3,2016,-112,4,2017,-17,3,2018,-114,4,2019,-13,3,2020,-14,3,2021,-15,3,2022,-16,3,2023,-119,4,2024,-119,4,2025,-119,4,2026,-17,3,2027,-123,4,2028,-124,4,2029,-13,3,2030,-14,3,2031,-15,3,2032,-16,3,2033,-17,3,2034,-119,4,2035,-131,4,2036,-13,3,2037,-14,3,2038,-15,3,2039,-16,3,2040,-17,3,2041,-17,3,2042,-112,4,2043,-112,4,2044,-112,4,2045,-112,4,2046,-112,4,2047,-112,4,0,0,0"), // leafSize 18 splitIntegerList("1,1,0,2,2,0,3,3,1,4,4,3,5,5,4,6,6,5,7,7,7,8,8,8,9,9,10,10,10,11,11,11,12,12,12,14,13,13,15,14,14,16,15,15,18,16,16,19,17,17,21,18,18,22,19,-1,1,20,-2,1,21,-3,1,22,-4,1,23,-5,2,24,-8,2,25,-7,2,26,-8,2,27,-11,2,28,-10,2,29,-13,2,30,-15,2,31,-13,2,32,-16,2,33,-15,2,34,-16,2,35,-17,2,36,-18,2,37,-1,1,38,-2,1,39,3,5,40,-4,2,41,-5,2,42,3,5,43,-7,2,44,-8,2,45,3,5,46,-10,2,47,-11,2,48,3,5,49,-13,2,50,-16,2,51,3,5,52,-16,2,53,-17,2,54,3,5,55,-1,1,56,4,8,57,-3,1,58,-4,2,59,-5,2,60,4,8,61,-7,2,62,-8,2,63,-15,2,64,4,8,65,5,11,66,-18,3,67,-13,2,68,4,9,69,-15,2,70,-16,3,71,-17,3,72,4,9,73,-1,1,74,-2,1,75,5,11,76,-4,2,77,-5,2,78,-6,2,79,-7,2,80,5,12,81,-9,2,82,-10,2,83,-11,2,84,-12,2,85,5,12,86,-14,2,87,-15,3,88,-16,3,89,-17,3,90,5,12,91,-1,1,92,-2,1,93,-3,1,94,-4,2,95,-5,2,96,6,15,97,-7,2,98,-8,2,99,-9,2,100,-10,2,101,-11,2,102,6,15,103,-13,2,104,-14,2,105,-15,3,106,-16,3,107,-17,3,108,6,15,109,-1,1,110,-2,1,111,-3,1,112,7,18,113,-5,2,114,-6,2,115,-7,2,116,-8,2,117,-9,2,118,-10,2,119,7,18,120,-12,2,121,-13,2,122,-14,3,123,-15,3,124,-16,3,125,-17,3,126,7,19,127,-1,1,128,8,21,129,-3,1,130,-4,2,131,-5,2,132,-6,2,133,-7,2,134,-8,2,135,-9,2,136,8,22,137,-11,2,138,-12,2,139,-13,2,140,-14,3,141,-15,3,142,-16,3,143,-17,3,144,-18,3,145,-17,3,146,-18,3,147,-11,2,148,-12,2,149,-13,2,150,-14,3,151,-15,3,152,-16,3,153,-17,3,154,-18,3,155,-29,3,156,-30,3,157,-31,3,158,-32,3,159,-33,3,160,-34,3,161,-35,3,162,-36,3,163,-35,3,164,-36,3,165,-39,3,166,-54,3,167,-31,3,168,-60,3,169,-33,3,170,-80,3,171,-45,3,172,-64,3,173,-54,3,174,-48,3,175,-85,3,176,-68,3,177,-51,3,178,-18,3,179,-18,3,180,-90,3,181,-85,3,182,-54,3,183,-75,3,184,-72,3,185,-5,2,186,-90,3,187,-85,3,188,-80,3,189,-9,2,190,-64,3,191,-72,3,192,-90,4,193,-85,4,194,-68,3,195,-15,3,196,-16,3,197,-17,3,198,-90,4,199,-80,4,200,-72,3,201,-75,4,202,-90,4,203,-5,2,204,-96,4,205,-7,2,206,-80,4,207,-99,4,208,-72,4,209,-90,4,210,-102,4,211,-85,4,212,-14,3,213,-15,3,214,-16,3,215,-17,3,216,-108,4,217,-1,0,218,-90,4,219,-3,1,220,-108,4,221,-102,4,222,-96,4,223,-7,2,224,-8,2,225,-9,2,226,-90,4,227,-108,4,228,-102,4,229,-108,4,230,-14,3,231,-15,3,232,-16,3,233,-17,3,234,-108,4,235,-1,0,236,-108,4,237,-3,1,238,-112,4,239,-5,2,240,-6,2,241,-7,2,242,-8,2,243,-9,2,244,-108,4,245,-119,4,246,-120,4,247,-121,4,248,-14,3,249,-15,3,250,-16,3,251,-17,3,252,-126,4,253,-1,0,254,-126,4,255,-119,4,256,-4,2,257,-5,2,258,-6,2,259,-7,2,260,-8,2,261,-9,2,262,-126,4,263,-126,4,264,-126,4,265,-126,4,266,-14,3,267,-15,3,268,-16,3,269,-17,3,270,3,8,271,-17,3,272,-136,4,273,-126,4,274,-126,4,275,-126,4,276,-14,3,277,-15,3,278,-16,3,279,-17,3,280,-18,3,281,-126,4,282,-126,4,283,-126,4,284,-126,4,285,-126,4,286,-126,4,287,-17,3,288,-18,3,289,-17,3,290,-18,3,291,-126,4,292,-112,4,293,-136,4,294,-90,4,295,-136,4,296,-80,4,297,-126,4,298,-126,4,299,-119,4,300,-126,4,301,-85,4,302,-126,4,303,-126,4,304,-34,3,305,-35,3,306,-90,4,307,-126,4,308,-128,4,309,-126,4,310,-90,4,311,-90,4,312,-96,4,313,-85,4,314,-80,4,315,-90,4,316,-126,4,317,-90,4,318,-102,4,319,-85,4,320,-126,4,321,-15,3,322,-16,3,323,-17,3,324,3,8,325,-1,0,326,-2,1,327,-3,1,328,-4,2,329,-5,2,330,-6,2,331,-7,2,332,-8,2,333,-9,2,334,-10,2,335,-11,2,336,-12,2,337,-13,3,338,-14,3,339,-15,3,340,-16,3,341,-17,3,342,-18,3,343,-18,3,344,-108,4,345,-90,4,346,-108,4,347,-102,4,348,-96,4,349,-25,3,350,-26,3,351,-27,3,352,-108,4,353,-108,4,354,-102,4,355,-31,3,356,-32,3,357,-33,3,358,-34,3,359,-35,3,360,4,12,361,-1,0,362,-108,4,363,-108,4,364,-112,4,365,-5,2,366,-6,2,367,-7,2,368,-8,2,369,-9,2,370,-108,4,371,-119,4,372,-12,2,373,-13,3,374,-14,3,375,-15,3,376,-16,3,377,-17,3,378,3,8,379,-1,0,380,-2,1,381,-3,1,382,-4,2,383,-5,2,384,-6,2,385,-7,2,386,-8,2,387,-9,2,388,-10,2,389,-11,2,390,-12,2,391,-13,3,392,-14,3,393,-15,3,394,-16,3,395,-17,3,396,-18,3,397,-18,3,398,-18,3,399,-18,3,400,-4,2,401,-18,3,402,-24,3,403,-25,3,404,-26,3,405,-27,3,406,-28,3,407,-29,3,408,-30,3,409,-31,3,410,-32,3,411,-33,3,412,-34,3,413,-35,3,414,-90,4,415,-90,4,416,-90,4,417,-39,3,418,-4,2,419,-90,4,420,-96,4,421,-90,4,422,-90,4,423,-45,3,424,-100,4,425,-101,4,426,-48,3,427,-103,4,428,-18,3,429,-51,3,430,-16,3,431,-17,3,432,4,13,433,-1,0,434,-2,1,435,-3,1,436,-4,2,437,-5,2,438,-6,2,439,-7,2,440,-8,2,441,-9,2,442,-10,2,443,-11,2,444,-12,2,445,-13,3,446,-14,3,447,-15,3,448,-16,3,449,-17,3,450,5,17,451,-1,0,452,-128,4,453,-75,4,454,-4,2,455,-5,2,456,-24,3,457,-7,2,458,-80,4,459,-27,3,460,-136,4,461,-29,3,462,-30,3,463,-85,4,464,-32,3,465,-33,3,466,-34,3,467,-35,3,468,-90,4,469,-90,4,470,-90,4,471,-90,4,472,-4,2,473,-90,4,474,-96,4,475,-90,4,476,-90,4,477,-45,3,478,-100,4,479,-101,4,480,-102,4,481,-103,4,482,-14,3,483,-15,3,484,-16,3,485,-17,3,486,-108,4,487,-108,4,488,-108,4,489,-108,4,490,-112,4,491,-5,2,492,-108,4,493,-108,4,494,-108,4,495,-108,4,496,-108,4,497,-119,4,498,-120,4,499,-121,4,500,-14,3,501,-15,3,502,-16,3,503,-17,3,504,4,13,505,-1,0,506,-2,1,507,-3,1,508,-4,2,509,-5,2,510,-6,2,511,-7,2,512,-8,2,513,-9,2,514,-10,2,515,-11,2,516,-12,2,517,-13,3,518,-14,3,519,-15,3,520,-16,3,521,-17,3,522,-18,3,523,-18,3,524,-18,3,525,-18,3,526,-4,2,527,-18,3,528,-24,3,529,-18,3,530,-26,3,531,-27,3,532,-28,3,533,-29,3,534,-30,3,535,-31,3,536,-32,3,537,-33,3,538,-34,3,539,-35,3,540,5,17,541,-1,0,542,-2,1,543,-3,1,544,-4,2,545,-5,2,546,-6,2,547,-7,2,548,-8,2,549,-9,2,550,-10,2,551,-11,2,552,-12,2,553,-13,3,554,-14,3,555,-15,3,556,-16,3,557,-17,3,558,-18,3,559,-18,3,560,-128,4,561,-18,3,562,-4,2,563,-18,3,564,-60,4,565,-18,3,566,-26,3,567,-27,3,568,-64,4,569,-29,3,570,-30,3,571,-31,3,572,-68,4,573,-33,3,574,-34,3,575,-35,3,576,-72,4,577,-72,4,578,-72,4,579,-75,4,580,-4,2,581,-72,4,582,-42,3,583,-72,4,584,-80,4,585,-45,3,586,-82,4,587,-83,4,588,-48,3,589,-85,4,590,-86,4,591,-15,3,592,-16,3,593,-72,4,594,-90,4,595,-90,4,596,-90,4,597,-90,4,598,-4,2,599,-90,4,600,-96,4,601,-90,4,602,-90,4,603,-99,4,604,-100,4,605,-101,4,606,-102,4,607,-103,4,608,-14,3,609,-15,3,610,-16,3,611,-17,3,612,-108,4,613,-108,4,614,-108,4,615,-108,4,616,-112,4,617,-5,2,618,-108,4,619,-108,4,620,-108,4,621,-108,4,622,-108,4,623,-119,4,624,-120,4,625,-121,4,626,-14,3,627,-15,3,628,-16,3,629,-17,3,630,5,18,631,-1,0,632,-2,1,633,-3,1,634,-4,2,635,-5,2,636,-6,2,637,-7,2,638,-8,2,639,-9,2,640,-10,2,641,-11,2,642,-12,2,643,-13,3,644,-14,3,645,-15,3,646,-16,3,647,-17,3,648,6,22,649,-1,0,650,-2,1,651,-3,1,652,-4,2,653,-5,2,654,-6,2,655,-7,2,656,-8,2,657,-9,2,658,-10,2,659,-11,2,660,-12,2,661,-13,3,662,-14,3,663,-15,3,664,-16,3,665,-17,3,666,-18,3,667,-18,3,668,-128,4,669,-39,3,670,-4,2,671,-18,3,672,-42,3,673,-18,3,674,-26,3,675,-45,3,676,-136,4,677,-29,3,678,-48,3,679,-31,3,680,-32,3,681,-51,4,682,-34,3,683,-35,3,684,-54,4,685,-54,4,686,-56,4,687,-39,3,688,-72,4,689,-54,4,690,-60,4,691,-61,4,692,-80,4,693,-45,3,694,-64,4,695,-65,4,696,-48,3,697,-67,4,698,-68,4,699,-51,4,700,-54,4,701,-54,4,702,-72,4,703,-72,4,704,-72,4,705,-75,4,706,-4,2,707,-72,4,708,-60,4,709,-72,4,710,-80,4,711,-81,4,712,-64,4,713,-83,4,714,-84,4,715,-85,4,716,-68,4,717,-15,3,718,-16,3,719,-72,4,720,-90,4,721,-90,4,722,-90,4,723,-90,4,724,-4,2,725,-90,4,726,-96,4,727,-90,4,728,-80,4,729,-99,4,730,-100,4,731,-101,4,732,-102,4,733,-103,4,734,-14,3,735,-15,3,736,-16,3,737,-17,3,738,-108,4,739,-108,4,740,-108,4,741,-108,4,742,-112,4,743,-5,2,744,-96,4,745,-108,4,746,-108,4,747,-108,4,748,-108,4,749,-119,4,750,-120,4,751,-121,4,752,-14,3,753,-15,3,754,-16,3,755,-17,3,756,-126,4,757,-126,4,758,-128,4,759,-126,4,760,-112,4,761,-5,2,762,-6,2,763,-126,4,764,-126,4,765,-126,4,766,-136,4,767,-126,4,768,-138,4,769,-13,3,770,-14,3,771,-15,3,772,-16,3,773,-17,3,774,-126,4,775,-17,3,776,-128,4,777,-147,4,778,-148,4,779,-149,4,780,-14,3,781,-15,3,782,-16,3,783,-17,3,784,-136,4,785,-126,4,786,-138,4,787,-13,3,788,-14,3,789,-15,3,790,-16,3,791,-17,3,792,-18,3,793,-17,3,794,-18,3,795,-165,4,796,-166,4,797,-149,4,798,-72,4,799,-15,3,800,-80,4,801,-171,4,802,-64,4,803,-173,4,804,-174,4,805,-85,4,806,-68,4,807,-177,4,808,-160,4,809,-161,4,810,-90,4,811,-85,4,812,-182,4,813,-75,4,814,-72,4,815,-5,2,816,-90,4,817,-85,4,818,-80,4,819,-171,4,820,-64,4,821,-72,4,822,-90,4,823,-85,4,824,-68,4,825,-15,3,826,-16,3,827,-17,3,828,-324,5,829,-80,4,830,-72,4,831,-75,4,832,-90,4,833,-329,5,834,-96,4,835,-331,5,836,-80,4,837,-333,5,838,-72,4,839,-90,4,840,-102,4,841,-85,4,842,-338,5,843,-339,5,844,-340,5,845,-341,5,846,-108,4,847,-108,4,848,-90,4,849,-108,4,850,-108,4,851,-102,4,852,-96,4,853,-108,4,854,-80,4,855,-108,4,856,-90,4,857,-108,4,858,-102,4,859,-13,3,860,-14,3,861,-15,3,862,-16,3,863,-17,3,864,-108,4,865,-108,4,866,-108,4,867,-108,4,868,-112,4,869,-5,2,870,-96,4,871,-108,4,872,-108,4,873,-108,4,874,-108,4,875,-119,4,876,-120,4,877,-13,3,878,-14,3,879,-15,3,880,-16,3,881,-17,3,882,-126,4,883,-126,4,884,-126,4,885,-119,4,886,-112,4,887,-5,2,888,-6,2,889,-126,4,890,-126,4,891,-126,4,892,-126,4,893,-126,4,894,-126,4,895,-13,3,896,-14,3,897,-15,3,898,-16,3,899,-17,3,900,-126,4,901,-17,3,902,-136,4,903,-126,4,904,-126,4,905,-126,4,906,-14,3,907,-15,3,908,-16,3,909,-17,3,910,-126,4,911,-126,4,912,-126,4,913,-13,3,914,-14,3,915,-15,3,916,-16,3,917,-17,3,918,-378,5,919,-379,5,920,-380,5,921,-381,5,922,-382,5,923,-383,5,924,-384,5,925,-385,5,926,-386,5,927,-387,5,928,-388,5,929,-389,5,930,-390,5,931,-13,3,932,-14,3,933,-393,5,934,-394,5,935,-395,5,936,-432,5,937,-433,5,938,-434,5,939,-435,5,940,-436,5,941,-437,5,942,-438,5,943,-439,5,944,-440,5,945,-441,5,946,-442,5,947,-443,5,948,-444,5,949,-13,3,950,-14,3,951,-15,3,952,-448,5,953,-449,5,954,-324,5,955,-325,5,956,-326,5,957,-327,5,958,-328,5,959,-329,5,960,-330,5,961,-331,5,962,-332,5,963,-333,5,964,-334,5,965,-335,5,966,-336,5,967,-13,3,968,-324,5,969,-339,5,970,-340,5,971,-341,5,972,-324,5,973,-325,5,974,-108,4,975,-327,5,976,-108,4,977,-102,4,978,-96,4,979,-331,5,980,-332,5,981,-333,5,982,-108,4,983,-108,4,984,-102,4,985,-13,3,986,-324,5,987,-339,5,988,-340,5,989,-341,5,990,-360,5,991,-361,5,992,-108,4,993,-108,4,994,-112,4,995,-365,5,996,-96,4,997,-367,5,998,-368,5,999,-369,5,1000,-108,4,1001,-119,4,1002,-372,5,1003,-13,3,1004,-14,3,1005,-360,5,1006,-376,5,1007,-377,5,1008,-504,5,1009,-504,5,1010,-504,5,1011,-504,5,1012,-4,2,1013,-504,5,1014,-504,5,1015,-504,5,1016,-504,5,1017,-504,5,1018,-504,5,1019,-504,5,1020,-504,5,1021,-13,3,1022,-14,3,1023,-15,3,1024,-16,3,1025,-504,5,1026,-378,5,1027,-379,5,1028,-380,5,1029,-381,5,1030,-382,5,1031,-383,5,1032,-384,5,1033,-385,5,1034,-386,5,1035,-387,5,1036,-388,5,1037,-389,5,1038,-390,5,1039,-13,3,1040,-14,3,1041,-15,3,1042,-394,5,1043,-395,5,1044,-504,5,1045,-505,5,1046,-506,5,1047,-507,5,1048,-4,2,1049,-509,5,1050,-510,5,1051,-511,5,1052,-512,5,1053,-513,5,1054,-514,5,1055,-515,5,1056,-516,5,1057,-13,3,1058,-14,3,1059,-15,3,1060,-16,3,1061,-504,5,1062,-432,5,1063,-433,5,1064,-434,5,1065,-435,5,1066,-4,2,1067,-437,5,1068,-438,5,1069,-439,5,1070,-440,5,1071,-441,5,1072,-442,5,1073,-443,5,1074,-444,5,1075,-13,3,1076,-14,3,1077,-15,3,1078,-16,3,1079,-449,5,1080,-540,5,1081,-540,5,1082,-452,5,1083,-504,5,1084,-4,2,1085,-540,5,1086,-540,5,1087,-540,5,1088,-504,5,1089,-540,5,1090,-460,5,1091,-540,5,1092,-540,5,1093,-504,5,1094,-14,3,1095,-15,3,1096,-16,3,1097,-17,3,1098,-504,5,1099,-505,5,1100,-506,5,1101,-507,5,1102,-4,2,1103,-509,5,1104,-504,5,1105,-511,5,1106,-512,5,1107,-504,5,1108,-504,5,1109,-504,5,1110,-504,5,1111,-504,5,1112,-14,3,1113,-15,3,1114,-16,3,1115,-17,3,1116,-504,5,1117,-505,5,1118,-506,5,1119,-507,5,1120,-504,5,1121,-509,5,1122,-474,5,1123,-511,5,1124,-512,5,1125,-513,5,1126,-514,5,1127,-504,5,1128,-504,5,1129,-504,5,1130,-14,3,1131,-15,3,1132,-16,3,1133,-17,3,1134,-504,5,1135,-505,5,1136,-506,5,1137,-507,5,1138,-4,2,1139,-509,5,1140,-510,5,1141,-511,5,1142,-512,5,1143,-513,5,1144,-514,5,1145,-515,5,1146,-516,5,1147,-13,3,1148,-14,3,1149,-15,3,1150,-16,3,1151,-17,3,1152,-504,5,1153,-505,5,1154,-506,5,1155,-507,5,1156,-4,2,1157,-509,5,1158,-510,5,1159,-511,5,1160,-512,5,1161,-513,5,1162,-514,5,1163,-515,5,1164,-516,5,1165,-13,3,1166,-14,3,1167,-15,3,1168,-16,3,1169,-17,3,1170,-540,5,1171,-541,5,1172,-542,5,1173,-543,5,1174,-4,2,1175,-545,5,1176,-546,5,1177,-547,5,1178,-548,5,1179,-549,5,1180,-550,5,1181,-551,5,1182,-552,5,1183,-13,3,1184,-14,3,1185,-15,3,1186,-16,3,1187,-17,3,1188,-540,5,1189,-541,5,1190,-560,5,1191,-543,5,1192,-4,2,1193,-545,5,1194,-60,4,1195,-547,5,1196,-548,5,1197,-549,5,1198,-64,4,1199,-551,5,1200,-552,5,1201,-13,3,1202,-68,4,1203,-15,3,1204,-16,3,1205,-17,3,1206,-72,4,1207,-72,4,1208,-72,4,1209,-75,4,1210,-4,2,1211,-72,4,1212,-60,4,1213,-72,4,1214,-504,5,1215,-585,5,1216,-64,4,1217,-504,5,1218,-588,5,1219,-504,5,1220,-68,4,1221,-15,3,1222,-16,3,1223,-17,3,1224,-594,5,1225,-595,5,1226,-596,5,1227,-597,5,1228,-4,2,1229,-599,5,1230,-600,5,1231,-601,5,1232,-504,5,1233,-603,5,1234,-64,4,1235,-605,5,1236,-606,5,1237,-607,5,1238,-14,3,1239,-15,3,1240,-16,3,1241,-17,3,1242,-612,5,1243,-613,5,1244,-614,5,1245,-615,5,1246,-616,5,1247,-617,5,1248,-600,5,1249,-619,5,1250,-620,5,1251,-621,5,1252,-622,5,1253,-623,5,1254,-624,5,1255,-625,5,1256,-14,3,1257,-15,3,1258,-16,3,1259,-17,3,1260,-630,5,1261,-630,5,1262,-630,5,1263,-630,5,1264,-4,2,1265,-5,2,1266,-630,5,1267,-630,5,1268,-630,5,1269,-630,5,1270,-630,5,1271,-630,5,1272,-630,5,1273,-13,3,1274,-14,3,1275,-15,3,1276,-16,3,1277,-17,3,1278,-630,5,1279,-631,5,1280,-632,5,1281,-633,5,1282,-4,2,1283,-5,2,1284,-636,5,1285,-637,5,1286,-638,5,1287,-639,5,1288,-640,5,1289,-641,5,1290,-642,5,1291,-13,3,1292,-14,3,1293,-15,3,1294,-16,3,1295,-17,3,1296,-648,5,1297,-648,5,1298,-630,5,1299,-630,5,1300,-4,2,1301,-5,2,1302,-630,5,1303,-648,5,1304,-648,5,1305,-630,5,1306,-630,5,1307,-648,5,1308,-630,5,1309,-13,3,1310,-14,3,1311,-51,4,1312,-16,3,1313,-17,3,1314,-54,4,1315,-54,4,1316,-56,4,1317,-648,5,1318,-72,4,1319,-54,4,1320,-60,4,1321,-61,4,1322,-80,4,1323,-648,5,1324,-64,4,1325,-65,4,1326,-648,5,1327,-67,4,1328,-68,4,1329,-51,4,1330,-54,4,1331,-54,4,1332,-72,4,1333,-72,4,1334,-72,4,1335,-75,4,1336,-4,2,1337,-72,4,1338,-60,4,1339,-72,4,1340,-80,4,1341,-81,4,1342,-64,4,1343,-83,4,1344,-84,4,1345,-85,4,1346,-68,4,1347,-15,3,1348,-16,3,1349,-17,3,1350,-90,4,1351,-90,4,1352,-90,4,1353,-90,4,1354,-4,2,1355,-5,2,1356,-630,5,1357,-90,4,1358,-80,4,1359,-630,5,1360,-630,5,1361,-630,5,1362,-630,5,1363,-630,5,1364,-14,3,1365,-15,3,1366,-16,3,1367,-17,3,1368,-630,5,1369,-631,5,1370,-632,5,1371,-633,5,1372,-630,5,1373,-5,2,1374,-630,5,1375,-637,5,1376,-638,5,1377,-639,5,1378,-640,5,1379,-630,5,1380,-630,5,1381,-13,3,1382,-14,3,1383,-15,3,1384,-16,3,1385,-17,3,1386,-630,5,1387,-631,5,1388,-630,5,1389,-633,5,1390,-648,5,1391,-5,2,1392,-636,5,1393,-637,5,1394,-638,5,1395,-639,5,1396,-630,5,1397,-641,5,1398,-630,5,1399,-13,3,1400,-14,3,1401,-15,3,1402,-16,3,1403,-17,3,1404,-648,5,1405,-17,3,1406,-648,5,1407,-630,5,1408,-630,5,1409,-13,3,1410,-14,3,1411,-15,3,1412,-16,3,1413,-17,3,1414,-648,5,1415,-659,5,1416,-648,5,1417,-13,3,1418,-14,3,1419,-15,3,1420,-16,3,1421,-17,3,1422,-648,5,1423,-17,3,1424,-648,5,1425,-630,5,1426,-630,5,1427,-13,3,1428,-72,4,1429,-15,3,1430,-80,4,1431,-630,5,1432,-648,5,1433,-630,5,1434,-630,5,1435,-85,4,1436,-68,4,1437,-630,5,1438,-16,3,1439,-17,3,1440,-432,5,1441,-85,4,1442,-630,5,1443,-75,4,1444,-72,4,1445,-432,5,1446,-90,4,1447,-85,4,1448,-80,4,1449,-648,5,1450,-64,4,1451,-72,4,1452,-90,4,1453,-85,4,1454,-68,4,1455,-15,3,1456,-16,3,1457,-17,3,1458,-324,5,1459,-325,5,1460,-72,4,1461,-75,4,1462,-90,4,1463,-329,5,1464,-96,4,1465,-331,5,1466,-80,4,1467,-333,5,1468,-72,4,1469,-90,4,1470,-630,5,1471,-85,4,1472,-14,3,1473,-15,3,1474,-16,3,1475,-341,5,1476,-738,5,1477,-738,5,1478,-90,4,1479,-738,5,1480,-738,5,1481,-630,5,1482,-96,4,1483,-738,5,1484,-80,4,1485,-738,5,1486,-90,4,1487,-738,5,1488,-630,5,1489,-13,3,1490,-14,3,1491,-15,3,1492,-16,3,1493,-17,3,1494,-738,5,1495,-739,5,1496,-738,5,1497,-741,5,1498,-742,5,1499,-5,2,1500,-96,4,1501,-745,5,1502,-746,5,1503,-747,5,1504,-738,5,1505,-749,5,1506,-750,5,1507,-13,3,1508,-14,3,1509,-15,3,1510,-16,3,1511,-17,3,1512,3,10,1513,-1,0,1514,-2,1,1515,-3,1,1516,-4,2,1517,-5,2,1518,-6,2,1519,-7,2,1520,-8,2,1521,-9,2,1522,-10,2,1523,-11,2,1524,-12,2,1525,-13,3,1526,-14,3,1527,-15,3,1528,-16,3,1529,-17,3,1530,-18,3,1531,-18,3,1532,-18,3,1533,-18,3,1534,-4,2,1535,-18,3,1536,-24,3,1537,-18,3,1538,-26,3,1539,-27,3,1540,-28,3,1541,-29,3,1542,-30,3,1543,-31,3,1544,-32,3,1545,-33,3,1546,-34,3,1547,-35,3,1548,-504,5,1549,-504,5,1550,-504,5,1551,-39,3,1552,-4,2,1553,-504,5,1554,-42,3,1555,-504,5,1556,-504,5,1557,-45,3,1558,-504,5,1559,-504,5,1560,-48,3,1561,-13,3,1562,-14,3,1563,-51,4,1564,-16,3,1565,-17,3,1566,-432,5,1567,-433,5,1568,-56,4,1569,-435,5,1570,-4,2,1571,-432,5,1572,-60,4,1573,-432,5,1574,-440,5,1575,-441,5,1576,-64,4,1577,-443,5,1578,-444,5,1579,-13,3,1580,-68,4,1581,-15,3,1582,-16,3,1583,-17,3,1584,-324,5,1585,-325,5,1586,-326,5,1587,-75,4,1588,-4,2,1589,-329,5,1590,-330,5,1591,-331,5,1592,-80,4,1593,-333,5,1594,-82,4,1595,-83,4,1596,-84,4,1597,-85,4,1598,-14,3,1599,-15,3,1600,-16,3,1601,-324,5,1602,-90,4,1603,-90,4,1604,-90,4,1605,-90,4,1606,-4,2,1607,-5,2,1608,-96,4,1609,-90,4,1610,-90,4,1611,-90,4,1612,-100,4,1613,-101,4,1614,-102,4,1615,-103,4,1616,-14,3,1617,-15,3,1618,-16,3,1619,-17,3,1620,-108,4,1621,-108,4,1622,-108,4,1623,-108,4,1624,-112,4,1625,-5,2,1626,-6,2,1627,-108,4,1628,-108,4,1629,-108,4,1630,-108,4,1631,-119,4,1632,-120,4,1633,-13,3,1634,-14,3,1635,-15,3,1636,-16,3,1637,-17,3,1638,-126,4,1639,-126,4,1640,-128,4,1641,-126,4,1642,-4,2,1643,-5,2,1644,-6,2,1645,-7,2,1646,-126,4,1647,-126,4,1648,-136,4,1649,-126,4,1650,-138,4,1651,-13,3,1652,-14,3,1653,-15,3,1654,-16,3,1655,-17,3,1656,-504,5,1657,-17,3,1658,-18,3,1659,-147,4,1660,-148,4,1661,-13,3,1662,-14,3,1663,-15,3,1664,-16,3,1665,-17,3,1666,-18,3,1667,-504,5,1668,-504,5,1669,-13,3,1670,-14,3,1671,-15,3,1672,-16,3,1673,-17,3,1674,-504,5,1675,-505,5,1676,-504,5,1677,-504,5,1678,-4,2,1679,-504,5,1680,-504,5,1681,-504,5,1682,-504,5,1683,-504,5,1684,-504,5,1685,-504,5,1686,-504,5,1687,-13,3,1688,-14,3,1689,-15,3,1690,-16,3,1691,-17,3,1692,-432,5,1693,-433,5,1694,-434,5,1695,-435,5,1696,-4,2,1697,-5,2,1698,-438,5,1699,-439,5,1700,-440,5,1701,-441,5,1702,-442,5,1703,-443,5,1704,-444,5,1705,-13,3,1706,-14,3,1707,-15,3,1708,-16,3,1709,-17,3,1710,-540,5,1711,-540,5,1712,-452,5,1713,-75,4,1714,-90,4,1715,-540,5,1716,-96,4,1717,-540,5,1718,-80,4,1719,-540,5,1720,-460,5,1721,-90,4,1722,-102,4,1723,-85,4,1724,-14,3,1725,-15,3,1726,-16,3,1727,-17,3,1728,-108,4,1729,-108,4,1730,-90,4,1731,-108,4,1732,-108,4,1733,-102,4,1734,-96,4,1735,-108,4,1736,-108,4,1737,-108,4,1738,-90,4,1739,-108,4,1740,-102,4,1741,-13,3,1742,-14,3,1743,-15,3,1744,-16,3,1745,-17,3,1746,-108,4,1747,-108,4,1748,-108,4,1749,-108,4,1750,-112,4,1751,-5,2,1752,-504,5,1753,-108,4,1754,-108,4,1755,-108,4,1756,-108,4,1757,-119,4,1758,-120,4,1759,-13,3,1760,-14,3,1761,-15,3,1762,-16,3,1763,-17,3,1764,-504,5,1765,-505,5,1766,-506,5,1767,-507,5,1768,-4,2,1769,-5,2,1770,-504,5,1771,-504,5,1772,-512,5,1773,-513,5,1774,-514,5,1775,-515,5,1776,-516,5,1777,-13,3,1778,-14,3,1779,-15,3,1780,-16,3,1781,-17,3,1782,-504,5,1783,-505,5,1784,-506,5,1785,-507,5,1786,-4,2,1787,-5,2,1788,-504,5,1789,-504,5,1790,-512,5,1791,-513,5,1792,-514,5,1793,-515,5,1794,-516,5,1795,-13,3,1796,-14,3,1797,-15,3,1798,-16,3,1799,-17,3,1800,-540,5,1801,-541,5,1802,-540,5,1803,-543,5,1804,-4,2,1805,-5,2,1806,-540,5,1807,-540,5,1808,-540,5,1809,-540,5,1810,-550,5,1811,-551,5,1812,-552,5,1813,-13,3,1814,-14,3,1815,-15,3,1816,-16,3,1817,-17,3,1818,-540,5,1819,-541,5,1820,-560,5,1821,-543,5,1822,-4,2,1823,-5,2,1824,-60,4,1825,-540,5,1826,-540,5,1827,-540,5,1828,-64,4,1829,-551,5,1830,-552,5,1831,-13,3,1832,-68,4,1833,-15,3,1834,-16,3,1835,-17,3,1836,-324,5,1837,-325,5,1838,-326,5,1839,-75,4,1840,-4,2,1841,-329,5,1842,-330,5,1843,-331,5,1844,-80,4,1845,-333,5,1846,-334,5,1847,-335,5,1848,-336,5,1849,-85,4,1850,-14,3,1851,-15,3,1852,-16,3,1853,-324,5,1854,-90,4,1855,-90,4,1856,-90,4,1857,-90,4,1858,-4,2,1859,-5,2,1860,-504,5,1861,-90,4,1862,-80,4,1863,-90,4,1864,-64,4,1865,-504,5,1866,-504,5,1867,-504,5,1868,-14,3,1869,-15,3,1870,-16,3,1871,-17,3,1872,-504,5,1873,-505,5,1874,-506,5,1875,-507,5,1876,-504,5,1877,-5,2,1878,-504,5,1879,-511,5,1880,-80,4,1881,-513,5,1882,-514,5,1883,-504,5,1884,-504,5,1885,-13,3,1886,-14,3,1887,-15,3,1888,-16,3,1889,-17,3,1890,3,11,1891,-1,0,1892,-2,1,1893,-3,1,1894,-4,2,1895,-5,2,1896,-6,2,1897,-7,2,1898,-8,2,1899,-9,2,1900,-10,2,1901,-11,2,1902,-12,2,1903,-13,3,1904,-14,3,1905,-15,3,1906,-16,3,1907,-17,3,1908,-18,3,1909,-18,3,1910,-18,3,1911,-18,3,1912,-4,2,1913,-18,3,1914,-24,3,1915,-18,3,1916,-26,3,1917,-27,3,1918,-28,3,1919,-29,3,1920,-30,3,1921,-31,3,1922,-32,3,1923,-33,3,1924,-34,3,1925,-35,3,1926,-18,3,1927,-18,3,1928,-18,3,1929,-39,3,1930,-4,2,1931,-18,3,1932,-42,3,1933,-18,3,1934,-18,3,1935,-45,3,1936,-18,3,1937,-18,3,1938,-48,4,1939,-18,3,1940,-18,3,1941,-51,4,1942,-18,3,1943,-18,3,1944,-54,4,1945,-54,4,1946,-56,4,1947,-54,4,1948,-54,4,1949,-54,4,1950,-60,4,1951,-61,4,1952,-62,4,1953,-18,3,1954,-64,4,1955,-65,4,1956,-66,4,1957,-67,4,1958,-68,4,1959,-69,4,1960,-54,4,1961,-54,4,1962,-72,4,1963,-72,4,1964,-72,4,1965,-75,4,1966,-4,2,1967,-72,4,1968,-72,4,1969,-72,4,1970,-80,4,1971,-81,4,1972,-82,4,1973,-83,4,1974,-84,4,1975,-85,4,1976,-14,3,1977,-15,3,1978,-16,3,1979,-17,3,1980,-90,4,1981,-90,4,1982,-90,4,1983,-90,4,1984,-4,2,1985,-5,2,1986,-96,4,1987,-90,4,1988,-90,4,1989,-90,4,1990,-100,4,1991,-101,4,1992,-102,4,1993,-103,4,1994,-14,3,1995,-15,3,1996,-16,3,1997,-17,3,1998,-108,4,1999,-108,4,2000,-108,4,2001,-108,4,2002,-112,4,2003,-5,2,2004,-6,2,2005,-108,4,2006,-108,4,2007,-108,4,2008,-108,4,2009,-119,4,2010,-120,4,2011,-13,3,2012,-14,3,2013,-15,3,2014,-16,3,2015,-17,3,2016,4,16,2017,-1,0,2018,-128,4,2019,-3,1,2020,-4,2,2021,-5,2,2022,-6,2,2023,-7,2,2024,-8,2,2025,-9,2,2026,-136,4,2027,-11,2,2028,-12,2,2029,-13,3,2030,-14,3,2031,-15,3,2032,-16,3,2033,-17,3,2034,-18,3,2035,-17,3,2036,-18,3,2037,-147,4,2038,-148,4,2039,-13,3,2040,-14,3,2041,-15,3,2042,-16,3,2043,-17,3,2044,-18,3,2045,-29,3,2046,-30,3,2047,-31,3,0,0,0") }; /** * When splitting a set evenly into two subsets, the minimum size of the * set where k = array index should be used for the Rice parameter k. */ private static final int[] RICE_SPLIT_2 = { 0, 4, 14, 50, 188, 726, 2858, 11346, 45214, 180512 }; // private static final int CACHE_SPLITS = 4 * 1024; private static final int CACHE_SPLITS = 10 * 1024; private final int leafSize; private final int averageBucketSize; private final int[] splits = new int[CACHE_SPLITS]; private final int[] rice = new int[CACHE_SPLITS]; /** * Constructor for settings. * * @param leafSize * @param averageBucketSize the load factor, at most 65536 */ public Settings(int leafSize, int averageBucketSize) { if (IMPROVED_SPLIT_RULES) { if (leafSize < 1 || leafSize > 32) { throw new IllegalArgumentException("leafSize out of range: " + leafSize); } } else { if (leafSize < 1 || leafSize > 25) { throw new IllegalArgumentException("leafSize out of range: " + leafSize); } } if (averageBucketSize < 2 || averageBucketSize > 65536) { throw new IllegalArgumentException("averageBucketSize out of range: " + averageBucketSize); } this.leafSize = leafSize; this.averageBucketSize = averageBucketSize; if (IMPROVED_SPLIT_RULES) { int[] splitRules = SPLIT_RULES[leafSize]; if (splitRules.length == 0 || splitRules[0] == 0) { throw new IllegalArgumentException("No split rules for leafSize: " + leafSize); } for (int i = 0; i < splitRules.length; i += 3) { int size = splitRules[i]; splits[size] = splitRules[i + 1]; rice[size] = splitRules[i + 2]; } for (int i = 0; i <= leafSize; i++) { splits[i] = i; rice[i] = RICE_LEAF[i]; } int last = leafSize; for (int i = leafSize; i < CACHE_SPLITS; i++) { if (splits[i] != 0) { last = i; } else { splits[i] = -last; rice[i] = calcRiceParamSplitByTwo(i); } } } else { for (int i = 0; i < CACHE_SPLITS; i++) { splits[i] = calcSplit(i, leafSize); rice[i] = calcGolombRiceShift(i, leafSize); } } } public int getMaxBucketSize() { // return averageBucketSize * 20; return 200 + averageBucketSize * 15 / 10; } public static int calcRiceParamSplitByTwo(int size) { // this will throw an exception for sizes >= 180172 for (int i = 0;; i++) { if (RICE_SPLIT_2[i] > size) { return i - 1; } } } static int calcNextSplit(int factor) { return Math.max(2, (int) (1.5 + factor * .35)); } private static int calcSplit(int size, int leafSize) { for (int x = leafSize, f = x;;) { if (size < x) { return -(x / f); } else if (size == x) { return f; } f = calcNextSplit(f); x *= f; } } public int getSplit(int size) { if (size < CACHE_SPLITS) { return splits[size]; } if (IMPROVED_SPLIT_RULES) { throw new IllegalArgumentException(); } return calcSplit(size, leafSize); } public static int calcGolombRiceShift(int size, int leafSize) { if (size <= leafSize) { return RICE_LEAF[size]; } int index = 0; for (int x = leafSize, f = x;;) { f = calcNextSplit(f); if (f <= 2) { break; } x *= f; if (size < x) { break; } else if (size == x) { return RICE_SPLIT_MORE[leafSize][index]; } index++; } return calcRiceParamSplitByTwo(size); } public int getGolombRiceShift(int size) { if (size < CACHE_SPLITS) { return rice[size]; } return calcGolombRiceShift(size, leafSize); } public static boolean needNewUniversalHashIndex(long index) { return (index & (SUPPLEMENTAL_HASH_CALLS - 1)) == 0; } public static long getUniversalHashIndex(long index) { return index >>> SUPPLEMENTAL_HASH_SHIFT; } public int getLeafSize() { return leafSize; } public int getAverageBucketSize() { return averageBucketSize; } public static int supplementalHashShift(long hash, long index) { long x = hash + (index * 0xbf58476d1ce4e5b9L); x ^= (x << 13); x ^= (x >>> 17); x ^= (x << 5); return (int) x; } public static int supplementalHashInt(long hash, long index) { // but with some JVMs, 32-bit multiplication // seem to be much faster // (about 1200 ms for 32 bit, about 2000 ms for 64 bit) int x = (int) (Long.rotateLeft(hash, (int) index) ^ index); x = ((x >>> 16) ^ x) * 0x45d9f3b; x = ((x >>> 16) ^ x) * 0x45d9f3b; x = (x >>> 16) ^ x; return x; } public static int supplementalHash(long hash, long index) { long x = hash + index; // from http://zimbry.blogspot.it/2011/09/better-bit-mixing-improving-on.html // also used in it.unimi.dsi.fastutil x = (x ^ (x >>> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >>> 27)) * 0x94d049bb133111ebL; x = x ^ (x >>> 31); return (int) x; } public static int getBucketCount(long size, int averageBucketSize) { return (int) ((size + averageBucketSize - 1) / averageBucketSize); } public static int reduce(int hash, int n) { // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return (int) (((hash & 0xffffffffL) * n) >>> 32); } } ================================================ FILE: src/main/java/org/minperf/bdz/BDZ.java ================================================ package org.minperf.bdz; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import org.minperf.BitBuffer; import org.minperf.Settings; import org.minperf.rank.VerySimpleRank; import org.minperf.universal.UniversalHash; /** * A simple implementation of the BDZ algorithm as documented in * "Simple and Space-Efficient Minimal Perfect Hash Functions" (F. C. Botelho, * R. Pagh, N. Ziviani). * * This implementation around 3.66 bits/key, which is much more than really * needed, mainly because no compression is used. * * @param the type */ public class BDZ { // needs 3.66 bits/key private static final int HASHES = 3; private static final int FACTOR_TIMES_100 = 123; private static final int BITS_PER_ENTRY = 2; // needs 3.78 bits/key // private static final int HASHES = 4; // private static final int FACTOR_TIMES_100 = 132; // private static final int BITS_PER_ENTRY = 2; // needs 4.25 bits/key // private static final int HASHES = 2; // private static final int FACTOR_TIMES_100 = 240; // private static final int BITS_PER_ENTRY = 1; private final UniversalHash hash; private final BitBuffer data; private final int hashIndex; private final int arrayLength; private final int size; private final int startPos; private final VerySimpleRank rank; private BDZ(UniversalHash hash, BitBuffer data) { this.hash = hash; this.data = data; this.size = (int) data.readEliasDelta() - 1; this.arrayLength = getArrayLength(size); this.hashIndex = (int) data.readEliasDelta() - 1; this.rank = VerySimpleRank.load(data); this.startPos = data.position(); data.seek(startPos + size * BITS_PER_ENTRY); } public int evaluate(T x) { int sum = 0; for (int hi = 0; hi < HASHES; hi++) { int h = getHash(x, hash, hashIndex, hi, arrayLength); if (rank.get(h)) { int pos = (int) rank.rank(h); sum += data.readNumber(startPos + pos * BITS_PER_ENTRY, BITS_PER_ENTRY); } } int h = getHash(x, hash, hashIndex, sum % HASHES, arrayLength); int pos = (int) rank.rank(h); return pos; } public static BDZ load(UniversalHash hash, BitBuffer data) { return new BDZ(hash, data); } @SuppressWarnings("unchecked") public static BitBuffer generate(UniversalHash hash, Collection set) { int size = set.size(); int arrayLength = getArrayLength(size); BitBuffer data = new BitBuffer(100 + arrayLength * (BITS_PER_ENTRY + 2)); data.writeEliasDelta(size + 1); ArrayList list = new ArrayList(set); int m = arrayLength; ArrayList order = new ArrayList(); HashSet done = new HashSet(); T[] at; int hashIndex = 0; while (true) { order.clear(); done.clear(); at = (T[]) new Object[m]; ArrayList> list2 = new ArrayList>(); for (int i = 0; i < m; i++) { list2.add(new HashSet()); } for (int i = 0; i < size; i++) { T x = list.get(i); for (int hi = 0; hi < HASHES; hi++) { int h = getHash(x, hash, hashIndex, hi, arrayLength); HashSet l = list2.get(h); l.add(x); } } LinkedList alone = new LinkedList(); for (int i = 0; i < arrayLength; i++) { if (list2.get(i).size() == 1) { alone.add(i); } } while (!alone.isEmpty()) { int i = alone.removeFirst(); HashSet l = list2.get(i); if (l.isEmpty()) { continue; } T x = l.iterator().next(); if (done.contains(x)) { continue; } order.add(x); done.add(x); boolean found = false; for (int hi = 0; hi < HASHES; hi++) { int h = getHash(x, hash, hashIndex, hi, arrayLength); l = list2.get(h); l.remove(x); if (l.isEmpty()) { if (!found) { at[h] = x; found = true; } } else if (l.size() == 1) { alone.add(h); } } } if (order.size() == size) { break; } hashIndex++; } data.writeEliasDelta(hashIndex + 1); BitSet visited = new BitSet(); BitSet used = new BitSet(); int[] g = new int[m]; for (int i = order.size() - 1; i >= 0; i--) { T x = order.get(i); int sum = 0; int change = 0; int target = 0; for (int hi = 0; hi < HASHES; hi++) { int h = getHash(x, hash, hashIndex, hi, arrayLength); if (visited.get(h)) { sum += g[h]; } else { visited.set(h); if (at[h] == x) { used.set(h); change = h; target = hi; } } } int result = (HASHES + target - (sum % HASHES)) % HASHES; g[change] = result; } VerySimpleRank.generate(used, data); for (int i = 0; i < m; i++) { if (used.get(i)) { data.writeNumber(g[i], BITS_PER_ENTRY); } else if (g[i] != 0) { throw new AssertionError(); } } return data; } public int getSize() { return size; } private static int getArrayLength(int size) { return HASHES + FACTOR_TIMES_100 * size / 100; } private static int getHash(T x, UniversalHash hash, int hashIndex, int index, int arrayLength) { long r = hash.universalHash(x, hashIndex + index); r = Settings.reduce((int) r, arrayLength / HASHES); r = r + index * arrayLength / HASHES; return (int) r; } @Override public String toString() { return getClass().getSimpleName() + " size " + size + " hashIndex " + hashIndex; } } ================================================ FILE: src/main/java/org/minperf/generator/ConcurrencyTool.java ================================================ package org.minperf.generator; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; /** * A tool that either runs tasks one after the other (in the caller thread), or * uses a ForkJoinPool to run the tasks in parallel. */ public class ConcurrencyTool { private final ForkJoinPool pool; public ConcurrencyTool(int parallelism) { if (parallelism > 1) { pool = new ForkJoinPool(parallelism); } else { pool = null; } } public T invoke(ForkJoinTask task) { if (pool != null) { return pool.invoke(task); } return task.invoke(); } public void invokeAll(ForkJoinTask... tasks) { if (pool != null) { ForkJoinTask.invokeAll(tasks); return; } for (ForkJoinTask t : tasks) { t.invoke(); } } public void shutdown() { if (pool != null) { pool.shutdown(); } } } ================================================ FILE: src/main/java/org/minperf/generator/Generator.java ================================================ package org.minperf.generator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.RecursiveAction; import org.minperf.BitBuffer; import org.minperf.Settings; import org.minperf.bdz.BDZ; import org.minperf.monotoneList.MonotoneList; import org.minperf.universal.UniversalHash; /** * Generator of a hybrid MPHF. It is guaranteed to use linear space, because * large buckets are encoded using an alternative algorithm. * * @param the type */ public class Generator { public static final int MAX_FILL = 8; public static final int MAX_BITS_PER_ENTRY = 8; final ConcurrencyTool pool; final UniversalHash hash; private final Settings settings; private final boolean eliasFanoMonotoneLists; private final int maxChunkSize; public Generator(ConcurrencyTool pool, UniversalHash hash, Settings settings, boolean eliasFanoMonotoneLists, int maxChunkSize) { this.pool = pool; this.settings = settings; this.hash = hash; this.eliasFanoMonotoneLists = eliasFanoMonotoneLists; this.maxChunkSize = maxChunkSize; } @SuppressWarnings("unchecked") public void generate(T[] data, long[] hashes, long startIndex, BitBuffer buff) { int size = data.length; if (size < 2) { return; } if (size <= settings.getLeafSize()) { long index = getIndex(data, hashes, startIndex); int shift = settings.getGolombRiceShift(size); long value = index - startIndex - 1; buff.writeGolombRice(shift, value); return; } long index = startIndex + 1; // num_split_count++; while (true) { if (Settings.needNewUniversalHashIndex(index)) { long x = Settings.getUniversalHashIndex(index); for (int i = 0; i < size; i++) { hashes[i] = hash.universalHash(data[i], x); } } if (trySplitEvenly(hashes, index)) { break; } index++; } int writeK = settings.getGolombRiceShift(size); long writeIndex = index - startIndex - 1; buff.writeGolombRice(writeK, writeIndex); int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } T[][] data2; long[][] hashes2; if (firstPart != otherPart) { data2 = (T[][]) new Object[][] { (T[]) new Object[firstPart], (T[]) new Object[otherPart] }; hashes2 = new long[][] { new long[firstPart], new long[otherPart] }; } else { data2 = (T[][]) new Object[split][firstPart]; hashes2 = new long[split][firstPart]; } splitEvenly(data, hashes, index, data2, hashes2); for (int i = 0; i < data2.length; i++) { generate(data2[i], hashes2[i], index, buff); } } private long getIndex(T[] data, long[] hashes, long startIndex) { int size = data.length; long index = startIndex + 1; // num_bij_counts[size]++; outer: while (true) { if (Settings.needNewUniversalHashIndex(index)) { long x = Settings.getUniversalHashIndex(index); for (int i = 0; i < size; i++) { hashes[i] = hash.universalHash(data[i], x); } Arrays.sort(hashes); for (int i = 1; i < size; i++) { if (hashes[i - 1] == hashes[i]) { index++; while (!Settings.needNewUniversalHashIndex(index)) { index++; } continue outer; } } } if (tryUnique(hashes, index)) { return index; } index++; } } private boolean trySplitEvenly(long[] hashes, long index) { int size = hashes.length; int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } if (firstPart != otherPart) { int limit = firstPart; for (int i = 0; i < size; i++) { long h = hashes[i]; int x = Settings.supplementalHash(h, index); // num_split_evals++; x = Settings.reduce(x, size); if (x < limit) { firstPart--; } } return firstPart == 0; } int[] count = new int[split]; Arrays.fill(count, firstPart); for (int i = 0; i < size; i++) { long h = hashes[i]; int x = Settings.supplementalHash(h, index); x = Settings.reduce(x, split); // num_split_evals++; count[x]--; } for (int i = 0; i < split; i++) { if (count[i] != 0) { return false; } } return true; } private void splitEvenly(T[] data, long[] hashes, long index, T[][] data2, long[][] hashes2) { int size = data.length; int split = data2.length; int firstPartSize = data2[0].length; int otherPartSize = data2[1].length; if (firstPartSize != otherPartSize) { int i0 = 0, i1 = 0; int limit = firstPartSize; for (int i = 0; i < size; i++) { T t = data[i]; long h = hashes[i]; int x = Settings.supplementalHash(h, index); x = Settings.reduce(x, size); if (x < limit) { data2[0][i0] = t; hashes2[0][i0] = h; i0++; } else { data2[1][i1] = t; hashes2[1][i1] = h; i1++; } } return; } int[] pos = new int[split]; for (int i = 0; i < size; i++) { T t = data[i]; long h = hashes[i]; int x = Settings.supplementalHash(h, index); int bucket = Settings.reduce(x, split); int p = pos[bucket]++; data2[bucket][p] = t; hashes2[bucket][p] = h; } } // public static long num_split_count; // public static long num_split_evals; // public static long[] num_bij_evals = new long[20]; // public static long[] num_bij_counts = new long[20]; static boolean tryUnique(long[] hashes, long index) { int bits = 0; int size = hashes.length; int found = (1 << size) - 1; for (int i = 0; i < size; i++) { long x = hashes[i]; int h = Settings.supplementalHash(x, index); h = Settings.reduce(h, size); // ++num_bij_evals[size]; // if ((bits & (1 << h)) != 0) { // return false; // } bits |= 1 << h; } return bits == found; // return true; } public BitBuffer generate(Collection collection) { long size = collection.size(); int bucketCount = Settings.getBucketCount(size, settings.getAverageBucketSize()); ArrayList buckets = new ArrayList(bucketCount); int averageBucketSize = settings.getAverageBucketSize(); if (size <= maxChunkSize || bucketCount == 1) { for (int i = 0; i < bucketCount; i++) { buckets.add(new Bucket(averageBucketSize)); } for (T t : collection) { int b; if (bucketCount == 1) { b = 0; } else { long h = hash.universalHash(t, 0); b = Settings.reduce((int) h, bucketCount); if (b >= bucketCount || b < 0) { throw new AssertionError(); } } buckets.get(b).add(t); } processBuckets(size, bucketCount, buckets); } else { // split into chunks int bucketsPerChunk = Math.max(1, maxChunkSize / averageBucketSize); int remaining = bucketCount; for (int bucketOffset = 0; bucketOffset < bucketCount; bucketOffset += bucketsPerChunk) { int chunkSize = Math.min(bucketsPerChunk, remaining); remaining -= chunkSize; ArrayList buckets2 = new ArrayList(chunkSize); for (int i = 0; i < chunkSize; i++) { buckets2.add(new Bucket(averageBucketSize)); } for (T t : collection) { int b; long h = hash.universalHash(t, 0); b = Settings.reduce((int) h, bucketCount); if (b >= bucketCount || b < 0) { throw new AssertionError(); } if (b >= bucketOffset && b < bucketOffset + bucketsPerChunk) { buckets2.get(b - bucketOffset).add(t); } } processBuckets(size, bucketCount, buckets2); for (Bucket b2 : buckets2) { buckets.add(b2); } buckets2.clear(); } } ArrayList alternativeList = new ArrayList(); for (int i = 0; i < buckets.size(); i++) { Bucket b = buckets.get(i); // move all buckets first, so overlap is not affected b.moveToAlternative(alternativeList); } int[] startList = new int[buckets.size() + 1]; int[] offsetList = new int[buckets.size() + 1]; int start = 0, offset = 0; for (int i = 0; i < buckets.size(); i++) { Bucket b = buckets.get(i); if (start - offset < 0) { throw new AssertionError(); } int pos = b.buff.position(); // possible overlap if (i < buckets.size() - 1) { Bucket next = buckets.get(i + 1); int maxOverlap = Math.min(16, next.buff.position()); // at least one bit per entry int minBitCount = getMinBitCount(b.entryCount); maxOverlap = Math.min(maxOverlap, b.buff.position() - minBitCount); int overlap = 0; for (; overlap < maxOverlap; overlap++) { if (next.buff.readNumber(0, overlap + 1) != b.buff.readNumber(pos - overlap - 1, overlap + 1)) { break; } } pos -= overlap; b.buff.seek(pos); } start += pos; offset += b.entryCount; startList[i + 1] = start; offsetList[i + 1] = offset; } shrinkList(startList, offsetList); int minOffsetDiff = shrinkList(offsetList); int minStartDiff = shrinkList(startList); if (minStartDiff < 0) { throw new AssertionError(); } BitBuffer alt = null; if (!alternativeList.isEmpty()) { alt = BDZ.generate(hash, alternativeList); } int bitCount = BitBuffer.getEliasDeltaSize(size + 1); bitCount += 1; bitCount += BitBuffer.getEliasDeltaSize(minOffsetDiff + 1); bitCount += BitBuffer.getEliasDeltaSize(minStartDiff + 1); bitCount += MonotoneList.getSize(offsetList, eliasFanoMonotoneLists); bitCount += MonotoneList.getSize(startList, eliasFanoMonotoneLists); bitCount += start; if (alt != null) { bitCount += alt.position(); } BitBuffer all = new BitBuffer(bitCount); all.writeEliasDelta(size + 1); all.writeBit(alternativeList.isEmpty() ? 0 : 1); all.writeEliasDelta(minOffsetDiff + 1); all.writeEliasDelta(minStartDiff + 1); MonotoneList.generate(offsetList, all, eliasFanoMonotoneLists); MonotoneList.generate(startList, all, eliasFanoMonotoneLists); for (int i = 0; i < buckets.size(); i++) { Bucket b = buckets.get(i); all.write(b.buff); } if (alt != null) { all.write(alt); } if (bitCount != all.position()) { throw new AssertionError(); } return all; } private void processBuckets(long size, int bucketCount, final ArrayList buckets) { int averageBucketSize = (int) (size / bucketCount); final int maxBucketSize = averageBucketSize * MAX_FILL; final int maxBits = maxBucketSize * MAX_BITS_PER_ENTRY; pool.invoke(new RecursiveAction() { private static final long serialVersionUID = 1L; @Override protected void compute() { RecursiveAction[] list = new RecursiveAction[buckets.size()]; for (int i = 0; i < buckets.size(); i++) { final Bucket b = buckets.get(i); list[i] = new RecursiveAction() { private static final long serialVersionUID = 1L; @Override protected void compute() { b.generateBucket(hash, maxBucketSize, maxBits); } }; } pool.invokeAll(list); } }); } public static void shrinkList(int[] targetList, int[] sourceList) { int sum = 0; for (int i = 1; i < sourceList.length; i++) { int d = sourceList[i] - sourceList[i - 1]; sum += d; targetList[i] -= getMinBitCount(sum); if (targetList[i] < targetList[i - 1]) { throw new IllegalArgumentException(""); } } } public static int shrinkList(int[] list) { int min = Integer.MAX_VALUE; for (int i = 0; i < list.length - 1; i++) { int d = list[i + 1] - list[i]; min = Math.min(min, d); } for (int i = 1; i < list.length; i++) { list[i] -= i * min; } return min; } public static int getMinBitCount(int size) { // at least 1.375 bits per key (if it is less, fill with zeroes) return (size * 11 + 7) >> 3; } /** * A bucket. */ class Bucket { ArrayList list; BitBuffer buff; int entryCount; boolean alternative; Bucket(int averageBucketSize) { list = new ArrayList(averageBucketSize * 11 / 10); } @Override public String toString() { return "" + entryCount; } public void moveToAlternative(ArrayList alternativeList) { if (alternative) { alternativeList.addAll(list); list = null; entryCount = 0; buff = new BitBuffer(0); } } void add(T obj) { list.add(obj); } void generateBucket(UniversalHash hash, int maxBucketSize, int maxBits) { int size = list.size(); entryCount = size; int minSize = getMinBitCount(size); if (size <= 1) { // zero or one entry buff = new BitBuffer(minSize); while (buff.position() < minSize) { buff.writeBit(1); } return; } if (size > maxBucketSize) { alternative = true; buff = new BitBuffer(0); return; } @SuppressWarnings("unchecked") T[] data = list.toArray((T[]) new Object[0]); list = null; long[] hashes = new long[size]; long startIndex = 0; for (int i = 0; i < size; i++) { hashes[i] = hash.universalHash(data[i], Settings.getUniversalHashIndex(startIndex)); } // this is very conservative; less memory could be allocated int bufferSize = 8 * size; if (settings.getLeafSize() < 6) { bufferSize *= 4; } buff = new BitBuffer(bufferSize); generate(data, hashes, startIndex, buff); if (buff.position() < minSize) { while (buff.position() < minSize) { buff.writeBit(1); } } if (buff.position() > maxBits) { alternative = true; } } } } ================================================ FILE: src/main/java/org/minperf/hash/HashPerformanceTest.java ================================================ package org.minperf.hash; import java.util.Random; public class HashPerformanceTest { public static void main(String... args) throws InterruptedException { test(); test(); test(); } private static void test() throws InterruptedException { Thread.sleep(1000); long dummy = 0; for (int avgSize = 16; avgSize < 128; avgSize *= 2) { byte[][] data = new byte[1024 * 1024][avgSize]; Random r = new Random(1); for (int i = 0; i < data.length; i++) { r.nextBytes(data[i]); } long time = System.nanoTime(); LongPair pair = new LongPair(); for (int j = 0; j < avgSize; j++) { for (int i = 0; i < data.length; i++) { // dummy += Murmur2.hash64(data[i], avgSize, j); // bits: 128 10 ns/key // bits: 256 17 ns/key // bits: 512 34 ns/key // dummy += Murmur2.hash64x8(data[i], avgSize, j); // bits: 128 8 ns/key // bits: 256 16 ns/key // bits: 512 31 ns/key // dummy += XXHash64.hash64(data[i], 0, avgSize, i); // bits: 128 16 ns/key // bits: 256 29 ns/key // bits: 512 51 ns/key // SpookyHash.hash128x32(data[i], 0, avgSize, i, pair); // dummy += pair.val1; // dummy += pair.val2; // bits: 256 34 ns/key // bits: 512 53 ns/key // dummy += Murmur3.hash64x8(data[i], avgSize, j); // bits: 128 8 ns/key // bits: 256 16 ns/key // bits: 512 31 ns/key Murmur3.hash128x16(data[i], 0, avgSize, i, pair); dummy += pair.val1; dummy += pair.val2; // bits: 128 16 ns/key // bits: 256 25 ns/key // bits: 512 42 ns/key } } time = System.nanoTime() - time; System.out.println("bits: " + avgSize * 8 + " " + time / data.length / avgSize + " ns/key"); } System.out.println("dummy " + dummy); } } ================================================ FILE: src/main/java/org/minperf/hash/LongPair.java ================================================ package org.minperf.hash; public class LongPair { public long val1; public long val2; } ================================================ FILE: src/main/java/org/minperf/hash/Mix.java ================================================ package org.minperf.hash; public class Mix { public static int supplementalHashWeyl(long hash, long index) { long x = hash + (index * 0xbf58476d1ce4e5b9L); x = (x ^ (x >>> 32)) * 0xbf58476d1ce4e5b9L; x = ((x >>> 32) ^ x); return (int) x; } public static int hash32(int x) { x = ((x >>> 16) ^ x) * 0x45d9f3b; x = ((x >>> 16) ^ x) * 0x45d9f3b; x = (x >>> 16) ^ x; return x; } public static int unhash32(int x) { x = ((x >>> 16) ^ x) * 0x119de1f3; x = ((x >>> 16) ^ x) * 0x119de1f3; x = (x >>> 16) ^ x; return x; } public static long hash64(long x) { x = (x ^ (x >>> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >>> 27)) * 0x94d049bb133111ebL; x = x ^ (x >>> 31); return x; } public static long unhash64(long x) { x = (x ^ (x >>> 31) ^ (x >>> 62)) * 0x319642b2d24d8ec3L; x = (x ^ (x >>> 27) ^ (x >>> 54)) * 0x96de1b173f119089L; x = x ^ (x >>> 30) ^ (x >>> 60); return x; } } ================================================ FILE: src/main/java/org/minperf/hash/Murmur2.java ================================================ package org.minperf.hash; public class Murmur2 { public static final long UINT_MASK = 0xFFFFFFFFl; public static long hash(byte[] data, int length, long seed) { long m = 0x5bd1e995l; int r = 24; long hash = ((seed ^ length) & UINT_MASK); int length4 = length >>> 2; for (int i = 0; i < length4; i++) { int i4 = i << 2; long k = (data[i4] & 0xff); k |= (data[i4 + 1] & 0xff) << 8; k |= (data[i4 + 2] & 0xff) << 16; k |= (data[i4 + 3] & 0xff) << 24; k = ((k * m) & UINT_MASK); k ^= ((k >>> r) & UINT_MASK); k = ((k * m) & UINT_MASK); hash = ((hash * m) & UINT_MASK); hash = ((hash ^ k) & UINT_MASK); } int offset = length4 << 2; switch (length & 3) { case 3: hash ^= ((data[offset + 2] << 16) & UINT_MASK); case 2: hash ^= ((data[offset + 1] << 8) & UINT_MASK); case 1: hash ^= (data[offset] & UINT_MASK); hash = ((hash * m) & UINT_MASK); } hash ^= ((hash >>> 13) & UINT_MASK); hash = ((hash * m) & UINT_MASK); hash ^= hash >>> 15; return hash; } public static long hash64(byte[] data, int length, long seed) { long m = 0xc6a4a7935bd1e995L; int r = 47; long h = (seed & UINT_MASK) ^ (length * m); int length8 = length >> 3; for (int i = 0; i < length8; i++) { int i8 = i << 3; long k = ((long) data[i8] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); k *= m; k ^= k >>> r; k *= m; h ^= k; h *= m; } switch (length & 7) { case 7: h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48; case 6: h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40; case 5: h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32; case 4: h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24; case 3: h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16; case 2: h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8; case 1: h ^= (long) (data[length & ~7] & 0xff); h *= m; } h ^= h >>> r; h *= m; h ^= h >>> r; return h; } public static long hash64x8(byte[] data, int length, long seed) { long m = 0xc6a4a7935bd1e995L; int r = 47; long h = seed; int length8 = length >> 3; for (int i = 0; i < length8; i++) { int i8 = i << 3; long k = ((long) data[i8] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); k *= m; k ^= k >>> r; k *= m; h ^= k; h *= m; } h ^= h >>> r; h *= m; h ^= h >>> r; return h; } } ================================================ FILE: src/main/java/org/minperf/hash/Murmur3.java ================================================ /** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.minperf.hash; public class Murmur3 { private static long UINT_MASK = 0xFFFFFFFFl; public static long hash64(byte[] data, int length, long seed) { long m = 0xc6a4a7935bd1e995L; int r = 47; long h = (seed & UINT_MASK) ^ (length * m); int length8 = length >> 3; for (int i = 0; i < length8; i++) { int i8 = i << 3; long k = ((long) data[i8] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); k *= m; k ^= k >>> r; k *= m; h ^= k; h *= m; } switch (length & 7) { case 7: h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48; case 6: h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40; case 5: h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32; case 4: h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24; case 3: h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16; case 2: h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8; case 1: h ^= (long) (data[length & ~7] & 0xff); h *= m; } h ^= h >>> r; h *= m; h ^= h >>> r; return h; } public static long hash64x8(byte[] data, int length, long seed) { long m = 0xc6a4a7935bd1e995L; int r = 47; long h = seed; int length8 = length >> 3; for (int i = 0; i < length8; i++) { int i8 = i << 3; long k = ((long) data[i8] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); k *= m; k ^= k >>> r; k *= m; h ^= k; h *= m; } h ^= h >>> r; h *= m; h ^= h >>> r; return h; } public static int fmix32(int h) { h ^= h >>> 16; h *= 0x85ebca6b; h ^= h >>> 13; h *= 0xc2b2ae35; h ^= h >>> 16; return h; } public static int hash32(byte[] data, int offset, int len, int seed) { int c1 = 0xcc9e2d51; int c2 = 0x1b873593; int h1 = seed; // round down to 4 byte block int roundedEnd = offset + (len & 0xfffffffc); for (int i = offset; i < roundedEnd; i += 4) { // little endian load order int k1 = (data[i] & 0xff) | ((data[i + 1] & 0xff) << 8) | ((data[i + 2] & 0xff) << 16) | (data[i + 3] << 24); k1 *= c1; // ROTL32(k1,15); k1 = (k1 << 15) | (k1 >>> 17); k1 *= c2; h1 ^= k1; // ROTL32(h1,13); h1 = (h1 << 13) | (h1 >>> 19); h1 = h1 * 5 + 0xe6546b64; } // tail int k1 = 0; switch (len & 0x03) { case 3: k1 = (data[roundedEnd + 2] & 0xff) << 16; // fallthrough case 2: k1 |= (data[roundedEnd + 1] & 0xff) << 8; // fallthrough case 1: k1 |= (data[roundedEnd] & 0xff); k1 *= c1; // ROTL32(k1,15); k1 = (k1 << 15) | (k1 >>> 17); k1 *= c2; h1 ^= k1; } // finalization h1 ^= len; // fmix(h1); h1 ^= h1 >>> 16; h1 *= 0x85ebca6b; h1 ^= h1 >>> 13; h1 *= 0xc2b2ae35; h1 ^= h1 >>> 16; return h1; } // https://github.com/yonik/java_util/blob/master/src/util/hash/MurmurHash3.java // This java port was authored by Yonik Seeley and also placed into the // public domain. public static void hash128(byte[] key, int offset, int len, int seed, LongPair out) { // The original algorithm does have a 32 bit unsigned seed. // We have to mask to match the behavior of the unsigned types and // prevent sign extension. long h1 = seed & 0x00000000FFFFFFFFL; long h2 = seed & 0x00000000FFFFFFFFL; long c1 = 0x87c37b91114253d5L; long c2 = 0x4cf5ad432745937fL; // round down to 16 byte block int roundedEnd = offset + (len & 0xFFFFFFF0); for (int i = offset; i < roundedEnd; i += 16) { long k1 = getLongLittleEndian(key, i); long k2 = getLongLittleEndian(key, i + 8); k1 *= c1; k1 = Long.rotateLeft(k1, 31); k1 *= c2; h1 ^= k1; h1 = Long.rotateLeft(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; k2 *= c2; k2 = Long.rotateLeft(k2, 33); k2 *= c1; h2 ^= k2; h2 = Long.rotateLeft(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } long k1 = 0; long k2 = 0; switch (len & 15) { case 15: k2 = (key[roundedEnd + 14] & 0xffL) << 48; case 14: k2 |= (key[roundedEnd + 13] & 0xffL) << 40; case 13: k2 |= (key[roundedEnd + 12] & 0xffL) << 32; case 12: k2 |= (key[roundedEnd + 11] & 0xffL) << 24; case 11: k2 |= (key[roundedEnd + 10] & 0xffL) << 16; case 10: k2 |= (key[roundedEnd + 9] & 0xffL) << 8; case 9: k2 |= (key[roundedEnd + 8] & 0xffL); k2 *= c2; k2 = Long.rotateLeft(k2, 33); k2 *= c1; h2 ^= k2; case 8: k1 = ((long) key[roundedEnd + 7]) << 56; case 7: k1 |= (key[roundedEnd + 6] & 0xffL) << 48; case 6: k1 |= (key[roundedEnd + 5] & 0xffL) << 40; case 5: k1 |= (key[roundedEnd + 4] & 0xffL) << 32; case 4: k1 |= (key[roundedEnd + 3] & 0xffL) << 24; case 3: k1 |= (key[roundedEnd + 2] & 0xffL) << 16; case 2: k1 |= (key[roundedEnd + 1] & 0xffL) << 8; case 1: k1 |= (key[roundedEnd] & 0xffL); k1 *= c1; k1 = Long.rotateLeft(k1, 31); k1 *= c2; h1 ^= k1; } // finalization h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; out.val1 = h1; out.val2 = h2; } public static void hash128x16(byte[] key, int offset, int len, long seed, LongPair out) { long h1 = seed; long h2 = seed; long c1 = 0x87c37b91114253d5L; long c2 = 0x4cf5ad432745937fL; int roundedEnd = offset + len; if (roundedEnd > key.length) { throw new IndexOutOfBoundsException(); } for (int i = offset; i < roundedEnd; i += 16) { long k1 = getLongLittleEndian(key, i); k1 *= c1; k1 = Long.rotateLeft(k1, 31); k1 *= c2; h1 ^= k1; h1 = Long.rotateLeft(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; long k2 = getLongLittleEndian(key, i + 8); k2 *= c2; k2 = Long.rotateLeft(k2, 33); k2 *= c1; h2 ^= k2; h2 = Long.rotateLeft(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; out.val1 = h1; out.val2 = h2; } private static long getLongLittleEndian(byte[] data, int pos) { return ((long) data[pos + 7] << 56) | ((data[pos + 6] & 0xffL) << 48) | ((data[pos + 5] & 0xffL) << 40) | ((data[pos + 4] & 0xffL) << 32) | ((data[pos + 3] & 0xffL) << 24) | ((data[pos + 2] & 0xffL) << 16) | ((data[pos + 1] & 0xffL) << 8) | ((data[pos] & 0xffL)); } private static long fmix64(long k) { k ^= k >>> 33; k *= 0xff51afd7ed558ccdL; k ^= k >>> 33; k *= 0xc4ceb9fe1a85ec53L; k ^= k >>> 33; return k; } } ================================================ FILE: src/main/java/org/minperf/hash/SpookyHash.java ================================================ package org.minperf.hash; public class SpookyHash { static void hash128x32(byte[] key, int offset, int len, int seed, LongPair out) { // 128 bit: 16 bytes // 256 bit: 32 bytes // 512 bit: 64 bytes int blocksOf32 = (len/32)*32; int end32 = offset + blocksOf32; // handle all complete sets of 32 bytes long a = seed, b = seed, c = 0xdeadbeefdeadbeefL, d = 0xdeadbeefdeadbeefL; for (; offset < end32; offset += 32) { c += readLong(key, offset); d += readLong(key, offset + 8); // shortMix(a,b,c,d); c = Long.rotateLeft(c,50); c += d; a ^= c; d = Long.rotateLeft(d,52); d += a; b ^= d; a = Long.rotateLeft(a,30); a += b; c ^= a; b = Long.rotateLeft(b,41); b += c; d ^= b; c = Long.rotateLeft(c,54); c += d; a ^= c; d = Long.rotateLeft(d,48); d += a; b ^= d; a = Long.rotateLeft(a,38); a += b; c ^= a; b = Long.rotateLeft(b,37); b += c; d ^= b; c = Long.rotateLeft(c,62); c += d; a ^= c; d = Long.rotateLeft(d,34); d += a; b ^= d; a = Long.rotateLeft(a,5); a += b; c ^= a; b = Long.rotateLeft(b,36); b += c; d ^= b; a += readLong(key, offset + 16); b += readLong(key, offset + 24); } d += len << 56; c += 0xdeadbeefdeadbeefL; d += 0xdeadbeefdeadbeefL; // shortEnd(a,b,c,d); d ^= c; c = Long.rotateLeft(c,15); d += c; a ^= d; d = Long.rotateLeft(d,52); a += d; b ^= a; a = Long.rotateLeft(a,26); b += a; c ^= b; b = Long.rotateLeft(b,51); c += b; d ^= c; c = Long.rotateLeft(c,28); d += c; a ^= d; d = Long.rotateLeft(d,9); a += d; b ^= a; a = Long.rotateLeft(a,47); b += a; c ^= b; b = Long.rotateLeft(b,54); c += b; d ^= c; c = Long.rotateLeft(c,32); d += c; a ^= d; d = Long.rotateLeft(d,25); a += d; b ^= a; a = Long.rotateLeft(a,63); b += a; out.val1 = a; out.val2 = b; } static void shortEnd(long a, long b, long c, long d) { d ^= c; c = Long.rotateLeft(c,15); d += c; a ^= d; d = Long.rotateLeft(d,52); a += d; b ^= a; a = Long.rotateLeft(a,26); b += a; c ^= b; b = Long.rotateLeft(b,51); c += b; d ^= c; c = Long.rotateLeft(c,28); d += c; a ^= d; d = Long.rotateLeft(d,9); a += d; b ^= a; a = Long.rotateLeft(a,47); b += a; c ^= b; b = Long.rotateLeft(b,54); c += b; d ^= c; c = Long.rotateLeft(c,32); d += c; a ^= d; d = Long.rotateLeft(d,25); a += d; b ^= a; a = Long.rotateLeft(a,63); b += a; } static void shortMix(long a, long b, long c, long d) { c = Long.rotateLeft(c,50); c += d; a ^= c; d = Long.rotateLeft(d,52); d += a; b ^= d; a = Long.rotateLeft(a,30); a += b; c ^= a; b = Long.rotateLeft(b,41); b += c; d ^= b; c = Long.rotateLeft(c,54); c += d; a ^= c; d = Long.rotateLeft(d,48); d += a; b ^= d; a = Long.rotateLeft(a,38); a += b; c ^= a; b = Long.rotateLeft(b,37); b += c; d ^= b; c = Long.rotateLeft(c,62); c += d; a ^= c; d = Long.rotateLeft(d,34); d += a; b ^= d; a = Long.rotateLeft(a,5); a += b; c ^= a; b = Long.rotateLeft(b,36); b += c; d ^= b; } private static long readLong(byte[] data, int pos) { return ((long) data[pos] & 0xff) + (((long) data[pos + 1] & 0xff) << 8) + (((long) data[pos + 2] & 0xff) << 16) + (((long) data[pos + 3] & 0xff) << 24) + (((long) data[pos + 4] & 0xff) << 32) + (((long) data[pos + 5] & 0xff) << 40) + (((long) data[pos + 6] & 0xff) << 48) + (((long) data[pos + 7] & 0xff) << 56); } } ================================================ FILE: src/main/java/org/minperf/hash/XXHash64.java ================================================ package org.minperf.hash; import static java.lang.Long.rotateLeft; import java.nio.ByteBuffer; import java.nio.ByteOrder; public class XXHash64 { private static final long PRIME64_1 = -7046029288634856825L; private static final long PRIME64_2 = -4417276706812531889L; private static final long PRIME64_3 = 1609587929392839161L; private static final long PRIME64_4 = -8796714831421723037L; private static final long PRIME64_5 = 2870177450012600261L; public static long hash64(byte[] buf, int off, int len, long seed) { checkRange(buf, off, len); int end = off + len; long h64; if (len >= 32) { int limit = end - 32; long v1 = seed + PRIME64_1 + PRIME64_2; long v2 = seed + PRIME64_2; long v3 = seed + 0; long v4 = seed - PRIME64_1; do { v1 += readLongLE(buf, off) * PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; off += 8; v2 += readLongLE(buf, off) * PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; off += 8; v3 += readLongLE(buf, off) * PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; off += 8; v4 += readLongLE(buf, off) * PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; off += 8; } while (off <= limit); h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; h64 = h64 * PRIME64_1 + PRIME64_4; v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; h64 = h64 * PRIME64_1 + PRIME64_4; v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; h64 = h64 * PRIME64_1 + PRIME64_4; v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; h64 = h64 * PRIME64_1 + PRIME64_4; } else { h64 = seed + PRIME64_5; } h64 += len; while (off <= end - 8) { long k1 = readLongLE(buf, off); k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; off += 8; } if (off <= end - 4) { h64 ^= (readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; off += 4; } while (off < end) { h64 ^= (readByte(buf, off) & 0xFF) * PRIME64_5; h64 = rotateLeft(h64, 11) * PRIME64_1; ++off; } h64 ^= h64 >>> 33; h64 *= PRIME64_2; h64 ^= h64 >>> 29; h64 *= PRIME64_3; h64 ^= h64 >>> 32; return h64; } public static long hash64(ByteBuffer buf, int off, int len, long seed) { if (buf.hasArray()) { return hash64(buf.array(), off + buf.arrayOffset(), len, seed); } checkRange(buf, off, len); buf = inLittleEndianOrder(buf); int end = off + len; long h64; if (len >= 32) { int limit = end - 32; long v1 = seed + PRIME64_1 + PRIME64_2; long v2 = seed + PRIME64_2; long v3 = seed + 0; long v4 = seed - PRIME64_1; do { v1 += readLongLE(buf, off) * PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; off += 8; v2 += readLongLE(buf, off) * PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; off += 8; v3 += readLongLE(buf, off) * PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; off += 8; v4 += readLongLE(buf, off) * PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; off += 8; } while (off <= limit); h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; h64 = h64 * PRIME64_1 + PRIME64_4; v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; h64 = h64 * PRIME64_1 + PRIME64_4; v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; h64 = h64 * PRIME64_1 + PRIME64_4; v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; h64 = h64 * PRIME64_1 + PRIME64_4; } else { h64 = seed + PRIME64_5; } h64 += len; while (off <= end - 8) { long k1 = readLongLE(buf, off); k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; off += 8; } if (off <= end - 4) { h64 ^= (readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; off += 4; } while (off < end) { h64 ^= (readByte(buf, off) & 0xFF) * PRIME64_5; h64 = rotateLeft(h64, 11) * PRIME64_1; ++off; } h64 ^= h64 >>> 33; h64 *= PRIME64_2; h64 ^= h64 >>> 29; h64 *= PRIME64_3; h64 ^= h64 >>> 32; return h64; } private static void checkRange(ByteBuffer buf, int off, int len) { checkLength(len); if (len > 0) { checkRange(buf, off); checkRange(buf, off + len - 1); } } private static void checkRange(ByteBuffer buf, int off) { if (off < 0 || off >= buf.capacity()) { throw new ArrayIndexOutOfBoundsException(off); } } private static ByteBuffer inLittleEndianOrder(ByteBuffer buf) { if (buf.order().equals(ByteOrder.LITTLE_ENDIAN)) { return buf; } else { return buf.duplicate().order(ByteOrder.LITTLE_ENDIAN); } } private static byte readByte(ByteBuffer buf, int i) { return buf.get(i); } private static int readIntLE(ByteBuffer buf, int i) { assert buf.order() == ByteOrder.LITTLE_ENDIAN; return buf.getInt(i); } private static long readLongLE(ByteBuffer buf, int i) { assert buf.order() == ByteOrder.LITTLE_ENDIAN; return buf.getLong(i); } private static void checkRange(byte[] buf, int off) { if (off < 0 || off >= buf.length) { throw new ArrayIndexOutOfBoundsException(off); } } private static void checkRange(byte[] buf, int off, int len) { checkLength(len); if (len > 0) { checkRange(buf, off); checkRange(buf, off + len - 1); } } private static void checkLength(int len) { if (len < 0) { throw new IllegalArgumentException("lengths must be >= 0"); } } private static byte readByte(byte[] buf, int i) { return buf[i]; } private static int readIntLE(byte[] buf, int i) { return (buf[i] & 0xFF) | ((buf[i + 1] & 0xFF) << 8) | ((buf[i + 2] & 0xFF) << 16) | ((buf[i + 3] & 0xFF) << 24); } private static long readLongLE(byte[] buf, int i) { return (buf[i] & 0xFFL) | ((buf[i + 1] & 0xFFL) << 8) | ((buf[i + 2] & 0xFFL) << 16) | ((buf[i + 3] & 0xFFL) << 24) | ((buf[i + 4] & 0xFFL) << 32) | ((buf[i + 5] & 0xFFL) << 40) | ((buf[i + 6] & 0xFFL) << 48) | ((buf[i + 7] & 0xFFL) << 56); } } ================================================ FILE: src/main/java/org/minperf/hem/HemGenerator.java ================================================ package org.minperf.hem; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Random; import org.minperf.BitBuffer; import org.minperf.hem.recsplit.Builder; import org.minperf.utils.RandomSetGenerator; import org.minperf.utils.RandomSetGenerator.RandomBlockProducer; public class HemGenerator { public static void main(String... args) throws Exception { new HemGenerator().test_10_12_64(); } public void test_10_12_64() throws Exception { // generate a MPHF with 10^12 keys, each 64 bit // 10^12 would take around 16 hours to generate // 2.87 bits/key, that is around 335 GB String userHome = System.getProperty("user.home"); String fileName = userHome + "/temp/hash.bin"; FileOutputStream fOut = new FileOutputStream(fileName); out = new DataOutputStream(new BufferedOutputStream(fOut)); totalSize = 1_000_000_000_000L; // totalSize = 1_000_000_00L; out.writeLong(totalSize); generator = new RandomHashGenerator(totalSize); lowBitCount = 64; long expectedBlockSize = totalSize; final int maxBlockSize = 2_000_000; while (expectedBlockSize > maxBlockSize) { lowBitCount--; expectedBlockSize >>>= 1; } int highBitCount = 64 - lowBitCount; System.out.println("lowBits: " + lowBitCount); System.out.println("highBits: " + highBitCount); System.out.println("expectedBlockSize: " + expectedBlockSize); max = 1L << highBitCount; startTime = System.nanoTime(); int threadCount = 8; Thread[] threads = new Thread[threadCount]; for (int id = 0; id < threadCount; id++) { Thread t = new Thread() { @Override public void run() { long[] keys = new long[maxBlockSize * 2]; while (true) { BlockInfo info = generateKeys(keys); if (info == null) { break; } BitBuffer buff = new Builder().generate(keys, info.len); write(info, buff); } } }; t.start(); threads[id] = t; } for (Thread t : threads) { t.join(); } out.writeLong(-1); out.writeInt(-1); out.close(); System.out.println("done"); testRead(fileName); } static void testRead(String fileName) throws IOException { FileInputStream fIn = new FileInputStream(fileName); DataInputStream in; in = new DataInputStream(new BufferedInputStream(fIn)); long totalSize = in.readLong(); System.out.println("total size " + totalSize); while(true) { long high = in.readLong(); int len = in.readInt(); byte[] data = new byte[len]; // System.out.println("read high: " + high + " len: " + len); if (high == -1) { break; } in.readFully(data); } in.close(); fIn.close(); // System.out.println("done"); } long totalSize; DataOutputStream out; HashGenerator generator; int lowBitCount; long startTime; long high; long max; long total; long totalBits; synchronized void write(BlockInfo info, BitBuffer buff) { total += info.len; byte[] data = buff.toByteArray(); totalBits += data.length * 8; long time = System.nanoTime() - startTime; double percentDone = 100. * total / totalSize; System.out.println(time / total + " ns/key " + (totalBits / 8 / 1024 / 1024) + " MB " + percentDone + "% " + (double) totalBits / total + " bits/key"); // System.out.println("write " + info.high + " len " + info.len + " " + data.length); try { out.writeLong(info.high); out.writeInt(info.len); out.write(buff.toByteArray()); } catch (IOException e) { throw new RuntimeException(); } } synchronized BlockInfo generateKeys(long[] data) { if (high >= max) { return null; } BlockInfo info = new BlockInfo(); info.high = high++; info.len = generator.generateBlock(data, lowBitCount); if (info.len == 0) { System.out.println("not generated! " + data.length + " " + lowBitCount); } return info; } static class BlockInfo { int len; long high; } static class RandomHashGenerator implements HashGenerator { private final long[] buffer; private final RandomBlockProducer producer; private final int blockSize = 8 * 1024 * 1024; private int offset, end; private long highBits; public RandomHashGenerator(long size) { this.producer = RandomSetGenerator.randomHashProducer(new Random(1), size); buffer = new long[blockSize]; } @Override public int generateBlock(long[] data, int lowBitCount) { int len = 0; // TODO why not 64 - lowBitCount? int shift = 65 - lowBitCount; while (true) { while (offset < end) { long x = buffer[offset]; if ((x >>> lowBitCount) != highBits) { highBits++; return len; } offset++; data[len++] = x << shift; } offset = 0; end = producer.produce(buffer, 0, blockSize, 0); if (end == 0) { return len; } } } } interface HashGenerator { int generateBlock(long[] data, int lowBits); } } ================================================ FILE: src/main/java/org/minperf/hem/KeyReader.java ================================================ package org.minperf.hem; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.AsynchronousFileChannel; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.util.Iterator; import java.util.PrimitiveIterator; import java.util.UUID; import java.util.concurrent.Future; import org.minperf.hash.Mix; import org.minperf.hash.Murmur2; public class KeyReader { private static final int MAX_KEY_SIZE = 1024; private static final UUID EMPTY = new UUID(0, 0); public static void main(String... args) { // seq -f "%.20g" 1 1000000 > ~/temp/hash/keys.txt // seq -f "%.20g" 1 100000000 > ~/temp/hash/keys.txt for(int test = 0; test < 10; test++) { Iterator it = KeyReader.readSignaturesFromTextFile2(args[0]); long time = System.nanoTime(); long count = 0; long dummy = 0; while(it.hasNext()) { UUID uuid = it.next(); count++; dummy += uuid.getMostSignificantBits() + uuid.getLeastSignificantBits(); } time = System.nanoTime() - time; System.out.println("time: " + time + " dummy "+ dummy + " count " + count + " " + time / count + " ns/key"); } } static Iterator readSignaturesFromTextFile(final String fileName) { return new Iterator() { private static final int BUFFER_SIZE = 512 * 1024 * 1024; private final InputStream input; private final byte[] key = new byte[MAX_KEY_SIZE]; private UUID current; { FileInputStream fileIn; try { fileIn = new FileInputStream(fileName); } catch (IOException e) { throw new RuntimeException(e); } input = new BufferedInputStream(fileIn, BUFFER_SIZE); fetchNext(); } private void fetchNext() { int i = 0; for (;; i++) { int x; try { x = input.read(); } catch (IOException e) { throw new RuntimeException(e); } if (x < 0) { try { input.close(); } catch (IOException e) { throw new RuntimeException(e); } if (i == 0) { current = null; return; } break; } if (x <= ' ') { if (i == 0) { i--; continue; } break; } if (i >= MAX_KEY_SIZE) { throw new RuntimeException("Key too long, max size " + MAX_KEY_SIZE); } key[i] = (byte) x; } current = EMPTY; // current = getSipHash24(key, 0, i); } @Override public boolean hasNext() { return current != null; } @Override public UUID next() { UUID result = current; if (result != null) { fetchNext(); } return result; } }; } public static Iterator readSignaturesFromTextFile2(final String fileName) { return new Iterator() { private final RandomAccessFile f; private final FileChannel fc; private final MappedByteBuffer buff; private final byte[] key = new byte[MAX_KEY_SIZE]; private UUID current; private int size; private int pos; { try { f = new RandomAccessFile(fileName, "r"); fc = f.getChannel(); size = (int) fc.size(); buff = fc.map(MapMode.READ_ONLY, 0, size); } catch (IOException e) { throw new RuntimeException(e); } fetchNext(); } private void fetchNext() { int i=0; for (;; i++) { if (pos >= size) { try { fc.close(); f.close(); } catch (IOException e) { throw new RuntimeException(e); } current = null; return; } int x = buff.get(); pos++; if (x <= ' ') { if (i == 0) { i--; continue; } break; } key[i] = (byte) x; // if (i >= MAX_KEY_SIZE) { // throw new RuntimeException("Key too long, max size " + MAX_KEY_SIZE); // } } // current = EMPTY; // TODO find a fast 128 bit hash function current = new UUID(Murmur2.hash64(key, i, 0), 0); // current = getSipHash24(buff, start, i); } @Override public boolean hasNext() { return current != null; } @Override public UUID next() { UUID result = current; if (result != null) { fetchNext(); } return result; } }; } static PrimitiveIterator.OfLong readSignaturesFromTextFile64(final String fileName) { return new PrimitiveIterator.OfLong() { private static final int MAX_BUFFER_SIZE = 512 * 1024 * 1024; private final RandomAccessFile f; private final FileChannel fc; private MappedByteBuffer buff; private long size; private long pos; { try { f = new RandomAccessFile(fileName, "r"); fc = f.getChannel(); size = fc.size(); remap(); } catch (IOException e) { throw new RuntimeException(e); } } void remap() { try { long s = Math.min(size - pos, MAX_BUFFER_SIZE); buff = fc.map(MapMode.READ_ONLY, pos, s); } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean hasNext() { return pos < size; } @Override public long nextLong() { if (!buff.hasRemaining()) { remap(); } pos += 8; // return buff.getLong(); return Mix.hash64(buff.getLong()); // return universalHash(buff.getLong(), 0); } }; } public static Iterator readSignaturesFromTextFile64input(final String fileName) { return new Iterator() { private static final int BUFFER_SIZE = 8 * 1024; private final DataInputStream in; private long size; private long pos; { try { size = new File(fileName).length(); in = new DataInputStream( new BufferedInputStream( new FileInputStream(fileName) , BUFFER_SIZE ) ); } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean hasNext() { return pos < size; } @Override public Long next() { try { pos += 8; return in.readLong(); } catch (IOException e) { throw new RuntimeException(e); } } }; } static Iterator readSignaturesFromTextFile64b(final String fileName) { return new Iterator() { private static final int BUFFER_SIZE = 64 * 1024; private final RandomAccessFile f; private final FileChannel fc; private final ByteBuffer buff; private int size; private int pos; { try { f = new RandomAccessFile(fileName, "r"); fc = f.getChannel(); size = (int) fc.size(); buff = ByteBuffer.allocate(BUFFER_SIZE); readBatch(); } catch (IOException e) { throw new RuntimeException(e); } } void readBatch() { buff.clear(); try { // TODO may not read all bytes (and possibly not a multiple of 64) fc.read(buff); } catch (IOException e) { throw new RuntimeException(e); } buff.flip(); } @Override public boolean hasNext() { return pos < size ; } @Override public Long next() { if (!buff.hasRemaining()) { readBatch(); } pos += 8; return buff.getLong(); // return mix64(buff.getLong()); // return universalHash(buff.getLong(), 0); } }; } static Iterator readSignaturesFromTextFile64async(final String fileName) { return new Iterator() { private static final int BUFFER_SIZE = 64 * 1024; private final AsynchronousFileChannel fc; private final ByteBuffer[] buffers = new ByteBuffer[2]; private Future future; private int currentBuff = 1; private int size; private int pos; { try { fc = AsynchronousFileChannel.open(Paths.get(fileName)); size = (int) fc.size(); buffers[0] = ByteBuffer.allocate(BUFFER_SIZE); buffers[1] = ByteBuffer.allocate(BUFFER_SIZE); readBatch(); readBatch(); } catch (IOException e) { throw new RuntimeException(e); } } void readBatch() { if (future != null) { try { while (!future.isDone()) { Thread.yield(); } Integer x = future.get(); pos += x; int readBuff = 1 ^ currentBuff; ByteBuffer buff = buffers[readBuff]; buff.flip(); currentBuff ^= 1; } catch (Exception e) { throw new RuntimeException(e); } } int readBuff = 1 ^ currentBuff; ByteBuffer buff = buffers[readBuff]; buff.clear(); // TODO may not read all bytes (and possibly not a multiple of 64) future = fc.read(buff, pos); } @Override public boolean hasNext() { return pos < size ; } @Override public Long next() { ByteBuffer buff = buffers[currentBuff]; if (!buff.hasRemaining()) { readBatch(); buff = buffers[currentBuff]; } return buff.getLong(); // return mix64(buff.getLong()); // return universalHash(buff.getLong(), 0); } }; } } ================================================ FILE: src/main/java/org/minperf/hem/Sort.java ================================================ package org.minperf.hem; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; public class Sort extends RecursiveAction { private static final long serialVersionUID = 1L; public static void main(String... args) { testSortUnsigned(); int count = 128 * 1024 * 1024; // * 1024 * 1024; for(int test = 0; test < 4; test++) { System.out.println("test " + test); Random r = new Random(test); long[] data = new long[count]; long sum = 0; for(int i = 0; i < data.length; i++) { long x = r.nextLong(); sum += x; data[i] = x; } long time = System.nanoTime(); parallelSortUnsigned(data); // 16M: sorted in 0.497069951 secs // 128M: sorted in 2.943904999 secs // 256M: sorted in 5.710293362 secs // sortUnsignedSimple(data); // 16M: sorted in 0.46763204 secs // 128M: sorted in 4.179389644 secs // 256M: sorted in 8.616092879 secs // Arrays.parallelSort(data); // 16M: sorted in 0.399732495 secs // 128M: sorted in 3.498315688 secs // 256M: sorted in 7.834619304 secs // Arrays.sort(data); // 16M: sorted in 1.50651212 secs // 128M: sorted in 14.864402323 secs time = System.nanoTime() - time; System.out.println("sorted in " + (time / 1_000_000_000.) + " secs"); long sum2 = 0; for(long x : data) { sum2 += x; } if (sum != sum2) { throw new AssertionError("sum changed"); } for (int i = 1; i < data.length; i++) { if (Long.compareUnsigned(data[i - 1], data[i]) > 0) { throw new AssertionError("index " + i); } } System.out.println("compared"); } } private static void testSortUnsigned() { Random r = new Random(1); for (int test = 0; test < 1000; test++) { int len = r.nextInt(10); long[] data = new long[len]; for (int i = 0; i < len; i++) { data[i] = r.nextInt(5) - 2; } parallelSortUnsigned(data); // sortUnsignedSimple(data); for (int i = 1; i < data.length; i++) { if (Long.compareUnsigned(data[i - 1], data[i]) > 0) { throw new AssertionError("index " + i); } } } } private static int S = 8; private static final long MASK = (1 << S) - 1; private static final int BUCKETS = 1 << S; private final long[] data; private final int start; private final int end; private final int shift; private final int level; Sort(long[] data, int start, int end, int shift, int level) { this.data = data; this.start = start; this.end = end; this.shift = shift; this.level = level; } public static void parallelSortUnsigned(long[] data, int offset, int len) { if (len < BUCKETS) { sortUnsignedSimple(data, offset, len); return; } ForkJoinPool.commonPool().invoke(new Sort(data, offset, offset + len, 64 - S, 0)); } public static void parallelSortUnsigned(long[] data) { parallelSortUnsigned(data, 0, data.length); } @Override protected void compute() { if (level > 0 && end - start < BUCKETS) { Arrays.sort(data, start, end); return; } int[] pos = new int[BUCKETS]; for (int i = start; i < end; i++) { long x = data[i]; int b = (int) ((x >>> shift) & MASK); pos[b]++; } int[] stop = new int[BUCKETS]; int sum = start; for (int i = 0; i < pos.length; i++) { int count = pos[i]; pos[i] = sum; sum += count; stop[i] = sum; } int i = start; long x = data[i]; outer: for(int bucket = 0;;) { int targetBucket = (int) ((x >>> shift) & MASK); int index = pos[targetBucket]++; long next = data[index]; data[index] = x; x = next; if (index == i) { while (true) { index = pos[bucket]; if (index < stop[bucket]) { break; } bucket++; if (bucket >= BUCKETS) { break outer; } } i = index; x = data[index]; } } ArrayList tasks = new ArrayList<>(); int startBucket = start; for (int bucket = 0; bucket < BUCKETS; bucket++) { int stopBucket = pos[bucket]; if (stopBucket - startBucket < BUCKETS) { Arrays.sort(data, startBucket, stopBucket); } else { tasks.add(new Sort(data, startBucket, stopBucket, shift - S, level + 1)); } startBucket = stopBucket; } if (!tasks.isEmpty()) { invokeAll(tasks); } } static void sortUnsignedSimple(long[] data, int offset, int len) { int left = offset, right = offset + len - 1; while(true) { while (left < data.length && data[left] >= 0) { left++; } while (right > 0 && data[right] < 0) { right--; } if (left >= right) { break; } long temp = data[left]; data[left++] = data[right]; data[right--] = temp; } Arrays.parallelSort(data, offset, left); Arrays.parallelSort(data, left, len); } static void sort(long[] data) { sort(data, 0, data.length, 64 - S); } static void sort(long[] data, int start, int end, int shift) { if (end - start < BUCKETS) { Arrays.sort(data, start, end); return; } int[] pos = new int[BUCKETS]; for(int i=start; i>> shift) & MASK); pos[b]++; } int[] stop = new int[BUCKETS]; int sum = start; for (int i = 0; i < pos.length; i++) { int count = pos[i]; pos[i] = sum; sum += count; stop[i] = sum; } int i = start; long x = data[i]; outer: for(int bucket = 0;;) { int targetBucket = (int) ((x >>> shift) & MASK); int index = pos[targetBucket]++; long next = data[index]; data[index] = x; x = next; if (index == i) { while (true) { index = pos[bucket]; if (index < stop[bucket]) { break; } bucket++; if (bucket >= BUCKETS) { break outer; } } i = index; x = data[index]; } } int startBucket = start; for (int bucket = 0; bucket < BUCKETS; bucket++) { int stopBucket = pos[bucket]; sort(data, startBucket, stopBucket, shift - S); startBucket = stopBucket; } } } ================================================ FILE: src/main/java/org/minperf/hem/SortedSignatures.java ================================================ package org.minperf.hem; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.PrimitiveIterator; import org.minperf.BitBuffer; public class SortedSignatures { private static final int MAP_SIZE = 512 * 1024 * 1024; private static final int BUFFER_SIZE = 4 * 1024 * 1024; private static final int OVERLAP = 1024; static class FileIterator { private final RandomAccessFile f; private final FileChannel fc; private MappedByteBuffer buff; private long offset; private final long size; FileIterator(String fileName) { try { f = new RandomAccessFile(fileName, "r"); fc = f.getChannel(); size = fc.size(); remap(); } catch (IOException e) { throw new RuntimeException(e); } } private long position() { return offset + buff.position(); } void remap() { try { long pos = buff == null ? 0 : position(); offset = pos / (MAP_SIZE / 2) * (MAP_SIZE / 2); long s = Math.min(size - offset, MAP_SIZE); buff = fc.map(MapMode.READ_ONLY, offset, s); buff.position((int) (pos - offset)); } catch (IOException e) { throw new RuntimeException(e); } } void remapIfNeeded() { if (buff.position() > MAP_SIZE / 2) { remap(); } } void close() { try { fc.close(); } catch (IOException e) { throw new RuntimeException(e); } } PrimitiveIterator.OfLong iteratorVarLong(final int len) { return new PrimitiveIterator.OfLong() { private int i; private long x; @Override public boolean hasNext() { return i < len; } @Override public long nextLong() { remapIfNeeded(); x += readVarLong(buff); return x; } }; } PrimitiveIterator.OfLong iteratorGolombRice(final int len, final int shift) { return new PrimitiveIterator.OfLong() { private int i; private long last; private byte[] bytes = new byte[(BUFFER_SIZE + OVERLAP) / 8]; private BitBuffer buffer; { int readBytes = (int) Math.min(BUFFER_SIZE / 8, size); remapIfNeeded(); buff.get(bytes, OVERLAP / 8, readBytes); buffer = new BitBuffer(bytes); buffer.seek(OVERLAP); } @Override public boolean hasNext() { return i < len; } @Override public long nextLong() { long x = buffer.readGolombRice(shift); last += x; int pos = buffer.position(); if (pos > BUFFER_SIZE) { System.arraycopy(bytes, BUFFER_SIZE / 8, bytes, 0, OVERLAP / 8); int readBytes = (int) Math.min(BUFFER_SIZE / 8, size - position()); remapIfNeeded(); buff.get(bytes, OVERLAP / 8, readBytes); buffer = new BitBuffer(bytes); buffer.seek(pos - BUFFER_SIZE); } return last; } }; } } static class FileWriter { private final FileChannel fc; FileWriter(String fileName) { try { @SuppressWarnings("resource") RandomAccessFile f = new RandomAccessFile(fileName, "rw"); fc = f.getChannel(); } catch (IOException e) { throw new RuntimeException(e); } } void close() { try { fc.truncate(fc.position()); fc.close(); } catch (IOException e) { throw new RuntimeException(e); } } void writeDiffsGolombRice(long[] data, int shift) { // Sort.parallelSort(data); BitBuffer buff = new BitBuffer(BUFFER_SIZE + OVERLAP); long last = 0; for(long x : data) { long diff = x - last; last = x; buff.writeGolombRice(shift, diff); if (buff.position() > BUFFER_SIZE) { writeBlock(buff); } } write(ByteBuffer.wrap(buff.toByteArray())); } void writeBlock(BitBuffer buff) { int pos = buff.position(); write(ByteBuffer.wrap(buff.toByteArray(), 0, BUFFER_SIZE / 8)); BitBuffer buff2 = new BitBuffer(OVERLAP); buff.seek(BUFFER_SIZE); while (buff.position() < pos) { buff2.writeBit(buff.readBit()); } buff.clear(); buff.seek(0); buff.write(buff2); } void writeDiffsVarLong(long[] data) { // Sort.parallelSort(data); ByteBuffer buff = ByteBuffer.allocate(BUFFER_SIZE); long last = 0; for(long x : data) { long diff = x - last; last = x; writeVarLong(buff, diff); if (buff.remaining() < 16) { buff.flip(); write(buff); buff.clear(); } } buff.flip(); write(buff); } void write(ByteBuffer data) { try { // TODO return value is currently not checked fc.write(data); } catch (IOException e) { throw new RuntimeException(e); } } } static class FileWriterChannel { private final FileChannel fc; FileWriterChannel(String fileName) { try { new File(fileName).delete(); @SuppressWarnings("resource") RandomAccessFile f = new RandomAccessFile(fileName, "rw"); fc = f.getChannel(); } catch (IOException e) { throw new RuntimeException(e); } } void close() { try { fc.close(); } catch (IOException e) { throw new RuntimeException(e); } } void writeDiffsEliasDelta(long[] data) { // Sort.parallelSort(data); ByteBuffer buffer = ByteBuffer.allocate(4); buffer.putInt(data.length); buffer.flip(); write(buffer); BitBuffer buff = new BitBuffer(BUFFER_SIZE); long last = 0; for(long x : data) { long diff = x - last; last = x; buff.writeEliasDelta(diff); writeBitBuffer(buff, false); } writeBitBuffer(buff, true); } void writeBitBuffer(BitBuffer buff, boolean always) { int remaining = 8 * BUFFER_SIZE - buff.position(); if (always) { remaining = 0; } if (remaining < 256) { byte[] b = buff.toByteArray(); ByteBuffer buffer = ByteBuffer.allocate(b.length + 1); buffer.put((byte) remaining); write(buffer); buff.seek(0); } } void writeDiffsGolombRice(long[] data, int shift) { // Sort.parallelSort(data); BitBuffer buff = new BitBuffer(BUFFER_SIZE); long last = 0; for(long x : data) { long diff = x - last; last = x; buff.writeGolombRice(shift, diff); writeBitBuffer(buff, false); } writeBitBuffer(buff, true); } void writeDiffs(long[] data) { // Sort.parallelSort(data); ByteBuffer buff = ByteBuffer.allocate(BUFFER_SIZE); long last = 0; for(long x : data) { long diff = x - last; last = x; writeVarLong(buff, diff); if (buff.remaining() < 16) { buff.flip(); write(buff); buff.clear(); } } buff.flip(); write(buff); } void write(ByteBuffer data) { try { fc.write(data); } catch (IOException e) { throw new RuntimeException(e); } } } public static void writeVarLong(ByteBuffer buff, long x) { while ((x & ~0x7f) != 0) { buff.put((byte) (0x80 | (x & 0x7f))); x >>>= 7; } buff.put((byte) x); } public static long readVarLong(ByteBuffer buff) { long x = buff.get(); if (x >= 0) { return x; } x &= 0x7f; for (int s = 7; s < 64; s += 7) { long b = buff.get(); x |= (b & 0x7f) << s; if (b >= 0) { break; } } return x; } } ================================================ FILE: src/main/java/org/minperf/hem/recsplit/Builder.java ================================================ package org.minperf.hem.recsplit; import org.minperf.BitBuffer; import org.minperf.hash.Mix; public class Builder { private static final int[] SHIFT = { 0, 0, 0, 1, 3, 4, 5, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; private int averageBucketSize = 16; private int leafSize = 5; public Builder leafSize(int leafSize) { this.leafSize = leafSize; return this; } public Builder averageBucketSize(int averageBucketSize) { this.averageBucketSize = averageBucketSize; return this; } public BitBuffer generate(long[] keys) { return new FastGenerator(leafSize, averageBucketSize).generate(keys); } public BitBuffer generate(long[] keys, int len) { return new FastGenerator(leafSize, averageBucketSize).generate(keys, len); } public FastEvaluator evaluator(BitBuffer buff) { return new FastEvaluator(buff, averageBucketSize, leafSize, 0); } public static int supplementalHash(long x, int index) { // TODO can save one multiplication for generation //System.out.println(" " + x + " [" + index + "]"); return Mix.supplementalHashWeyl(x, index); } public static int reduce(int hash, int n) { // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return (int) (((hash & 0xffffffffL) * n) >>> 32); } public static int getBucketCount(long size, int averageBucketSize) { int bucketCount = (int) ((size + averageBucketSize - 1) / averageBucketSize); return nextPowerOf2(bucketCount); } static int nextPowerOf2(int x) { if (Integer.bitCount(x) == 1) { return x; } return Integer.highestOneBit(x) * 2; } public static int getGolombRiceShift(int size) { return SHIFT[size]; } } ================================================ FILE: src/main/java/org/minperf/hem/recsplit/FastEvaluator.java ================================================ package org.minperf.hem.recsplit; import org.minperf.BitBuffer; import org.minperf.monotoneList.MultiStageMonotoneList; public class FastEvaluator { private final BitBuffer buffer; private final long size; private final int leafSize; private final int bucketCount; private final int bucketShift; private final MultiStageMonotoneList startList; private final MultiStageMonotoneList offsetList; private final int startBuckets; FastEvaluator(BitBuffer buffer, int averageBucketSize, int leafSize, int shift) { this.buffer = buffer; this.size = (int) (buffer.readEliasDelta() - 1); this.bucketCount = Builder.getBucketCount(size, averageBucketSize); this.leafSize = leafSize; int bucketBitCount = 31 - Integer.numberOfLeadingZeros(bucketCount); this.bucketShift = 64 - bucketBitCount - shift; this.startList = MultiStageMonotoneList.load(buffer); this.offsetList = MultiStageMonotoneList.load(buffer); this.startBuckets = buffer.position(); } public int evaluate(long hashCode) { int b; if (bucketCount == 1) { b = 0; } else { b = (int) (hashCode >>> bucketShift); } int startPos; long offsetPair = offsetList.getPair(b); int offset = (int) (offsetPair >>> 32); int offsetNext = (int) offsetPair; if (offsetNext == offset) { // entry not found return -1; } int bucketSize = offsetNext - offset; startPos = startBuckets + startList.get(b); return evaluate(startPos, hashCode, 0, offset, bucketSize); } private int skip(int pos, int size) { if (size < 2) { return pos; } pos = buffer.skipGolombRice(pos, Builder.getGolombRiceShift(size)); if (size <= leafSize) { return pos; } int firstPart = size / 2; pos = skip(pos, firstPart); pos = skip(pos, size - firstPart); return pos; } private int evaluate(int pos, long hashCode, int index, int add, int size) { while (true) { if (size < 2) { return add; } int shift = Builder.getGolombRiceShift(size); long q = buffer.readUntilZero(pos); pos += q + 1; long value = (q << shift) | buffer.readNumber(pos, shift); pos += shift; index += value; if (size <= leafSize) { int h = Builder.supplementalHash(hashCode, index); switch(size) { case 2: h = h & 1; break; case 4: h = h & 3; break; default: h = Builder.reduce(h, size); } return add + h; } int firstPart = size / 2; int h = Builder.supplementalHash(hashCode, index++); h = h & 1; if (h == 1) { pos = skip(pos, firstPart); add += firstPart; size = size - firstPart; } else { size = firstPart; } } } } ================================================ FILE: src/main/java/org/minperf/hem/recsplit/FastGenerator.java ================================================ package org.minperf.hem.recsplit; import java.util.Arrays; import org.minperf.BitBuffer; import org.minperf.monotoneList.MultiStageMonotoneList; public class FastGenerator { private static final int MAX_BUCKET_SIZE = 64; private final int leafSize; private final int averageBucketSize; private BitBuffer buff; private BitBuffer bucketBuff; FastGenerator(int leafSize, int averageBucketSize) { this.leafSize = leafSize; this.averageBucketSize = averageBucketSize; } BitBuffer generate(long[] keys) { return generate(keys, keys.length); } BitBuffer generate(long[] keys, int len) { buff = new BitBuffer(len * 10); if (len <= 1) { return buff; } int bucketCount = Builder.getBucketCount(len, averageBucketSize); bucketBuff = new BitBuffer(len * 10 / bucketCount); int bucketBitCount = 31 - Integer.numberOfLeadingZeros(bucketCount); int bucketShift = 64 - bucketBitCount; int[] startList = new int[bucketCount]; int[] offsetList = new int[bucketCount + 1]; sortIntoBuckets(keys, len, bucketShift, bucketCount, offsetList, startList); Arrays.fill(startList, 0); // move to the right (this could be avoided) System.arraycopy(offsetList, 0, offsetList, 1, bucketCount); offsetList[0] = 0; int startBucket = 0; for (int i = 0; i < bucketCount; i++) { int endBucket = offsetList[i + 1]; generateBucket(keys, startBucket, endBucket, startList, i); startBucket = endBucket; } BitBuffer buff2 = new BitBuffer(len * 10); buff2.writeEliasDelta(len + 1); MultiStageMonotoneList.generate(startList, buff2); MultiStageMonotoneList.generate(offsetList, buff2); buff2.write(buff); buff = buff2; return buff; } private static void sortIntoBuckets(long[] keys, int len, int shift, int bucketCount, int[] offsetList, int[] array2) { if (bucketCount == 1) { offsetList[0] = keys.length; // in this case, shift is 64, which is problematic return; } boolean monotone = true; int last = 0; int[] pos = offsetList; for (int i = 0; i < len; i++) { long x = keys[i]; int b = (int) (x >>> shift); pos[b]++; if (b < last) { monotone = false; } last = b; } int[] stop = array2; int sum = 0; int maxCount = 0; for (int i = 0; i < bucketCount; i++) { int count = pos[i]; maxCount = Math.max(maxCount, count); pos[i] = sum; sum += count; stop[i] = sum; } if (maxCount > MAX_BUCKET_SIZE) { throw new IllegalArgumentException("max=" + maxCount); } if (monotone) { // shortcut if the list is already sorted return; } int i = 0; long x = keys[i]; for(int bucket = 0;;) { int targetBucket = (int) (x >>> shift); int index = pos[targetBucket]++; long next = keys[index]; keys[index] = x; x = next; if (index == i) { while (true) { index = pos[bucket]; if (index < stop[bucket]) { break; } bucket++; if (bucket >= bucketCount) { return; } } i = index; x = keys[index]; } } } private void generateBucket(long[] keys, int start, int end, int[] startList, int bucketId) { if (end == start) { startList[bucketId] = buff.position(); return; } bucketBuff.clear(); bucketBuff.seek(0); generateSet(keys, start, end, 0); if (buff.position() > 0) { int lastStart = startList[bucketId - 1]; for(int overlap = 8; overlap > 0; overlap--) { if (buff.position() > overlap && bucketBuff.position() > overlap && buff.position() - lastStart >= overlap) { long a = buff.readNumber(buff.position() - overlap, overlap); long b = bucketBuff.readNumber(0, overlap); if (a == b) { buff.seek(buff.position() - overlap); break; } } } } startList[bucketId] = buff.position(); while (bucketId > 0) { bucketId--; if (startList[bucketId] > buff.position()) { startList[bucketId] = buff.position(); } else { break; } } buff.write(bucketBuff); } private void generateSet(long[] keys, int start, int end, int index) { int len = end - start; if (len <= leafSize) { switch (len) { case 0: case 1: return; case 2: generateBucket2(keys, start, index); return; case 3: generateBucket3(keys, start, index); return; case 4: generateBucket4(keys, start, index); return; case 5: generateBucket5(keys, start, index); return; case 6: generateBucket6(keys, start, index); return; } } generateSetHalf(keys, start, end, index); } private void generateSetHalf(long[] keys, int start, int end, int index) { int size = end - start; int first = size / 2; long bits; int count; int oldIndex = index; for (;; index++) { bits = 0; count = 0; for (int i = start; i < end; i++) { long x = Builder.supplementalHash(keys[i], index) & 1; bits |= x << (i - start); count += x; } if (count == size - first) { break; } if ((index & 0xffff) == 0xffff) { checkDuplicateKey(keys, start, end); } } sort(keys, start, end, bits); emit(size, index - oldIndex); generateSet(keys, start, start + size - count, index + 1); generateSet(keys, start + size - count, start + size, index + 1); } private void checkDuplicateKey(long[] keys, int start, int end) { Arrays.sort(keys, start, end); for (int i = start + 1; i < end; i++) { if (keys[i - 1] == keys[i]) { throw new IllegalArgumentException("Duplicate key"); } } } private static void sort(long[] keys, int start, int end, long bits) { int len = end - start; for (int l = 0, r = len - 1; l < r;) { while (((bits >>> l) & 1) == 0) { l++; if (l >= r) { return; } } while (((bits >>> r) & 1) == 1) { r--; if (l >= r) { return; } } long temp = keys[start + l]; keys[start + l] = keys[start + r]; keys[start + r] = temp; l++; r--; } } private void emit(int size, int indexDiff) { int shift = Builder.getGolombRiceShift(size); bucketBuff.writeGolombRice(shift, indexDiff); } private void generateBucket2(long[] keys, int start, int index) { int oldIndex = index; for (;; index++) { int a = Builder.supplementalHash(keys[start], index) & 1; int b = Builder.supplementalHash(keys[start + 1], index) & 1; if (a != b) { break; } } emit(2, index - oldIndex); } private void generateBucket3(long[] keys, int start, int index) { int oldIndex = index; for (;; index++) { int a = Builder.reduce(Builder.supplementalHash(keys[start], index), 3); int b = Builder.reduce(Builder.supplementalHash(keys[start + 1], index), 3); int c = Builder.reduce(Builder.supplementalHash(keys[start + 2], index), 3); if (a != b && a != c && b != c) { break; } } emit(3, index - oldIndex); } private void generateBucket4(long[] keys, int start, int index) { int oldIndex = index; for (;; index++) { int a = Builder.supplementalHash(keys[start], index) & 3; int b = Builder.supplementalHash(keys[start + 1], index) & 3; int c = Builder.supplementalHash(keys[start + 2], index) & 3; int d = Builder.supplementalHash(keys[start + 3], index) & 3; if (((1 << a) | (1 << b) | (1 << c) | (1 << d)) == 0xf) { break; } } emit(4, index - oldIndex); } private void generateBucket5(long[] keys, int start, int index) { int oldIndex = index; for (;; index++) { int a = Builder.reduce(Builder.supplementalHash(keys[start], index), 5); int b = Builder.reduce(Builder.supplementalHash(keys[start + 1], index), 5); int c = Builder.reduce(Builder.supplementalHash(keys[start + 2], index), 5); int d = Builder.reduce(Builder.supplementalHash(keys[start + 3], index), 5); int e = Builder.reduce(Builder.supplementalHash(keys[start + 4], index), 5); if (((1 << a) | (1 << b) | (1 << c) | (1 << d) | (1 << e)) == 0x1f) { break; } } emit(5, index - oldIndex); } private void generateBucket6(long[] keys, int start, int index) { int oldIndex = index; for (;; index++) { int a = Builder.reduce(Builder.supplementalHash(keys[start], index), 6); int b = Builder.reduce(Builder.supplementalHash(keys[start + 1], index), 6); int c = Builder.reduce(Builder.supplementalHash(keys[start + 2], index), 6); int d = Builder.reduce(Builder.supplementalHash(keys[start + 3], index), 6); int e = Builder.reduce(Builder.supplementalHash(keys[start + 4], index), 6); int f = Builder.reduce(Builder.supplementalHash(keys[start + 5], index), 6); if (((1 << a) | (1 << b) | (1 << c) | (1 << d) | (1 << e) | (1 << f)) == 0x3f) { break; } } emit(6, index - oldIndex); } } ================================================ FILE: src/main/java/org/minperf/monotoneList/EliasFanoMonotoneList.java ================================================ package org.minperf.monotoneList; import java.util.BitSet; import org.minperf.BitBuffer; import org.minperf.select.Select; /** * Each entry needs 2 + log(gap) bits, where gap is the average gap between * entries, plus the overhead for the "select" structure. A lookup needs one * "select" call, and is otherwise constant. */ public class EliasFanoMonotoneList extends MonotoneList { private final BitBuffer buffer; private final int start; private final int lowBitCount; private final Select select; private EliasFanoMonotoneList(BitBuffer buffer, int start, int lowBitCount, Select select) { this.buffer = buffer; this.start = start; this.lowBitCount = lowBitCount; this.select = select; } public static EliasFanoMonotoneList generate(int[] data, BitBuffer buffer) { int len = data.length; // verify it is monotone for (int i = 1; i < len; i++) { if (data[i - 1] > data[i]) { throw new IllegalArgumentException(); } } buffer.writeEliasDelta(len + 1); int max = data[len - 1]; int lowBitCount = 32 - Integer.numberOfLeadingZeros(Integer.highestOneBit(max / len)); buffer.writeEliasDelta(lowBitCount + 1); int start = buffer.position(); BitSet set = new BitSet(); for (int i = 0; i < len; i++) { int x = i + (data[i] >>> lowBitCount); set.set(x); } int mask = (1 << lowBitCount) - 1; for (int i = 0; i < len; i++) { buffer.writeNumber(data[i] & mask, lowBitCount); } Select select = Select.generate(set, buffer); return new EliasFanoMonotoneList(buffer, start, lowBitCount, select); } public static int getSize(int[] data) { int len = data.length; int result = BitBuffer.getEliasDeltaSize(len + 1); int max = data[len - 1]; int lowBitCount = 32 - Integer.numberOfLeadingZeros(Integer.highestOneBit(max / len)); result += BitBuffer.getEliasDeltaSize(lowBitCount + 1); BitSet set = new BitSet(); for (int i = 0; i < len; i++) { int x = i + (data[i] >>> lowBitCount); set.set(x); } result += lowBitCount * len; result += Select.getSize(set); return result; } public static EliasFanoMonotoneList load(BitBuffer buffer) { int len = (int) (buffer.readEliasDelta() - 1); int lowBitCount = (int) (buffer.readEliasDelta() - 1); int start = buffer.position(); buffer.seek(start + len * lowBitCount); Select select = Select.load(buffer); return new EliasFanoMonotoneList(buffer, start, lowBitCount, select); } @Override public int get(int i) { int low = (int) buffer.readNumber(start + i * lowBitCount, lowBitCount); int high = (int) select.select(i) - i; return (high << lowBitCount) + low; } @Override public long getPair(int i) { long lowPair = buffer.readNumber(start + i * lowBitCount, lowBitCount + lowBitCount); int low1 = (int) (lowPair >>> lowBitCount); int low2 = (int) (lowPair - ((long) low1 << lowBitCount)); long highPair = select.selectPair(i); int high1 = (int) (highPair >>> 32) - i; int high2 = (int) highPair - i - 1; int result1 = (high1 << lowBitCount) + low1; int result2 = (high2 << lowBitCount) + low2; return ((long) result1 << 32) | result2; } } ================================================ FILE: src/main/java/org/minperf/monotoneList/MonotoneList.java ================================================ package org.minperf.monotoneList; import org.minperf.BitBuffer; /** * A list of monotone increasing values. */ public abstract class MonotoneList { public abstract int get(int i); public abstract long getPair(int i); public static MonotoneList generate(int[] data, BitBuffer buffer, boolean eliasFano) { return eliasFano ? EliasFanoMonotoneList.generate(data, buffer) : MultiStageMonotoneList.generate(data, buffer); } /** * Get the number of bits needed. * * @param data the data * @param eliasFano whether the Elias-Fano datastructure should be used * @return the number of bits */ public static int getSize(int[] data, boolean eliasFano) { return eliasFano ? EliasFanoMonotoneList.getSize(data) : MultiStageMonotoneList.getSize(data); } public static MonotoneList load(BitBuffer buffer, boolean eliasFano) { return eliasFano ? EliasFanoMonotoneList.load(buffer) : MultiStageMonotoneList.load(buffer); } } ================================================ FILE: src/main/java/org/minperf/monotoneList/MultiStageMonotoneList.java ================================================ package org.minperf.monotoneList; import org.minperf.BitBuffer; /** * This implementation uses a linear regression, and 3 levels of offsets. It is * much simpler and typically faster than an EliasFanoMonotoneList, but space * usage is not linear. */ public class MultiStageMonotoneList extends MonotoneList { public static final int SHIFT1 = 6; public static final int SHIFT2 = 3; public static final int FACTOR1 = 32; public static final int FACTOR2 = 16; private final BitBuffer buffer; private final int startLevel1, startLevel2, startLevel3; private final int bitCount1, bitCount2, bitCount3; private final int count1, count2, count3; private final long factor; private final int add; private MultiStageMonotoneList(BitBuffer buffer) { this.buffer = buffer; this.count3 = (int) buffer.readEliasDelta() - 1; int diff = (int) buffer.readEliasDelta() - 1; this.factor = getScaleFactor(diff, count3); this.add = (int) BitBuffer.unfoldSigned(buffer.readEliasDelta() - 1); this.bitCount1 = (int) buffer.readEliasDelta() - 1; this.bitCount2 = (int) buffer.readEliasDelta() - 1; this.bitCount3 = (int) buffer.readEliasDelta() - 1; startLevel1 = buffer.position(); count2 = (count3 + (1 << SHIFT2) - 1) >> SHIFT2; count1 = (count3 + (1 << SHIFT1) - 1) >> SHIFT1; startLevel2 = startLevel1 + count1 * bitCount1; startLevel3 = startLevel2 + count2 * bitCount2; buffer.seek(startLevel3 + bitCount3 * count3); } private static long getScaleFactor(int multiply, int divide) { return divide == 0 ? 0 : ((long) multiply << 32) / divide + 1; } public static MultiStageMonotoneList generate(int[] data, BitBuffer buffer) { int start = buffer.position(); int count3 = data.length; // verify it is monotone for (int i = 1; i < count3; i++) { if (data[i - 1] > data[i]) { throw new IllegalArgumentException(); } } int diff = data[count3 - 1] - data[0]; long factor = getScaleFactor(diff, count3); int add = data[0]; for (int i = 1; i < count3; i++) { int expected = (int) ((i * factor) >>> 32); int x = data[i]; add = Math.min(add, x - expected); } buffer.writeEliasDelta(count3 + 1); buffer.writeEliasDelta(diff + 1); buffer.writeEliasDelta(BitBuffer.foldSigned(add) + 1); int count2 = (count3 + (1 << SHIFT2) - 1) >> SHIFT2; int count1 = (count3 + (1 << SHIFT1) - 1) >> SHIFT1; int[] group1 = new int[count1]; int[] group2 = new int[count2]; int[] group3 = new int[count3]; for (int i = 0; i < count3; i++) { // int expected = (int) (i * max / count3); int expected = (int) ((i * factor) >>> 32) + add; int got = data[i]; int x = got - expected; if (x < 0) { throw new AssertionError(); } group3[i] = x; } int a = Integer.MAX_VALUE; for (int i = 0; i < count3; i++) { int x = group3[i]; a = Math.min(a, x); if ((i +1) >> SHIFT2 != i >> SHIFT2 || i == count3 - 1) { group2[i >> SHIFT2] = a / FACTOR2; a = Integer.MAX_VALUE; } } a = Integer.MAX_VALUE; for (int i = 0; i < count3; i++) { int d = group2[i >> SHIFT2] * FACTOR2; int x = group3[i]; group3[i] -= d; if (group3[i] < 0) { throw new AssertionError(); } a = Math.min(a, x); if ((i + 1) >> SHIFT1 != i >> SHIFT1 || i == count3 - 1) { group1[i >> SHIFT1] = a / FACTOR1; a = Integer.MAX_VALUE; } } int last = -1; for (int i = 0; i < count3; i++) { int i2 = i >> SHIFT2; if (i2 == last) { continue; } int d = group1[i >> SHIFT1] * FACTOR1; group2[i2] -= d / FACTOR2; last = i2; } int max1 = 0, max2 = 0, max3 = 0; for (int i = 0; i < group3.length; i++) { max3 = Math.max(max3, group3[i]); } for (int i = 0; i < group2.length; i++) { max2 = Math.max(max2, group2[i]); } for (int i = 0; i < group1.length; i++) { max1 = Math.max(max1, group1[i]); } int bitCount1 = 32 - Integer.numberOfLeadingZeros(max1); int bitCount2 = 32 - Integer.numberOfLeadingZeros(max2); int bitCount3 = 32 - Integer.numberOfLeadingZeros(max3); buffer.writeEliasDelta(bitCount1 + 1); buffer.writeEliasDelta(bitCount2 + 1); buffer.writeEliasDelta(bitCount3 + 1); for (int x : group1) { buffer.writeNumber(x, bitCount1); } for (int x : group2) { buffer.writeNumber(x, bitCount2); } for (int x : group3) { buffer.writeNumber(x, bitCount3); } buffer.seek(start); return new MultiStageMonotoneList(buffer); } public static int getSize(int[] data) { int result = 0; int count3 = data.length; // verify it is monotone for (int i = 1; i < count3; i++) { if (data[i - 1] > data[i]) { throw new IllegalArgumentException(); } } int diff = data[count3 - 1] - data[0]; long factor = getScaleFactor(diff, count3); int add = data[0]; for (int i = 1; i < count3; i++) { int expected = (int) ((i * factor) >>> 32); int x = data[i]; add = Math.min(add, x - expected); } result += BitBuffer.getEliasDeltaSize(count3 + 1); result += BitBuffer.getEliasDeltaSize(diff + 1); result += BitBuffer.getEliasDeltaSize(BitBuffer.foldSigned(add) + 1); int count2 = (count3 + (1 << SHIFT2) - 1) >> SHIFT2; int count1 = (count3 + (1 << SHIFT1) - 1) >> SHIFT1; int[] group1 = new int[count1]; int[] group2 = new int[count2]; int[] group3 = new int[count3]; for (int i = 0; i < count3; i++) { // int expected = (int) (i * max / count3); int expected = (int) ((i * factor) >>> 32) + add; int got = data[i]; int x = got - expected; if (x < 0) { throw new AssertionError(); } group3[i] = x; } int a = Integer.MAX_VALUE; for (int i = 0; i < count3; i++) { int x = group3[i]; a = Math.min(a, x); if ((i +1) >> SHIFT2 != i >> SHIFT2 || i == count3 - 1) { group2[i >> SHIFT2] = a / FACTOR2; a = Integer.MAX_VALUE; } } a = Integer.MAX_VALUE; for (int i = 0; i < count3; i++) { int d = group2[i >> SHIFT2] * FACTOR2; int x = group3[i]; group3[i] -= d; if (group3[i] < 0) { throw new AssertionError(); } a = Math.min(a, x); if ((i + 1) >> SHIFT1 != i >> SHIFT1 || i == count3 - 1) { group1[i >> SHIFT1] = a / FACTOR1; a = Integer.MAX_VALUE; } } int last = -1; for (int i = 0; i < count3; i++) { int i2 = i >> SHIFT2; if (i2 == last) { continue; } int d = group1[i >> SHIFT1] * FACTOR1; group2[i2] -= d / FACTOR2; last = i2; } int max1 = 0, max2 = 0, max3 = 0; for (int i = 0; i < group3.length; i++) { max3 = Math.max(max3, group3[i]); } for (int i = 0; i < group2.length; i++) { max2 = Math.max(max2, group2[i]); } for (int i = 0; i < group1.length; i++) { max1 = Math.max(max1, group1[i]); } int bitCount1 = 32 - Integer.numberOfLeadingZeros(max1); int bitCount2 = 32 - Integer.numberOfLeadingZeros(max2); int bitCount3 = 32 - Integer.numberOfLeadingZeros(max3); result += BitBuffer.getEliasDeltaSize(bitCount1 + 1); result += BitBuffer.getEliasDeltaSize(bitCount2 + 1); result += BitBuffer.getEliasDeltaSize(bitCount3 + 1); result += bitCount1 * group1.length; result += bitCount2 * group2.length; result += bitCount3 * group3.length; return result; } public static MultiStageMonotoneList load(BitBuffer buffer) { return new MultiStageMonotoneList(buffer); } @Override public int get(int i) { int expected = (int) ((i * factor) >>> 32) + add; long a = buffer.readNumber(startLevel1 + (i >>> SHIFT1) * bitCount1, bitCount1); long b = buffer.readNumber(startLevel2 + (i >>> SHIFT2) * bitCount2, bitCount2); long c = buffer.readNumber(startLevel3 + i * bitCount3, bitCount3); return (int) (expected + a * FACTOR1 + b * FACTOR2 + c); } @Override public long getPair(int i) { return ((long) get(i) << 32) | (get(i + 1)); } } ================================================ FILE: src/main/java/org/minperf/rank/Rank9.java ================================================ /* * Sux4J: Succinct data structures for Java * * Copyright (C) 2008-2016 Sebastiano Vigna * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ package org.minperf.rank; import java.util.Arrays; import java.util.BitSet; /** * A fast rank implementation that uses 25% additional space. This is a copy of * the (very good) implementation in Sux4J it.unimi.dsi.sux4j.bits.Rank9 by * Sebastiano Vigna (see copyright), with a custom serialization format. */ public class Rank9 { private final long[] bits; private final long[] counts; long mostSignificantBit(long x) { return 63 - Long.numberOfLeadingZeros(x); } public Rank9(BitSet set, long bitCount) { long[] bits = set.toLongArray(); // One zero entry is needed at the end bits = Arrays.copyOf(bits, 1 + (int) ((bitCount + 63) / 64)); this.bits = bits; long length = bits.length * 64; int numWords = (int) ((length + 63) / 64); final int numCounts = (int) ((length + 8 * 64 - 1) / (8 * 64)) * 2; counts = new long[numCounts + 1]; long c = 0; int pos = 0; for (int i = 0; i < numWords; i += 8, pos += 2) { counts[pos] = c; c += Long.bitCount(bits[i]); for (int j = 1; j < 8; j++) { counts[pos + 1] |= (i + j <= numWords ? c - counts[pos] : 0x1ffL) << 9 * (j - 1); if (i + j < numWords) { c += Long.bitCount(bits[i + j]); } } } counts[numCounts] = c; } /** * Get the number of bits set before this position. * * @param pos the position * @return the number of ones */ public long rank(long pos) { int word = (int) (pos >>> 6); int block = (word >> 2) & ~1; int offset = (word & 7) - 1; return counts[block] + (counts[block + 1] >>> (offset + (offset >>> 32 - 4 & 8)) * 9 & 0x1ff) + Long.bitCount(bits[word] & ((1L << pos) - 1)); } /** * Get the bit at this position * * @param pos the position * @return 0 or 1 */ public long get(long pos) { return (bits[(int) (pos >>> 6)] >>> pos) & 1; } /** * Get the number of bits set before this position, and the bit itself. * * @param pos the position * @return the number of ones multiplied by 2, plus the bit (0 or 1) */ public long rankAndGet(long pos) { int word = (int) (pos >>> 6); int block = (word >> 2) & ~1; int offset = (word & 7) - 1; long x = bits[word]; return ((counts[block] + (counts[block + 1] >>> (offset + (offset >>> 32 - 4 & 8)) * 9 & 0x1ff) + Long.bitCount(x & ((1L << pos) - 1))) << 1) + ((x >>> pos) & 1); } /** * Get the bit itself, and a part of the rank (use remainingRank to get the * rest). * * @param pos the position * @return the number of ones multiplied by 2, plus the bit (0 or 1) */ public long getAndPartialRank(long pos) { int word = (int) (pos >>> 6); long x = bits[word]; return ((Long.bitCount(x & ((1L << pos) - 1))) << 1) + ((x >>> pos) & 1); } /** * Get the second part of the rank (see getAndPartialRank). * * @param pos the position * @return the number of ones */ public long remainingRank(long pos) { int word = (int) (pos >>> 6); int block = (word >> 2) & ~1; int offset = (word & 7) - 1; return counts[block] + (counts[block + 1] >>> (offset + (offset >>> 32 - 4 & 8)) * 9 & 0x1ff); } public int getBitCount() { return bits.length * 64 + counts.length * 64; } } ================================================ FILE: src/main/java/org/minperf/rank/VerySimpleRank.java ================================================ package org.minperf.rank; import java.util.BitSet; import org.minperf.BitBuffer; /** * A simple rank+select data structure implementation for a list of bits. *

* Rank(x) gets the number of bits set to 1, before position x (positions * starting at 0). It takes constant time in the RAM model, that means, reads a * constant number of log(n) numbers. *

* Select(x) gets the position of the xth 1 bit (positions starting at 0). It * takes logarithmic time (using binary search on rank). */ public class VerySimpleRank { private final BitBuffer buffer; private final int size; private final int superBlockPos; private final int superBlockBits; private final int superBlockShift; private final int superBlockCount; private final int superBlockEntrySize; private final int blockPos; private final int blockBits; private final int blockShift; private final int blockCount; private final int blockEntrySize; private final int dataPos; private VerySimpleRank(BitBuffer buffer, int size) { this.buffer = buffer; this.size = size; superBlockPos = buffer.position(); int bb = Math.max(1, 31 - Integer.numberOfLeadingZeros(size)); int sbb = bb * bb; blockShift = 32 - Integer.numberOfLeadingZeros(bb - 1); blockBits = 1 << blockShift; superBlockShift = 32 - Integer.numberOfLeadingZeros(sbb - 1); superBlockBits = 1 << superBlockShift; blockCount = (size + blockBits - 1) / blockBits; superBlockCount = (size + superBlockBits - 1) / superBlockBits; superBlockEntrySize = 32 - Integer.numberOfLeadingZeros(size - 1); blockEntrySize = 32 - Integer.numberOfLeadingZeros(superBlockBits); blockPos = superBlockPos + superBlockEntrySize * superBlockCount; dataPos = blockPos + blockEntrySize * blockCount; } /** * Generate a rank/select object, and store it into the provided buffer. * * @param set the bit set * @param buffer the buffer * @return the generated object */ public static VerySimpleRank generate(BitSet set, BitBuffer buffer) { int size = set.length() + 1; buffer.writeEliasDelta(size + 1); VerySimpleRank rank = new VerySimpleRank(buffer, size); int[] superBlocks = new int[rank.superBlockCount]; int count = 0; long last = 0; int maxSuperBlock = (1 << rank.superBlockEntrySize) - 1; for (long i = 0; i < rank.superBlockCount; i++) { buffer.writeNumber(count, rank.superBlockEntrySize); superBlocks[(int) i] = count; if (count > maxSuperBlock) { throw new AssertionError(); } long next = last + rank.superBlockBits; count += countBits(set, (int) last, (int) next); last = next; } if (buffer.position() != rank.blockPos) { throw new AssertionError(); } count = 0; last = 0; int maxBlock = (1 << rank.blockEntrySize) - 1; for (long i = 0; i < rank.blockCount; i++) { int s = (int) (last / rank.superBlockBits); int countRelative = count - superBlocks[s]; if (countRelative > maxBlock) { throw new AssertionError(); } buffer.writeNumber(countRelative, rank.blockEntrySize); long next = last + rank.blockBits; count += countBits(set, (int) last, (int) next); last = next; } if (buffer.position() != rank.dataPos) { throw new AssertionError(); } for (int i = 0; i < size; i++) { buffer.writeBit(set.get(i) ? 1 : 0); } return rank; } /** * Generate a rank/select object from the provided buffer. * * @param buffer the buffer * @return the loaded object */ public static VerySimpleRank load(BitBuffer buffer) { int size = (int) (buffer.readEliasDelta() - 1); VerySimpleRank result = new VerySimpleRank(buffer, size); buffer.seek(result.dataPos + size); return result; } /** * Get the bit at position x. * * @param x the position * @return true if the bit is set */ public boolean get(long x) { if (x >= size) { // read past the end return false; } return buffer.readNumber(dataPos + (int) x, 1) == 1L; } /** * Get the number of 1 bits up to the given position. * * @param x the position * @return the number of 1 bits */ public long rank(long x) { x = Math.min(x, size - 1); int s = (int) (x >>> superBlockShift); int b = (int) (x >>> blockShift); return (int) buffer.readNumber(superBlockPos + s * superBlockEntrySize, superBlockEntrySize) + (int) buffer.readNumber(blockPos + b * blockEntrySize, blockEntrySize) + countBits(b << blockShift, (int) x); } private int countBits(int start, int end) { long x = buffer.readNumber(dataPos + start, end - start); return Long.bitCount(x); } /** * Get the position of the xth 1 bit. * * @param x the value (starting with 0) * @return the position, or -1 if x is too large */ public long select(long x) { int min = 0, max = size + 1; while (min < max) { int n = (min + max) >>> 1; long k = rank(n); if (k > x) { max = n; } else { min = n + 1; } } return min >= size ? -1 : min - 1; } private static int countBits(BitSet set, int start, int end) { int count = 0; for (int i = start; i < end; i++) { count += set.get(i) ? 1 : 0; } return count; } public int getReadBits() { return superBlockEntrySize + blockEntrySize + blockBits; } public int getOverhead() { return dataPos - superBlockPos - size; } public int getSize() { return size; } @Override public String toString() { return getClass().getSimpleName() + " size " + size; } } ================================================ FILE: src/main/java/org/minperf/select/Select.java ================================================ package org.minperf.select; import java.util.BitSet; import org.minperf.BitBuffer; /** * A select data structure for a list of bits. *

* Select(x) gets the position of the xth 1 bit (positions starting at 0). */ public abstract class Select { public static final boolean SIMPLE_SELECT = true; /** * Generate a select object, and store it into the provided buffer. * * @param set the bit set * @param buffer the buffer * @return the generated object */ public static Select generate(BitSet set, BitBuffer buffer) { if (SIMPLE_SELECT) { return SimpleSelect.generate(set, buffer); } return VerySimpleSelect.generate(set, buffer); } /** * Get the number of bits needed. * * @param set the bit set * @return the number of bits */ public static int getSize(BitSet set) { if (SIMPLE_SELECT) { return SimpleSelect.getSize(set); } return VerySimpleSelect.getSize(set); } /** * Generate a rank/select object from the provided buffer. * * @param buffer the buffer * @return the loaded object */ public static Select load(BitBuffer buffer) { if (SIMPLE_SELECT) { return SimpleSelect.load(buffer); } return VerySimpleSelect.load(buffer); } /** * Get the position of the xth 1 bit. * * @param x the value (starting with 0) * @return the position (0 is the first bit), or -1 if x is too large */ public abstract long select(long x); public abstract long selectPair(long x); } ================================================ FILE: src/main/java/org/minperf/select/SimpleSelect.java ================================================ /* * Sux4J: Succinct data structures for Java * * Copyright (C) 2008-2016 Sebastiano Vigna * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ package org.minperf.select; import java.util.BitSet; import org.minperf.BitBuffer; /** * A select implementation with guaranteed O(1) query time. This is a copy of * the (very good) implementation in Sux4J it.unimi.dsi.sux4j.bits.SimpleSelect * by Sebastiano Vigna (see copyright), with a custom serialization format. */ public class SimpleSelect extends Select { private static final long ONES_STEP_4 = 0x1111111111111111L; private static final long ONES_STEP_8 = 0x0101010101010101L; private static final long MSBS_STEP_8 = 0x80L * ONES_STEP_8; private static final byte[] SELECT_IN_BYTE = { -1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, -1, -1, -1, 1, -1, 2, 2, 1, -1, 3, 3, 1, 3, 2, 2, 1, -1, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, -1, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, -1, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, -1, 7, 7, 1, 7, 2, 2, 1, 7, 3, 3, 1, 3, 2, 2, 1, 7, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 7, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 7, 6, 6, 1, 6, 2, 2, 1, 6, 3, 3, 1, 3, 2, 2, 1, 6, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, 6, 5, 5, 1, 5, 2, 2, 1, 5, 3, 3, 1, 3, 2, 2, 1, 5, 4, 4, 1, 4, 2, 2, 1, 4, 3, 3, 1, 3, 2, 2, 1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, 3, -1, 3, 3, 2, -1, -1, -1, 4, -1, 4, 4, 2, -1, 4, 4, 3, 4, 3, 3, 2, -1, -1, -1, 5, -1, 5, 5, 2, -1, 5, 5, 3, 5, 3, 3, 2, -1, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, -1, -1, -1, 6, -1, 6, 6, 2, -1, 6, 6, 3, 6, 3, 3, 2, -1, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2, -1, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2, 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, -1, -1, -1, 7, -1, 7, 7, 2, -1, 7, 7, 3, 7, 3, 3, 2, -1, 7, 7, 4, 7, 4, 4, 2, 7, 4, 4, 3, 4, 3, 3, 2, -1, 7, 7, 5, 7, 5, 5, 2, 7, 5, 5, 3, 5, 3, 3, 2, 7, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, -1, 7, 7, 6, 7, 6, 6, 2, 7, 6, 6, 3, 6, 3, 3, 2, 7, 6, 6, 4, 6, 4, 4, 2, 6, 4, 4, 3, 4, 3, 3, 2, 7, 6, 6, 5, 6, 5, 5, 2, 6, 5, 5, 3, 5, 3, 3, 2, 6, 5, 5, 4, 5, 4, 4, 2, 5, 4, 4, 3, 4, 3, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, 4, -1, 4, 4, 3, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1, -1, 5, -1, 5, 5, 3, -1, -1, -1, 5, -1, 5, 5, 4, -1, 5, 5, 4, 5, 4, 4, 3, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, 6, 6, 3, -1, -1, -1, 6, -1, 6, 6, 4, -1, 6, 6, 4, 6, 4, 4, 3, -1, -1, -1, 6, -1, 6, 6, 5, -1, 6, 6, 5, 6, 5, 5, 3, -1, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 3, -1, -1, -1, 7, -1, 7, 7, 4, -1, 7, 7, 4, 7, 4, 4, 3, -1, -1, -1, 7, -1, 7, 7, 5, -1, 7, 7, 5, 7, 5, 5, 3, -1, 7, 7, 5, 7, 5, 5, 4, 7, 5, 5, 4, 5, 4, 4, 3, -1, -1, -1, 7, -1, 7, 7, 6, -1, 7, 7, 6, 7, 6, 6, 3, -1, 7, 7, 6, 7, 6, 6, 4, 7, 6, 6, 4, 6, 4, 4, 3, -1, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 3, 7, 6, 6, 5, 6, 5, 5, 4, 6, 5, 5, 4, 5, 4, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1, -1, 5, -1, 5, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, 6, 6, 4, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, 6, 6, 5, -1, -1, -1, 6, -1, 6, 6, 5, -1, 6, 6, 5, 6, 5, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 4, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 5, -1, -1, -1, 7, -1, 7, 7, 5, -1, 7, 7, 5, 7, 5, 5, 4, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 6, -1, -1, -1, 7, -1, 7, 7, 6, -1, 7, 7, 6, 7, 6, 6, 4, -1, -1, -1, 7, -1, 7, 7, 6, -1, 7, 7, 6, 7, 6, 6, 5, -1, 7, 7, 6, 7, 6, 6, 5, 7, 6, 6, 5, 6, 5, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, 6, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 6, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 6, -1, -1, -1, 7, -1, 7, 7, 6, -1, 7, 7, 6, 7, 6, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 7, -1, 7, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7 }; private static final boolean ASSERTS = true; private static final int MAX_ONES_PER_INVENTORY = 8192; private static final int MAX_LOG2_LONGWORDS_PER_SUBINVENTORY = 3; /** * The maximum size of span to qualify for a subinventory made of 16-bit * offsets. */ private static final int MAX_SPAN = 1 << 16; // The number of ones private final long numOnes; // The number of words private final int numWords; // The cached result of the bits private transient long[] bits; /** * The first-level inventory containing information about one bit each * {@link #onesPerInventory}. If the entry is nonnegative, it is the rank of * the bit and subsequent information is recorded in {@link #subinventory16} * as offsets of one bit each {@link #onesPerSub16} (then, sequential search * is necessary). Otherwise, a negative value means that offsets are too * large and they have been recorded as 64-bit values. If * {@link #onesPerSub64} is 1, then offsets are directly stored into * {@link #subinventory}. Otherwise, the first {@link #subinventory} entry * is actually a pointer to {@link #exactSpill}, where the offsets can be * found. */ private final long[] inventory; /** The logarithm of the number of ones per {@link #inventory} entry. */ private final int log2OnesPerInventory; /** The number of ones per {@link #inventory} entry. */ private final int onesPerInventory; /** The mask associated to the number of ones per {@link #inventory} entry. */ private final int onesPerInventoryMask; /** * The second-level inventory (records the offset of each bit w.r.t. the * first-level inventory). */ private final long[] subinventory; /** * The logarithm of the number of longwords used in the part of the * subinventory associated to an inventory entry. */ private final int log2LongwordsPerSubinventory; /** * The logarithm of the number of ones for each {@link #subinventory} * longword. */ private final int log2OnesPerSub64; /** The number of ones for each {@link #subinventory} longword. */ private final int onesPerSub64; /** * The logarithm of the number of ones for each {@link #subinventory} short. */ private final int log2OnesPerSub16; /** The number of ones for each {@link #subinventory} short. */ private final int onesPerSub16; /** * The mask associated to number of ones for each {@link #subinventory} * short. */ private final int onesPerSub16Mask; /** The list of exact spills. */ private final long[] exactSpill; private SimpleSelect(BitBuffer buffer) { numOnes = buffer.readEliasDelta() - 1; numWords = (int) buffer.readEliasDelta() - 1; bits = new long[numWords]; for (int i = 0; i < bits.length; i++) { bits[i] = buffer.readLong(); } inventory = new long[(int) buffer.readEliasDelta() - 1]; for (int i = 0; i < inventory.length; i++) { inventory[i] = buffer.readLong(); } log2OnesPerInventory = (int) buffer.readEliasDelta() - 1; onesPerInventory = 1 << log2OnesPerInventory; onesPerInventoryMask = onesPerInventory - 1; subinventory = new long[(int) buffer.readEliasDelta() - 1]; for (int i = 0; i < subinventory.length; i++) { subinventory[i] = buffer.readLong(); } log2LongwordsPerSubinventory = Math.min( MAX_LOG2_LONGWORDS_PER_SUBINVENTORY, Math.max(0, log2OnesPerInventory - 2)); log2OnesPerSub64 = Math.max(0, log2OnesPerInventory - log2LongwordsPerSubinventory); onesPerSub64 = 1 << log2OnesPerSub64; log2OnesPerSub16 = Math.max(0, log2OnesPerSub64 - 2); onesPerSub16 = 1 << log2OnesPerSub16; onesPerSub16Mask = onesPerSub16 - 1; exactSpill = new long[(int) buffer.readEliasDelta() - 1]; for (int i = 0; i < exactSpill.length; i++) { exactSpill[i] = buffer.readLong(); } } private SimpleSelect(BitSet bitSet) { long length = bitSet.length(); numWords = (int) ((length + 63) / 64); this.bits = new long[numWords]; for (int i = 0; i < length; i++) { bits[i / 64] |= bitSet.get(i) ? (1L << (i % 64)) : 0; } // We compute quickly the number of ones (possibly counting spurious // bits in the last word). long d = 0; for (int i = numWords; i-- != 0;) { d += Long.bitCount(bits[i]); } int x = length == 0 ? 1 : (int) ((d * MAX_ONES_PER_INVENTORY + length - 1) / length); int mostSignificantBit = 63 - Long.numberOfLeadingZeros(x); log2OnesPerInventory = mostSignificantBit; onesPerInventory = 1 << log2OnesPerInventory; onesPerInventoryMask = onesPerInventory - 1; int inventorySize = (int) ((d + onesPerInventory - 1) / onesPerInventory); inventory = new long[inventorySize + 1]; // First phase: we build an inventory for each one out of // onesPerInventory. d = 0; for (int i = 0; i < numWords; i++) { for (int j = 0; j < 64; j++) { if (i * 64L + j >= length) { break; } if ((bits[i] & 1L << j) != 0) { if ((d & onesPerInventoryMask) == 0) { inventory[(int) (d >>> log2OnesPerInventory)] = i * 64L + j; } d++; } } } numOnes = d; inventory[inventorySize] = length; log2LongwordsPerSubinventory = Math.min( MAX_LOG2_LONGWORDS_PER_SUBINVENTORY, Math.max(0, log2OnesPerInventory - 2)); log2OnesPerSub64 = Math.max(0, log2OnesPerInventory - log2LongwordsPerSubinventory); log2OnesPerSub16 = Math.max(0, log2OnesPerSub64 - 2); onesPerSub64 = 1 << log2OnesPerSub64; onesPerSub16 = 1 << log2OnesPerSub16; onesPerSub16Mask = onesPerSub16 - 1; if (onesPerInventory <= 1) { subinventory = exactSpill = new long[0]; return; } d = 0; int ones; long diff16 = 0, start = 0, span = 0; int spilled = 0, inventoryIndex = 0; for (int i = 0; i < numWords; i++) { // We estimate the subinventory and exact spill size for (int j = 0; j < 64; j++) { if (i * 64L + j >= length) { break; } if ((bits[i] & 1L << j) != 0) { if ((d & onesPerInventoryMask) == 0) { inventoryIndex = (int) (d >>> log2OnesPerInventory); start = inventory[inventoryIndex]; span = inventory[inventoryIndex + 1] - start; ones = (int) Math .min(numOnes - d, onesPerInventory); // We must always count (possibly unused) diff16's. // And we cannot store less then 4 diff16. diff16 += Math.max(4, (ones + onesPerSub16 - 1) >>> log2OnesPerSub16); if (span >= MAX_SPAN && onesPerSub64 > 1) { spilled += ones; } } d++; } } } int subinventorySize = (int) ((diff16 + 3) / 4); int exactSpillSize = spilled; subinventory = new long[subinventorySize]; exactSpill = new long[exactSpillSize]; int offset = 0; spilled = 0; d = 0; for (int i = 0; i < numWords; i++) { for (int j = 0; j < 64; j++) { if (i * 64L + j >= length) { break; } if ((bits[i] & 1L << j) != 0) { if ((d & onesPerInventoryMask) == 0) { inventoryIndex = (int) (d >>> log2OnesPerInventory); start = inventory[inventoryIndex]; span = inventory[inventoryIndex + 1] - start; offset = 0; } if (span < MAX_SPAN) { if (ASSERTS) { assert i * 64L + j - start <= MAX_SPAN; } if ((d & onesPerSub16Mask) == 0) { setSubInventory16((inventoryIndex << log2LongwordsPerSubinventory + 2) + offset++, (int) (i * 64L + j - start)); } } else { if (onesPerSub64 == 1) { subinventory[(inventoryIndex << log2LongwordsPerSubinventory) + offset++] = i * 64L + j; } else { if ((d & onesPerInventoryMask) == 0) { inventory[inventoryIndex] |= 1L << 63; subinventory[inventoryIndex << log2LongwordsPerSubinventory] = spilled; } exactSpill[spilled++] = i * 64L + j; } } d++; } } } } public static SimpleSelect load(BitBuffer buffer) { return new SimpleSelect(buffer); } public static SimpleSelect generate(BitSet bitSet, BitBuffer buffer) { SimpleSelect s = new SimpleSelect(bitSet); buffer.writeEliasDelta(s.numOnes + 1); buffer.writeEliasDelta(s.numWords + 1); for (long x : s.bits) { buffer.writeNumber(x, 64); } buffer.writeEliasDelta(s.inventory.length + 1); for (long x : s.inventory) { buffer.writeNumber(x, 64); } buffer.writeEliasDelta(s.log2OnesPerInventory + 1); buffer.writeEliasDelta(s.subinventory.length + 1); for (long x : s.subinventory) { buffer.writeNumber(x, 64); } buffer.writeEliasDelta(s.exactSpill.length + 1); for (long x : s.exactSpill) { buffer.writeNumber(x, 64); } return s; } public static int getSize(BitSet bitSet) { int result = 0; SimpleSelect s = new SimpleSelect(bitSet); result += BitBuffer.getEliasDeltaSize(s.numOnes + 1); result += BitBuffer.getEliasDeltaSize(s.numWords + 1); result += 64 * s.bits.length; result += BitBuffer.getEliasDeltaSize(s.inventory.length + 1); result += 64 * s.inventory.length; result += BitBuffer.getEliasDeltaSize(s.log2OnesPerInventory + 1); result += BitBuffer.getEliasDeltaSize(s.subinventory.length + 1); result += 64 * s.subinventory.length; result += BitBuffer.getEliasDeltaSize(s.exactSpill.length + 1); result += 64 * s.exactSpill.length; return result; } void setSubInventory16(int index, int x) { subinventory[index / 4] |= ((long) x) << (16 * (index % 4)); } int getSubInventory16(int index) { return (short) (subinventory[index / 4] >>> (16 * (index % 4))); } @Override public long select(long rank) { if (rank >= numOnes) { return -1; } int inventoryIndex = (int) (rank >>> log2OnesPerInventory); long inventoryRank = inventory[inventoryIndex]; int subrank = (int) (rank & onesPerInventoryMask); if (subrank == 0) { return inventoryRank & ~(1L << 63); } long start; int residual; if (inventoryRank >= 0) { start = inventoryRank + getSubInventory16((inventoryIndex << log2LongwordsPerSubinventory + 2) + (subrank >>> log2OnesPerSub16)); residual = subrank & onesPerSub16Mask; } else { if (onesPerSub64 == 1) { return subinventory[(inventoryIndex << log2LongwordsPerSubinventory) + subrank]; } return exactSpill[(int) (subinventory[inventoryIndex << log2LongwordsPerSubinventory] + subrank)]; } if (residual == 0) { return start; } long[] bits = this.bits; int wordIndex = (int) (start / 64); long word = bits[wordIndex] & (-1L << start); for (;;) { int bitCount = Long.bitCount(word); if (residual < bitCount) { break; } word = bits[++wordIndex]; residual -= bitCount; } return wordIndex * 64L + selectInLong(word, residual); } public static int selectInLong(long x, int rank) { assert rank < Long.bitCount(x); // Phase 1: sums by byte long byteSums = x - ((x & 0xa * ONES_STEP_4) >>> 1); byteSums = (byteSums & 3 * ONES_STEP_4) + ((byteSums >>> 2) & 3 * ONES_STEP_4); byteSums = (byteSums + (byteSums >>> 4)) & 0x0f * ONES_STEP_8; byteSums *= ONES_STEP_8; // Phase 2: compare each byte sum with rank to obtain the relevant byte long rankStep8 = rank * ONES_STEP_8; int byteOffset = (int) (((((rankStep8 | MSBS_STEP_8) - byteSums) & MSBS_STEP_8) >>> 7) * ONES_STEP_8 >>> 53) & ~0x7; int byteRank = (int) (rank - (((byteSums << 8) >>> byteOffset) & 0xFF)); return byteOffset + SELECT_IN_BYTE[(int) (x >>> byteOffset & 0xFF) | byteRank << 8]; } @Override public long selectPair(long i) { long x = select(i); int curr = (int) (x / Long.SIZE); long window = bits[curr] & -1L << x; window &= window - 1; while (window == 0) { window = bits[++curr]; } long y = curr * Long.SIZE + Long.numberOfTrailingZeros(window); return (x << 32) | y; } } ================================================ FILE: src/main/java/org/minperf/select/SimpleSelectWrapper.java ================================================ package org.minperf.select; //import it.unimi.dsi.bits.LongArrayBitVector; //import it.unimi.dsi.sux4j.bits.SimpleSelect; // //import java.io.ByteArrayInputStream; //import java.io.ByteArrayOutputStream; //import java.io.IOException; //import java.io.ObjectInputStream; //import java.io.ObjectOutputStream; //import java.util.BitSet; // //import org.minperf.BitBuffer; /** * A select implementation with guaranteed O(1) query time. This is a wrapper * around the the (very good) implementation in Sux4J * it.unimi.dsi.sux4j.bits.SimpleSelect by Sebastiano Vigna. */ public class SimpleSelectWrapper { // extends Select { // // private final SimpleSelect select; // // private SimpleSelectWrapper(SimpleSelect select) { // this.select = select; // } // // public static SimpleSelectWrapper generate(BitSet bitSet, BitBuffer buffer) { // int len = bitSet.length(); // LongArrayBitVector bv = LongArrayBitVector.ofLength(len); // for (int i = 0; i < len; i++) { // if (bitSet.get(i)) { // bv.set(i); // } // } // SimpleSelect select = new SimpleSelect(bv); // ByteArrayOutputStream out = new ByteArrayOutputStream(); // try { // ObjectOutputStream oo = new ObjectOutputStream(out); // oo.writeObject(select); // } catch (IOException e) { // throw new RuntimeException(); // } // byte[] data = out.toByteArray(); // buffer.writeEliasDelta(data.length + 1); // for (byte b : data) { // buffer.writeNumber(b & 255, 8); // } // return new SimpleSelectWrapper(select); // } // // public static Select load(BitBuffer buffer) { // int len = (int) buffer.readEliasDelta() - 1; // byte[] data = new byte[len]; // for (int i = 0; i < len; i++) { // data[i] = (byte) buffer.readNumber(8); // } // SimpleSelect select; // ByteArrayInputStream in = new ByteArrayInputStream(data); // try { // ObjectInputStream oi = new ObjectInputStream(in); // select = (SimpleSelect) oi.readObject(); // } catch (Exception e) { // throw new RuntimeException(); // } // return new SimpleSelectWrapper(select); // } // // @Override // public long select(long x) { // return select.select(x); // } // // @Override // public long selectPair(long x) { // long[] dest = new long[2]; // select.select(x, dest); // return (dest[0] << 32) | dest[1]; // } } ================================================ FILE: src/main/java/org/minperf/select/VerySimpleSelect.java ================================================ package org.minperf.select; import java.util.ArrayList; import java.util.BitSet; import org.minperf.BitBuffer; /** * A very simple implementation that assumes one bits are somewhat evenly * distributed. *

* The select operation is fast and space usage is quite low, but there is no * strict guarantee that space usage is O(n) and the select operation is * constant time in the RAM model, in the mathematical sense. */ public class VerySimpleSelect extends Select { public static final byte[] SELECT_BIT_IN_BYTE; public static final byte[] SELECT_BIT_IN_BYTE_REVERSE; static { byte[] data = new byte[256 * 8]; byte[] reverse = new byte[256 * 8]; for (int n = 0; n < 8; n++) { for (int i = 0; i < 256; i++) { data[i + (n << 8)] = (byte) selectBitSlow(i, n); reverse[i + (n << 8)] = (byte) selectBitSlowReverse((long) i << (64 - 8), n); } } SELECT_BIT_IN_BYTE = data; SELECT_BIT_IN_BYTE_REVERSE = reverse; } // must be a power of 2 private static final int BITS_PER_BLOCK = 32; private static final int BITS_PER_BLOCK_SHIFT = 31 - Integer.numberOfLeadingZeros(BITS_PER_BLOCK); private final BitBuffer buffer; private final int blockCount; private final long blockCountScale; private final int size; private final int cardinality; private final int bitCount; private final int added; private final int offsetPos; private final int dataPos; private VerySimpleSelect(BitBuffer buffer) { this.buffer = buffer; this.size = (int) (buffer.readEliasDelta() - 1); this.cardinality = (int) (buffer.readEliasDelta() - 1); this.blockCount = (cardinality + BITS_PER_BLOCK - 1) / BITS_PER_BLOCK; this.blockCountScale = getScaleFactor(size, blockCount); this.added = (int) BitBuffer.unfoldSigned(buffer.readEliasDelta() - 1); this.bitCount = (int) (buffer.readEliasDelta() - 1); this.offsetPos = buffer.position(); this.dataPos = offsetPos + bitCount * blockCount; buffer.seek(dataPos + size); } public static VerySimpleSelect generate(BitSet set, BitBuffer buffer) { int start = buffer.position(); int size = set.length() + 1; buffer.writeEliasDelta(size + 1); int cardinality = set.cardinality(); buffer.writeEliasDelta(cardinality + 1); int blockCount = (cardinality + BITS_PER_BLOCK - 1) / BITS_PER_BLOCK; ArrayList list = new ArrayList(); int pos = set.nextSetBit(0); for (int i = 0; i < blockCount; i++) { list.add(pos); for (int j = 0; j < BITS_PER_BLOCK; j++) { pos = set.nextSetBit(pos + 1); } } long blockCountScale = getScaleFactor(size, blockCount); int minDiff = Integer.MAX_VALUE; for (int i = 0; i < list.size(); i++) { // int expected = (int) ((long) size * i / blockCount); int expected = (int) ((i * blockCountScale) >>> 32); int got = list.get(i); int diff = got - expected; list.set(i, diff); minDiff = Math.min(minDiff, diff); } if (list.size() == 0) { minDiff = 0; } buffer.writeEliasDelta(BitBuffer.foldSigned(-minDiff) + 1); int max = 0; for (int i = 0; i < list.size(); i++) { int x = list.get(i) - minDiff; max = Math.max(max, x); list.set(i, x); } int bitCount = 32 - Integer.numberOfLeadingZeros(max); buffer.writeEliasDelta(bitCount + 1); for (int i = 0; i < list.size(); i++) { buffer.writeNumber(list.get(i), bitCount); } for (int i = 0; i < size; i++) { buffer.writeBit(set.get(i) ? 1 : 0); } buffer.seek(start); return new VerySimpleSelect(buffer); } public static int getSize(BitSet set) { int result = 0; int size = set.length() + 1; result += BitBuffer.getEliasDeltaSize(size + 1); int cardinality = set.cardinality(); result += BitBuffer.getEliasDeltaSize(cardinality + 1); int blockCount = (cardinality + BITS_PER_BLOCK - 1) / BITS_PER_BLOCK; ArrayList list = new ArrayList(); int pos = set.nextSetBit(0); for (int i = 0; i < blockCount; i++) { list.add(pos); for (int j = 0; j < BITS_PER_BLOCK; j++) { pos = set.nextSetBit(pos + 1); } } long blockCountScale = getScaleFactor(size, blockCount); int minDiff = Integer.MAX_VALUE; for (int i = 0; i < list.size(); i++) { // int expected = (int) ((long) size * i / blockCount); int expected = (int) ((i * blockCountScale) >>> 32); int got = list.get(i); int diff = got - expected; list.set(i, diff); minDiff = Math.min(minDiff, diff); } if (list.size() == 0) { minDiff = 0; } result += BitBuffer.getEliasDeltaSize(BitBuffer.foldSigned(-minDiff) + 1); int max = 0; for (int i = 0; i < list.size(); i++) { int x = list.get(i) - minDiff; max = Math.max(max, x); list.set(i, x); } int bitCount = 32 - Integer.numberOfLeadingZeros(max); result += BitBuffer.getEliasDeltaSize(bitCount + 1); result += bitCount * list.size(); result += size; return result; } private static long getScaleFactor(int multiply, int divide) { return divide == 0 ? 0 : ((long) multiply << 32) / divide + 1; } public static VerySimpleSelect load(BitBuffer buffer) { return new VerySimpleSelect(buffer); } @Override public long select(long x) { int block = (int) (x >>> BITS_PER_BLOCK_SHIFT); int expected = (int) ((block * blockCountScale) >>> 32); long read = buffer.readNumber(offsetPos + block * bitCount, bitCount); long result = expected + read - added; int remaining = (int) (x - ((long) block << BITS_PER_BLOCK_SHIFT)); while (true) { int data = (int) buffer.readNumber((int) result + dataPos, 32); int bitCount = Integer.bitCount(data); if (remaining < bitCount) { return result + selectBitReverse(data, remaining); } result += 32; remaining -= bitCount; } } @Override public long selectPair(long x) { int block = (int) (x >>> BITS_PER_BLOCK_SHIFT); int expected = (int) ((block * blockCountScale) >>> 32); long read = buffer.readNumber(offsetPos + block * bitCount, bitCount); long result = expected + read - added; int remaining = (int) (x - ((long) block << BITS_PER_BLOCK_SHIFT)); while (true) { int data = (int) buffer.readNumber((int) result + dataPos, 32); int bitCount = Integer.bitCount(data); if (remaining < bitCount) { int bit1 = selectBitReverse(data, remaining); int bit2; if (bit1 != 31 && data << (bit1 + 1) != 0) { data <<= bit1 + 1; bit1 += result; bit2 = bit1 + 1 + Integer.numberOfLeadingZeros(data); } else { bit1 += result; bit2 = bit1 + 1; while (true) { data = (int) buffer.readNumber(bit2 + dataPos, 32); if (data != 0) { bit2 += Integer.numberOfLeadingZeros(data); break; } bit2 += 32; } } return ((long) bit1 << 32) | bit2; } result += 32; remaining -= bitCount; } } public static int selectBitSlowReverse(long x, int n) { return selectBitSlow(Long.reverse(x), n); } public static int selectBitSlow(long x, int n) { n++; for (int i = 0; i < 64; i++) { if ((x & 1) == 1) { n--; if (n == 0) { return i; } } x >>>= 1; } return -1; } public static int selectBitLongReverse(long x, int n) { // int bitCount = Long.bitCount(x & 0xffffffff00000000L); int bitCount = Long.bitCount(x >>> 32); int more = (bitCount - n - 1) >> 31; int result = more & 32; n -= bitCount & more; // bitCount = Long.bitCount((x << result) & 0xffff000000000000L); bitCount = Long.bitCount((x << result) >>> 48); more = (bitCount - n - 1) >> 31; result += more & 16; n -= bitCount & more; // bitCount = Long.bitCount((x << result) & 0xff00000000000000L); bitCount = Long.bitCount((x << result) >>> 56); more = (bitCount - n - 1) >> 31; result += more & 8; n -= bitCount & more; return SELECT_BIT_IN_BYTE_REVERSE[(int) ((x << result) >>> 56) | (n << 8)] + result; } public static int selectBitLong(long x, int n) { int bitCount = Long.bitCount(x & 0xffffffffL); int more = (bitCount - n - 1) >> 31; int result = more & 32; n -= bitCount & more; bitCount = Long.bitCount((x >>> result) & 0xffff); more = (bitCount - n - 1) >> 31; result += more & 16; n -= bitCount & more; bitCount = Long.bitCount((x >>> result) & 0xff); more = (bitCount - n - 1) >> 31; result += more & 8; n -= bitCount & more; return SELECT_BIT_IN_BYTE[(int) ((x >>> result) & 0xff) | (n << 8)] + result; } public static int selectBitReverse(int x, int n) { // int bitCount = Integer.bitCount(x & 0xffff0000); int bitCount = Integer.bitCount(x >>> 16); int more = (bitCount - n - 1) >> 31; int result = more & 16; n -= bitCount & more; // bitCount = Integer.bitCount((x << result) & 0xff000000); bitCount = Integer.bitCount((x << result) >>> 24); more = (bitCount - n - 1) >> 31; result += more & 8; n -= bitCount & more; return SELECT_BIT_IN_BYTE_REVERSE[((x << result) >>> 24) | (n << 8)] + result; } public static int selectBit(int x, int n) { int bitCount = Integer.bitCount(x & 0xffff); int more = (bitCount - n - 1) >> 31; int result = more & 16; n -= bitCount & more; bitCount = Integer.bitCount((x >>> result) & 0xff); more = (bitCount - n - 1) >> 31; result += more & 8; n -= bitCount & more; return SELECT_BIT_IN_BYTE[((x >>> result) & 0xff) | (n << 8)] + result; } } ================================================ FILE: src/main/java/org/minperf/universal/LongHash.java ================================================ package org.minperf.universal; /** * A sample hash implementation for long keys. */ public class LongHash implements UniversalHash { public static long universalHash(long x, long index) { long v0 = index ^ 0x736f6d6570736575L; long v1 = index ^ 0x646f72616e646f6dL; long v2 = index ^ 0x6c7967656e657261L; long v3 = index ^ 0x7465646279746573L; v3 ^= x; for (int i = 0; i < 4; i++) { v0 += v1; v2 += v3; v1 = Long.rotateLeft(v1, 13); v3 = Long.rotateLeft(v3, 16); v1 ^= v0; v3 ^= v2; v0 = Long.rotateLeft(v0, 32); v2 += v1; v0 += v3; v1 = Long.rotateLeft(v1, 17); v3 = Long.rotateLeft(v3, 21); v1 ^= v2; v3 ^= v0; v2 = Long.rotateLeft(v2, 32); } v0 ^= x; return v0 ^ v1 ^ v2 ^ v3; } @Override public long universalHash(Long key, long index) { return universalHash((long) key, index); } @Override public String toString() { return "LongHash (SipHash)"; } } ================================================ FILE: src/main/java/org/minperf/universal/StringHash.java ================================================ package org.minperf.universal; import java.nio.charset.Charset; /** * A hash implementation for string keys. */ public class StringHash implements UniversalHash { private static final Charset UTF8 = Charset.forName("UTF-8"); @Override public long universalHash(String key, long index) { return getSipHash24(key, index, index); } /** * A cryptographically relatively secure hash function. It is supposed * to protected against hash-flooding denial-of-service attacks. * * @param o the string * @param k0 key 0 * @param k1 key 1 * @return the hash value */ public static long getSipHash24(String o, long k0, long k1) { byte[] b = o.getBytes(UTF8); return getSipHash24(b, 0, b.length, k0, k1); } /** * A cryptographically relatively secure hash function. It is supposed * to protected against hash-flooding denial-of-service attacks. * * @param b the data * @param start the start position * @param end the end position plus one * @param k0 key 0 * @param k1 key 1 * @return the hash value */ public static long getSipHash24(byte[] b, int start, int end, long k0, long k1) { long v0 = k0 ^ 0x736f6d6570736575L; long v1 = k1 ^ 0x646f72616e646f6dL; long v2 = k0 ^ 0x6c7967656e657261L; long v3 = k1 ^ 0x7465646279746573L; int repeat; for (int off = start; off <= end + 8; off += 8) { long m; if (off <= end) { m = 0; int i = 0; for (; i < 8 && off + i < end; i++) { m |= ((long) b[off + i] & 255) << (8 * i); } if (i < 8) { m |= ((long) end - start) << 56; } v3 ^= m; repeat = 2; } else { m = 0; v2 ^= 0xff; repeat = 4; } for (int i = 0; i < repeat; i++) { v0 += v1; v2 += v3; v1 = Long.rotateLeft(v1, 13); v3 = Long.rotateLeft(v3, 16); v1 ^= v0; v3 ^= v2; v0 = Long.rotateLeft(v0, 32); v2 += v1; v0 += v3; v1 = Long.rotateLeft(v1, 17); v3 = Long.rotateLeft(v3, 21); v1 ^= v2; v3 ^= v0; v2 = Long.rotateLeft(v2, 32); } v0 ^= m; } return v0 ^ v1 ^ v2 ^ v3; } @Override public String toString() { return "StringHash (SipHash)"; } } ================================================ FILE: src/main/java/org/minperf/universal/UniversalHash.java ================================================ package org.minperf.universal; /** * An interface that can calculate multiple hash values for an object. The * returned hash value of two distinct objects may be the same for a given * hash function index, but as more hash functions indices are called for * those objects, the returned value must eventually be different * (the earlier the better). *

* The returned value does not need to be uniformly distributed. * * @param the type */ public interface UniversalHash { /** * Calculate the hash of the given object. * * @param key the key in the set * @param index the hash function index (0, 1, 2,...) * @return the universal hash (64 bits) */ long universalHash(T key, long index); } ================================================ FILE: src/main/java/org/minperf/utils/LargeLongList.java ================================================ package org.minperf.utils; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Spliterator; /** * A persisted list of longs. */ public class LargeLongList extends AbstractList { static final int CHUNK_SHIFT = 27; static final int CHUNK_SIZE = 1 << CHUNK_SHIFT; private static final int CHUNK_MASK = CHUNK_SIZE - 1; private final int size; private ArrayList list = new ArrayList(); LargeLongList(int size, ArrayList list) { this.size = size; this.list = list; } @Override public Long get(int index) { if (index == 0) { System.out.println("...LargeLongList.get[0]"); } return list.get(index >>> CHUNK_SHIFT).get(index & CHUNK_MASK); } @Override public int size() { return size; } @Override public void finalize() { dispose(); } public void dispose() { for (LargeLongArray a : list) { a.dispose(); } } public static LargeLongList create(Collection collection) { int size = collection.size(); int chunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE; int remaining = size; Iterator iterator = collection.iterator(); ArrayList list = new ArrayList(); for (int i = 0; i < chunks; i++) { int len = Math.min(CHUNK_SIZE, remaining); list.add(LargeLongArray.create(iterator, len)); remaining -= len; } return new LargeLongList(size, list); } @Override public Spliterator spliterator() { throw new UnsupportedOperationException(); } /** * A large long array. */ static class LargeLongArray { private File file; private FileChannel channel; private MappedByteBuffer map; private final int size; private boolean closed; public LargeLongArray(int size, File file, FileChannel channel, MappedByteBuffer map) { this.size = size; this.file = file; this.channel = channel; this.map = map; } static LargeLongArray create(Iterator iterator, int size) { try { File file = File.createTempFile("list", ".tmp"); file.deleteOnExit(); try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { FileChannel channel = raf.getChannel(); MappedByteBuffer map = channel .map(MapMode.READ_WRITE, 0, size * 8L); for (int i = 0; i < size; i++) { long x = iterator.next(); map.putLong(x); } return new LargeLongArray(size, file, channel, map); } } catch (IOException e) { throw new RuntimeException(e); } } @Override public void finalize() { dispose(); } public void dispose() { if (closed) { return; } closed = true; try { channel.close(); file.delete(); } catch (IOException e) { throw new RuntimeException(e); } } public Long get(int index) { if (index > size) { throw new IllegalArgumentException(); } return map.getLong(index * 8); } } } ================================================ FILE: src/main/java/org/minperf/utils/LongSet.java ================================================ package org.minperf.utils; import java.util.AbstractSet; import java.util.Iterator; import java.util.Spliterator; import java.util.function.Consumer; /** * A map of longs. Only add an iteration are not supported. This implementation * doesn't use much memory. */ public class LongSet extends AbstractSet { LargeLongArray data; boolean containsZero; private long size; public LongSet(int capacity) { // 80% fill rate long len = capacity * 12L / 10; data = new LargeLongArray(len); } @Override public int size() { return (int) size; } @Override public boolean isEmpty() { return size == 0; } @Override public boolean add(Long e) { if (e == 0) { if (containsZero) { return false; } containsZero = true; size++; return true; } long x = e; if (size * 10 > data.size() * 11) { // more than 90% full throw new UnsupportedOperationException(); } long index = (x & 0xffffffffffffL) % data.size(); while (true) { if (data.get(index) == x) { return false; } if (data.get(index) == 0) { data.set(index, x); size++; return true; } index++; if (index >= data.size()) { index = 0; } } } @Override public Iterator iterator() { return new Iterator() { private long index; { index = -1; if (!containsZero) { fetchNext(); } } private void fetchNext() { while (true) { index++; if (index >= data.size()) { return; } if (data.get(index) != 0) { return; } } } @Override public boolean hasNext() { return index < data.size(); } @Override public Long next() { if (!hasNext()) { throw new UnsupportedOperationException(); } if (index == -1) { fetchNext(); return 0L; } long result = data.get(index); fetchNext(); return result; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void forEachRemaining(Consumer action) { throw new UnsupportedOperationException(); } }; } @Override public Spliterator spliterator() { throw new UnsupportedOperationException(); } /** * A large long array. */ static class LargeLongArray { private static final int CHUNK_SHIFT = 20; private static final int CHUNK_SIZE = 1 << CHUNK_SHIFT; private static final int CHUNK_MASK = CHUNK_SIZE - 1; private final long[][] data; private final long size; LargeLongArray(long size) { this.size = size; int chunkCount = (int) ((size + CHUNK_SIZE - 1) >>> CHUNK_SHIFT); data = new long[chunkCount][]; long remaining = size; for (int i = 0; i < chunkCount - 1; i++) { data[i] = new long[CHUNK_SIZE]; remaining -= CHUNK_SIZE; } data[chunkCount - 1] = new long[(int) remaining]; } long get(long i) { return data[(int) (i >>> CHUNK_SHIFT)][(int) (i & CHUNK_MASK)]; } void set(long i, long x) { data[(int) (i >>> CHUNK_SHIFT)][(int) (i & CHUNK_MASK)] = x; } long size() { return size; } } } ================================================ FILE: src/main/java/org/minperf/utils/PoissonDistribution.java ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.minperf.utils; /** * Poisson distribution. This code is based on the * implementation at Apache Commons Math 4. * * See http://en.wikipedia.org/wiki/Poisson_distribution, * http://mathworld.wolfram.com/PoissonDistribution.html */ public class PoissonDistribution { private static final double PI = 105414357.0 / 33554432.0 + 1.984187159361080883e-9; private static final double TWO_PI = 2 * PoissonDistribution.PI; // Exact Stirling expansion error for certain values. private static final double[] EXACT_STIRLING_ERRORS = { 0.0, 0.1534264097200273452913848, 0.0810614667953272582196702, 0.0548141210519176538961390, 0.0413406959554092940938221, 0.03316287351993628748511048, 0.02767792568499833914878929, 0.02374616365629749597132920, 0.02079067210376509311152277, 0.01848845053267318523077934, 0.01664469118982119216319487, 0.01513497322191737887351255, 0.01387612882307074799874573, 0.01281046524292022692424986, 0.01189670994589177009505572, 0.01110455975820691732662991, 0.010411265261972096497478567, 0.009799416126158803298389475, 0.009255462182712732917728637, 0.008768700134139385462952823, 0.008330563433362871256469318, 0.007934114564314020547248100, 0.007573675487951840794972024, 0.007244554301320383179543912, 0.006942840107209529865664152, 0.006665247032707682442354394, 0.006408994188004207068439631, 0.006171712263039457647532867, 0.005951370112758847735624416, 0.005746216513010115682023589, 0.005554733551962801371038690 }; public static double probability(double p, int x) { double logProbability = logProbability(p, x); if (logProbability == Double.NEGATIVE_INFINITY) { return 0; } return Math.exp(logProbability); } private static double logProbability(double p, int x) { if (x < 0 || x == Integer.MAX_VALUE) { return Double.NEGATIVE_INFINITY; } else if (x == 0) { return -p; } return -getStirlingError(x) - getDeviancePart(x, p) - 0.5 * Math.log(PoissonDistribution.TWO_PI) - 0.5 * Math.log(x); } /** * Compute the error of Stirling's series at the given value. *

* Reference: Eric W. Weisstein. "Stirling's Series." From MathWorld - A * Wolfram Web Resource. http://mathworld.wolfram.com/StirlingsSeries.html * * @param z the value. * @return the Striling's series error. */ private static double getStirlingError(double z) { if (z < 15.0) { double z2 = 2.0 * z; if (Math.floor(z2) == z2) { return EXACT_STIRLING_ERRORS[(int) z2]; } throw new IllegalArgumentException("Unsupported z value " + z); } double z2 = z * z; return (0.083333333333333333333 - (0.00277777777777777777778 - (0.00079365079365079365079365 - (0.000595238095238095238095238 - 0.0008417508417508417508417508 / z2) / z2) / z2) / z2) / z; } /** * A part of the deviance portion of the saddle point approximation. *

* Reference: Catherine Loader (2000). "Fast and Accurate Computation of * Binomial Probabilities." http://www.herine.net/stat/papers/dbinom.pdf * * @param x the x value. * @param mu the average. * @return a part of the deviance. */ private static double getDeviancePart(double x, double mu) { if (Math.abs(x - mu) < 0.1 * (x + mu)) { double d = x - mu; double v = d / (x + mu); double s1 = v * d; double s = Double.NaN; double ej = 2.0 * x * v; v *= v; int j = 1; while (s1 != s) { s = s1; ej *= v; s1 = s + ej / ((j * 2) + 1); ++j; } return s1; } if (x == 0) { return mu; } return x * Math.log(x / mu) + mu - x; } } ================================================ FILE: src/main/java/org/minperf/utils/RandomSetGenerator.java ================================================ package org.minperf.utils; import java.util.Random; /** * A PRNG that returns unique (distinct) 64-bit entries in somewhat sorted * order. Not fully sorted, for speed, but the largest entry of a block is * guaranteed to be smaller of the smallest entry of the next block. The set * doesn't need to be kept fully in memory. */ public class RandomSetGenerator { public static void main(String... args) { long size = 1_000_000_000_000L; System.out.println(size * 150 / 1000000 / 1000 / 60 / 60. + " h"); // sorted: with 24 ns / key, it would take 6 h to generate // not fully sorted: with 4 ns / key, it would take 1 h to generate System.out.println((double) Long.MAX_VALUE + " max long"); long[] data = new long[2_000_000]; Random r = new Random(1); RandomBlockProducer it = randomHashProducer(r, size); long len = 0; long start = System.nanoTime(); long maxLastBlock = -1; for (long remaining = size; remaining >= 0;) { int produced = it.produce(data, 0, data.length, 0); len += produced; if (produced == 0) { break; } long minBlock = Long.MAX_VALUE, maxBlock = -1; for (int i = 0; i < produced; i++) { long x = data[i]; minBlock = Math.min(minBlock, x); maxBlock = Math.max(maxBlock, x); } if (minBlock > maxBlock) { throw new AssertionError(); } if (minBlock < maxLastBlock) { throw new AssertionError(); } maxLastBlock = maxBlock; System.out.println("produced " + produced); remaining -= produced; long time = System.nanoTime() - start; System.out.println(size + " " + (double) time / len + " ns/key len " + len); } } public static RandomBlockProducer randomHashProducer(Random r, long size) { return randomProducer(r, size, 63); } private static RandomBlockProducer randomProducer(final Random r, final long size, final int shift) { if (shift <= 44) { if (shift != 44) { throw new IllegalArgumentException(); } return new RandomBlockProducer() { long remaining = size; @Override public int produce(long[] data, int offset, int len, long add) { if (len < size) { return 0; } for (int i = 0; i < size; i++) { data[i + offset] = hash44(i + offset + add + 1) + add; // break; } // Arrays.parallelSort(data, offset, offset + (int) size); // Arrays.sort(data, offset, offset + (int) size); remaining = 0; return (int) size; } @Override public long remaining() { return remaining; } }; } return new RandomBlockProducer() { long remaining = size, zeros = randomHalf(r, size); long bitMask; RandomBlockProducer child = randomProducer(r, zeros, shift - 1); @Override public int produce(long[] data, int offset, int len, long add) { int produced = 0; while (true) { if (child.remaining() == 0) { if (bitMask != 0) { return produced; } bitMask = 1L << shift; child = randomProducer(r, size - zeros, shift - 1); } int p = child.produce(data, offset, len, bitMask + add); if (p == 0) { return produced; } produced += p; offset += p; len -= p; remaining -= p; } } @Override public long remaining() { return remaining; } }; } /** * Get the number of entries that are supposed to be below the half, * according to the probability theory. For example, for a number of coin * flips, how many are heads. * * @param r the random generator * @param samples the total number of entries * @return the number of entries that should be used for one half */ static long randomHalf(Random r, long samples) { long low = 0, high = samples; double x = r.nextDouble(); while (low + 1 < high) { long mid = (low + high) / 2; double p = probabilityBucketAtMost(samples, mid); if (x > p) { low = mid; } else { high = mid; } } return (low + high) / 2; } static double probabilityBucketAtMost(long flips, long heads) { // https://www.fourmilab.ch/rpkp/experiments/statistics.html long x = heads; long n = flips; double variance = Math.sqrt(n / 4); // mean long mu = n / 2; // https://en.wikipedia.org/wiki/Normal_distribution // Numerical approximations for the normal CDF // the probability that the value of a standard normal random variable X // is <= x return phi((x - mu) / variance); } static double phi(double x) { return 0.5 * (1 + Math.signum(x) * Math.sqrt(1 - Math.exp(-2 * x * x / Math.PI))); } public static long hash64(long x) { x = (x ^ (x >>> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >>> 27)) * 0x94d049bb133111ebL; x = x ^ (x >>> 31); return x; } public static long hash44(long x) { x = (x ^ (x >>> 20)) * 0xbf58476d1ce4e5b9L; x &= (1L << 44) - 1; x = (x ^ (x >>> 17)) * 0x94d049bb133111ebL; x &= (1L << 44) - 1; x = x ^ (x >>> 21); x &= (1L << 44) - 1; return x; } public static long hash32(long x) { x = (x ^ (x >>> 16)) * 0xbf58476d1ce4e5b9L; x &= 0xffffffffL; x = (x ^ (x >>> 16)) * 0x94d049bb133111ebL; x &= 0xffffffffL; x = x ^ (x >>> 16); return x & 0xffffffffL; } public static long hash16(long x) { x = (x ^ (x >>> 7)) * 0xbf58476d1ce4e5b9L; x &= 0xffff; x = (x ^ (x >>> 5)) * 0x94d049bb133111ebL; x &= 0xffff; x = x ^ (x >>> 9); return x & 0xffff; } public interface RandomBlockProducer { int produce(long[] data, int offset, int len, long add); long remaining(); } } ================================================ FILE: src/main/java/org/minperf/utils/RandomSetGeneratorSlow.java ================================================ package org.minperf.utils; import java.util.HashSet; import java.util.Iterator; import java.util.Random; /** * A PRNG that returns unique (distinct) entries in sorted order. The set * doesn't need to be kept fully in memory. Also, with more than 100'000 * entries, this is faster that generating the set fully in memory (due to not * having to keep the whole set in memory, to check for duplicates). */ public class RandomSetGeneratorSlow { public static void main(String... args) { Random r = new Random(); for(long size = 1000; size > 0; size *= 10) { for(int limit = 10; limit < size; limit*=10) { long time = System.nanoTime(); Iterator it = randomSequence(r, size, 64, limit); while(it.hasNext()) { it.next(); } time = System.nanoTime() - time; System.out.println(size + " " + time / size + " ns/key limit " + limit); } } // Iterator it = randomSequence(r, 10, 32, 100); // while(it.hasNext()) { // System.out.println(it.next()); // } } public static Iterable randomSequence(final long size) { return new Iterable() { @Override public Iterator iterator() { return randomSequence(new Random(size), size, 64, 10000); } }; } /** * Random sequence generator. * * @param r the random generator * @param size the number of entries to generate * @param shift the number of bits of the result * @return the iterator */ static Iterator randomSequence(final Random r, final long size, final int shift, final int limit) { if (size < limit) { // small lists are generated using a regular hash set HashSet set = new HashSet((int) size); // this would ensure the list is fully sorted // TreeSet set = new TreeSet(); if (shift == 64) { while (set.size() < size) { set.add(r.nextLong()); } } else { while (set.size() < size) { set.add(r.nextLong() & ((2L << shift) - 1)); } } return set.iterator(); } // large lists are created recursively return new Iterator() { long remaining = size, zeros = randomHalf(r, size); Iterator lowBits0 = randomSequence(r, zeros, shift - 1, limit); Iterator lowBits1; @Override public boolean hasNext() { return remaining > 0; } @Override public Long next() { remaining--; if (lowBits0 != null) { if (lowBits0.hasNext()) { return lowBits0.next(); } lowBits0 = null; } if (lowBits1 == null) { lowBits1 = randomSequence(r, size - zeros, shift - 1, limit); } return (1L << shift) + lowBits1.next(); } }; } /** * Get the number of entries that are supposed to be below the half, * according to the probability theory. For example, for a number of coin * flips, how many are heads. * * @param r the random generator * @param samples the total number of entries * @return the number of entries that should be used for one half */ static long randomHalf(Random r, long samples) { long low = 0, high = samples; double x = r.nextDouble(); while (low + 1 < high) { long mid = (low + high) / 2; double p = probabilityBucketAtMost(samples, mid); if (x > p) { low = mid; } else { high = mid; } } return (low + high) / 2; } static double probabilityBucketAtMost(long flips, long heads) { // https://www.fourmilab.ch/rpkp/experiments/statistics.html long x = heads; long n = flips; double variance = Math.sqrt(n/4); // mean long mu = n / 2; // https://en.wikipedia.org/wiki/Normal_distribution // Numerical approximations for the normal CDF // the probability that the value of a standard normal random variable X is <= x return phi((x - mu) / variance); } static double phi(double x) { return 0.5 * (1 + Math.signum(x) * Math.sqrt(1 - Math.exp(-2 * x * x / Math.PI))); } } ================================================ FILE: src/main/java/org/minperf/utils/Text.java ================================================ package org.minperf.utils; import java.util.Comparator; import org.minperf.universal.StringHash; import org.minperf.universal.UniversalHash; /** * A text. It is similar to a String, but needs less memory. */ public class Text implements CharSequence { /** * The byte data (may be shared, so must not be modified). */ final byte[] data; /** * The offset (start location). */ private final int offset; /** * The length. */ private final int len; public Text(byte[] data, int offset, int len) { this.data = data; this.offset = offset; this.len = len; } public static int indexOf(byte[] data, int index, int character) { while (data[index] != character) { index++; } return index; } /** * The hash code (using a universal hash function). * * @param index the hash function index * @return the hash code */ public long hashCode(long index) { return StringHash.getSipHash24(data, offset, offset + len, index, 0); } @Override public int hashCode() { return (int) hashCode(0); } @Override public boolean equals(Object other) { if (other == this) { return true; } else if (!(other instanceof Text)) { return false; } Text o = (Text) other; if (o.len != len) { return false; } for (int i = 0; i < len; i++) { if (data[offset + i] != o.data[o.offset + i]) { return false; } } return true; } public int compareFast(Text o) { int comp = Integer.compare(len, o.len); if (comp != 0) { return comp; } for (int i = 0; i < len; i++) { int b = data[offset + i] & 0xff; int b2 = o.data[o.offset + i] & 0xff; if (b != b2) { return b > b2 ? 1 : -1; } } return 0; } @Override public String toString() { return new String(data, offset, len); } @Override public int length() { return len; } @Override public char charAt(int index) { return (char) data[offset + index]; } @Override public CharSequence subSequence(int start, int end) { return new Text(data, offset + start, end - start); } /** * The universal hash function for text. */ public static class UniversalTextHash implements UniversalHash { @Override public long universalHash(Text o, long index) { return o.hashCode(index); } } /** * Compare two texts. For improved speed, sorting is a bit unusual: sorting * is first done by size, and only then by data. This is a bit faster then * always sorting by data. */ public static class FastComparator implements Comparator { private int equalCount; @Override public int compare(Text o1, Text o2) { int comp = o1.compareFast(o2); if (comp == 0) { equalCount++; } return comp; } public int equalCount() { return equalCount; } } } ================================================ FILE: src/test/java/org/minperf/BitCodes.java ================================================ package org.minperf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.Random; import org.junit.Test; /** * Tests for Rice-Golomb codes, Elias Delta codes, and the FastBitBuffer class. */ public class BitCodes { public static void main(String... args) { for (int i = 0; i < 10; i++) { testPerformance(); } } private static void testPerformance() { BitBuffer buff = new BitBuffer(8 * 1024 * 1024); int len = 10000; for (int i = 0; i < len; i++) { buff.writeGolombRice(10, i); } long time = System.currentTimeMillis(); for (int j = 0; j < 1000; j++) { buff.seek(0); for (int i = 0; i < len; i++) { assertEquals(i, buff.readGolombRice(10)); } } time = System.currentTimeMillis() - time; System.out.println("time: " + time); } public static void printRiceExamples() { for (int i = 0; i < 20; i++) { System.out.println(" " + i + " & " + getRice(i, 0) + " & " + getRice(i, 1) + " & " + getRice(i, 2) + " & " + getRice(i, 3) + " & " + getRice(i, 4) + " & " + getRice(i, 5) + " \\\\"); } } @Test public void testGolombRiceCoding() { assertEquals("0", getRice(0, 0)); assertEquals("10", getRice(1, 0)); assertEquals("110", getRice(2, 0)); assertEquals("11..10", getRice(15, 0)); assertEquals("00", getRice(0, 1)); assertEquals("01", getRice(1, 1)); assertEquals("100", getRice(2, 1)); assertEquals("11..101", getRice(15, 1)); assertEquals("000", getRice(0, 2)); assertEquals("001", getRice(1, 2)); assertEquals("010", getRice(2, 2)); assertEquals("111011", getRice(15, 2)); assertEquals("0000", getRice(0, 3)); assertEquals("0001", getRice(1, 3)); assertEquals("0010", getRice(2, 3)); assertEquals("10111", getRice(15, 3)); for (int shift = 1; shift < 60; shift++) { for (int i = 1; i < 100; i++) { getRice(i, shift); } for (int i = 10; i < 100000; i *= 4) { getRice(i, shift); } } Random r = new Random(); for (int i = 0; i < 1000; i++) { getRice(r.nextLong() & 0x7fffffffL, 60); } } private static String getRice(long value, int shift) { BitBuffer buff = new BitBuffer(128 * 1024); buff.writeGolombRice(shift, value); int size = BitBuffer.getGolombRiceSize(shift, value); buff.seek(0); StringBuilder b = new StringBuilder(); for (int i = 0; i < size; i++) { b.append((char) ('0' + buff.readBit())); } String s = b.toString(); s = s.replaceFirst("^111111*", "11..1"); return s; } static double calcEntropy(double p) { // On the Determination of Optimal Parameterized Prefix Codes // for Adaptive Entropy Coding // Amir Said // 2.13, page 12 double m = 1 - p; return -Math.log(1 - m) / Math.log(2) - (m / (1 - m)) * Math.log(m) / Math.log(2); } public static double calcAverageRiceGolombBits(int k, double p) { double alpha = 1 - p; return k + (1 / (1 - Math.pow(alpha, Math.pow(2, k)))); } public static int calcBestGolombRiceShift(double p) { double mean = (1 - p) / p; // double p = 1 / (mean + 1); return calcBestGolombRiceShiftFromMean(mean); } public static int calcBestGolombRiceShiftFromMean(double mean) { // variant a double goldenRatio = (Math.sqrt(5) + 1) / 2; double logGoldenRatioMinus1 = Math.log(goldenRatio - 1); double k = 1 + (Math.log(logGoldenRatioMinus1 / Math.log(mean / (mean + 1))) / Math.log(2)); // variant b // from "On the Determination of Optimal Parameterized // Prefix Codes for Adaptive Entropy Coding" // double k2 = 1 + Math.log(Math.log(goldenRatio) / // Math.log(1 / (1 - p))) / Math.log(2); return Math.max(0, (int) k); } public static void verifyRiceParameterFormula() { System.out.println("Rice Parameter Formula"); for (int leafSize = 4; leafSize < 12; leafSize++) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN( leafSize, 1); verifyRiceParameterFormula(p); } } private static void verifyRiceParameterFormula(double p) { System.out.println("Pr(X = x) = (1-p)^(x-1) * p with p=" + p); int[] counts = new int[11]; Random r = new Random(2); int repeat = 1000000; for (int i = 0; i < repeat; i++) { for (int j = 1; j <= 10; j++) { boolean success = r.nextDouble() <= p; if (success) { counts[j]++; break; } } } for (int j = 1; j <= 10; j++) { double px = counts[j] / (double) repeat; double pFormula = Math.pow(1 - p, j - 1) * p; System.out.println(" try " + j + ": simulated " + px + " calculated " + pFormula); if (Math.abs(px - pFormula) > px / 2) { fail(); } } System.out.println(" Best Rice parameter"); double a = 1 - p; double goldenRatio = (Math.sqrt(5) + 1) / 2; double mu = (1 - p) / p; double mu2 = a / (1 - a); System.out.println(" mu:" + mu); System.out.println(" mu2:" + mu2); if (Math.abs(mu - mu2) > mu / 10) { fail(); } double logGoldenRatio = Math.log(goldenRatio - 1); double logMu = Math.log(mu / (mu + 1)); int bestK = (int) Math.max(0, 1 + Math.log(logGoldenRatio / logMu) / Math.log(2)); System.out.println(" bestK = " + bestK); int test = calcBestGolombRiceShift(p); assertEquals(test, bestK); System.out.println(" Average Bits Needed"); long bitCount = 0; r = new Random(1); repeat = 10000; for (int i = 0; i < repeat; i++) { for (int j = 0;; j++) { boolean success = r.nextDouble() <= p; if (success) { bitCount += BitBuffer.getGolombRiceSize(test, j); break; } } } double averageBits = bitCount / (double) repeat; System.out.println(" averageBits simulated " + averageBits); double averageBits2 = calcAverageRiceGolombBits(test, p); System.out.println(" averageBits calculated " + averageBits2); if (Math.abs(averageBits - averageBits2) > averageBits / 10) { fail(); } } public static void printEliasDeltaExample() { System.out.println("Elias Delta code examples"); for (int i = 1; i < 10; i++) { System.out.println(" " + i + " & " + getEliasDelta(i) + " \\\\"); } for (int i = 10; i < 1000000; i *= 10) { System.out.println(" " + i + " & " + getEliasDelta(i) + " \\\\"); } } @Test public void testEliasDeltaRoundtrip() { Random r = new Random(1); for (int i = 0; i < 1000; i++) { BitBuffer buff = new BitBuffer(8 * 1024 * 1024); long val = (r.nextLong() & 0xfffffffL) + 1; buff.writeEliasDelta(val); assertEquals(buff.position(), BitBuffer.getEliasDeltaSize(val)); buff.writeNumber(123, 10); int pos = buff.position(); byte[] data = buff.toByteArray(); assertEquals((pos + 7) / 8, data.length); buff = new BitBuffer(buff.toByteArray()); assertEquals(val, buff.readEliasDelta()); assertEquals(123, buff.readNumber(10)); assertEquals(pos, buff.position()); } } // this test fails!!!!! //@Test public void testNumberRoundtrip() { Random r = new Random(1); BitBuffer buff = new BitBuffer(8 * 1024 * 1024); buff.writeNumber(-1L, 64); buff.writeNumber(0, 64); for (int i = 0; i < 1000; i++) { long val = r.nextLong(); int bitCount = r.nextInt(65); if (bitCount < 64) { val &= ((1L << bitCount) - 1); } buff.writeNumber(val, bitCount); } buff.seek(0); r = new Random(1); //assertEquals(-1L, buff.readNumber(64)); assertEquals(0, buff.readNumber(64)); for (int i = 0; i < 1000; i++) { long val = r.nextLong(); int bitCount = r.nextInt(65); if (bitCount < 64) { val &= ((1L << bitCount) - 1); } long x = buff.readNumber(bitCount); assertEquals(val, x); } } @Test public void testEliasDeltaCoding() { assertEquals("1", getEliasDelta(1)); assertEquals("0100", getEliasDelta(2)); assertEquals("0101", getEliasDelta(3)); assertEquals("01100", getEliasDelta(4)); assertEquals("01111", getEliasDelta(7)); for (int i = 1; i < 100; i++) { getEliasDelta(i); } for (int i = 10; i < 1000000000; i *= 1.1) { getEliasDelta(i); } } static String getEliasDelta(int value) { BitBuffer buff = new BitBuffer(8 * 1024 * 1024); buff.writeEliasDelta(value); assertEquals(buff.position(), BitBuffer.getEliasDeltaSize(value)); int size = buff.position(); buff.seek(0); long test = buff.readEliasDelta(); assertEquals(value, test); assertEquals(size, buff.position()); buff.seek(0); StringBuilder b = new StringBuilder(); for (int i = 0; i < size; i++) { b.append((char) ('0' + buff.readBit())); } String s = b.toString(); return s; } @Test public void testWriteBuffer() { BitBuffer buff = new BitBuffer(8000); for (int i = 1; i < 100; i++) { BitBuffer b = new BitBuffer(160); b.writeEliasDelta(i); assertEquals(b.position(), BitBuffer.getEliasDeltaSize(i)); buff.write(b); } buff.seek(0); for (int i = 1; i < 100; i++) { assertEquals(i, buff.readEliasDelta()); } } @Test public void testWriteBuffer2() { BitBuffer buff = new BitBuffer(8000); for (int i = 1; i < 100; i++) { BitBuffer b = new BitBuffer(100); // b.writeEliasDelta(i); // assertEquals(b.position(), BitBuffer.getEliasDeltaSize(i)); for (int j = 0; j < i; j++) { b.writeBit(1); } b.writeBit(0); buff.write(b); } buff.seek(0); for (int i = 1; i < 100; i++) { // if (i != buff.readEliasDelta()) { // System.out.println("??"); // } // assertEquals(i, buff.readEliasDelta()); for (int j = 0; j < i; j++) { if (1 != buff.readBit()) { System.out.println("??"); } // assertEquals(1, buff.readBit()); } assertEquals(0, buff.readBit()); } } @Test public void testSeek() { BitBuffer buff = new BitBuffer(8000); for (int i = 0; i < 100; i++) { buff.seek(10 * i); buff.writeNumber(i, 8); } buff = new BitBuffer(buff.toByteArray()); for (int i = 0; i < 100; i++) { buff.seek(10 * i); assertEquals(i, buff.readNumber(8)); } } @Test public void testFoldUnfold() { assertEquals(0, BitBuffer.foldSigned(0)); assertEquals(1, BitBuffer.foldSigned(1)); assertEquals(2, BitBuffer.foldSigned(-1)); testFoldUnfold(0); testFoldUnfold(1); testFoldUnfold(-1); testFoldUnfold(2); testFoldUnfold(-2); testFoldUnfold(Long.MAX_VALUE / 2); testFoldUnfold(Long.MIN_VALUE / 2 + 1); Random r = new Random(1); for (int i = 0; i < 1000; i++) { long x = r.nextLong() & 0xfffffffL; testFoldUnfold(x); testFoldUnfold(-x); } } private static void testFoldUnfold(long x) { assertEquals(x, BitBuffer.unfoldSigned(BitBuffer.foldSigned(x))); } @Test public void testGolombRice() { Random r = new Random(1); for (int i = 0; i < 1000; i++) { BitBuffer buff = new BitBuffer(8 * 1024 * 1024); int shift = r.nextInt(8); int val = r.nextInt(100000); buff.writeGolombRice(shift, val); buff.writeGolombRice(1, 10); int len = buff.position(); assertEquals(len, BitBuffer.getGolombRiceSize(shift, val) + BitBuffer.getGolombRiceSize(1, 10)); buff = new BitBuffer(buff.toByteArray()); int p = buff.position(); assertEquals(val, buff.readGolombRice(shift)); assertEquals(val, buff.readGolombRice(p, shift)); p = buff.position(); assertEquals(10, buff.readGolombRice(1)); assertEquals(10, buff.readGolombRice(p, 1)); } } public static void printPositiveMapping() { StringBuilder b1 = new StringBuilder(); StringBuilder b2 = new StringBuilder(); b1.append(0); b2.append(1); for (int i = 1; i <= 3; i++) { b1.append(", ").append(i); b1.append(", ").append(-i); b2.append(", ").append(BitBuffer.foldSigned(i) + 1); b2.append(", ").append(BitBuffer.foldSigned(-i) + 1); } System.out.println("(" + b1 + ", ...) is mapped to (" + b2 + ", ...)"); } } ================================================ FILE: src/test/java/org/minperf/FunctionInfo.java ================================================ package org.minperf; /** * The information (time and space usage) of a minimum perfect hash function. */ public class FunctionInfo { int size; int split; int firstPartSize; int leafSize; int averageBucketSize; public double bitsPerKey; public double generateNanos; double evaluateNanos; public long hashCalls; @Override public String toString() { String s = "size " + size + " leafSize " + leafSize + " bits/key " + bitsPerKey + " generate " + generateNanos + " evaluate " + evaluateNanos; if (averageBucketSize > 0) { s += " averageBucketSize " + averageBucketSize; } if (split != 0) { s += " split " + (split > 0 ? split : (-split + ":" + (size + split))); } return s; } @Override public int hashCode() { return leafSize ^ averageBucketSize ^ (int) Double.doubleToLongBits(bitsPerKey); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof FunctionInfo)) { return false; } FunctionInfo other = (FunctionInfo) o; if (bitsPerKey != other.bitsPerKey) { return false; } if (leafSize != other.leafSize) { return false; } if (averageBucketSize != other.averageBucketSize) { return false; } return true; } } ================================================ FILE: src/test/java/org/minperf/Graphics.java ================================================ package org.minperf; import java.util.HashMap; import java.util.HashSet; import org.minperf.generator.Generator; import org.minperf.monotoneList.MonotoneList; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * A tool to generate Tikz graphics and a textual description of a hash function. */ public class Graphics { public static void main(String... args) { generateSplitTrees(); // generateSampleTikz(); } public static void generateSplitTrees() { int leafSize = 8; int columns = 5; System.out.print("\\begin{tabular}{"); for (int i = 0; i < columns; i++) { System.out.print("|c"); } System.out.println("|}"); HashMap cache = new HashMap<>(); for (int size = 0; size < 30; size++) { if (size % columns == 0) { System.out.println("\\hline"); } else { System.out.println("&"); } System.out.println(generateSplitTree(leafSize, size)); if (size % columns == columns - 1) { System.out.println("\\\\"); for (int i = 0; i < columns; i++) { if (i > 0) { System.out.print(" & "); } int s = (size - columns + 1 + i); System.out.print(s + " entries"); } System.out.println("\\\\"); int averageBucketSize = 1024; Settings settings = new Settings(leafSize, averageBucketSize); for (int i = 0; i < columns; i++) { if (i > 0) { System.out.print(" & "); } int s = (size - columns + 1 + i); double bits = SpaceEstimator.getExpectedBucketSpace(settings, s, 0, cache); if (s > 0) { bits /= s; } System.out.printf("%2.2f bits/key", bits); } System.out.println("\\\\"); } } } public static String generateSplitTree(int leafSize, int size) { if (size < 1) { return ""; } int averageBucketSize = 1024; Settings settings = new Settings(leafSize, averageBucketSize); StringBuilder buff = new StringBuilder(); buff.append("% size " + size + "\n"); buff.append("\\begin{tikzpicture}\n"); buff.append("\\node {} child[grow cyclic, rotate=-90, sibling angle=30, " + getSizeTikz(size) + ", level distance=7mm] {\n"); buff.append(generateSampleTikz(settings, size) + " }; \n"); buff.append("\\end{tikzpicture}"); return buff.toString(); } public static void generateSampleTikz() { StringBuilder bits = new StringBuilder(); System.out.println("4.4 Data Format"); int leafSize = 6; int averageBucketSize = 32; int size = 70; HashSet set = RandomizedTest.createSet(size, 6); UniversalHash hash = new LongHash(); Settings settings = new Settings(leafSize, averageBucketSize); boolean eliasFano = true; BitBuffer buff = RecSplitBuilder.newInstance(hash). eliasFanoMonotoneLists(eliasFano). leafSize(leafSize).averageBucketSize(averageBucketSize).generate(set); buff.seek(0); System.out.println("\\begin{tikzpicture}"); System.out.println("\\node {}"); System.out.println("child[line width=1ex, level distance=6mm] {"); System.out.println("child[edge from parent=draw, line width=.1ex, sibling distance=30mm, level distance=0mm] {"); bits.append(" & Header \\\\\n"); long size2 = buff.readEliasDelta() - 1; appendLastBits(bits, buff, buff.position()); bits.append(" & size: " + size2 + " (Elias Delta code, plus 1)\\\\\n"); boolean alternativeHashOption = buff.readBit() != 0; appendLastBits(bits, buff, 1); bits.append(" & alternativeHash: " + alternativeHashOption + " (0 false, 1 true)\\\\\n"); int bucketCount = (size + (averageBucketSize - 1)) / averageBucketSize; int start = buff.position(); int minOffsetDiff = (int) (buff.readEliasDelta() - 1); MonotoneList offsetList = MonotoneList.load(buff, eliasFano); appendLastBits(bits, buff, buff.position() - start); bits.append(" & offset list (an EliasFano monotone list)\\\\\n"); start = buff.position(); int minStartDiff = (int) (buff.readEliasDelta() - 1); MonotoneList startList = MonotoneList.load(buff, eliasFano); appendLastBits(bits, buff, buff.position() - start); bits.append(" & start list (an EliasFano monotone list)\\\\\n"); int startBuckets = buff.position(); for (int x = 0; x < bucketCount; x++) { int offset = 0; long offsetPair = offsetList.getPair(x); int o = (int) (offsetPair >>> 32) + x * minOffsetDiff; offset += o; int offsetNext = ((int) offsetPair) + (x + 1) * minOffsetDiff; int bucketSize = offsetNext - o; int startPos = startBuckets + Generator.getMinBitCount(offset) + startList.get(x) + x * minStartDiff; System.out.println(" child["+getSizeTikz(bucketSize)+"] {child[level distance=8mm]{node {$b_" + x + "$}"); System.out.println(" child[grow cyclic, rotate=-90, sibling angle=30, "+ getSizeTikz(bucketSize)+"] {"); buff.seek(startPos); bits.append("\\hline\n"); bits.append(" & Bucket " + x + " \\\\\n"); generateBitDescription(settings, buff, bits, bucketSize); String t = generateSampleTikz(settings, bucketSize); System.out.println(" " + t); System.out.println(" }}}"); } System.out.println(";}};"); System.out.println("\\end{tikzpicture}"); System.out.println(bits.toString()); } private static String generateSampleTikz(Settings settings, int size) { String t = generateSampleTikzWithX(settings, size); for (int i = 0;; i++) { if (t.indexOf("$x$") < 0) { break; } t = t.replaceFirst("\\$x\\$", "\\$_{" + (char) ('a' + i) + "}\\$"); } return t; } private static String generateSampleTikzWithX(Settings settings, int size) { if (size == 0) { return "node {}"; } else if (size == 1) { return "node {} child[sibling angle=5, level distance=5mm, " + getSizeTikz(1) + "]"; } if (size <= settings.getLeafSize()) { StringBuilder buff = new StringBuilder(); buff.append("node {$x$} child[sibling angle=5, level distance=5mm, "+ getSizeTikz(1)+"] foreach \\x in {"); for (int i = 0; i < size; i++) { if (i > 0) { buff.append(", "); } buff.append(i); } buff.append("} "); return buff.toString(); } int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int childSize = firstPart; StringBuilder buff = new StringBuilder(); buff.append("node {$x$} "); int angle = 30; if (size / split <= settings.getLeafSize()) { angle = 15; } for (int i = 0; i < split; i++) { buff.append("child[sibling angle=" + angle + ", "+getSizeTikz(size)+"] { "); buff.append(generateSampleTikzWithX(settings, childSize)); buff.append("} "); childSize = otherPart; } return buff.toString(); } private static String getSizeTikz(int size) { if (size == 1) { return "line width=0.02em"; } return "line width="+(size/35.)+"ex"; } private static void generateBitDescription(Settings settings, BitBuffer in, StringBuilder bits, int size) { if (size <= 1) { return; } if (size <= settings.getLeafSize()) { int shift = settings.getGolombRiceShift(size); long x = in.readGolombRice(shift); int count = BitBuffer.getGolombRiceSize(shift, x); appendLastBits(bits, in, count); bits.append(" & index: " + x + " (leaf of size " + size + ", k=" + shift +") \\\\\n"); return; } int shift = settings.getGolombRiceShift(size); long x = in.readGolombRice(shift); int count = BitBuffer.getGolombRiceSize(shift, x); appendLastBits(bits, in, count); bits.append(" & index: " + x + " (inner node of size " + size + ", k=" + shift +") \\\\\n"); int split = settings.getSplit(size); int firstPart, otherPart; if (split < 0) { firstPart = -split; otherPart = size - firstPart; split = 2; } else { firstPart = size / split; otherPart = firstPart; } int childSize = firstPart; for (int i = 0; i < split; i++) { generateBitDescription(settings, in, bits, childSize); childSize = otherPart; } } private static void appendLastBits(StringBuilder bits, BitBuffer in, int count) { in.seek(in.position() - count); int i = 0; if (count > 15) { for (; i < 5; i++) { long x = in.readBit(); bits.append((char) ('0' + x)); } bits.append("..."); for (; i < count - 5; i++) { in.readBit(); } } for (; i < count; i++) { long x = in.readBit(); bits.append((char) ('0' + x)); } } } ================================================ FILE: src/test/java/org/minperf/LargeSetTest.java ================================================ package org.minperf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.BitSet; import java.util.Random; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; import org.minperf.universal.LongHash; import org.minperf.utils.LargeLongList; import org.minperf.utils.LongSet; /** * Tests with large sets. */ public class LargeSetTest { public static final int MAX_CHUNK_SIZE = 100_000_000; public static void main(String... args) { test(true); test(false); } private static void test(boolean eliasFano) { System.out.println(eliasFano ? "EliasFano" : "Fast"); int leafSize = 8; // int averageBucketSize = 128; // int averageBucketSize = 4096; int averageBucketSize = 1024; System.out.println("leafSize " + leafSize + ", averageBucketSize " + averageBucketSize + ", calcualted " + SpaceEstimator.getExpectedSpace(leafSize, averageBucketSize) + " bits/key"); for (long len = 1_000; len <= 1_000_000_000; len *= 10) { LongSet set = createSet((int) len, 1); LargeLongList list = LargeLongList.create(set); set = null; LongHash hash = new LongHash(); long time = System.nanoTime(); BitBuffer buff = RecSplitBuilder. newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize). eliasFanoMonotoneLists(eliasFano). maxChunkSize(MAX_CHUNK_SIZE). generate(list); time = System.nanoTime() - time; int bitCount = buff.position(); buff.seek(0); double bitsPerKEy = (double) bitCount / len; System.out.println(" (" + len + ", " + bitsPerKEy + ")"); System.out.println("...generated " + (double) time / len + " ns/key"); RecSplitEvaluator eval = RecSplitBuilder. newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize). eliasFanoMonotoneLists(eliasFano). buildEvaluator(buff); BitSet known = new BitSet(); int i = 0; for (long x : list) { int index = eval.evaluate(x); if (index > len || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } if (known.get(index)) { eval.evaluate(x); Assert.fail("duplicate entry: " + x + " " + index); } known.set(index); if ((i++ & 0xffffff) == 0xffffff) { System.out.println("...evaluated " + i); } } list.dispose(); list = null; } } public static LongSet createSet(int size, int seed) { Random r = new Random(seed); LongSet set = new LongSet(size); long i = 0; while (set.size() < size) { set.add(r.nextLong()); if ((i++ & 0xffffff) == 0xffffff) { System.out.println("...createSet size " + set.size() + " of " + size); } } return set; } @Test public void randomSet() { for (int i = 0; i < 100; i++) { Random r = new Random(i); LongSet set = new LongSet(10); TreeSet s2 = new TreeSet(); while (set.size() < 10) { long x = r.nextInt(100); set.add(x); s2.add(x); assertEquals(s2.size(), set.size()); assertEquals(s2.toString(), toString(set)); } } } @Test public void smallSet() { LongSet set = new LongSet(10); assertEquals("[]", toString(set)); assertTrue(set.isEmpty()); assertEquals(0, set.size()); assertTrue(set.add(0L)); assertFalse(set.isEmpty()); assertEquals("[0]", toString(set)); assertEquals(1, set.size()); assertFalse(set.add(0L)); assertEquals("[0]", toString(set)); assertEquals(1, set.size()); assertTrue(set.add(1L)); assertEquals("[0, 1]", toString(set)); assertEquals(2, set.size()); assertFalse(set.add(1L)); assertEquals(2, set.size()); for (int i = 2; i < 10; i++) { assertTrue(set.add(i * 10L)); assertEquals(i + 1, set.size()); } assertEquals("[0, 1, 20, 30, 40, 50, 60, 70, 80, 90]", toString(set)); } private static String toString(LongSet set) { TreeSet s2 = new TreeSet(); for (long x : set) { s2.add(x); } return s2.toString(); } } ================================================ FILE: src/test/java/org/minperf/Paper.java ================================================ package org.minperf; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; /** * A helper class to produce data of the RecSplit paper. */ public class Paper { public static void main(String... args) { // -mx4g, Java 8: minGen 9982.65602 minEval 277.78663 // -mx8g, Java 7: minGen 10098.0 minEval 283.86 double minGen = Double.POSITIVE_INFINITY, minEval = Double.POSITIVE_INFINITY; for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } FunctionInfo info = RandomizedTest.test(10, 256, 100000, true, 3, true); minGen = Math.min(minGen, info.generateNanos); minEval = Math.min(minEval, info.evaluateNanos); System.out.println(info); } System.out.println("minGen " + minGen + " minEval " + minEval); // System.out.println(SpaceEstimator.getExpectedSpace(18, 1024)); // System.out.println(SpaceEstimator.getExpectedSpace(10, 1024)); WikipediaTest.main(); RandomizedTest.printEvaluationAndGenerationTimeVersusSpace(); LargeSetTest.main(); RandomizedTest.experimentalResults(); RandomizedTest.printEvaluationAndGenerationTimeVersusSpace(); LargeSetTest.main(); WikipediaTest.main(); RandomizedTest.experimentalResults(); // TODO improve simple monotone list // B Generation Time Versus Space // RandomizedTest.printEvaluationAndGenerationTimeVersusSpace(); // RandomizedTest.printTimeVersusSpace(); BitCodes.verifyRiceParameterFormula(); // RandomizedTest.printTimeVersusSpace(); // RandomizedTest.printGenerationTimeVersusSpace(); // does not always work (performance depends on the hardware): // 4.1 Parameters RandomizedTest.verifyParameters(); simpleTest(); // 4.3 Split Rule SettingsTest.printSplitRule(); SettingsTest.printSplitRulesList(); // 4.4 Data Format BitCodes.printPositiveMapping(); Graphics.generateSampleTikz(); // 4.7 Probabilities Probability.simulateKeyInOverflow(); Probability.veryLargeBucketProbability(); Probability.asymmetricCase(); // 4.8 Rice BitCodes.printRiceExamples(); BitCodes.printEliasDeltaExample(); // 4.9 Space Usage and Generation Time SpaceEstimator.spaceUsageEstimateSmallSet(); SpaceEstimator.spaceUsageEstimate(); // 5.2 Time and Space Complexity of Evaluation SpaceEstimator.listMaxRecursionDepth(); // 6.1 Reasonable Parameter Values RandomizedTest.reasonableParameterValues(); // 6.2 Using Real-World Data WikipediaTest.main(); // A Evaluation Time SpaceEstimator.listEvalulationTimes(); RandomizedTest.printTimeVersusSpace(); // B Generation Time Versus Space RandomizedTest.printGenerationTimeVersusSpace(); RandomizedTest.printEvaluationAndGenerationTimeVersusSpace(); // slow: LargeSetTest.main(); RandomizedTest.verifyParametersBestSize(); // 6 Experimental Results RandomizedTest.experimentalResults(); for (int averageBucketSize = 64; averageBucketSize < 1024; averageBucketSize *= 2) { SpaceEstimator.getExpectedSpace(8, averageBucketSize); } supplementalHashPerfTest(); } private static void simpleTest() { for (int i = 8; i < 1000; i *= 2) { RandomizedTest.test(2, i, i, true); } for (int i = 100; i < 10000; i *= 2) { RandomizedTest.test(6, 20, i, true); } } private static void supplementalHashPerfTest() { final AtomicLong count = new AtomicLong(); final AtomicLong dummy = new AtomicLong(); for (int i = 0; i < 8; i++) { new Thread() { @Override public void run() { Random r = new Random(); long x = r.nextLong(); int sum = 0; while (count.get() >= 0) { x += r.nextLong(); for (int i = 0; i < 1000; i++) { int h = Settings.supplementalHash(x, i); h = Settings.reduce(h, 30); sum += h; } count.incrementAndGet(); } dummy.set(sum); } }.start(); } try { long c0 = count.get(); Thread.sleep(1000); long c2 = count.get(); count.set(-11111111); System.out.println("count: " + c2 + " " + c0); Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/test/java/org/minperf/PerformanceTest.java ================================================ package org.minperf; import java.util.ArrayList; import java.util.BitSet; import java.util.HashSet; import java.util.Random; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * A simple micro-benchmark. */ public class PerformanceTest { private int size = 100_000; // CHD: 2.25 bits/key, 182 nanoseconds/key // GOV: 2.32 bits/key, 132 nanoseconds/key // RecSplit with FastLongHash: // 1.97 bits/key, 163 nanoseconds/key evaluation time private int leafSize = 11; private int averageBucketSize = 55; // RecSplit with FastLongHash: // 2.32 bits/key, 138 nanoseconds/key evaluation time // private int leafSize = 12; // private int averageBucketSize = 26; private int repeat = 100; public static void main(String... args) { new PerformanceTest().execute(args); } private void execute(String... args) { for (int i = 0; i < args.length; i++) { if ("-size".equals(args[i])) { size = Integer.parseInt(args[++i]); } else if ("-leafSize".equals(args[i])) { leafSize = Integer.parseInt(args[++i]); } else if ("-averageBucketSize".equals(args[i])) { averageBucketSize = Integer.parseInt(args[++i]); } else if ("-repeat".equals(args[i])) { repeat = Integer.parseInt(args[++i]); } else { printUsage(); } } System.out.println("Settings: leafSize=" + leafSize + ", averageBucketSize=" + averageBucketSize + ", size=" + size); for (int i = 0; i < 5; i++) { runMicroBenchmark(false); } for (int i = 0; i < 5; i++) { runMicroBenchmark(true); } } void printUsage() { System.out.println("Usage: java " + getClass().getName() + " [options]"); System.out.println("Options:"); System.out.println("-size the number of randomly generated numbers, default " + size); System.out.println("-leafSize leafSize parameter, default " + leafSize); System.out.println("-averageBucketSize averageBucketSize parameter, default " + averageBucketSize); System.out.println("-repeat repeat count for the evalution benchmark loop, 0 to just verify; default " + repeat); } void runMicroBenchmark(boolean fastHash) { System.out.println(); HashSet set = createSet(size, 1); ArrayList list = new ArrayList(set); UniversalHash hash; if (fastHash) { hash = new FastLongHash(); } else { hash = new LongHash(); } long start = System.nanoTime(); byte[] data = RecSplitBuilder.newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize). generate(set).toByteArray(); long time = System.nanoTime() - start; int bits = data.length * 8; System.out.printf("Generated in %.2f seconds at %.2f bits/key, using %s\n", time / 1_000_000_000., (double) bits / size, hash); CountingHash count = new CountingHash(hash); RecSplitEvaluator eval = RecSplitBuilder. newInstance(count). leafSize(leafSize).averageBucketSize(averageBucketSize). buildEvaluator(new BitBuffer(data)); BitSet bitSet = new BitSet(); for (Long x : set) { int y = eval.evaluate(x); if (y < 0 || y >= size) { throw new AssertionError("y=" + y + " of " + size); } if (bitSet.get(y)) { throw new AssertionError(); } bitSet.set(y); } for (int i = 0; i < 10; i++) { System.gc(); } if (repeat > 0) { eval = RecSplitBuilder. newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize). buildEvaluator(new BitBuffer(data)); start = System.nanoTime(); for (int i = 0; i < repeat; i++) { for (int j = 0; j < size; j++) { Long x = list.get(j); int y = eval.evaluate(x); if (y < 0 || y >= size) { throw new AssertionError("y=" + y + " of " + size); } } } time = System.nanoTime() - start; System.out.printf("Evaluation time: %d nanoseconds/key, " + "universalHash: %f calls/key \n", (int) (((double) time / repeat / size)), (double) count.getCount() / size); } } public static HashSet createSet(int size, int seed) { Random r = new Random(seed); HashSet set = new HashSet(size); while (set.size() < size) { set.add(r.nextLong()); } return set; } /** * A fast long hash implementation. It is not recommended to use this in the * real world, it is just used to test the effect of using a faster hash * function. */ static class FastLongHash implements UniversalHash { @Override public long universalHash(Long key, long index) { return Long.rotateLeft(key, (int) index) ^ (key >>> 16); } @Override public String toString() { return "FastLongHash (rotate&xor)"; } } /** * A fast long hash implementation. It is not recommended to use this in the * real world, it is just used to test the effect of using a faster hash * function. */ static class CountingHash implements UniversalHash { private final UniversalHash base; private long count; CountingHash(UniversalHash base) { this.base = base; } long getCount() { return count; } @Override public long universalHash(K key, long index) { count++; return base.universalHash(key, index); } @Override public String toString() { return "MeasuredHash " + base; } } } ================================================ FILE: src/test/java/org/minperf/Probability.java ================================================ package org.minperf; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Random; import org.minperf.utils.PoissonDistribution; /** * Probability methods. */ public class Probability { private static final HashMap FACTORIALS = new HashMap(); private static final HashMap PROBABILITY_CACHE = new HashMap(); public static void veryLargeBucketProbability() { for (int averageBucketSize = 8; averageBucketSize < 16 * 1024; averageBucketSize *= 2) { for (int multiple = 2; multiple <= 20; multiple *= 10) { double p = probabilityLargeBucket(averageBucketSize, multiple * averageBucketSize); double p2 = probabilityLargeBucket2(averageBucketSize, multiple * averageBucketSize); double simulated = Double.NaN; if (p > 0.000001) { simulated = simulateProbabilityBucketLargerOrEqualTo(averageBucketSize, multiple * averageBucketSize); } System.out.println("averageBucketSize " + averageBucketSize + " p[bucketSize >= " + multiple * averageBucketSize + "] ~ " + p2 + "; <= " + p + "; simulated " + simulated); } } } public static double probabilityLargeBucket2(int averageBucketSize, int atLeast) { double p = 1.0; if (averageBucketSize > 128) { return Double.NaN; } for (int size = 0; size < atLeast; size++) { p -= getProbabilityOfBucketSize(averageBucketSize, size); } return p; } public static void simulateKeyInOverflow() { int size = 1000000; int averageBucketSize = 3; int maxLoad = 5; System.out.println("size " + size); // calculated double pBalls = 0; double pTooLarge = 1; for (int i = 0; i <= maxLoad; i++) { double p = getProbabilityOfBucketSize(averageBucketSize, i); System.out.println("bucket size " + i + ": p=" + p); pBalls += i * p; pTooLarge -= p; } System.out.println("average expected number of balls in regular buckets: " + pBalls); System.out.println("probability of bucket too large: " + pTooLarge); System.out.println("probability of ball in overflow: " + (1. - (pBalls / averageBucketSize))); // simulated Random r = new Random(); int buckets = size / averageBucketSize; int[] counts = new int[buckets]; for (int i = 0; i < size; i++) { counts[r.nextInt(buckets)]++; } int ballsInOverflow = 0; int bucketsOverflow = 0; int totalInRegularBuckets = 0; for (int i = 0; i < buckets; i++) { int c = counts[i]; if (c > maxLoad) { bucketsOverflow++; ballsInOverflow += c; counts[i] = 0; } else { totalInRegularBuckets += c; } } System.out.println("simulated balls in overflow: " + ballsInOverflow); System.out.println("simulated balls in regular buckets: " + totalInRegularBuckets); System.out.println("simulated average expected number of balls in regular buckets: " + (double) totalInRegularBuckets / buckets); System.out.println("simulated probability of bucket too large: " + (double) bucketsOverflow / buckets); System.out.println("simulated probability of ball in overflow: " + (double) ballsInOverflow / size); } private static double simulateProbabilityBucketLargerOrEqualTo(int lambda, int x) { int count = 100000000; Random r = new Random(x); int larger = 0; int testCount = count / 1000; int loop = count / testCount; int bucketCount = loop / lambda; for (int j = 0; j < testCount; j++) { int c = 0; for (int i = 0; i < loop; i++) { if (r.nextInt(bucketCount) == 0) { c++; } } if (c >= x) { larger++; } } return (double) larger / testCount; } public static double probabilityLargeBucket(int lambda, int x) { // Poisson distribution, tail probability return Math.exp(-lambda) * Math.pow(Math.E * lambda, x) / Math.pow(x, x); } public static double getProbabilityOfBucketSize(int averageBucketSize, int bucketSize) { int a = averageBucketSize; int x = bucketSize; return PoissonDistribution.probability(a, x); } public static double getProbabilityOfBucketFallsIntoBinOfSize(int averageBucketSize, int bucketSize) { int a = averageBucketSize; int x = bucketSize; double average = bucketSize * PoissonDistribution.probability(a, x); return average / averageBucketSize; } public static void asymmetricCase() { System.out.println("4.7 Probabilities"); System.out.println("Asymmetric Split"); int size = 64, step = 2; for (int i = 0; i <= size; i += step) { double n = size, k = i; double p = calcCombinations((int) n, (int) k) * Math.pow(k / n, k) * Math.pow(1 - (k / n), n - k); System.out.println(" (" + i + ", " + p + ")"); } } public static void asymmetricSplitProbability() { Random r = new Random(1); for (int size = 20; size < 1000000; size *= 10) { for (int i = 1; i < size / 2; i += Math.max(1, size / 20)) { System.out.println("size " + size + " first " + i + " p approx " + simulateAsymmetricSplitProbability(size, i, r) + " calc " + calcAsymmetricSplitProbability(size, i)); } } } public static double calcExactAsymmetricSplitProbability(int size, int firstSet) { double p; if (size <= 143) { p = Probability.calcAsymmetricSplitProbability(size, firstSet); if (p == 0 || p == 1 || Double.isNaN(p)) { System.out.println("fail at " + size + " split " + firstSet); } } else { p = 0; } if (p == 0 || p == 1 || Double.isNaN(p)) { p = Probability.calcApproxAsymmetricSplitProbability(size, firstSet); } return p; } private static double calcApproxAsymmetricSplitProbability(int size, int firstSet) { // http://math.stackexchange.com/questions/64716/approximating-the-logarithm-of-the-binomial-coefficient int n = size; int k = firstSet; double logComb = (n + .5) * Math.log(n) - (k + .5) * Math.log(k) - (n - k + .5) * Math.log(n - k) - .5 * Math.log(2 * Math.PI); return Math.exp(logComb + Math.log(k) * k - Math.log(n) * n + Math.log(n - k) * (n - k)); } private static double calcAsymmetricSplitProbability(int size, int firstSet) { // http://math.stackexchange.com/questions/951236/probability-of-exactly-2-low-rolls-in-5-throws-of-a-die // (n k) p^k q^(n-k) // p: probability of the event (prob of r.nextInt(size) < firstSet) // q=1−p // n: number of trials (size) // k: the number of times the event occurs during those n trials (firstSet) // Here, the probability that the outcome of a roll is less than 33 is 1/3 int n = size; int k = firstSet; // double p = (double) firstSet / size; // double q = 1 - p; // (n k) = (n!) / ((k! * (n-k)!)) // ((n!) / (k! * (n-k)!)) * ((k/n)^k) * ((1-(k/n))^(n-k)) // (k^k)*(n^-n)*n!*(n-k)^(n-k) / (k!*(n-k)!) // return calcCombinations(n, k) * Math.pow(p, k) * Math.pow(q, n - k); // (k/n)^k * (1-(k/n))^(n - k) = (k^k) / (n^n) * (n-k)^(n-k) return calcCombinations(n, k) * Math.pow(k, k) / Math.pow(n, n) * Math.pow(n - k, n - k); } private static double simulateAsymmetricSplitProbability(int size, int firstSet, Random r) { double good = 0; int trials = Math.max(1, 100000000 / size); for (int j = 0; j < trials; j++) { int count = 0; boolean success = true; for (int i = 0; i < size; i++) { if (r.nextInt(size) < firstSet) { count++; if (count > firstSet) { success = false; break; } } } if (success) { if (count == firstSet) { good++; } } } return good / trials; } public static double calcCombinations(int n, int k) { String key = "comb-" + n + "/" + k; Double cached = PROBABILITY_CACHE.get(key); if (cached != null) { return cached; } BigInteger nf = factorial(n); BigInteger kf = factorial(k); BigInteger nmkf = factorial(n - k); BigInteger u = kf.multiply(nmkf); BigDecimal r = new BigDecimal(nf).divide( new BigDecimal(u), 30, BigDecimal.ROUND_HALF_UP); // System.out.println("nCk n=" + n + " k=" + k + " = " + r.doubleValue()); // approximation: // log(n k) ~ (n log n) - (k log k) - (n-k) log(n-k) double result = r.doubleValue(); PROBABILITY_CACHE.put(key, result); return result; } public static double probabilitySplitIntoMSubsetsOfSizeN(int m, int n) { String key = "split-" + m + "/" + n; Double cached = PROBABILITY_CACHE.get(key); if (cached != null) { return cached; } BigInteger mm = BigInteger.valueOf(m); BigInteger mnf = factorial(n * m); BigInteger nf = factorial(n); BigInteger u = nf.pow(m).multiply(mm.pow(m * n)); BigDecimal r = new BigDecimal(mnf).divide( new BigDecimal(u), 100, BigDecimal.ROUND_HALF_UP); double result = r.doubleValue(); PROBABILITY_CACHE.put(key, result); return result; } static BigInteger factorial(long n) { BigInteger f = FACTORIALS.get(n); if (f == null) { f = recursiveFactorial(1, n); FACTORIALS.put(null, f); } return f; } private static BigInteger recursiveFactorial(long start, long n) { long i; if (n <= 16) { BigInteger r = BigInteger.valueOf(start); for (i = start + 1; i < start + n; i++) { r = r.multiply(BigInteger.valueOf(i)); } return r; } i = n / 2; return recursiveFactorial(start, i).multiply(recursiveFactorial(start + i, n - i)); } public static double probabilityOfDuplicates(long n, int bits) { double x = Math.pow(2, bits); double p = 1.0 - Math.pow(Math.E, -(double) n * n / 2 / x); return p; } } ================================================ FILE: src/test/java/org/minperf/RandomizedTest.java ================================================ package org.minperf; import static org.junit.Assert.assertTrue; import java.lang.ref.WeakReference; import java.sql.Timestamp; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * Methods to test the MPHF with random data. */ public class RandomizedTest { private static final char[] HEX = "0123456789abcdef".toCharArray(); private static final int[] HEX_DECODE = new int['f' + 1]; static { for (int i = 0; i < HEX_DECODE.length; i++) { HEX_DECODE[i] = -1; } for (int i = 0; i <= 9; i++) { HEX_DECODE[i + '0'] = i; } for (int i = 0; i <= 5; i++) { HEX_DECODE[i + 'a'] = HEX_DECODE[i + 'A'] = i + 10; } } public static void main(String... args) { printLargeSet(); } public static void printLargeSet() { for (int i = 10; i <= 100_000_000; i *= 10) { FunctionInfo info = RandomizedTest.test(8, 128, i, false); System.out.println(info); } } public static void printTimeVersusSpace() { System.out.println("A Time Versus Space"); final double evaluateWeight = 20; int size = 100000; System.out.println("size: " + size); ArrayList list = new ArrayList(); outer: for (int leafSize = 2; leafSize <= 12; leafSize++) { int minAverageBucketSize = 4; for (int averageBucketSize = minAverageBucketSize; averageBucketSize <= 1024;) { System.out.println("leafSize " + leafSize + " " + averageBucketSize); FunctionInfo info = test(leafSize, averageBucketSize, size, true); if (info.evaluateNanos >= 10000) { if (averageBucketSize == minAverageBucketSize) { // done break outer; } // next leaf size break; } if (info.bitsPerKey < 4.0) { list.add(info); } if (averageBucketSize < 16) { averageBucketSize += 2; } else if (averageBucketSize < 32) { averageBucketSize += 4; } else { averageBucketSize *= 2; } } } Collections.sort(list, new Comparator() { @Override public int compare(FunctionInfo o1, FunctionInfo o2) { double time1 = o1.evaluateNanos * evaluateWeight + o1.generateNanos; double time2 = o2.evaluateNanos * evaluateWeight + o2.generateNanos; int comp = Double.compare(time1, time2); if (comp == 0) { comp = Double.compare(o1.bitsPerKey, o2.bitsPerKey); } return comp; } }); FunctionInfo last = null; int minAverageBucketSize = Integer.MAX_VALUE, maxAverageBucketSize = 0; int minLeafSize = Integer.MAX_VALUE, maxLeafSize = 0; for (FunctionInfo info : list) { if (last != null && info.bitsPerKey > last.bitsPerKey) { continue; } System.out.println(" (" + info.bitsPerKey + ", " + info.evaluateNanos + ")"); minAverageBucketSize = Math.min(minAverageBucketSize, info.averageBucketSize); maxAverageBucketSize = Math.max(maxAverageBucketSize, info.averageBucketSize); minLeafSize = Math.min(minLeafSize, info.leafSize); maxLeafSize = Math.max(maxLeafSize, info.leafSize); last = info; } System.out.println("for averageBucketSize between " + minAverageBucketSize + " and " + maxAverageBucketSize); System.out.println("and leafSize between " + minLeafSize + " and " + maxLeafSize); last = null; System.out.println("bits/key leafSize averageBucketSize evalTime genTime tableBitsPerKey"); for (FunctionInfo info : list) { if (last != null && info.bitsPerKey > last.bitsPerKey) { continue; } System.out.println(info.bitsPerKey + " " + info.leafSize + " " + info.averageBucketSize + " " + info.evaluateNanos + " " + info.generateNanos); last = info; } } public static void printEvaluationTimeVersusSpaceMedium() { System.out.println("A Evaluation Time Versus Space"); int size = 100000; System.out.println("size: " + size); ArrayList list = new ArrayList(); for (int i = 2; i < 22; i++) { int leafSize = (int) Math.round(0.18 * i + 6.83); int averageBucketSize = (int) Math.round(Math.pow(2, 0.3 * i + 2.79)); // FunctionInfo info = test(leafSize, averageBucketSize, size / 10, true); // System.out.println("leafSize " + leafSize + " " + averageBucketSize + " " + // info.evaluateNanos + " " + info.generateNanos + " " + info.bitsPerKey); } for (int leafSize = 8; leafSize < 14; leafSize++) { System.out.println("leafSize " + leafSize); // int leafSize = (int) Math.round(0.18 * i + 6.83); for (int averageBucketSize : new int[] { 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64 }) { // int averageBucketSize = (int) Math.round(Math.pow(2, 0.3 * i + 2.79)); test(leafSize, averageBucketSize, size, true); FunctionInfo info = test(leafSize, averageBucketSize, size, true); if (info.bitsPerKey < 2.4 && info.evaluateNanos < 250) { System.out.println("leafSize " + leafSize + " averageBucketSize " + averageBucketSize + " " + info.evaluateNanos + " " + info.generateNanos + " " + info.bitsPerKey); list.add(info); break; } } } System.out.println("A Evaluation Time Versus Space"); for (FunctionInfo info : list) { System.out.println(" (" + info.bitsPerKey + ", " + info.evaluateNanos + ")"); } System.out.println("B Generation Time Versus Space"); for (FunctionInfo info : list) { System.out.println(" (" + info.bitsPerKey + ", " + info.generateNanos + ")"); } } public static void printEvaluationAndGenerationTimeVersusSpace() { System.out.println("A Evaluation Time Versus Space"); int size = 200000; System.out.println("size: " + size); ArrayList list = new ArrayList(); double[] data = { // // WikipediaTest, IMPROVED_SPLIT_RULES 2, singlethreaded 5, 20, 2.359670168001244, 1079, 334, 6, 12, 2.4303717869483954, 951, 342, 6, 14, 2.357967341920603, 1155, 322, 6, 16, 2.3323145349758843, 998, 321, 6, 20, 2.2812246357299055, 1025, 334, 7, 16, 2.2096194929015978, 1242, 345, 7, 20, 2.1742915744950557, 1293, 314, 7, 24, 2.1007140296084508, 1355, 309, 8, 10, 2.377269897563485, 1694, 308, 8, 18, 2.131266063511737, 1593, 300, 8, 20, 2.11845541447963, 1650, 308, 8, 24, 2.0638098592566307, 1872, 305, 8, 64, 1.921307580838036, 2155, 363, 8, 96, 1.8674475931743013, 1985, 398, 8, 128, 1.8262873001942272, 2289, 428, 9, 12, 2.2458253510555526, 2827, 295, 9, 14, 2.21441342095181, 2695, 299, 9, 16, 2.1511489750305923, 2648, 299, 9, 18, 2.1036982168296405, 2627, 304, 9, 20, 2.0933374505834883, 2575, 302, 9, 24, 2.046982770533447, 2760, 311, 9, 28, 1.9954129667997726, 3132, 321, 9, 32, 1.9533855063391257, 3608, 305, 9, 64, 1.878018957237164, 3058, 337, 10, 32, 1.9282185286288644, 5108, 298, 10, 64, 1.8208755826407748, 8252, 332, 10, 192, 1.7124357505817613, 10393, 437, 11, 32, 1.909093715388833, 9766, 298, 11, 64, 1.7988263682605121, 17889, 313, 11, 256, 1.6815680892600802, 20773, 422, 12, 64, 1.7663794926201724, 48849, 298, 13, 128, 1.6847065892037447, 104284, 334, 13, 192, 1.657460294686805, 125301, 395, 14, 192, 1.647726743677378, 250957, 386, 14, 256, 1.6321486992942247, 264697, 445, 14, 512, 1.6139085584683803, 262655, 546, // // WikipediaTest, IMPROVED_SPLIT_RULES = true, multithreaded gen // 5, 20, 2.361259077360058, 754, 352, // 6, 12, 2.415688110019585, 514, 331, // 6, 14, 2.3353765518249885, 535, 323, // 6, 16, 2.3029213276778484, 501, 321, // 6, 20, 2.231098315752713, 481, 321, // 7, 16, 2.2096194929015978, 527, 315, // 7, 20, 2.174290497268372, 527, 316, // 7, 24, 2.1007199543552124, 534, 321, // 8, 10, 2.3725640327946853, 772, 312, // 8, 18, 2.130093771572937, 612, 310, // 8, 20, 2.1442086728126264, 622, 316, // 8, 24, 2.063070343138105, 664, 310, // 8, 64, 1.873041900650682, 768, 332, // 8, 96, 1.822206496208785, 772, 357, // 8, 128, 1.7964158042485214, 783, 379, // 9, 12, 2.236310207756295, 903, 308, // 9, 14, 2.1689636113162796, 900, 306, // 9, 16, 2.142868602817803, 887, 307, // 9, 18, 2.0976237355588925, 847, 309, // 9, 20, 2.0890584368882004, 929, 316, // 9, 24, 2.043965727898397, 777, 324, // 9, 28, 1.9915222933240457, 821, 309, // 9, 32, 1.9494118864087353, 912, 305, // 9, 64, 1.8568808073248453, 1067, 329, // 10, 32, 1.9271515355984201, 1319, 316, // 10, 64, 1.821963042978214, 1889, 323, // 10, 192, 1.7192844885315923, 2135, 392, // 11, 32, 1.9121759302382613, 2252, 303, // 11, 64, 1.7827549540561136, 5444, 311, // 11, 256, 1.6774269605803371, 5891, 411, // 12, 64, 1.7675248538918735, 9955, 327, // 13, 128, 1.6836334021198676, 24108, 332, // 13, 192, 1.6562855789879665, 24268, 365, // 14, 192, 1.6426212278088357, 78389, 364, // 14, 256, 1.6305463246018612, 80050, 425, // 14, 512, 1.609989338485529, 82630, 482, // 15, 256, 1.6175950974875, 155998, 403, // 15, 512, 1.5984638208873647, 169226, 493, // 15, 1024, 1.5884895097140768, 176353, 672, // 16, 1024, 1.5794946669031629, 408296, 664, // WikipediaTest, IMPROVED_SPLIT_RULES disabled // 5, 20, 2.2934250358455595, 1257, 334, // 6, 20, 2.2301333899505726, 1177, 338, // 7, 16, 2.2056644551315054, 1459, 316, // 7, 20, 2.1689277935290385, 1625, 325, // 7, 24, 2.097395632808568, 1537, 322, // 8, 10, 2.3722683340699433, 1990, 314, // 8, 18, 2.127560134412306, 2131, 310, // 8, 20, 2.146903893975849, 2270, 310, // 8, 24, 2.0783823125303242, 2244, 315, // 9, 12, 2.228793858569091, 3609, 302, // 9, 14, 2.157263044381975, 3573, 304, // 9, 16, 2.131986997469965, 3813, 307, // 9, 18, 2.088727997602901, 3888, 305, // 9, 20, 2.085344159281969, 4008, 304, // 9, 24, 2.0510659982789283, 4119, 312, // 9, 28, 2.006607237192625, 4283, 311, // 9, 32, 1.9651353563942207, 4473, 314, // 9, 64, 1.8612538090483068, 5012, 335, // 10, 32, 1.9459898065405232, 8674, 310, // 10, 64, 1.7997964108894018, 14669, 317, // 11, 32, 1.921768633858763, 19027, 302, // 11, 64, 1.7817167768394646, 26900, 318, // 12, 64, 1.7734789550806909, 54813, 310, // 13, 128, 1.6908858307695058, 179575, 338, // 13, 192, 1.6529698752547937, 199952, 362, // 14, 192, 1.6437121891329978, 402364, 372, // 14, 256, 1.6302684001174041, 404552, 401, // improved_split_simple // 4, 20, 2.48048, 542.205, 187.983, // 4, 24, 2.42928, 548.34, 207.861, // 4, 28, 2.41148, 541.045, 220.402, // 4, 32, 2.39106, 587.445, 243.149, // 4, 64, 2.29868, 655.455, 327.285, // 4, 96, 2.29236, 786.245, 413.026, // 4, 128, 2.288445, 906.31, 521.701, // 4, 192, 2.291665, 1071.705, 697.729, // 4, 256, 2.338615, 1222.465, 896.842, // 5, 12, 2.481445, 590.775, 169.932, // 5, 14, 2.448975, 571.65, 180.65, // 5, 16, 2.39643, 596.26, 181.333, // 5, 18, 2.36181, 753.025, 186.659, // 5, 20, 2.329635, 609.895, 199.902, // 5, 24, 2.293775, 653.46, 209.456, // 5, 28, 2.27104, 655.55, 214.243, // 5, 32, 2.236775, 650.24, 233.336, // 5, 64, 2.13028, 702.23, 298.263, // 5, 96, 2.09587, 834.61, 369.641, // 5, 128, 2.069325, 848.365, 441.676, // 5, 192, 2.046835, 1037.25, 570.772, // 5, 256, 2.040805, 1006.82, 677.424, // 5, 512, 2.03622, 1288.425, 1194.245, // 6, 12, 2.422205, 679.755, 157.864, // 6, 14, 2.348045, 667.87, 167.241, // 6, 16, 2.32142, 676.895, 169.298, // 6, 18, 2.278715, 669.18, 175.292, // 6, 20, 2.24959, 692.475, 188.489, // 6, 24, 2.22949, 711.53, 204.391, // 6, 28, 2.214285, 970.4, 212.011, // 6, 32, 2.17879, 746.96, 225.202, // 6, 64, 2.08618, 948.195, 296.483, // 6, 96, 2.046315, 841.445, 357.629, // 6, 128, 2.018775, 893.045, 424.116, // 6, 192, 2.009245, 955.695, 542.052, // 6, 256, 1.999575, 1060.675, 625.659, // 6, 512, 1.981275, 1173.515, 1056.154, // 7, 10, 2.40649, 934.015, 134.917, // 7, 12, 2.342685, 963.06, 143.637, // 7, 14, 2.256735, 969.365, 150.691, // 7, 16, 2.199585, 1011.25, 155.369, // 7, 18, 2.153345, 1021.715, 158.227, // 7, 20, 2.144645, 1044.6, 158.149, // 7, 24, 2.07744, 1116.36, 172.383, // 7, 28, 2.058695, 1117.425, 175.712, // 7, 32, 2.041425, 1171.02, 179.791, // 7, 64, 1.9657, 1195.51, 246.424, // 7, 96, 1.88927, 1239.09, 282.475, // 7, 128, 1.865005, 1374.745, 334.15, // 7, 192, 1.84498, 1498.185, 408.65, // 7, 256, 1.837535, 1556.655, 484.289, // 7, 512, 1.81903, 1750.75, 837.628, // 8, 10, 2.37867, 1495.74, 141.054, // 8, 12, 2.27321, 1456.335, 143.75, // 8, 14, 2.22702, 1361.485, 147.132, // 8, 16, 2.169285, 1352.785, 155.101, // 8, 18, 2.121765, 1341.24, 155.674, // 8, 20, 2.08732, 1433.205, 162.626, // 8, 24, 2.04113, 1703.765, 155.698, // 8, 28, 2.01393, 1872.255, 164.095, // 8, 32, 1.980275, 1890.935, 172.217, // 8, 64, 1.90684, 1933.78, 238.195, // 8, 96, 1.86306, 1861.995, 276.053, // 8, 128, 1.820795, 2277.445, 305.552, // 8, 192, 1.808465, 2200.21, 384.153, // 8, 256, 1.796845, 2432.63, 457.88, // 8, 512, 1.772775, 2627.925, 703.463, // 9, 10, 2.342775, 2684.44, 138.739, // 9, 12, 2.23828, 2764.14, 138.49, // 9, 14, 2.201795, 2536.575, 156.542, // 9, 16, 2.14278, 2562.21, 151.358, // 9, 18, 2.093535, 2484.925, 153.452, // 9, 20, 2.064785, 2525.45, 159.702, // 9, 24, 2.022555, 2510.515, 168.457, // 9, 28, 1.99749, 2936.525, 170.246, // 9, 32, 1.950545, 3745.315, 169.586, // 9, 64, 1.85788, 2844.985, 211.073, // 9, 96, 1.82558, 3854.355, 269.473, // 9, 128, 1.78969, 3643.745, 292.384, // 9, 192, 1.75526, 4951.505, 330.786, // 9, 256, 1.75941, 4076.285, 421.23, // 9, 512, 1.74503, 4833.62, 642.042, // 9, 1024, 1.733485, 5085.565, 1089.779, // 10, 10, 2.26647, 5994.385, 127.127, // 10, 12, 2.20895, 6063.425, 133.45, // 10, 14, 2.13613, 5793.905, 142.325, // 10, 16, 2.116405, 5765.98, 149.818, // 10, 18, 2.069445, 5045.955, 156.418, // 10, 20, 2.0417, 5055.235, 157.168, // 10, 24, 2.001495, 4501.335, 164.326, // 10, 28, 1.97065, 4593.175, 166.087, // 10, 32, 1.92606, 5303.8, 162.621, // 10, 64, 1.80934, 8765.025, 191.302, // 10, 96, 1.77682, 9685.165, 239.698, // 10, 128, 1.73736, 10384.885, 255.39, // 10, 192, 1.714485, 10500.3, 298.054, // 10, 256, 1.697815, 10949.625, 340.407, // 10, 512, 1.683975, 11039.485, 556.995, // 10, 1024, 1.674175, 11566.535, 928.485, // 11, 10, 2.25684, 12098.465, 122.634, // 11, 12, 2.196525, 13093.21, 135.831, // 11, 14, 2.12515, 11829.54, 139.618, // 11, 16, 2.103765, 10143.83, 150.215, // 11, 18, 2.050875, 8985.145, 152.343, // 11, 20, 2.02091, 8399.96, 153.928, // 11, 24, 1.961535, 8003.69, 155.773, // 11, 28, 1.95324, 8601.655, 157.764, // 11, 32, 1.907145, 10127.125, 160.339, // 11, 64, 1.789405, 19404.98, 190.851, // 11, 96, 1.754935, 19952.2, 234.153, // 11, 128, 1.717035, 18210.85, 235.3, // 11, 192, 1.69452, 23306.635, 279.228, // 11, 256, 1.68079, 22824.155, 327.211, // 11, 512, 1.669035, 22311.835, 527.925, // 11, 1024, 1.65286, 25076.04, 845.709, // 12, 10, 2.227455, 22643.33, 120.518, // 12, 12, 2.174015, 26268.445, 133.791, // 12, 14, 2.104935, 24317.83, 137.754, // 12, 16, 2.05659, 21180.665, 145.368, // 12, 18, 2.039165, 19377.72, 144.846, // 12, 20, 2.006935, 21047.65, 153.12, // 12, 24, 1.94188, 21689.085, 153.664, // 12, 28, 1.936375, 22849.425, 158.15, // 12, 32, 1.89074, 24265.935, 159.824, // 12, 64, 1.75587, 53140.95, 175.05, // 12, 96, 1.73322, 32236.755, 228.272, // 12, 128, 1.69005, 55498.53, 229.207, // 12, 192, 1.669415, 59977.9, 263.345, // 12, 256, 1.656925, 60746.665, 300.781, // 12, 512, 1.635245, 63767.805, 444.26, // 12, 1024, 1.62973, 63569.64, 764.569, // 13, 10, 2.21143, 48328.62, 115.192, // 13, 12, 2.10655, 70906.805, 124.305, // 13, 14, 2.07424, 80581.4, 134.342, // 13, 16, 2.02719, 80590.855, 140.495, // 13, 18, 1.98329, 72108.925, 144.81, // 13, 20, 1.981005, 68867.005, 147.033, // 13, 24, 1.921855, 66303.785, 153.31, // 13, 28, 1.899895, 59542.925, 158.96, // 13, 32, 1.877085, 49317.545, 159.739, // 13, 64, 1.74228, 98851.095, 174.524, // 13, 96, 1.70931, 101044.625, 213.047, // 13, 128, 1.675095, 113230.025, 228.566, // 13, 192, 1.66094, 129685.525, 270.841, // 13, 256, 1.65179, 109093.085, 317.52, // 13, 512, 1.624375, 144465.47, 445.139, // 13, 1024, 1.61636, 142650.605, 736.177, // 14, 10, 2.2058, 88520.57, 117.954, // 14, 12, 2.10081, 150779.355, 120.79, // 14, 14, 2.06992, 176648.28, 132.509, // 14, 16, 2.02013, 180465.45, 133.61, // 14, 18, 1.980155, 158706.445, 143.226, // 14, 20, 1.977005, 136961.23, 146.619, // 14, 24, 1.91441, 112793.52, 152.261, // 14, 28, 1.891705, 99822.595, 154.993, // 14, 32, 1.867235, 84616.375, 156.02, // 14, 64, 1.741715, 146225.17, 173.922, // 14, 96, 1.734425, 100017.37, 214.876, // 14, 128, 1.67377, 183360.02, 232.579, // 14, 192, 1.65015, 265466.69, 256.558, // 14, 256, 1.63248, 280133.905, 284.565, // 14, 512, 1.6124, 266907.36, 419.022, // 14, 1024, 1.596, 347519.705, 622.838, // 15, 10, 2.195685, 150867.115, 118.481, // 15, 12, 2.07947, 282712.365, 121.882, // 15, 14, 2.01334, 373176.395, 135.476, // 15, 16, 2.000635, 402008.86, 137.389, // 15, 18, 1.963445, 351145.795, 140.495, // 15, 20, 1.965355, 307988.545, 147.11, // 15, 24, 1.905775, 258812.12, 153.401, // 15, 28, 1.880975, 284525.995, 161.962, // 15, 32, 1.857065, 261793.815, 163.351, // 15, 64, 1.728615, 385004.37, 175.091, // 15, 96, 1.67177, 793105.765, 241.753, // 15, 128, 1.658245, 609987.68, 242.138, // 15, 192, 1.632715, 822227.365, 295.025, // 15, 256, 1.62191, 673538.2, 317.271, // 15, 512, 1.594745, 856301.05, 423.374, // 15, 1024, 1.58679, 835599.4, 645.089, // 16, 10, 2.192835, 248874.97, 116.926, // 16, 12, 2.072645, 580146.355, 116.796, // 16, 14, 2.001895, 921183.485, 129.44, // 16, 16, 1.98756, 1098012.84, 139.59, // 16, 18, 1.949805, 1059253.645, 144.09, // 16, 20, 1.928705, 929469.745, 146.691, // 16, 24, 1.898435, 625411.835, 153.217, // 16, 28, 1.872735, 475906.27, 162.709, // 16, 32, 1.834805, 379328.83, 160.678, // 16, 64, 1.724945, 536426.38, 220.324, // 16, 96, 1.663645, 1558653.495, 238.056, // 16, 128, 1.65562, 1353223.825, 243.548, // 16, 192, 1.624235, 1737980.76, 277.142, // 16, 256, 1.612295, 1818499.86, 314.535, // 16, 512, 1.585075, 2093046.415, 387.522, // 16, 1024, 1.57934, 2179367.145, 613.495, // 17, 10, 2.190335, 396320.4, 117.013, // 17, 12, 2.06505, 951679.075, 124.288, // 17, 14, 1.98992, 1505756.795, 129.896, // 17, 16, 1.98164, 1794380.93, 140.186, // 17, 18, 1.944195, 1706422.67, 145.83, // 17, 20, 1.926945, 1351289.8, 151.975, // 17, 24, 1.8967, 900890.055, 153.829, // 17, 28, 1.86655, 913972.885, 165.015, // 17, 32, 1.822715, 1086560.89, 159.539, // 17, 64, 1.717095, 1023016.75, 208.903, // 17, 96, 1.663115, 2264625.19, 225.333, // 17, 128, 1.6467, 1849601.285, 262.772, // 17, 192, 1.631145, 2260000.275, 284.572, // 17, 256, 1.615295, 2569679.075, 324.995, // 17, 512, 1.57983, 4903492.61, 391.586, // 17, 1024, 1.572335, 5224942.645, 589.941, // Elias Fano, Java 7, 200'000, IMPROVED_SPLIT_RULES disabled // 4, 20, 2.470905, 531.61, 197.398, // 4, 24, 2.41963, 568.145, 207.133, // 4, 28, 2.398395, 567.185, 225.119, // 4, 32, 2.37902, 604.32, 239.061, // 4, 64, 2.249965, 642.395, 335.572, // 4, 96, 2.21621, 715.185, 409.494, // 4, 128, 2.194625, 742.52, 488.872, // 4, 192, 2.17983, 804.17, 640.52, // 4, 256, 2.168565, 877.52, 788.277, // 5, 12, 2.458, 602.225, 158.36, // 5, 14, 2.375055, 608.48, 164.219, // 5, 16, 2.34033, 620.12, 166.763, // 5, 18, 2.295295, 630.01, 174.235, // 5, 20, 2.261475, 657.19, 166.937, // 5, 24, 2.232125, 606.675, 181.288, // 5, 28, 2.21222, 623.59, 186.955, // 5, 32, 2.172215, 672.1, 199.667, // 5, 64, 2.064325, 691.135, 260.398, // 5, 96, 2.019225, 770.985, 306.411, // 5, 128, 1.99705, 758.04, 374.651, // 5, 192, 1.98014, 885.875, 450.713, // 5, 256, 1.964155, 883.88, 569.645, // 5, 512, 1.94885, 995.81, 938.224, // 6, 12, 2.41533, 670.18, 144.191, // 6, 14, 2.32433, 754.12, 149.013, // 6, 16, 2.29281, 701.435, 155.742, // 6, 18, 2.236085, 709.57, 158.417, // 6, 20, 2.19864, 750.54, 164.125, // 6, 24, 2.143515, 753.055, 169.618, // 6, 28, 2.14354, 777.41, 173.123, // 6, 32, 2.1053, 756.74, 187.027, // 6, 64, 1.99434, 912.135, 237.705, // 6, 96, 1.9514, 886.66, 281.506, // 6, 128, 1.929665, 960.31, 346.94, // 6, 192, 1.911185, 1037.55, 397.602, // 6, 256, 1.896875, 1092.905, 484.627, // 6, 512, 1.87757, 1294.395, 826.612, // 7, 10, 2.407235, 929.085, 133.486, // 7, 12, 2.34057, 976.235, 140.682, // 7, 14, 2.25626, 1022.26, 146.484, // 7, 16, 2.194895, 1016.405, 155.009, // 7, 18, 2.147625, 1030.625, 155.313, // 7, 20, 2.139175, 1064.085, 158.7, // 7, 24, 2.075395, 1100.655, 158.711, // 7, 28, 2.05703, 1126.74, 169.578, // 7, 32, 2.040645, 1119.86, 176.318, // 7, 64, 1.92282, 1201.13, 224.148, // 7, 96, 1.885075, 1255.05, 268.332, // 7, 128, 1.861525, 1354.2, 303.348, // 7, 192, 1.841595, 1422.635, 373.644, // 7, 256, 1.832, 1457.54, 437.773, // 7, 512, 1.813535, 1659.425, 718.17, // 8, 10, 2.374405, 1524.01, 128.843, // 8, 12, 2.26062, 1619.3, 138.92, // 8, 14, 2.21941, 1682.965, 141.848, // 8, 16, 2.15729, 1702.655, 150.036, // 8, 18, 2.11754, 1745.77, 154.274, // 8, 20, 2.116515, 1790.735, 157.817, // 8, 24, 2.056035, 1875.415, 159.602, // 8, 28, 2.021495, 2049.04, 165.693, // 8, 32, 1.97279, 2253.855, 168.18, // 8, 64, 1.86501, 2519.11, 203.176, // 8, 96, 1.81827, 2678.035, 232.168, // 8, 128, 1.79172, 2743.885, 264.59, // 8, 192, 1.76805, 2862.525, 325.277, // 8, 256, 1.76191, 2964.595, 386.131, // 8, 512, 1.741125, 3186.06, 579.771, // 8, 1024, 1.734195, 3431.95, 992.839, // 9, 10, 2.33308, 2921.67, 126.492, // 9, 12, 2.220135, 3079.505, 131.824, // 9, 14, 2.14388, 3228.985, 135.728, // 9, 16, 2.12304, 3336.66, 143.909, // 9, 18, 2.079285, 3363.255, 144.216, // 9, 20, 2.053395, 3495.825, 149.323, // 9, 24, 2.029165, 3667.125, 151.586, // 9, 28, 2.00448, 3832.785, 162.268, // 9, 32, 1.96081, 4133.775, 166.158, // 9, 64, 1.83966, 4695.99, 194.566, // 9, 96, 1.7966, 4795.8, 225.375, // 9, 128, 1.771115, 5047.68, 251.695, // 9, 192, 1.750705, 5154.9, 305.649, // 9, 256, 1.74, 5394.745, 346.547, // 9, 512, 1.721885, 5566.685, 524.276, // 9, 1024, 1.70978, 5713.12, 908.291, // 10, 10, 2.26205, 5947.835, 123.408, // 10, 12, 2.19927, 6757.7, 130.215, // 10, 14, 2.11739, 6956.82, 133.934, // 10, 16, 2.063445, 7278.955, 136.91, // 10, 18, 2.04491, 7254.605, 138.898, // 10, 20, 2.01783, 7595.075, 148.459, // 10, 24, 1.99413, 8046.975, 145.846, // 10, 28, 1.97355, 8273.41, 152.016, // 10, 32, 1.94444, 8527.48, 161.704, // 10, 64, 1.786515, 14256.225, 226.411, // 10, 96, 1.75497, 14041.155, 258.34, // 10, 128, 1.73047, 14866.12, 270.534, // 10, 192, 1.70212, 16027.155, 306.938, // 10, 256, 1.68637, 16439.44, 344.142, // 10, 512, 1.664455, 17227.03, 498.827, // 10, 1024, 1.652735, 17443.67, 727.342, // 11, 10, 2.252615, 12481.5, 122.729, // 11, 12, 2.1877, 14806.225, 124.975, // 11, 14, 2.1082, 15984.395, 132.277, // 11, 16, 2.0481, 16268.345, 134.219, // 11, 18, 2.033125, 16860.275, 138.096, // 11, 20, 2.000705, 17646.71, 143.937, // 11, 24, 1.953535, 18600.325, 148.093, // 11, 28, 1.94897, 18719.355, 151.751, // 11, 32, 1.92184, 19596.005, 159.312, // 11, 64, 1.769915, 27585.715, 218.831, // 11, 96, 1.747495, 26992.02, 249.497, // 11, 128, 1.71072, 28903.025, 264.075, // 11, 192, 1.68457, 30589.52, 278.156, // 11, 256, 1.670325, 30208.16, 311.294, // 11, 512, 1.65012, 31505.045, 444.507, // 11, 1024, 1.638485, 32038.875, 688.599, // 12, 10, 2.22231, 24705.74, 165.762, // 12, 12, 2.119925, 32694.4, 126.67, // 12, 14, 2.082335, 36846.79, 130.963, // 12, 16, 2.025845, 38237.275, 139.186, // 12, 18, 1.98414, 39442.495, 159.341, // 12, 20, 1.975065, 40671.705, 192.188, // 12, 24, 1.924715, 44007.85, 192.783, // 12, 28, 1.930985, 45227.92, 207.617, // 12, 32, 1.898935, 46104.27, 203.27, // 12, 64, 1.76138, 56904.4, 213.47, // 12, 96, 1.723725, 57794.79, 222.003, // 12, 128, 1.69288, 61194.555, 243.219, // 12, 192, 1.66851, 62373.59, 280.033, // 12, 256, 1.65517, 62835.26, 304.335, // 12, 512, 1.634315, 64540.61, 440.262, // 12, 1024, 1.625505, 65182.255, 652.904, // 13, 10, 2.210815, 46968.995, 121.201, // 13, 12, 2.104695, 70740.235, 131.175, // 13, 14, 2.06871, 85441.665, 148.519, // 13, 16, 2.02016, 94314.83, 182.841, // 13, 18, 1.96975, 92865.16, 182.024, // 13, 20, 1.96359, 95121.82, 184.699, // 13, 24, 1.906365, 102406.62, 158.303, // 13, 28, 1.892015, 107833.15, 197.621, // 13, 32, 1.87882, 109796.23, 219.467, // 13, 64, 1.78156, 128046.31, 217.352, // 13, 96, 1.689325, 192932.635, 201.226, // 13, 128, 1.686015, 177628.955, 214.069, // 13, 192, 1.653795, 199516.91, 245.216, // 13, 256, 1.631885, 208383.39, 285.236, // 13, 512, 1.614825, 213491.69, 392.089, // 13, 1024, 1.60361, 215338.425, 590.956, // 14, 10, 2.20516, 83841.54, 113.454, // 14, 12, 2.09932, 145263.425, 123.554, // 14, 14, 2.066495, 185532.01, 156.049, // 14, 16, 2.01251, 212825.645, 175.047, // 14, 18, 1.97061, 222303.735, 175.905, // 14, 20, 1.93663, 226160.615, 182.177, // 14, 24, 1.897995, 231756.305, 142.045, // 14, 28, 1.88113, 248882.52, 147.99, // 14, 32, 1.867775, 262275.93, 151.812, // 14, 64, 1.77499, 291933.53, 214.264, // 14, 96, 1.691255, 385253.28, 209.619, // 14, 128, 1.67255, 367162.19, 230.257, // 14, 192, 1.64475, 398807.275, 260.316, // 14, 256, 1.63077, 400178.26, 266.605, // 14, 512, 1.609675, 420093.275, 393.8, // 14, 1024, 1.597995, 421539.69, 565.925, // 15, 10, 2.19504, 143709.415, 123.098, // 15, 12, 2.077205, 281957.6, 148.828, // 15, 14, 2.00704, 407922.98, 165.173, // 15, 16, 1.99023, 515948.185, 177.669, // 15, 18, 1.94782, 543192.28, 181.059, // 15, 20, 1.91518, 565792.075, 143.719, // 15, 24, 1.878835, 563904.165, 141.983, // 15, 28, 1.85811, 598985.05, 148.883, // 15, 32, 1.832645, 623808.285, 158.145, // 15, 64, 1.754495, 706206.44, 182.353, // 15, 96, 1.676545, 828029.2, 217.235, // 15, 128, 1.654275, 844462.385, 230.093, // 15, 192, 1.629975, 869058.975, 264.801, // 15, 256, 1.624035, 862970.86, 293.028, // 15, 512, 1.596255, 881522.125, 373.269, // 15, 1024, 1.58612, 920050.94, 537.217, // 16, 10, 2.19248, 231490.225, 148.896, // 16, 12, 2.072425, 550874.24, 129.056, // 16, 14, 1.998785, 914380.8, 126.612, // 16, 16, 1.982985, 1188278.2, 133.524, // 16, 18, 1.942255, 1325198.03, 136.67, // 16, 20, 1.913635, 1401516.895, 140.11, // 16, 24, 1.87398, 1403906.515, 142.839, // 16, 28, 1.84712, 1459527.195, 147.986, // 16, 32, 1.819705, 1566934.57, 154.035, // 16, 64, 1.74261, 1738419.015, 183.85, // 16, 96, 1.702985, 1894356.81, 229.92, // 16, 128, 1.63374, 2955705.28, 219.608, // 16, 192, 1.633705, 2692669.76, 262.579, // 16, 256, 1.605415, 3106663.265, 289.028, // 16, 512, 1.586955, 3130124.865, 360.31, // 16, 1024, 1.573265, 3225744.865, 539.198, // 17, 10, 2.18964, 403617.2, 111.84, // 17, 12, 2.06287, 1058321.445, 116.22, // 17, 14, 1.982145, 1907377.25, 122.586, // 17, 16, 1.93782, 2757154.59, 131.185, // 17, 18, 1.926615, 3331391.5, 134.022, // 17, 20, 1.898345, 3534194.99, 137.826, // 17, 24, 1.84277, 3639365.975, 141.108, // 17, 28, 1.836025, 3619969.58, 144.644, // 17, 32, 1.8045, 3893431.88, 151.667, // 17, 64, 1.737125, 4569860.49, 176.539, // 17, 96, 1.698355, 4780985.505, 217.297, // 17, 128, 1.633505, 5969816.71, 239.5, // 17, 192, 1.624175, 5864469.5, 265.114, // 17, 256, 1.596265, 6203171.485, 285.022, // 17, 512, 1.578285, 6432832.395, 370.582, // 17, 1024, 1.569395, 6439802.705, 541.282, // Elias Fano, Java 7, 200'000, IMPROVED_SPLIT_RULES = true // 4, 20, 2.48048, 512.325, 187.492, // 4, 24, 2.42928, 522.44, 199.791, // 4, 28, 2.41148, 541.0, 203.64, // 4, 32, 2.39106, 548.22, 216.052, // 4, 64, 2.29868, 631.86, 309.16, // 4, 96, 2.29236, 736.03, 405.923, // 4, 128, 2.288445, 835.855, 474.497, // 4, 192, 2.291665, 1029.565, 653.614, // 4, 256, 2.338615, 1150.045, 830.998, // 5, 12, 2.49213, 569.56, 156.05, // 5, 14, 2.45962, 591.465, 159.745, // 5, 16, 2.406045, 592.14, 170.758, // 5, 18, 2.36519, 573.265, 175.087, // 5, 20, 2.3282, 585.93, 181.257, // 5, 24, 2.294605, 629.065, 188.515, // 5, 28, 2.272095, 603.55, 197.512, // 5, 32, 2.241275, 617.39, 209.133, // 5, 64, 2.129795, 676.85, 278.654, // 5, 96, 2.095895, 777.125, 333.663, // 5, 128, 2.0692, 817.78, 396.659, // 5, 192, 2.04735, 862.545, 511.027, // 5, 256, 2.04075, 922.57, 629.603, // 5, 512, 2.03622, 1164.66, 1091.621, // 6, 12, 2.408935, 623.395, 143.725, // 6, 14, 2.32586, 671.455, 149.329, // 6, 16, 2.29345, 688.095, 155.945, // 6, 18, 2.23773, 679.62, 159.455, // 6, 20, 2.20193, 702.43, 159.165, // 6, 24, 2.145385, 762.84, 164.981, // 6, 28, 2.146795, 785.365, 172.633, // 6, 32, 2.109045, 767.41, 183.731, // 6, 64, 1.99843, 861.545, 229.73, // 6, 96, 1.951715, 889.78, 275.149, // 6, 128, 1.93365, 930.85, 316.318, // 6, 192, 1.916535, 1007.91, 401.383, // 6, 256, 1.90533, 1108.97, 494.761, // 6, 512, 1.8973, 1418.46, 844.836, // 7, 10, 2.40649, 910.04, 134.784, // 7, 12, 2.342685, 993.155, 142.055, // 7, 14, 2.256735, 927.335, 146.97, // 7, 16, 2.199585, 973.33, 149.324, // 7, 18, 2.153345, 962.96, 156.355, // 7, 20, 2.144645, 987.305, 157.497, // 7, 24, 2.077455, 1071.125, 162.731, // 7, 28, 2.05867, 1096.12, 169.757, // 7, 32, 2.04204, 1108.09, 177.653, // 7, 64, 1.932225, 1235.985, 218.343, // 7, 96, 1.88419, 1275.755, 259.539, // 7, 128, 1.8626, 1355.63, 300.261, // 7, 192, 1.84437, 1396.935, 373.008, // 7, 256, 1.8383, 1544.06, 442.3, // 7, 512, 1.823355, 1750.82, 740.437, // 8, 10, 2.374485, 1514.07, 129.072, // 8, 12, 2.263715, 1467.805, 135.175, // 8, 14, 2.2227, 1413.265, 139.292, // 8, 16, 2.16154, 1380.64, 145.35, // 8, 18, 2.12091, 1352.48, 148.251, // 8, 20, 2.08644, 1387.02, 148.455, // 8, 24, 2.040295, 1629.39, 152.894, // 8, 28, 2.006125, 1910.88, 160.54, // 8, 32, 1.95964, 2201.905, 159.756, // 8, 64, 1.85237, 2434.215, 199.322, // 8, 96, 1.81621, 2637.71, 227.705, // 8, 128, 1.793235, 2762.435, 271.012, // 8, 192, 1.77067, 2787.885, 318.298, // 8, 256, 1.760585, 2907.465, 376.116, // 8, 512, 1.74589, 3209.165, 603.672, // 8, 1024, 1.742855, 3635.42, 1057.028, // 9, 10, 2.33629, 2814.55, 126.652, // 9, 12, 2.227765, 2857.27, 132.771, // 9, 14, 2.155205, 2838.27, 141.183, // 9, 16, 2.133065, 2743.4, 157.977, // 9, 18, 2.085855, 2598.85, 147.983, // 9, 20, 2.06114, 2421.91, 150.531, // 9, 24, 2.02023, 2145.005, 156.36, // 9, 28, 1.993545, 2461.175, 160.944, // 9, 32, 1.94532, 2972.725, 157.951, // 9, 64, 1.836165, 4199.4, 195.934, // 9, 96, 1.797485, 4508.95, 216.692, // 9, 128, 1.772115, 4760.695, 243.604, // 9, 192, 1.747785, 4931.75, 297.077, // 9, 256, 1.73752, 5074.6, 350.191, // 9, 512, 1.721915, 5358.975, 544.411, // 9, 1024, 1.72051, 5765.0, 946.249, // 10, 10, 2.31985, 5532.26, 127.423, // 10, 12, 2.21901, 5695.73, 132.357, // 10, 14, 2.153965, 5402.715, 150.689, // 10, 16, 2.13306, 5406.525, 144.521, // 10, 18, 2.085745, 5197.74, 150.922, // 10, 20, 2.05117, 5060.285, 149.832, // 10, 24, 2.005075, 4946.91, 153.765, // 10, 28, 1.968035, 4882.67, 159.438, // 10, 32, 1.924895, 5276.94, 158.758, // 10, 64, 1.810935, 8778.07, 192.856, // 10, 96, 1.767075, 9695.245, 207.671, // 10, 128, 1.73814, 9774.565, 233.667, // 10, 192, 1.718085, 9978.115, 279.348, // 10, 256, 1.70646, 10380.25, 325.488, // 10, 512, 1.692565, 10735.725, 517.318, // 10, 1024, 1.68614, 11369.23, 869.157, // 11, 10, 2.26196, 11681.355, 126.062, // 11, 12, 2.210445, 12090.35, 129.264, // 11, 14, 2.15326, 10860.655, 142.715, // 11, 16, 2.138095, 9001.275, 145.573, // 11, 18, 2.091575, 7605.06, 152.51, // 11, 20, 2.056725, 7407.825, 153.707, // 11, 24, 2.00323, 7526.725, 158.037, // 11, 28, 1.957745, 8824.12, 158.663, // 11, 32, 1.909345, 10359.315, 160.264, // 11, 64, 1.772895, 25984.87, 178.063, // 11, 96, 1.731035, 24138.265, 200.68, // 11, 128, 1.713125, 27240.675, 221.134, // 11, 192, 1.69108, 27817.545, 259.051, // 11, 256, 1.67512, 28034.72, 301.68, // 11, 512, 1.655815, 29773.44, 449.153, // 11, 1024, 1.65083, 27722.74, 749.245, // 12, 10, 2.232555, 21684.515, 118.031, // 12, 12, 2.188375, 24816.085, 131.308, // 12, 14, 2.136095, 22334.98, 137.327, // 12, 16, 2.131525, 17667.01, 141.235, // 12, 18, 2.09187, 15266.93, 146.799, // 12, 20, 2.05911, 15722.29, 153.575, // 12, 24, 1.998115, 17725.66, 152.031, // 12, 28, 1.946535, 20558.46, 156.045, // 12, 32, 1.89468, 23382.52, 153.817, // 12, 64, 1.75713, 50011.425, 168.481, // 12, 96, 1.71889, 48595.12, 190.094, // 12, 128, 1.69678, 56721.185, 217.878, // 12, 192, 1.673845, 58555.555, 251.484, // 12, 256, 1.66158, 60103.57, 279.879, // 12, 512, 1.64087, 62022.52, 421.797, // 12, 1024, 1.636115, 62682.005, 697.759, // 13, 10, 2.210815, 46633.88, 114.862, // 13, 12, 2.104695, 69929.665, 116.099, // 13, 14, 2.06868, 85122.835, 128.55, // 13, 16, 2.020125, 93111.61, 131.044, // 13, 18, 1.96981, 91965.28, 136.545, // 13, 20, 1.93844, 91637.88, 135.952, // 13, 24, 1.905935, 85360.755, 140.874, // 13, 28, 1.890695, 71220.105, 150.175, // 13, 32, 1.87284, 62915.33, 153.989, // 13, 64, 1.741925, 95439.845, 167.771, // 13, 96, 1.700325, 117642.56, 183.434, // 13, 128, 1.67455, 118047.255, 207.787, // 13, 192, 1.656405, 124605.6, 244.722, // 13, 256, 1.642845, 130119.215, 276.055, // 13, 512, 1.62088, 133387.9, 415.556, // 13, 1024, 1.61508, 137823.6, 653.189, // 14, 10, 2.20516, 85786.615, 116.196, // 14, 12, 2.09932, 149587.88, 118.289, // 14, 14, 2.06649, 190811.015, 128.583, // 14, 16, 2.012475, 218382.685, 132.342, // 14, 18, 1.970125, 225597.945, 133.783, // 14, 20, 1.93746, 221999.445, 139.745, // 14, 24, 1.89763, 191213.015, 144.212, // 14, 28, 1.879945, 144918.735, 151.997, // 14, 32, 1.86595, 109697.295, 152.652, // 14, 64, 1.737575, 162423.67, 166.307, // 14, 96, 1.68244, 372972.755, 228.849, // 14, 128, 1.667675, 312236.055, 246.35, // 14, 192, 1.643375, 383708.085, 273.699, // 14, 256, 1.627495, 383618.075, 307.331, // 14, 512, 1.612215, 406045.51, 377.666, // 14, 1024, 1.602745, 419758.895, 613.333, // 15, 10, 2.19504, 146619.355, 111.976, // 15, 12, 2.077205, 287076.2, 121.578, // 15, 14, 2.00704, 421559.075, 129.984, // 15, 16, 1.99023, 529179.905, 131.414, // 15, 18, 1.947735, 555597.37, 136.639, // 15, 20, 1.915185, 569832.705, 137.801, // 15, 24, 1.878695, 527880.235, 141.678, // 15, 28, 1.858525, 460067.35, 147.99, // 15, 32, 1.830895, 339825.68, 155.367, // 15, 64, 1.727485, 402200.83, 205.692, // 15, 96, 1.67126, 767329.495, 216.997, // 15, 128, 1.65341, 761072.575, 239.168, // 15, 192, 1.62686, 861076.8, 266.652, // 15, 256, 1.618475, 800519.57, 292.045, // 15, 512, 1.59838, 873461.75, 393.447, // 15, 1024, 1.58921, 900375.855, 591.985, // 16, 10, 2.19248, 237202.64, 157.263, // 16, 12, 2.072425, 566139.105, 125.365, // 16, 14, 1.998785, 934930.44, 131.752, // 16, 16, 1.982985, 1217565.345, 133.588, // 16, 18, 1.942285, 1387565.16, 136.453, // 16, 20, 1.913775, 1438086.85, 141.268, // 16, 24, 1.873775, 1424060.21, 145.843, // 16, 28, 1.846725, 1333494.76, 151.103, // 16, 32, 1.818285, 1126361.91, 157.529, // 16, 64, 1.72333, 576836.96, 210.774, // 16, 96, 1.6634, 1525764.045, 218.326, // 16, 128, 1.641725, 1919115.55, 227.214, // 16, 192, 1.62129, 1815559.185, 232.165, // 16, 256, 1.612415, 1827617.57, 290.803, // 16, 512, 1.59076, 2086348.4, 391.111, // 16, 1024, 1.57934, 2087350.045, 558.129, // 17, 10, 2.18964, 401031.365, 125.065, // 17, 12, 2.06287, 1045783.635, 122.734, // 17, 14, 1.982145, 1891244.035, 127.894, // 17, 16, 1.93782, 2745287.44, 133.509, // 17, 18, 1.926575, 3319014.455, 144.654, // 17, 20, 1.898375, 3520220.58, 141.428, // 17, 24, 1.84248, 3537904.71, 144.53, // 17, 28, 1.836415, 3107551.58, 149.173, // 17, 32, 1.802425, 2472730.47, 152.651, // 17, 64, 1.71758, 1325601.97, 207.873, // 17, 96, 1.65917, 3098177.73, 212.668, // 17, 128, 1.633325, 4726102.23, 215.806, // 17, 192, 1.61545, 4100457.075, 267.917, // 17, 256, 1.603505, 4421849.97, 296.195, // 17, 512, 1.58355, 4990337.785, 349.06, // 17, 1024, 1.57532, 5113673.05, 547.186, }; for (int i = 0; i < data.length; i += 5) { FunctionInfo info = new FunctionInfo(); info.leafSize = (int) data[i]; info.averageBucketSize = (int) data[i + 1]; info.bitsPerKey = data[i + 2]; info.generateNanos = data[i + 3]; info.evaluateNanos = data[i + 4]; list.add(info); } // printTables(list); // int[] pairs = new int[] { 4, 20, 4, 24, 5, 20, 5, 24, 5, 64, 5, 128, 6, 128, 6, 256, // 7, 256, 7, 512, 8, 128, 8, 256, 8, 512, 9, 256, 9, 512, 10, 12, // 10, 256, 10, 512, 10, 1024, 11, 14, 11, 24, 11, 28, 11, 32, 11, // 64, 11, 128, 11, 1024, 12, 32, 12, 64, 12, 128, 12, 512, 12, // 1024, 13, 28, 13, 512, 13, 1024, 14, 28, 14, 128, 14, 256, 14, // 1024, 15, 14, 15, 20, 15, 28, 15, 32, 15, 64, 15, 128, 15, 256, // 15, 512, 16, 16, 16, 20, 16, 28, 16, 32, 16, 64, 16, 128, 16, // 256, 16, 512, }; // for (int i = 0; i < pairs.length; i += 2) { // int leafSize = pairs[i], averageBucketSize = pairs[i + 1]; // double expectedBits = SpaceEstimator.getExpectedSpace(leafSize, // averageBucketSize); // if (expectedBits > 2.6) { // continue; // } // FunctionInfo info = test(leafSize, averageBucketSize, size, true, 5, true); // if (info.bitsPerKey > 2.35) { // continue; // } // if (info.evaluateNanos > 950) { // continue; // } // System.out.println(" " + info.leafSize + ", " + info.averageBucketSize + // ", " + info.bitsPerKey + ", " + info.generateNanos + ", " + // info.evaluateNanos + ","); // list.add(info); // } // for (int leafSize = 4; leafSize <= 17; leafSize++) { // for (int averageBucketSize : new int[] { // 10, 12, 14, 16, 18, 20, 24, 28, 32, // 64, 96, 128, 192, 256, 512, 1024 }) { // double expectedBits = SpaceEstimator.getExpectedSpace(leafSize, averageBucketSize); // if (expectedBits > 3.2) { // continue; // } // FunctionInfo info = test(leafSize, averageBucketSize, size, true, 5, true); // if (info.bitsPerKey > 2.5) { // continue; // } // if (info.evaluateNanos > 1200) { // continue; // } // System.out.println(" " + info.leafSize + ", " + // info.averageBucketSize + ", " + info.bitsPerKey + ", " + // info.generateNanos + ", " + info.evaluateNanos + ","); // list.add(info); // } // } Collections.sort(list, new Comparator() { @Override public int compare(FunctionInfo o1, FunctionInfo o2) { return Double.compare(o1.bitsPerKey, o2.bitsPerKey); } }); FunctionInfo last; ArrayList evaluate = new ArrayList(); last = null; for (FunctionInfo info : list) { if (last == null) { last = info; continue; } if (info.evaluateNanos < last.evaluateNanos) { evaluate.add(info); last = info; } } last = null; ArrayList generate = new ArrayList(); for (FunctionInfo info : list) { if (last == null) { last = info; continue; } if (info.generateNanos < last.generateNanos) { generate.add(info); last = info; } } last = null; ArrayList balanced = new ArrayList(); double bestScore = Double.MAX_VALUE; for (FunctionInfo info : list) { if (info.bitsPerKey > 2.26 && info.generateNanos > 2000) { continue; } if (last == null) { balanced.add(info); last = info; continue; } double score = 500 * info.evaluateNanos + info.generateNanos; if (score > bestScore) { continue; } bestScore = score; if (info.evaluateNanos > last.evaluateNanos * 1.02) { continue; } if (info.generateNanos > last.generateNanos * 1.02) { continue; } last = info; balanced.add(info); } HashSet used = new HashSet(); System.out.println("Balanced: generation time"); for (FunctionInfo info : balanced) { System.out.println(" (" + info.bitsPerKey + ", " + info.generateNanos + ")"); } System.out.println("Balanced: evaluation time"); for (FunctionInfo info : balanced) { used.add(info); System.out.println(" (" + info.bitsPerKey + ", " + info.evaluateNanos + ")"); } // System.out.println("Best Generation: evaluation time"); // for (FunctionInfo info : generate) { // used.add(info); // System.out.println(" (" + info.bitsPerKey + ", " + info.evaluateNanos + ")"); // } // System.out.println("Best Evaluation: evaluation time"); // for (FunctionInfo info : evaluate) { // used.add(info); // System.out.println(" (" + info.bitsPerKey + ", " + info.evaluateNanos + ")"); // } // System.out.println("Best Generation: generation time"); // for (FunctionInfo info : generate) { // System.out.println(" (" + info.bitsPerKey + ", " + info.generateNanos + ")"); // } // System.out.println("Best Evaluation: generation time"); // for (FunctionInfo info : evaluate) { // System.out.println(" (" + info.bitsPerKey + ", " + info.generateNanos + ")"); // } ArrayList usedList = new ArrayList(used); Collections.sort(usedList, new Comparator() { @Override public int compare(FunctionInfo o1, FunctionInfo o2) { int comp = Integer.compare(o1.leafSize, o2.leafSize); if (comp != 0) { return comp; } return Integer.compare(o1.averageBucketSize, o2.averageBucketSize); } }); System.out.println("All used"); for (FunctionInfo info : usedList) { System.out.println(info.leafSize + ", " + info.averageBucketSize + ", "); } } private static void printTables(ArrayList list) { System.out.println("Space"); printTables(list, 0); System.out.println("Generation"); printTables(list, 1); System.out.println("Evaluation"); printTables(list, 2); } private static void printTables(ArrayList list, int type) { System.out.print(" "); for (int averageBucketSize : new int[] { 10, 12, 16, 32, 64, 128, 256, 512, 1024 }) { System.out.print(" & " + averageBucketSize); } System.out.println(" \\\\"); for (int leafSize = 5; leafSize <= 17; leafSize++) { System.out.print(leafSize); for (int averageBucketSize : new int[] { 10, 12, 16, 32, 64, 128, 256, 512, 1024 }) { boolean found = false; System.out.print(" & "); for (FunctionInfo info : list) { if (leafSize != info.leafSize || averageBucketSize != info.averageBucketSize) { continue; } if (type == 0) { System.out.printf("%1.2f", info.bitsPerKey); found = true; } else if (type == 1) { System.out.printf("%1.1f", info.generateNanos / 1000); found = true; } else { System.out.printf("%d", (int) info.evaluateNanos); found = true; } } if (!found) { System.out.print(" "); } } System.out.println(" \\\\"); } } public static void printGenerationTimeVersusSpace() { System.out.println("B Generation Time Versus Space"); int size = 10000; System.out.println("size: " + size); ArrayList list = new ArrayList(); outer: for (int leafSize = 2; leafSize <= 20; leafSize++) { int minAverageBucketSize = 16; for (int averageBucketSize = minAverageBucketSize; averageBucketSize < 8 * 1024; averageBucketSize *= 2) { System.out.println("leafSize " + leafSize + " " + averageBucketSize); FunctionInfo info = test(leafSize, averageBucketSize, size, true); if (info.generateNanos >= 1000000) { if (averageBucketSize == minAverageBucketSize) { // done break outer; } // next leaf size break; } if (info.bitsPerKey < 2.4) { list.add(info); } } } Collections.sort(list, new Comparator() { @Override public int compare(FunctionInfo o1, FunctionInfo o2) { int comp = Double.compare(o1.generateNanos, o2.generateNanos); if (comp == 0) { comp = Double.compare(o1.bitsPerKey, o2.bitsPerKey); } return comp; } }); FunctionInfo last = null; int minAverageBucketSize = Integer.MAX_VALUE, maxAverageBucketSize = 0; int minLeafSize = Integer.MAX_VALUE, maxLeafSize = 0; for (FunctionInfo info : list) { if (last != null && info.bitsPerKey > last.bitsPerKey) { continue; } System.out.println(" (" + info.bitsPerKey + ", " + info.generateNanos + ")"); minAverageBucketSize = Math.min(minAverageBucketSize, info.averageBucketSize); maxAverageBucketSize = Math.max(maxAverageBucketSize, info.averageBucketSize); minLeafSize = Math.min(minLeafSize, info.leafSize); maxLeafSize = Math.max(maxLeafSize, info.leafSize); last = info; } System.out.println("for averageBucketSize between " + minAverageBucketSize + " and " + maxAverageBucketSize); System.out.println("and leafSize between " + minLeafSize + " and " + maxLeafSize); last = null; System.out.println("bits/key leafSize averageBucketSize evalTime genTime"); for (FunctionInfo info : list) { if (last != null && info.bitsPerKey > last.bitsPerKey) { continue; } System.out.println(info.bitsPerKey + " " + info.leafSize + " " + info.averageBucketSize + " " + info.evaluateNanos + " " + info.generateNanos); last = info; } } public static void runTests() { int[] pairs = { 23, 828, 23, 1656, 23, 3312, 23, 6624, 25, 1250, 25, 3750, 25, 7500, 25, 15000 }; for (int i = 0; i < pairs.length; i += 2) { int leafSize = pairs[i], size = pairs[i + 1]; FunctionInfo info = test(leafSize, size, size, true); System.out.println(new Timestamp(System.currentTimeMillis()).toString()); System.out.println(info); } } static void verifyParameters() { System.out.println("4.1 Parameters"); // size 100000 // CHD: generated in 1.52 seconds, 2.257 bits/key, eval 219 nanoseconds/key // GOV: generated in 0.32 seconds, 2.324 bits/key, eval 207 nanoseconds/key // size 1000000 // CHD: // GOV: RandomizedTest.test(8, 1024, 8 * 1024, true); for (int i = 0; i < 5; i++) { if (verifyOneTest()) { return; } RandomizedTest.test(8, 1024, 8 * 1024, true); } Assert.fail(); } static void verifyParametersBestSize() { // System.out.println(RandomizedTest.test(23, 828, 828, true)); System.out.println(RandomizedTest.test(23, 1656, 1656, true)); // System.out.println(RandomizedTest.test(23, 3312, 3312, true)); // System.out.println(RandomizedTest.test(23, 6624, 6624, true)); // System.out.println(RandomizedTest.test(25, 1250, 1250, true)); // System.out.println(RandomizedTest.test(25, 3750, 3750, true)); // System.out.println(RandomizedTest.test(25, 7500, 7500, true)); // System.out.println(RandomizedTest.test(25, 15000, 15000, true)); // size: 1656 leafSize: 23 averageBucketSize: 1656 bitsPerKey: 1.517512077294686 // generateSeconds: 907.279643 evaluateNanosPerKey: 554.3478260869565 // size: 1250 leafSize: 25 averageBucketSize: 1250 bitsPerKey: 1.5112 // generateSeconds: 7416.210937 evaluateNanosPerKey: 312.8 } private static boolean verifyOneTest() { int size = 100_000; int leafSize = 11; int averageBucketSize = 12; for (int j = 0; j < 5; j++) { System.gc(); } System.out.println(" size " + size + " leafSize " + leafSize + " averageBucketSize " + averageBucketSize); FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, size, true); System.out.println(" " + info.bitsPerKey + " bits/key"); System.out.println(" " + info.generateNanos * size / 1_000_000_000 + " seconds to generate"); System.out.println(" " + info.evaluateNanos + " nanoseconds to evaluate"); if (info.bitsPerKey < 2.27 && info.generateNanos * size / 1_000_000_000 < 0.5 && info.evaluateNanos < 250) { // all tests passed return true; } return false; } public static void experimentalResults() { System.out.println("6 Experimental Results"); int size = 1_000_000; System.out.println("size " + size); // experimentalResults(size, 16); // experimentalResults(size, 64); // experimentalResults(size, 128); experimentalResults(size, 1024); // experimentalResults(size, 4096); } static void experimentalResults(int size, int averageBucketSize) { System.out.println("averageBucketSize " + averageBucketSize); System.out.println("leafSize, bits/key"); System.out.println("calculated"); double last = 10; for (int leafSize = 6; leafSize <= 18; leafSize++) { double bitsPerKey = SpaceEstimator.getExpectedSpace(leafSize, averageBucketSize); if (bitsPerKey > last) { System.out.println("% increased"); } last = bitsPerKey; System.out.println(" (" + leafSize + ", " + bitsPerKey + ")"); // System.out.println("size: " + size); } System.out.println("experimental"); for (int leafSize = 6; leafSize <= 18; leafSize++) { FunctionInfo info = test(leafSize, averageBucketSize, size, false); System.out.println(" (" + info.leafSize + ", " + info.bitsPerKey + ")"); } } public static void reasonableParameterValues() { System.out.println("6.1 Reasonable Parameter Values"); int leafSize = 10; int size = 16 * 1024; System.out.println("(leafSize=" + leafSize + ", size=" + size + "): averageBucketSize, generation time in nanos/key"); ArrayList infos = new ArrayList(); for (int averageBucketSize = 8; averageBucketSize <= 16 * 1024; averageBucketSize *= 2) { FunctionInfo info = test(leafSize, averageBucketSize, 16 * 1024, true); infos.add(info); System.out.println(" (" + info.averageBucketSize + ", " + info.generateNanos + ")"); } System.out .println("averageBucketSize, evaluation time in nanos/key"); for (FunctionInfo info : infos) { System.out.println(" (" + info.averageBucketSize + ", " + info.evaluateNanos + ")"); } System.out .println("averageBucketSize, bits/key"); for (FunctionInfo info : infos) { System.out.println(" (" + info.averageBucketSize + ", " + info.bitsPerKey + ")"); } } private static long test(HashSet set, UniversalHash hash, byte[] description, int leafSize, int averageBucketSize, int measureCount) { BitSet known = new BitSet(); RecSplitEvaluator eval = RecSplitBuilder.newInstance(hash).leafSize(leafSize).averageBucketSize(averageBucketSize). buildEvaluator(new BitBuffer(description)); // verify for (T x : set) { int index = eval.evaluate(x); if (index > set.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index + " leafSize " + leafSize + " averageBucketSize " + averageBucketSize + " hash " + convertBytesToHex(description)); } if (known.get(index)) { eval.evaluate(x); Assert.fail("duplicate entry: " + x + " " + index + " leafSize " + leafSize + " averageBucketSize " + averageBucketSize + " hash " + convertBytesToHex(description)); } known.set(index); } known.clear(); known = null; attemptGc(); // measure // Profiler prof = new Profiler().startCollecting(); long best = Long.MAX_VALUE; ArrayList list = new ArrayList(set); for (int i = 0; i < measureCount; i++) { if (list.size() > 100000) { try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } long evaluateNanos = System.nanoTime(); for (int j = 0; j < measureCount; j++) { for (T x : list) { int index = eval.evaluate(x); if (index > list.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index + " leafSize " + leafSize + " averageBucketSize " + averageBucketSize + " hash " + convertBytesToHex(description)); } } } evaluateNanos = System.nanoTime() - evaluateNanos; // System.out.println(" eval " + evaluateNanos / set.size()); best = Math.min(best, evaluateNanos); } // System.out.println(prof.getTop(5)); return best / measureCount; } public static int attemptGc() { AtomicInteger obj = new AtomicInteger(); WeakReference ref = new WeakReference(obj); // some dummy operation int count = obj.getAndIncrement(); obj = null; while (ref.get() != null) { System.gc(); count++; } // System.out.println("count: " + count); return count; } public static FunctionInfo testAndMeasure(int leafSize, int averageBucketSize, int size) { return test(leafSize, averageBucketSize, size, true, 1_000_000_000 / size, false); } public static FunctionInfo test(int leafSize, int averageBucketSize, int size, boolean evaluate) { return test(leafSize, averageBucketSize, size, evaluate, 5, false); } public static FunctionInfo test(int leafSize, int averageBucketSize, int size, boolean evaluate, int measureCount, boolean singleThreadedGeneration) { HashSet set = createSet(size, 1); UniversalHash hash = new LongHash(); long generateNanos = System.nanoTime(); RecSplitBuilder builder = RecSplitBuilder.newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize); if (singleThreadedGeneration) { builder.parallelism(1); } BitBuffer buff; buff = builder.generate(set); int bits = buff.position(); byte[] data = buff.toByteArray(); generateNanos = System.nanoTime() - generateNanos; assertTrue(bits <= data.length * 8); long evaluateNanos = 0; if (evaluate) { if (size > 100000) { // let the CPU cool or something... // if this is not done, the evaluation time is much slower int generateSeconds = (int) (generateNanos / 1000000) / 1000; try { Thread.sleep((5 + generateSeconds) * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } evaluateNanos = test(set, hash, data, leafSize, averageBucketSize, measureCount); } FunctionInfo info = new FunctionInfo(); info.leafSize = leafSize; info.size = size; info.averageBucketSize = averageBucketSize; info.bitsPerKey = (double) bits / size; if (evaluate) { info.evaluateNanos = (double) evaluateNanos / size; } info.generateNanos = (double) generateNanos / size; return info; } public static HashSet createSet(int size, int seed) { Random r = new Random(seed); HashSet set = new HashSet(size); while (set.size() < size) { set.add(r.nextLong()); } return set; } /** * Convert a byte array to a hex encoded string. * * @param value the byte array * @return the hex encoded string */ public static String convertBytesToHex(byte[] value) { int len = value.length; char[] buff = new char[len + len]; char[] hex = HEX; for (int i = 0; i < len; i++) { int c = value[i] & 0xff; buff[i + i] = hex[c >> 4]; buff[i + i + 1] = hex[c & 0xf]; } return new String(buff); } /** * Convert a hex encoded string to a byte array. * * @param s the hex encoded string * @return the byte array */ public static byte[] convertHexToBytes(String s) { int len = s.length(); if (len % 2 != 0) { throw new IllegalArgumentException(s); } len /= 2; byte[] buff = new byte[len]; int[] hex = HEX_DECODE; for (int i = 0; i < len; i++) { int d = hex[s.charAt(i + i)] << 4 | hex[s.charAt(i + i + 1)]; buff[i] = (byte) d; } return buff; } } ================================================ FILE: src/test/java/org/minperf/SettingsTest.java ================================================ package org.minperf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.TreeMap; import java.util.TreeSet; import org.junit.Test; /** * Tests the constants, and generate the constants used in the Settings class. */ public class SettingsTest { /** * Calculate the constants from scratch. This it quite slow. */ public static void main(String... args) { System.out.println("Constants"); printSplit(); printSplitRulesList(); generateSplitRules(); generateRiceLeaf(); generateRiceSplitMore(); generateRiceSplit2(); } public static void printSplitRulesList() { System.out.println("Split Rules Used"); int max = 10 * 1024; TreeSet sizes = new TreeSet<>(); int minLeafSize = 1000; for (int leafSize = 2; leafSize <= 18; leafSize++) { Settings s = new Settings(leafSize, max); for (int i = leafSize + 1; i < max; i++) { int split = s.getSplit(i); if (split > 0) { minLeafSize = Math.min(minLeafSize, leafSize); sizes.add(i); } } } for (int leafSize = minLeafSize; leafSize <= 18; leafSize++) { TreeMap> splitMap = new TreeMap<>(); Settings s = new Settings(leafSize, max); System.out.print("\\item $leafSize$ " + leafSize + ": "); for (int i = leafSize + 1; i < max; i++) { int split = s.getSplit(i); if (split > 0) { ArrayList list = splitMap.get(split); if (list == null) { list = new ArrayList<>(); splitMap.put(split, list); } list.add(i); } } boolean first = true; for (int split : splitMap.keySet()) { ArrayList list = splitMap.get(split); StringBuilder buff = new StringBuilder(); for (int i = 0; i < list.size(); i++) { if (i > 0) { buff.append(",~"); } buff.append(list.get(i)); } if (!first) { System.out.print("; "); } first = false; System.out.print("split~into~" + split + ":~" + buff); } System.out.println(); } if(true) return; for (int x : sizes) { if (x > 50) { continue; } System.out.print(" & " + x); } System.out.println(" \\\\"); for (int leafSize = minLeafSize; leafSize <= 18; leafSize++) { System.out.print(leafSize); for (int x : sizes) { if (x > 50) { continue; } System.out.print(" & "); Settings s = new Settings(leafSize, max); int a = s.getSplit(x); if (a > 0 && x > leafSize) { System.out.print(a); } } System.out.println(" \\\\"); } for (int x : sizes) { if (x <= 50) { continue; } System.out.print(" & " + x); } System.out.println(" \\\\"); for (int leafSize = minLeafSize; leafSize <= 18; leafSize++) { System.out.print(" & " + leafSize); } System.out.println(" \\\\"); for (int x : sizes) { System.out.print(x); for (int leafSize = minLeafSize; leafSize <= 18; leafSize++) { System.out.print(" & "); Settings s = new Settings(leafSize, max); int a = s.getSplit(x); if (a > 0 && x > leafSize) { System.out.print(a); } } System.out.println(" \\\\"); } for (int leafSize = 2; leafSize <= 18; leafSize++) { String[] list = new String[7]; Settings s = new Settings(leafSize, max); for (int i = leafSize + 1; i < max; i++) { int split = s.getSplit(i); if (split > 0) { String x = list[split]; if (x == null) { x = "" + i; } else { x = x + ", " + i; } list[split] = x; } } System.out.print(leafSize); for (int i = 2; i <= 6; i++) { System.out.print(" & " + (list[i] == null ? "" : list[i])); } System.out.println(" \\\\"); } for (int leafSize = 2; leafSize <= 18; leafSize++) { TreeMap> splitMap = new TreeMap<>(); Settings s = new Settings(leafSize, max); System.out.println("\\item With $leafSize$ " + leafSize + ":"); for (int i = leafSize + 1; i < max; i++) { int split = s.getSplit(i); if (split > 0) { ArrayList list = splitMap.get(split); if (list == null) { list = new ArrayList<>(); splitMap.put(split, list); } list.add(i); } } for (int split : splitMap.keySet()) { ArrayList list = splitMap.get(split); StringBuilder buff = new StringBuilder(); for (int i = 0; i < list.size(); i++) { if (i > 0) { buff.append(", "); } buff.append(list.get(i)); } System.out.println("\\subitem split into " + split + ": " + buff); } } } static void generateSplitRules() { System.out.println(" private static final int[][] SPLIT_RULES = {"); for (int leafSize = 0; leafSize <= 32; leafSize++) { if (leafSize > 0) { System.out.println(","); } System.out.println(" // leafSize " + leafSize); System.out.print(" { "); generateSplitRules(leafSize); System.out.print(" }"); } System.out.println(); System.out.println(" };"); } private static void generateSplitRules(int leafSize) { if (leafSize < 2) { return; } int max = 1000; double[] bitsPerKeyList = new double[max]; for (int i = 2; i <= leafSize; i++) { bitsPerKeyList[i] = getBitsPerKey(bitsPerKeyList, i, i); } int[] splitList = new int[max]; int last = -1; splitList[leafSize] = leafSize; double pLeaf = Probability.probabilitySplitIntoMSubsetsOfSizeN( leafSize, 1); int lineLen = 0; boolean first = true; for (int size = leafSize + 1; size < max; size++) { int bestSplit = 0; double bestBits = 0; for (int i = size - 1; i >= leafSize; i--) { if (splitList[i] > 0) { bestSplit = -i; bestBits = getBitsPerKey(bitsPerKeyList, size, -i); // System.out.println(" " + size + " " + -bestSplit + ":" + (size+bestSplit) + " " + bestBits); break; } } if (size < leafSize * 10) { for (int i = -1; i > -leafSize; i--) { double pSplit = getProbabilitySplit(size, i); if (size / pSplit > 2 * leafSize / pLeaf) { // average operations of split should be less than // average operations of leaf processing continue; } double bits = getBitsPerKey(bitsPerKeyList, size, i); if (bits < bestBits) { bestSplit = i; bestBits = bits; } } } for (int split = 2; split < leafSize; split++) { if (size % split != 0) { continue; } double pSplit = Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); if (size / pSplit > 2 * leafSize / pLeaf) { // average operations of split should be less than // average operations of leaf processing continue; } double bits = getBitsPerKey(bitsPerKeyList, size, split); if (bits < bestBits) { if (size < leafSize * 2 || bits < 0.99 * bestBits) { bestSplit = split; bestBits = bits; } } } splitList[size] = bestSplit; bitsPerKeyList[size] = getBitsPerKey(bitsPerKeyList, size, bestSplit); //System.out.println(" " + size + ", " + bestSplit + ", "); // if(bestSplit > 0) { // System.out.println(" " + size + " /" + bestSplit + " " + bitsPerKeyList[size]); // } else { // System.out.println(" " + size + " " + -bestSplit + ":" + (size+bestSplit) + " " + bitsPerKeyList[size]); // } if (bestSplit > 0) { double p = getProbabilitySplit(size, bestSplit); int k = BitCodes.calcBestGolombRiceShift(p); if (!first) { System.out.print(", "); } if (lineLen > 3) { System.out.println(); System.out.print(" "); lineLen = 0; } lineLen++; first = false; System.out.print(size + ", " + bestSplit + ", " + k); } if (bestSplit != last) { last = bestSplit; } } } private static double getBitsPerKey(double[] bitsPerKeyList, int size, int split) { double p = getProbabilitySplit(size, split); double p2 = getSimplifiedProbabilitySplit(size, split); int k = BitCodes.calcBestGolombRiceShift(p2); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size; if (split > 0) { bitsPerKey += bitsPerKeyList[size / split]; } else { int a = -split; int b = size - a; bitsPerKey += bitsPerKeyList[a] * a / size; bitsPerKey += bitsPerKeyList[b] * b / size; } return bitsPerKey; } private static double getSimplifiedProbabilitySplit(int size, int split) { if (split > 0) { return Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); } return Probability.probabilitySplitIntoMSubsetsOfSizeN( 2, size / 2); } private static double getProbabilitySplit(int size, int split) { if (split > 0) { return Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); } return Probability.calcExactAsymmetricSplitProbability(size, -split); } static void printSplit() { for (int leafSize = 8; leafSize < 20; leafSize++) { Settings s = new Settings(leafSize, 8 * 1024); System.out.println("With leafSize " + leafSize + ":"); String lastRule = null; int lastRuleStart = 1; for (int i = 1; i < 8 * 1024; i++) { String rule = null; if (i <= leafSize) { rule = "map directly."; } else { int split = s.getSplit(i); if (split > 0) { rule = "split evenly into " + split + " subsets."; } else { rule = "split into two subsets such that the first has " + -split + " keys."; } } if (lastRule != null && !lastRule.equals(rule)) { String range; if (lastRuleStart == i - 1) { range = "" + lastRuleStart; } else { range = lastRuleStart + " to " + (i - 1); } System.out.println("Sets of size " + range + ": " + lastRule); lastRuleStart = i; } lastRule = rule; } } } public static void printSplitRule() { if (Settings.IMPROVED_SPLIT_RULES) { return; } System.out.println("4.3 Recursion: Split Rule"); int x = 14; int f = x; while (x < 1024) { f = Settings.calcNextSplit(f); System.out.println("size " + x + ", now " + f + " such sets are combined"); x *= f; } x = 6; f = x; while (x < 32) { f = Settings.calcNextSplit(f); System.out.println("size " + x + ", now " + f + " such sets are combined"); x *= f; } } public void verifyRiceLeaf() { for (int i = 2; i < 25; i++) { Settings s = new Settings(i, 65536); double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(i, 1); int k = BitCodes.calcBestGolombRiceShift(p); assertEquals(k, s.getGolombRiceShift(i)); } } //@Test public void verifyRiceSplitMore() { for (int leafSize = 2; leafSize <= 25; leafSize++) { Settings s = new Settings(leafSize, 65536); int split = Settings.calcNextSplit(leafSize); double lastP = Probability.probabilitySplitIntoMSubsetsOfSizeN(leafSize, 1); for (int i = leafSize; i < 64 * 1024;) { i *= split; int m = split, n = i / m; double p = Probability .probabilitySplitIntoMSubsetsOfSizeN(m, n); // System.out.println("leafSize " + leafSize + " size=" + i + // " split " + split + " p=" + p); if (leafSize > 2) { assertTrue(p > lastP); } lastP = p; if (split <= 2) { break; } int k = BitCodes.calcBestGolombRiceShift(p); int k2 = s.getGolombRiceShift(i); assertEquals(k2, k); assertEquals(split, s.getSplit(i)); split = Settings.calcNextSplit(split); } } } @Test public void verifyUniversalHashIndex() { long div = 1 << Settings.SUPPLEMENTAL_HASH_SHIFT; for (long i = div - 100; i <= div + 100; i++) { long index = Settings.getUniversalHashIndex(i); assertEquals("i: " + i, i / div, index); boolean needNew = Settings.needNewUniversalHashIndex(index); assertEquals(index % div == 0, needNew); } } static void generateRiceLeaf() { StringBuilder buff = new StringBuilder(); buff.append("int[] RICE_LEAF = {"); for (int leafSize = 0; leafSize <= 32; leafSize++) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN( leafSize, 1); int k = BitCodes.calcBestGolombRiceShift(p); if (leafSize > 0) { buff.append(", "); } buff.append(k); } System.out.println(buff.append("};")); } private static void generateRiceSplitMore() { StringBuilder buff = new StringBuilder(); buff.append("int[][] RICE_SPLIT_MORE = { "); for (int leafSize = 0; leafSize <= 25; leafSize++) { if (leafSize > 0) { buff.append(", "); } buff.append("{ "); int split = Settings.calcNextSplit(leafSize); for (int i = leafSize; i < 64 * 1024;) { if (split <= 2) { break; } if (i > leafSize) { buff.append(", "); } i *= split; int m = split, n = i / m; double p = Probability .probabilitySplitIntoMSubsetsOfSizeN(m, n); int k = BitCodes.calcBestGolombRiceShift(p); buff.append(k); split = Settings.calcNextSplit(split); // split = split / 2; } buff.append("}"); } System.out.println(buff.append("};")); } private static void generateRiceSplit2() { StringBuilder buff = new StringBuilder(); buff.append("private static final int[] RICE_SPLIT_2 = {0"); for (int testk = 1; testk < 10; testk++) { int expected = (int) Math.pow(10, 0.5862358 * testk - 0.3675082); System.out.println("for " + testk + " search at " + (int) (expected * 0.9) + ".." + (int) (expected * 1.2 + 2)); int border = binarySearchFirstSizeWithRice((int) (expected * 0.9), (int) (expected * 1.2) + 2, testk); int m = 2, n = border; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(m, n); int k = BitCodes.calcBestGolombRiceShift(p); System.out.println(" at " + border + ": " + k); n = border + 1; p = Probability.probabilitySplitIntoMSubsetsOfSizeN(m, n); k = BitCodes.calcBestGolombRiceShift(p); System.out.println(" at " + (border + 1) + ": " + k); buff.append(", "); buff.append((border + 1) * 2); } buff.append("};"); System.out.println(buff); } private static int binarySearchFirstSizeWithRice(int min, int max, int search) { for (int i = 0; i < 30; i++) { int n = (min + max) / 2; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(2, n); int k = BitCodes.calcBestGolombRiceShift(p); if (k < search) { min = n; } else { max = n; } if (min + 1 >= max) { break; } } return (min + max) / 2; } // this test fails!!! // @Test public void testSplit() { Settings s = new Settings(8, 1024); for (int i = 9; i < 200; i++) { int split = s.getSplit(i); int expected; if (i < 32) { expected = -8; } else if (i == 32) { expected = 4; } else if (i < 64) { expected = -32; } else if (i == 64) { expected = 2; } else if (i < 128) { expected = -64; } else if (i == 128) { expected = 2; } else { expected = -128; } assertEquals("i:" + i, expected, split); if (i > 0) { if (split < 0) { split = 2; } // System.out.println(i + " into m subsets: " + split + " of size " + i / split); double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(split, i / split); int riceExp = BitCodes.calcBestGolombRiceShift(p); int rice = s.getGolombRiceShift(i); assertEquals(riceExp, rice); } } } } ================================================ FILE: src/test/java/org/minperf/SpaceEstimator.java ================================================ package org.minperf; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * Methods to estimate the space needed to generate a MPHF. */ public class SpaceEstimator { public static final boolean OPTIMAL_SPLIT_RULE = false; public static final boolean EXACT_ASYMMETRIC_SPLIT = true; private static final HashMap SPLIT_PROBABILITY_CACHE = new HashMap(); public static void main(String... args) { int size = 100000; for(int leafSize = 3; leafSize < 10; leafSize++) { for(int averageBucketSize = 4; averageBucketSize < 4 * 1024; averageBucketSize *= 2) { int testCount = 10; long offsetListSum = 0; long startListSum = 0; long bucketBitsSum = 0; for(int test = 0; test < testCount; test++) { HashSet set = RandomizedTest.createSet(size, size); UniversalHash hash = new LongHash(); RecSplitBuilder builder = RecSplitBuilder.newInstance(hash). leafSize(leafSize).averageBucketSize(averageBucketSize); BitBuffer buff; buff = builder.generate(set); int bits = buff.position(); byte[] description = buff.toByteArray(); RecSplitEvaluator eval = RecSplitBuilder.newInstance(hash).leafSize(leafSize).averageBucketSize(averageBucketSize). buildEvaluator(new BitBuffer(description)); int headerBits = eval.getHeaderSize(); int offsetListSize = eval.getOffsetListSize(); int startListSize = eval.getStartListSize(); int bucketBits = bits - offsetListSize - startListSize - headerBits; offsetListSum += offsetListSize; startListSum += startListSize; bucketBitsSum += bucketBits; } System.out.println(leafSize + " " + averageBucketSize + " " + (double) bucketBitsSum / size / testCount + " " + (double) offsetListSum / size / testCount + " " + (double) startListSum / size / testCount); } } } public static double getExpectedSpaceEstimate(int leafSize, int averageBucketSize) { HashMap cache = new HashMap(); Settings s = new Settings(leafSize, averageBucketSize); double totalBits = 0; for (int i = 0; i <= s.getMaxBucketSize(); i++) { double probBucketSize = Probability.getProbabilityOfBucketSize( averageBucketSize, i); double bits = getExpectedBucketSpace(s, i, 0, cache); totalBits += bits * probBucketSize; } // System.out.println(" probability that an entry is in an overflow bucket: " + pInOverflow); // pInOverflow = Math.max(minP, pInOverflow); // System.out.println(" probability that an entry is in an overflow bucket: " + pInOverflow + " (adjusted)"); double bitsPerBucketStartOverhead = 2 + Math.log(totalBits) / Math.log(2); double bitsPerBucketOffsetOverhead = 2 + Math.log(averageBucketSize) / Math.log(2); // System.out.println("offset list overhead " + bitsPerBucketOffsetOverhead / averageBucketSize); // System.out.println("start list overhead " + bitsPerBucketStartOverhead / averageBucketSize); // System.out.println("lists overhead " + (bitsPerBucketOffsetOverhead + bitsPerBucketStartOverhead) / averageBucketSize); double bitsPerKeyCalc = (totalBits + bitsPerBucketStartOverhead + bitsPerBucketOffsetOverhead) / averageBucketSize; // System.out.println("averageBucketSize " + averageBucketSize + " leafSize " + leafSize + " calc " + bitsPerKeyCalc); // int size = 10000 * averageBucketSize; // FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, size, false); // int sizeBits = BitCodes.getEliasDelta(size).length(); // double bitsPerKeyReal = (info.bitsPerKey * size - sizeBits) / size; // System.out.println(" real " + bitsPerKeyReal); return bitsPerKeyCalc; } public static double getExpectedSpace(int leafSize, int averageBucketSize) { HashMap cache = new HashMap(); return getExpectedSpace(leafSize, averageBucketSize, cache); } public static double getExpectedSpace(int leafSize, int averageBucketSize, HashMap cache) { // System.out.println(" Estimated space for leafSize " + leafSize + " / averageBucketSize " + averageBucketSize); // System.out.println(" Bucket sizes"); Settings s = new Settings(leafSize, averageBucketSize); double totalBits = 0; double inRegularBucket = 0; double worst = 0; // int worstSize = -1; for (int i = 0; i <= s.getMaxBucketSize(); i++) { // System.out.println("size " + i); double probBucketSize = Probability.getProbabilityOfBucketSize( averageBucketSize, i); double bits = getExpectedBucketSpace(s, i, 0, cache); if (bits > 0 && bits / i > worst) { worst = bits / i; // worstSize = i; } inRegularBucket += probBucketSize * i; totalBits += bits * probBucketSize; // if(bits * probBucketSize > 1) // System.out.println(" " + i + " " + bits * probBucketSize); } // System.out.println("worst case space: " + worst + " at size " + worstSize + " max " + s.getMaxBucketSize()); // worst case (disregarding probabilities) // averageBucketSize 1024 leafSize 20 calc 1.5842701617288442 // leaf 20 lf 1024 ********** // totalBits = worst * averageBucketSize; // System.out.println(" total average bits for a bucket: " + totalBits); // System.out.println(" size > than max, p=" + overflow); // System.out.println(" size > than max, p=" + overflow + " (adjusted)"); double pInOverflow = 1.0 - averageBucketSize / inRegularBucket; // System.out.println(" probability that an entry is in an overflow bucket: " + pInOverflow); // pInOverflow = Math.max(minP, pInOverflow); // System.out.println(" probability that an entry is in an overflow bucket: " + pInOverflow + " (adjusted)"); double bitsPerEntryInOverflow = 4.0; totalBits += pInOverflow * bitsPerEntryInOverflow; double bitsPerBucketStartOverhead = 2 + Math.log(totalBits) / Math.log(2); double bitsPerBucketOffsetOverhead = 2 + Math.log(averageBucketSize) / Math.log(2); // System.out.println("offset list overhead " + bitsPerBucketOffsetOverhead / averageBucketSize); // System.out.println("start list overhead " + bitsPerBucketStartOverhead / averageBucketSize); // System.out.println("lists overhead " + (bitsPerBucketOffsetOverhead + bitsPerBucketStartOverhead) / averageBucketSize); double bitsPerKeyCalc = (totalBits + bitsPerBucketStartOverhead + bitsPerBucketOffsetOverhead) / averageBucketSize; // System.out.println("averageBucketSize " + averageBucketSize + " leafSize " + leafSize + " calc " + bitsPerKeyCalc); // int size = 10000 * averageBucketSize; // FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, size, false); // int sizeBits = BitCodes.getEliasDelta(size).length(); // double bitsPerKeyReal = (info.bitsPerKey * size - sizeBits) / size; // System.out.println(" real " + bitsPerKeyReal); return bitsPerKeyCalc; } public static double getExpectedBucketSpace(Settings s, int size, int indent, HashMap cache) { if (size <= 1) { return 0; } Double cached = cache.get(size); if (cached != null) { return cached; } double p; int k; // String spaces = new String(new char[2 + indent * 2]).replace((char) 0, ' '); if (size <= s.getLeafSize()) { p = Probability.probabilitySplitIntoMSubsetsOfSizeN(size, 1); k = BitCodes.calcBestGolombRiceShift(p); double bits = BitCodes.calcAverageRiceGolombBits(k, p); // System.out.println(spaces + "leaf size " + size + " bits " + bits); cache.put(size, bits); return bits; } int split = s.getSplit(size); double result = getExpectedBucketSpace(s, size, indent, cache, split); if (!OPTIMAL_SPLIT_RULE) { cache.put(size, result); return result; } int split2 = split; for (int i = -size / 2; i < 10; i++) { if (i == 0 || i == 1 || i > s.getLeafSize() / 2) { continue; } if (i > 0) { if (split > 0 && i > split) { continue; } if (size / i * i != size) { continue; } } if (i > 0) { double p2 = Probability.probabilitySplitIntoMSubsetsOfSizeN(i, size / i); double p3 = Probability.probabilitySplitIntoMSubsetsOfSizeN(s.getLeafSize(), 1); if (p2 < p3) { // at least more probably than direct mapping continue; } if (s.getLeafSize() / p3 < size / p2) { continue; } } double x = getExpectedBucketSpace(s, size, indent, cache, i); if (x < result) { result = x; split2 = i; } } // if (split != split2 || split > 0) { // double p2 = 0, p3 = 0; // if (split2 > 0) { // p2 = Probability.probabilitySplitIntoMSubsetsOfSizeN(split2, size / split2); // p3 = Probability.probabilitySplitIntoMSubsetsOfSizeN(s.getLeafSize(), 1); // if (p2 < p3) { // // } // System.out.println(size + ", " + split2 + ", "); //// System.out.println(" size " + size + " oldSplit " + split + " newSplit " + split2 + " p2=" + p2 + " p3=" + p3); // } // } // if (split != split2) { if (split2 < 0) { System.out.println(" size " + size + " split " + -split2 + ":" + (size+split2) + "; " + result); } else { System.out.println(" size " + size + " split by " + split2 + "; " + result / size + " *** old:" + split); } // } else { // if (split2 < 0) { // System.out.println(" size " + size + " split " + -split2 + ":" + (size+split2) + "; " + result); // } else { // System.out.println(" size " + size + " split by " + split2 + "; " + result / size); // } // } cache.put(size, result); return result; } public static double getSplitProbability(int size, int split) { String key = "split-" + size + "/" + split; Double cached = SPLIT_PROBABILITY_CACHE.get(key); if (cached != null) { return cached; } double p; if (split < 0) { p = Probability.calcExactAsymmetricSplitProbability(size, -split); } else { p = Probability.probabilitySplitIntoMSubsetsOfSizeN(split, size / split); } SPLIT_PROBABILITY_CACHE.put(key, p); return p; } private static double getExpectedBucketSpace(Settings s, int size, int indent, HashMap cache, int split) { double p = getSplitProbability(size, split); int k = s.getGolombRiceShift(size); // BitCodes.calcBestGolombRiceShift(p); double bits = BitCodes.calcAverageRiceGolombBits(k, p); // System.out.println(spaces + "node size " + size + " split " + split + " bits " + bits); if (split < 0) { bits += getExpectedBucketSpace(s, -split, indent + 1, cache); bits += getExpectedBucketSpace(s, size + split, indent + 1, cache); } else { for (int i = 0; i < split; i++) { bits += getExpectedBucketSpace(s, size / split, indent + 1, cache); } } return bits; } public static void listEvalulationTimes() { System.out.println("4.5 Evaluation times"); int size = 100000; for (int averageBucketSize = 16; averageBucketSize <= 1024; averageBucketSize *= 8) { System.out.println(" \\addplot"); System.out.println(" plot coordinates {"); double minBitsPerKey = 10, maxBitsPerKey = 0; for (int leafSize = 6; leafSize <= 12; leafSize++) { FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, size, true); System.out.println(" (" + leafSize + ", " + info.evaluateNanos + ")"); minBitsPerKey = Math.min(minBitsPerKey, info.bitsPerKey); maxBitsPerKey = Math.max(maxBitsPerKey, info.bitsPerKey); } System.out.println(" };"); System.out.printf(" \\addlegendentry{$averageBucketSize$ %d; from %.2f to %.2f bits/key}\n", averageBucketSize, maxBitsPerKey, minBitsPerKey); } } public static void listMaxRecursionDepth() { System.out.println("5.2 Time and Space Complexity of Evaluation - maximum recursion depth"); int leafSize = 2; for (int averageBucketSize = 8; averageBucketSize < 100000; averageBucketSize *= 2) { int recursionDepth = 1; int size = averageBucketSize * 20; Settings settings = new Settings(leafSize, averageBucketSize); while (size > leafSize) { int split = settings.getSplit(size); if (split < 0) { split = 2; } size /= split; recursionDepth++; } System.out.println("averageBucketSize=" + averageBucketSize + " max recursion depth: " + recursionDepth); } } public static void spaceUsageEstimateSmallSet() { System.out.println("4.9 Space Usage and Generation Time"); for (int leafSize = 8; leafSize <= 14; leafSize++) { long hashesPerKey = TimeEstimator.calcEstimatedHashCallsPerKey(leafSize); double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(leafSize, 1); int k = BitCodes.calcBestGolombRiceShift(p); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / leafSize; System.out.printf(" %d & %d & %.2f \\\\\n", leafSize, hashesPerKey, bitsPerKey); } } public static void spaceUsageEstimate() { System.out.println("4.9 Space Usage and Generation Time"); System.out.println("Reality"); for (int leafSize = 4; leafSize < 14; leafSize++) { FunctionInfo info = RandomizedTest.test(leafSize, 4 * 1024, 4 * 1024, false); System.out.printf(" (%d, %.4f)\n", (long) info.generateNanos * 221, info.bitsPerKey); } } public static void calcGoodAverageBucketSizes() { ArrayList bestPlans = new ArrayList(); int maxLeafSize = 12; for (int averageBucketSize = 16; averageBucketSize <= 4 * 1024; averageBucketSize *= 2) { System.out.println("averageBucketSize " + averageBucketSize); for (int leafSize = 6; leafSize <= maxLeafSize; leafSize++) { FunctionInfo info = new FunctionInfo(); info.leafSize = leafSize; info.averageBucketSize = averageBucketSize; info.bitsPerKey = SpaceEstimator.getExpectedSpace(leafSize, averageBucketSize); if (info.bitsPerKey > 2.4) { continue; } info.generateNanos = TimeEstimator.getExpectedGenerationTime(leafSize, averageBucketSize); boolean add = true; for (int i = 0; i < bestPlans.size(); i++) { FunctionInfo info2 = bestPlans.get(i); if (info.generateNanos < info2.generateNanos && info.bitsPerKey < info2.bitsPerKey) { bestPlans.remove(i); i--; continue; } else if (info2.generateNanos < info.generateNanos && info2.bitsPerKey < info.bitsPerKey) { add = false; break; } } if (add) { bestPlans.add(info); } } } Collections.sort(bestPlans, new Comparator() { @Override public int compare(FunctionInfo o1, FunctionInfo o2) { if (o1.leafSize != o2.leafSize) { return Integer.compare(o1.leafSize, o2.leafSize); } return Integer.compare(o1.averageBucketSize, o2.averageBucketSize); } }); FunctionInfo[][] minMax = new FunctionInfo[maxLeafSize + 1][2]; int leafIndex = 0; for (int i = 0; i < bestPlans.size(); i++) { FunctionInfo info2 = bestPlans.get(i); if (info2.leafSize > leafIndex) { leafIndex = info2.leafSize; } if (minMax[leafIndex][0] == null) { FunctionInfo last = null; if (leafIndex > 0) { last = minMax[leafIndex - 1][0]; } if (last != null && last.averageBucketSize > info2.averageBucketSize) { info2 = null; } minMax[leafIndex][0] = info2; } minMax[leafIndex][1] = info2; } System.out.println("Reasonable Parameter Values"); for (int i = 6; i <= maxLeafSize; i++) { FunctionInfo min = minMax[i][0]; if (min == null) { break; } System.out.printf(" %d & %d & %1.2f \\\\\n", i, min.averageBucketSize, min.bitsPerKey); } } } ================================================ FILE: src/test/java/org/minperf/SplitRuleTest.java ================================================ package org.minperf; import java.util.ArrayList; /** * Generate and test split rules. */ public class SplitRuleTest { public static void main(String... args) { generateSplitRules(); } static void generateSplitRules() { System.out.println(" private static final int[][] SPLIT_RULES = {"); for (int leafSize = 0; leafSize <= 18; leafSize++) { if (leafSize > 0) { System.out.println(","); } System.out.println(" // leafSize " + leafSize); System.out.print(" splitIntegerList(\""); generateSplitRules(leafSize); System.out.print("\")"); } System.out.println(); System.out.println(" };"); } private static void generateSplitRules(int leafSize) { if (leafSize < 2) { return; } int max = 1024; double[] bitsPerKeyList = new double[max]; for (int i = 2; i <= leafSize; i++) { bitsPerKeyList[i] = getBitsPerKey(bitsPerKeyList, i, i); } int[] splitList = new int[max]; splitList[leafSize] = leafSize; boolean first = true; for (int size = leafSize + 1; size < max; size++) { int bestSplit = 0; double bestBits = Double.POSITIVE_INFINITY; for (int i = size - 1; i >= leafSize; i--) { double b = getBitsPerKey(bitsPerKeyList, size, -i); if (b < bestBits) { bestSplit = -i; bestBits = b; } } for (int split = 2; split < leafSize; split++) { if (size % split != 0) { continue; } double bits = getBitsPerKey(bitsPerKeyList, size, split); if (bits < bestBits) { bestSplit = split; bestBits = bits; } } splitList[size] = bestSplit; bitsPerKeyList[size] = getBitsPerKey(bitsPerKeyList, size, bestSplit); double p = getProbabilitySplit(size, bestSplit); int k = BitCodes.calcBestGolombRiceShift(p); if (!first) { System.out.print(", "); } first = false; System.out.print(size + ", " + bestSplit + ", " + k); } } private static double getBitsPerKey(double[] bitsPerKeyList, int size, int split) { double p = getProbabilitySplit(size, split); double p2 = getSimplifiedProbabilitySplit(size, split); int k = BitCodes.calcBestGolombRiceShift(p2); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size; if (split > 0) { bitsPerKey += bitsPerKeyList[size / split]; } else { int a = -split; int b = size - a; bitsPerKey += bitsPerKeyList[a] * a / size; bitsPerKey += bitsPerKeyList[b] * b / size; } return bitsPerKey; } private static double getSimplifiedProbabilitySplit(int size, int split) { if (split > 0) { return Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); } return Probability.probabilitySplitIntoMSubsetsOfSizeN( 2, size / 2); } private static double getProbabilitySplit(int size, int split) { if (split > 0) { return Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); } return Probability.calcExactAsymmetricSplitProbability(size, -split); } private static void calcBest() { int maxSize = 1024; int maxLeaf = 20; ArrayList> list = new ArrayList<>(); for (int i = 0; i < maxSize; i++) { list.add(new ArrayList()); } for (int leafSize = 2; leafSize <= maxLeaf; leafSize++) { FunctionInfo info = new FunctionInfo(); info.leafSize = leafSize; info.size = leafSize; info.evaluateNanos = 1; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN( leafSize, 1); int k = BitCodes.calcBestGolombRiceShift(p); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / leafSize; info.bitsPerKey = bitsPerKey; info.generateNanos = leafSize / p; list.get(leafSize).add(info); } for (int size = 2; size < maxSize; size++) { for (int split = -(size - 1); split < maxLeaf; split++) { if (split == 0 || split == 1 || (split > 0 && size % split != 0)) { continue; } if (split > 0) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN( split, size / split); int k = BitCodes.calcBestGolombRiceShift(p); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size; if (Double.isInfinite(bitsPerKey) || bitsPerKey > 1000) { continue; } ArrayList partList = list.get(size / split); for (FunctionInfo part : partList) { FunctionInfo info = new FunctionInfo(); info.size = size; info.split = split; info.leafSize = part.leafSize; info.evaluateNanos = part.evaluateNanos + 1; info.generateNanos = part.generateNanos + size / p; info.bitsPerKey = part.bitsPerKey + bitsPerKey; addOrReplace(list.get(size), info); } } else { int p1 = -split; int p2 = size + split; if (p1 == p2) { continue; } double p = Probability.calcExactAsymmetricSplitProbability(size, p1); // this is the "wrong" k, as used by the implementation int k = Settings.calcRiceParamSplitByTwo(size); double bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size; if (Double.isInfinite(bitsPerKey)) { continue; } ArrayList part1List = list.get(p1); ArrayList part2List = list.get(p2); for (FunctionInfo part1 : part1List) { for (FunctionInfo part2 : part2List) { FunctionInfo info = new FunctionInfo(); info.size = size; info.split = split; info.leafSize = Math.max(part1.leafSize, part2.leafSize); info.evaluateNanos = (p1 * part1.evaluateNanos + p2 * part2.evaluateNanos) / size + 1; info.generateNanos = (p1 * part1.generateNanos + p2 * part2.generateNanos) / size + size / p; info.bitsPerKey = (p1 * part1.bitsPerKey + p2 * part2.bitsPerKey) / size + bitsPerKey; addOrReplace(list.get(size), info); } } } } ArrayList l = list.get(size); System.out.println("size " + size); double bestBits = 10; for (FunctionInfo info : l) { bestBits = Math.min(bestBits, info.bitsPerKey); if (info.leafSize == 10 && info.split > 0) { System.out.println(" " + info); } if (Double.isInfinite(info.bitsPerKey)) { return; } if (info.bitsPerKey > 1000) { return; } } if (bestBits < 1.5 && size > 1024) { break; } } } static void addOrReplace(ArrayList list, FunctionInfo info) { boolean add = list.size() == 0; for (int i = 0; i < list.size(); i++) { FunctionInfo old = list.get(i); if (old.bitsPerKey <= info.bitsPerKey && old.generateNanos <= info.generateNanos && old.evaluateNanos <= info.evaluateNanos) { return; } if (old.bitsPerKey >= info.bitsPerKey && old.generateNanos >= info.generateNanos && old.evaluateNanos >= info.evaluateNanos) { add = true; list.remove(i); i--; continue; } if (info.bitsPerKey < old.bitsPerKey || info.generateNanos < old.generateNanos || info.evaluateNanos < old.evaluateNanos) { add = true; } if (info.leafSize == old.leafSize) { // for the same leaf size, favor faster evaluation if (info.evaluateNanos <= old.evaluateNanos) { add = true; list.remove(i); i--; continue; } return; } } if (add) { list.add(info); } } } ================================================ FILE: src/test/java/org/minperf/SplitRuleTest2.java ================================================ package org.minperf; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; /** * Generate and test split rules. */ public class SplitRuleTest2 { static final int MAX_SIZE = 2 * 1024; static final int MAX_LEAF_SIZE = 17; // map of size, split HashMap> bestSplits = new HashMap>(); int leafSize; double baseTime; double scaleFactor = 1.1; public static void main(String... args) { generateSplitRules(); } static void generateSplitRules() { String[] result; result = TimeEstimator.test(); System.out.println("current: " + Arrays.toString(result)); for(double sf = 0.09; sf <= 0.4; sf += 0.04) { SplitRuleTest2 s = new SplitRuleTest2(); s.scaleFactor = sf; int[][] splitRules = new int[MAX_LEAF_SIZE + 1][]; for(int leafSize = 0; leafSize <= MAX_LEAF_SIZE; leafSize++) { if (leafSize < 2) { splitRules[leafSize] = new int[0]; } else { s.bestSplits.clear(); s.generate(leafSize); splitRules[leafSize] = s.getBest(); } } Settings.SPLIT_RULES = splitRules; result = TimeEstimator.test(); System.out.println(sf + " " + Arrays.toString(result)); } } int[] getBest() { Split[] splits = new Split[MAX_SIZE + 1]; for (int i = MAX_SIZE - 1; i > 0; i--) { if (splits[i] != null) { continue; } ArrayList list = bestSplits.get(i); Split best = null; for (Split s : list) { if (best == null) { best = s; } else { if (s.getTimePerKey() < best.getTimePerKey()) { best = s; } } } splits[i] = best; if (best.left != null) { if (splits[best.left.size] == null) { splits[best.left.size] = best.left; } } if (best.right != null) { if (splits[best.right.size] == null) { splits[best.right.size] = best.right; } } } int[] splitRules = new int[MAX_SIZE * 3]; // System.out.println(" // leafSize " + leafSize); // System.out.print(" splitIntegerList(\""); for (int i = 1, j = 0; i < MAX_SIZE; i++) { Split s = splits[i]; // if (i > 1) { // System.out.print(","); // } // System.out.print(i + "," + s.splitBy + "," + s.k); splitRules[j++] = i; splitRules[j++] = s.splitBy; splitRules[j++] = s.k; } return splitRules; // System.out.println("\"),"); // for (int i = 1; i < MAX_SIZE; i++) { // Split s = splits[i]; // System.out.println(i + ": " + s.splitBy + " " + s.bitsPerKey + " " + s.getTimePerKey()); // } } void generate(int leafSize) { this.leafSize = leafSize; for (int i = 0; i <= leafSize; i++) { Split s = new Split(); s.size = i; s.splitBy = i; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(i, 1); double averageTries = 1 / p; s.generatePerKey = i * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / i; merge(s); if (i == leafSize) { baseTime = s.getTimePerKey(); } } for (int size = 2; size < MAX_SIZE; size++) { for (int split = 2; split < size; split++) { if (size % split != 0) { continue; } for(Split s1 : bestSplits.get(size / split)) { Split s = new Split(); s.size = size; s.splitBy = split; s.left = s1; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(split, size / split); double averageTries = 1 / p; s.generatePerKey = size * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = s1.bitsPerKey + BitCodes.calcAverageRiceGolombBits(k, p) / size; merge(s); } } for (int first = 1; first <= size / 2; first++) { for(Split s1 : bestSplits.get(first)) { for(Split s2 : bestSplits.get(size - first)) { Split s = new Split(); s.size = size; s.left = s1; s.right = s2; s.splitBy = -first; double p = Probability.calcExactAsymmetricSplitProbability(size, first); double averageTries = 1 / p; s.generatePerKey = size * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size + (first * s1.bitsPerKey + (size - first) * s2.bitsPerKey) / size; merge(s); } } } } } void merge(Split s) { ArrayList list = bestSplits.get(s.size); if (list == null) { list = new ArrayList(); list.add(s); bestSplits.put(s.size, list); return; } double t = s.getTimePerKey(); for (int i = 0; i < list.size(); i++) { Split s2 = list.get(i); if (s2.getTimePerKey() < t && s2.bitsPerKey < s.bitsPerKey) { return; } else if (s2.getTimePerKey() > t && s2.bitsPerKey > s.bitsPerKey) { list.remove(i); i--; } } if (list.isEmpty()) { list.add(s); return; } double maxTime = (leafSize * leafSize / s.size * baseTime) + (baseTime / leafSize * s.size * scaleFactor); if (list.size() > 0 && s.getTimePerKey() > maxTime) { return; } list.add(s); int maxSize = 2; if (list.size() > maxSize) { Collections.sort(list, new Comparator() { @Override public int compare(Split o1, Split o2) { return Double.compare(o1.bitsPerKey, o2.bitsPerKey); // return Double.compare(o1.getTimePerKey(), o2.getTimePerKey()); } }); while(list.size() > maxSize) { list.remove(list.size() - 1); } } } static class Split { int size; int k; int splitBy; double generatePerKey; double bitsPerKey; int evaluatePerKey; Split left, right; double time; int getFirstSize() { return splitBy < 0 ? -splitBy : size / splitBy; } int getOtherSize() { return splitBy < 0 ? size + splitBy : size / splitBy; } public String toString() { return "s:" + size + " bits:" + bitsPerKey + " split:" + splitBy + " time:" + getTimePerKey(); } double getTimePerKey() { if (time != 0.0) { return time; } double result = generatePerKey; if (right != null) { result += (getFirstSize() * left.getTimePerKey() + getOtherSize() * right.getTimePerKey()) / size; } else { if (left != null) { result += left.getTimePerKey(); } } time = result; return result; } } } ================================================ FILE: src/test/java/org/minperf/SplitRuleTest3.java ================================================ package org.minperf; import java.util.Arrays; import java.util.HashMap; /** * Generate and test split rules. */ public class SplitRuleTest3 { static final int MAX_SIZE = 2 * 1024; static final int MAX_LEAF_SIZE = 18; // map of size, split HashMap bestSplits = new HashMap(); int leafSize; double baseTime; double scaleFactor = 1.1; public static void main(String... args) { generateSplitRules(); } static void generateSplitRules() { String[] result; for(double sf = 1.0; sf <= 1.0; sf += 0.1) { SplitRuleTest3 s = new SplitRuleTest3(); s.scaleFactor = sf; int[][] splitRules = new int[MAX_LEAF_SIZE + 1][]; for(int leafSize = 0; leafSize <= MAX_LEAF_SIZE; leafSize++) { if (leafSize < 2) { splitRules[leafSize] = new int[0]; } else { s.bestSplits.clear(); s.generate(leafSize); splitRules[leafSize] = s.getBest(); } } Settings.SPLIT_RULES = splitRules; result = TimeEstimator.test(); System.out.println(sf + " " + Arrays.toString(result)); } System.out.println(" private static final int[][] SPLIT_RULES = {"); for (int leafSize = 0; leafSize <= 18; leafSize++) { if (leafSize > 0) { System.out.println(","); } System.out.println(" // leafSize " + leafSize); System.out.print(" splitIntegerList(\""); int[] rules = Settings.SPLIT_RULES[leafSize]; for (int i = 0; i < rules.length; i++) { if (i > 0) { System.out.print(","); } System.out.print(rules[i]); } System.out.print("\")"); } System.out.println(); System.out.println(" };"); } int[] getBest() { Split[] splits = new Split[MAX_SIZE + 1]; for (int i = MAX_SIZE - 1; i > 0; i--) { if (splits[i] != null) { continue; } Split best = bestSplits.get(i); if (best == null) { break; } splits[i] = best; if (best.left != null) { if (splits[best.left.size] == null) { splits[best.left.size] = best.left; } } if (best.right != null) { if (splits[best.right.size] == null) { splits[best.right.size] = best.right; } } } int[] splitRules = new int[MAX_SIZE * 3]; // System.out.println(" // leafSize " + leafSize); // System.out.print(" splitIntegerList(\""); for (int i = 1, j = 0; i < MAX_SIZE; i++) { Split s = splits[i]; if (s == null) { break; } // if (i > 1) { // System.out.print(","); // } // System.out.print(i + "," + s.splitBy + "," + s.k); splitRules[j++] = i; splitRules[j++] = s.splitBy; splitRules[j++] = s.k; } return splitRules; // System.out.println("\"),"); // for (int i = 1; i < MAX_SIZE; i++) { // Split s = splits[i]; // System.out.println(i + ": " + s.splitBy + " " + s.bitsPerKey + " " + s.getTimePerKey()); // } } void generate(int leafSize) { this.leafSize = leafSize; baseTime = Double.POSITIVE_INFINITY; for (int i = 0; i <= leafSize; i++) { Split s = new Split(); s.size = i; s.splitBy = i; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(i, 1); double averageTries = 1 / p; s.generatePerKey = i * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / i; merge(s); if (i == leafSize) { baseTime = s.getTimePerKey(); // System.out.println("leafSize " + leafSize + " base " + baseTime); } } for (int size = 2; size < MAX_SIZE; size++) { // System.out.println("size " + size); for (int split = 2; split < size; split++) { if (size % split != 0) { continue; } Split s1 = bestSplits.get(size / split); Split s = new Split(); s.size = size; s.splitBy = split; s.left = s1; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(split, size / split); double averageTries = 1 / p; s.generatePerKey = size * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = s1.bitsPerKey + BitCodes.calcAverageRiceGolombBits(k, p) / size; merge(s); } for (int first = 1; first <= size / 2; first++) { Split s1 = bestSplits.get(first); Split s2 = bestSplits.get(size - first); Split s = new Split(); s.size = size; s.left = s1; s.right = s2; s.splitBy = -first; double p = Probability.calcExactAsymmetricSplitProbability(size, first); double averageTries = 1 / p; s.generatePerKey = size * averageTries; int k = BitCodes.calcBestGolombRiceShift(p); s.k = k; s.bitsPerKey = BitCodes.calcAverageRiceGolombBits(k, p) / size + (first * s1.bitsPerKey + (size - first) * s2.bitsPerKey) / size; merge(s); } Split s = bestSplits.get(size); if (s == null) { System.out.println(" leafSize " + leafSize + " end at size " + (size - 1)); break; } if (s.splitBy > 2 && s.size > leafSize) { System.out.println(" leafSize " + leafSize + " best " + s + " base " + baseTime); } } } boolean merge(Split s) { Split old = bestSplits.get(s.size); // power rule, best scaleFactor = 0.96 // double limit = Math.pow(baseTime / leafSize * s.size, scaleFactor); // linear rule, bestScaleFactor is about 1 +/- 0.5 double limit = baseTime / leafSize * s.size * scaleFactor; // System.out.println(" " + s.size + " budget " + limit + " base " + baseTime + " now " + s.getTimePerKey() + " " + s); if (s.getTimePerKey() > limit) { return false; } if (old != null && old.bitsPerKey < s.bitsPerKey) { return true; } bestSplits.put(s.size, s); return true; } static class Split { int size; int k; int splitBy; double generatePerKey; double bitsPerKey; int evaluatePerKey; Split left, right; double time; int getFirstSize() { return splitBy < 0 ? -splitBy : size / splitBy; } int getOtherSize() { return splitBy < 0 ? size + splitBy : size / splitBy; } public String toString() { return "s:" + size + " bits:" + bitsPerKey + " split:" + splitBy + " time:" + getTimePerKey(); } double getTimePerKey() { if (time != 0.0) { return time; } double result = generatePerKey; if (right != null) { result += (getFirstSize() * left.getTimePerKey() + getOtherSize() * right.getTimePerKey()) / size; } else { if (left != null) { result += left.getTimePerKey(); } } time = result; return result; } } } ================================================ FILE: src/test/java/org/minperf/TestSplitStrategy.java ================================================ package org.minperf; import java.util.Arrays; import java.util.Locale; public class TestSplitStrategy { Split[] splitList; public static int reduce(int hash, int n) { return (int) (((hash & 0xffffffffL) * n) >>> 32); } public static void main(String... args) { new TestSplitStrategy().test(); } public void test() { int leafSize = 8; int averageBucketSize = 256; System.out.println("Using the default split strategy ================"); showSplitStrategy(leafSize, averageBucketSize, false, OptimizeTarget.SPACE); System.out.println(splitList[averageBucketSize - 1]); System.out.println(); System.out.println("Using the \"best\" split strategy for everything (pareto) ================"); showSplitStrategy(leafSize, averageBucketSize, true, OptimizeTarget.PARETO); System.out.println(splitList[averageBucketSize - 1]); // System.out.println("Using the \"best\" split strategy for space ================"); // showSplitStrategy(leafSize, averageBucketSize, true, OptimizeTarget.SPACE); // System.out.println(splitList[averageBucketSize - 1]); // System.out.println("Using the \"best\" split strategy for generation time ================"); // showSplitStrategy(leafSize, averageBucketSize, true, OptimizeTarget.GENERATION_TIME); // System.out.println(splitList[averageBucketSize - 1]); // System.out.println("Using the \"best\" split strategy for evaluation time ================"); // showSplitStrategy(leafSize, averageBucketSize, true, OptimizeTarget.EVALUATION_TIME); // System.out.println(splitList[averageBucketSize - 1]); System.out.println(); } private void showSplitStrategy(int leafSize, int maxBucketSize, boolean useBest, OptimizeTarget target) { splitList = new Split[maxBucketSize]; for (int m = 0; m < maxBucketSize; m++) { int[] parts; double p; if (m <= 1) { Split s = new Split(); s.parts = new int[0]; s.evaluation = 0; s.generation = 0; s.size = m; s.spaceUsage = 0; splitList[m] = s; continue; } else if (m <= leafSize) { p = Probability.probabilitySplitIntoMSubsetsOfSizeN(m, 1); parts = new int[0]; } else { parts = getParts(m, leafSize); p = approximateProbability(parts); } //System.out.println("size " + m + " parts " + Arrays.toString(parts)); Split s = getSplit(m, p, parts); // System.out.println("split " + s); if (m > leafSize) { for(int partCount = 3; partCount < 10 && partCount < m; partCount++) { if (m % partCount == 0) { parts = new int[partCount]; Arrays.fill(parts, m / partCount); p = Probability.probabilitySplitIntoMSubsetsOfSizeN(partCount, m / partCount); Split alt = getSplit(m, p, parts); if (useBest && target.isBetter(alt, s)) { System.out.println("alt is better, s=" + s + "\nalt=" + alt + "\n"); s = alt; } } else { parts = new int[partCount]; Arrays.fill(parts, (m + partCount - 1) / partCount); parts[partCount - 1] = m - (partCount - 1) * parts[0]; if (parts[partCount - 1] < 0) { continue; } int sum = 0; for(int pp: parts) { sum += pp; } if (sum != m || parts[0] < parts[partCount - 1]) { throw new AssertionError(); } p = approximateProbability(parts); Split alt = getSplit(m, p, parts); if (useBest && target.isBetter(alt, s)) { System.out.println("alt is better, s=" + s + "\nalt=" + alt + "\n"); s = alt; } } } for(int firstPart = m / 2; firstPart < m; firstPart++) { parts = new int[] {firstPart, m - firstPart}; p = Probability.calcExactAsymmetricSplitProbability(m, parts[0]); Split alt = getSplit(m, p, parts); if (useBest && target.isBetter(alt, s)) { System.out.println("alt is better, s=" + s + "\nalt=" + alt + "\n"); s = alt; } } } splitList[m] = s; } } enum OptimizeTarget { PARETO { @Override boolean isBetter(Split alt, Split old) { return alt.spaceUsage < old.spaceUsage && alt.generation < old.generation && alt.evaluation < old.evaluation; } }, SPACE { @Override boolean isBetter(Split alt, Split old) { return alt.spaceUsage < old.spaceUsage && alt.generation < old.generation; } }, SPACE2 { @Override boolean isBetter(Split alt, Split old) { return alt.spaceUsage < old.spaceUsage; } }, EVALUATION_TIME { @Override boolean isBetter(Split alt, Split old) { return alt.evaluation < old.evaluation; } }, GENERATION_TIME { @Override boolean isBetter(Split alt, Split old) { return alt.generation < old.generation; } }; abstract boolean isBetter(Split alt, Split old); } private Split getSplit(int m, double p, int[] parts) { int k = BitCodes.calcBestGolombRiceShift(p); double bits = BitCodes.calcAverageRiceGolombBits(k, p); Split s = new Split(); s.parts = parts; s.size = m; double eval = 1; double space = bits; double gen = m / p; for (int i = 0; i < parts.length; i++) { int part = parts[i]; Split sub = splitList[part]; eval += 1. * part / m * sub.evaluation; space += sub.spaceUsage; gen += sub.generation; } s.evaluation = eval; s.spaceUsage = space; s.generation = gen; return s; } private static int[] getParts1(int m, int leafSize, int fanout) { int[] parts = new int[fanout]; if (m <= leafSize) { parts[0] = m; return parts; } int part, partlim, unit; parts[fanout - 1] = m; int l = (int) (m + leafSize - 1) / leafSize; int bij_threshold = (int) Math.ceil(leafSize / 3. + 1. / 2); if (l > bij_threshold) { int bsplit = bij_threshold * leafSize; int p = (int) (m + bsplit - 1) / bsplit; part = p / fanout; partlim = p % fanout; unit = bsplit; } else { part = l / fanout; partlim = l % fanout; unit = leafSize; } for (int i = 0; i < fanout - 1; ++i) { int plus = (fanout - i <= partlim) ? 1 : 0; parts[i] = (part + plus) * unit; parts[fanout - 1] -= parts[i]; } return parts; } private static int[] getParts(int m, int leafSize) { if (m <= leafSize) { throw new AssertionError(); } int level1PartCount = Math.max(2, (int) Math.ceil(leafSize / 3. + 1. / 2)); int level1Size = level1PartCount * leafSize; if (m <= level1Size) { int parts[] = new int[(m + leafSize - 1) / leafSize]; for (int i = 0; i < parts.length - 1; i++) { parts[i] = leafSize; } parts[parts.length - 1] = m - leafSize * (parts.length - 1); return parts; } int level2PartCount = Math.max(2, (int) Math.ceil(leafSize / 4. + 1. / 3)); int level2Size = level2PartCount * level1Size; if (m <= level2Size) { int parts[] = new int[(m + level1Size - 1) / level1Size]; for (int i = 0; i < parts.length - 1; i++) { parts[i] = level1Size; } parts[parts.length - 1] = m - level1Size * (parts.length - 1); return parts; } int parts[] = new int[2]; // TODO m / 2 ? parts[0] = (int) Math.ceil((m / 2.) / level2Size) * level2Size; parts[1] = m - parts[0]; return parts; } public static void main2(String... args) { for (int size = 2; size < 300; size += 2) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(2, size / 2); int[] parts = new int[] { size / 2, size / 2 }; double p2 = approximateProbability(parts); System.out.println(size + " " + p + " " + p2 + " diff " + (100. / p * p2 - 100) + " " + " " + Arrays.toString(parts)); } for (int size = 3; size < 300; size += 3) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(3, size / 3); int[] parts = new int[] { size / 3, size / 3, size / 3 }; double p2 = approximateProbability(parts); System.out.println(size + " " + p + " " + p2 + " diff " + (100. / p * p2 - 100) + " " + " " + Arrays.toString(parts)); } for(int size = 20; size < 300; size += 10) { for(int first = 1; first < size / 2; first++) { double p = Probability.calcExactAsymmetricSplitProbability(size, first); int[] parts = new int[] {first, size - first}; double p2 = approximateProbability(parts); System.out.println(size + " " + p + " " + p2 + " diff " + (100. / p * p2 - 100) + " " + " " + Arrays.toString(parts)); } } } // probability to find a function that splits a set into the given parts static double approximateProbability(int[] parts) { double x = 1; int m = 0; for(int part : parts) { x *= part; m += part; } int s = parts.length; if (m < 1000) { double result = 1 / Math.pow(m, m); for (int i = 0; i < s; i++) { int sum = 0; for (int j = i; j < s; j++) { sum += parts[j]; } double c = Probability.calcCombinations(sum, parts[i]); result *= c; } for (int i = 0; i < s; i++) { double p = Math.pow(parts[i], parts[i]); result *= p; } double r2 = Math.sqrt(m / Math.pow(2 * Math.PI, s - 1) / x); if (result == 0 || Double.isNaN(result)) { // System.out.println("approx " + r2 + " exact? " + result); return r2; } return result; } return Math.sqrt(m / Math.pow(2 * Math.PI, s - 1) / x); } /* Random r = new Random(1); for (int size = 2; size < 1024; size++) { // System.out.println("size " + size); for (int fanout = 2; fanout < 8; fanout++) { if (size < fanout) { continue; } int minFirstPart = (size + fanout - 1) / fanout; for (int firstPart = minFirstPart; firstPart * (fanout - 1) < size; firstPart++) { int incorrect = 0; int trials = 1; long invDivMulSize = ((((long) size << 50) / firstPart) + 1) >>> 22; long[] counts = new long[fanout]; // for(long i = 0; i < 0x100000000L; i++) { //// int cf_idx = (int) (((i & 0xffffffffL) * invDivMulSize) >>> 60); // // long hmod = reduce((int) i, size); // int cf_idx = (int) hmod / firstPart; // counts[cf_idx]++; // } // double[] prob = new double[fanout]; // for(int i=0; i>> 60); int zero = (int) ((((min - 0) & 0xffffffffL) * invDivMulSize) >>> 60); int bb = (int) ((((min + 1) & 0xffffffffL) * invDivMulSize) >>> 60); if (aa == bb) { System.out.println("no boundary: " + min + " aa " + aa + " bb " + bb); } if (zero == aa) { min++; } prob[i - 1] = (double) (min - last) / (1L << 32); last = min; // if (aa != a) { // System.out.println("unexpected: " + aa + " expected: " + a); // } // if (bb != b) { // System.out.println("unexpected: " + bb + " expected: " + b); // } } if (100000000 * (prob[0] - prob2[0]) > 1) { System.out.println("expected " + Arrays.toString(prob2) + " got " + Arrays.toString(prob)); } for (int i = 0; i < trials; i++) { long x = r.nextLong(); long hmod = reduce((int) x, size); int cf_idx = (int) hmod / firstPart; // long invDiv = (0x100000000L / firstPart) + 1; // long invDivMulSize = (invDiv * size) >>> 6; // int cf_idx2 = (int) (((x & 0xffffffffL) * size / firstPart) >>> 32); // int cf_idx2 = (int) (((((x & 0xffffffffL) * size) >>> 32) * invDiv) >>> 32); int cf_idx2 = (int) (((x & 0xffffffffL) * invDivMulSize) >>> 60); if (cf_idx2 != cf_idx) { incorrect++; System.out.println(" size " + size + " fanout " + fanout + " firstPart " + firstPart + " expected " + cf_idx + " got " + cf_idx2); } if (cf_idx2 < 0 || cf_idx2 >= fanout) { throw new AssertionError(); } } if (incorrect > 0) { System.out.println("size " + size + " fanout " + fanout + " firstPart " + firstPart + " incorrect: " + incorrect + " of " + trials); } } } } */ static class Split { int size; int[] parts; double evaluation; double generation; double spaceUsage; public String toString() { return String.format(Locale.ENGLISH, "size %d eval %3.2f gen %3.2f space %3.2f (bits/key: %3.2f) parts %s", size, evaluation, generation, spaceUsage, spaceUsage / size, Arrays.toString(parts)); } } } ================================================ FILE: src/test/java/org/minperf/TextFileTest.java ================================================ package org.minperf; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import org.minperf.utils.Text; /** * Process a text file. */ public class TextFileTest { private int leafSize = 8; private int averageBucketSize = 14; private String textFile; private String hashFile; private String indexFile; private String outputFile; public static void main(String... args) throws IOException { new TextFileTest().execute(args); } private void execute(String... args) throws IOException { int threadCount = Runtime.getRuntime().availableProcessors(); int repeat = 1; if (args.length == 0) { printUsage(threadCount); } for (int i = 0; i < args.length; i++) { if ("-leafSize".equals(args[i])) { leafSize = Integer.parseInt(args[++i]); } else if ("-averageBucketSize".equals(args[i])) { averageBucketSize = Integer.parseInt(args[++i]); } else if ("-textFile".equals(args[i])) { textFile = args[++i]; } else if ("-hashFile".equals(args[i])) { hashFile = args[++i]; } else if ("-indexFile".equals(args[i])) { indexFile = args[++i]; outputFile = args[++i]; } else if ("-threadCount".equals(args[i])) { threadCount = Integer.parseInt(args[++i]); } else if ("-repeat".equals(args[i])) { repeat = Integer.parseInt(args[++i]); } else { printUsage(threadCount); } } System.out.println("Settings: leafSize=" + leafSize + ", averageBucketSize=" + averageBucketSize); if (textFile != null) { System.out.println("Generating MPHF from text file: " + textFile); if (hashFile == null) { System.out.println("hashFile option not set, so hash not stored"); } for (int i = 0; i < repeat; i++) { generateFromTextFile(threadCount); } } if (indexFile != null) { System.out.println("Listing indexes for index file: " + indexFile); if (hashFile == null) { throw new IllegalArgumentException("hashFile option not set"); } for (int i = 0; i < repeat; i++) { generateIndexes(threadCount); } } } private void generateIndexes(int threadCount) throws IOException { byte[] desc = readFile(hashFile); final RecSplitEvaluator eval = RecSplitBuilder. newInstance(new Text.UniversalTextHash()). leafSize(leafSize). averageBucketSize(averageBucketSize). buildEvaluator(new BitBuffer(desc)); final ArrayList list = readTextFile(indexFile); final int[] indices = new int[list.size()]; System.out.println("Calculating indices"); long time = System.nanoTime(); Thread[] threads = new Thread[threadCount]; for (int i = 0; i < threadCount; i++) { final int start = (int) ((long) i * list.size() / threadCount); final int end = (int) ((long) (i + 1) * list.size() / threadCount) - 1; Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = start; i <= end; i++) { indices[i] = eval.evaluate(list.get(i)); } } }); thread.start(); threads[i] = thread; } for (Thread t : threads) { try { t.join(); } catch (InterruptedException e) { // ignore } } time = System.nanoTime() - time; System.out.println("Calculated in " + time / 1000000 / 1000 + " seconds, at " + time / list.size() + " ns/key, " + "using " + threadCount + " thread(s)"); System.out.println("Writing index file: " + outputFile); Writer writer = new BufferedWriter(new FileWriter(outputFile)); for (int x : indices) { writer.write(x + "\n"); } writer.close(); System.out.println("Done"); } private static byte[] readFile(String fileName) throws IOException { RandomAccessFile f = new RandomAccessFile(fileName, "r"); byte[] data = new byte[(int) f.length()]; f.readFully(data); f.close(); return data; } private static void writeFile(String fileName, byte[] data) throws IOException { FileOutputStream out = new FileOutputStream(fileName); out.write(data); out.close(); } private static ArrayList makeListUnique(ArrayList list) { long time = System.currentTimeMillis(); Text.FastComparator comp = new Text.FastComparator(); Collections.sort(list, comp); time = System.currentTimeMillis() - time; System.out.println("Sorted list (to check entries are unique) in " + time / 1000 + " seconds"); if (comp.equalCount() == 0) { System.out.println("List is unique"); return list; } time = System.currentTimeMillis(); ArrayList list2 = new ArrayList(list.size()); Text previous = null; for (Text t : list) { if (previous != null && previous.equals(t)) { continue; } list2.add(t); previous = t; } time = System.currentTimeMillis() - time; System.out.println("Made unique in " + time / 1000 + " seconds"); return list2; } private static ArrayList readTextFile(String fileName) throws IOException { ArrayList list = new ArrayList(); byte[] data = readFile(fileName); int end = Text.indexOf(data, 0, '\n'); Text t = new Text(data, 0, end); System.out.println("Splitting into lines"); long time = System.currentTimeMillis(); while (true) { list.add(t); if (end >= data.length - 1) { break; } int start = end + 1; end = Text.indexOf(data, start, '\n'); t = new Text(data, start, end - start); long now = System.currentTimeMillis(); if (now - time > 2000) { System.out.print(100L * start / data.length + "% "); time = now; } } System.out.println(); System.out.println("Lines: " + list.size()); return list; } private void generateFromTextFile(int threadCount) throws IOException { ArrayList list = readTextFile(textFile); list = makeListUnique(list); // this is much slower and uses a lot of memory // HashSet set = new HashSet(list); System.out.println("Unique entries: " + list.size()); System.out.println("Generating hash function..."); long nanoSeconds = System.nanoTime(); byte[] desc = RecSplitBuilder. newInstance(new Text.UniversalTextHash()). parallelism(threadCount). leafSize(leafSize). averageBucketSize(averageBucketSize). generate(list).toByteArray(); nanoSeconds = System.nanoTime() - nanoSeconds; int seconds = (int) (nanoSeconds / 1000 / 1000000); System.out.println("Generated in " + seconds + " seconds, at " + nanoSeconds / list.size() + " ns/key, using " + threadCount + " thread(s)"); System.out.println("Bytes: " + desc.length); int bits = desc.length * 8; System.out.println(((double) bits / list.size()) + " bits/key"); if (hashFile != null) { System.out.println("Storing to hash file: " + hashFile); writeFile(hashFile, desc); } System.out.println("Done"); } void printUsage(int threadCount) { System.out.println("Usage: java " + getClass().getName() + " [options]"); System.out.println("Options:"); System.out.println("-leafSize " + "leafSize parameter, default " + leafSize); System.out.println("-averageBucketSize " + "averageBucketSize parameter, default " + averageBucketSize); System.out.println("-textFile " + "read from the file, store the hash function in hashFile"); System.out.println("-hashFile " + "file with the minimal perfect hash function"); System.out.println("-indexFile " + "for each line, calculate the hash value (hashFile is used as input)"); System.out.println("-threadCount " + "use that many threads (default: " + threadCount + ")"); System.out.println(); System.out.println("Example (index one million entries, and evaluate):"); System.out.println("seq -f \"%.20g\" 1 1000000 > seq.txt"); System.out.println("java -cp bin org.minperf.TextFileTest " + "-textFile seq.txt -hashFile seq.bin " + "-threadCount 1 -repeat 3"); System.out.println("java -cp bin org.minperf.TextFileTest " + "-hashFile seq.bin -indexFile seq.txt seq.index.txt " + "-threadCount 1 -repeat 3"); } } ================================================ FILE: src/test/java/org/minperf/TimeEstimator.java ================================================ package org.minperf; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; /** * Methods to estimate the time needed to generate a MPHF. */ public class TimeEstimator { public static void main(String... args) { test(); } static String[] test() { ArrayList bestList = new ArrayList<>(); for (int leafSize = 4; leafSize < 18; leafSize++) { for (int avgBucketSize = 10; avgBucketSize < 2 * 1024; avgBucketSize *= 1.1) { double genTime = getExpectedGenerationTime(leafSize, avgBucketSize); double bitsPerKey = SpaceEstimator.getExpectedSpace(leafSize, avgBucketSize); // System.out.println(leafSize + " " + avgBucketSize + " " + genTime + " " + bitsPerKey); FunctionInfo info = new FunctionInfo(); info.averageBucketSize = avgBucketSize; info.leafSize = leafSize; info.generateNanos = genTime * 1000000; info.bitsPerKey = bitsPerKey; boolean add = true; for (int i = 0; i < bestList.size(); i++) { FunctionInfo b = bestList.get(i); if (b.bitsPerKey < info.bitsPerKey && b.generateNanos < info.generateNanos) { add = false; break; } else if (b.bitsPerKey > info.bitsPerKey && b.generateNanos > info.generateNanos) { bestList.remove(i); i--; } } if (add) { bestList.add(info); } } } double[] result = new double[20]; String[] config = new String[2 * 20]; Arrays.fill(result, Double.POSITIVE_INFINITY); FunctionInfo[] bestInfos = new FunctionInfo[20]; for (int i = 0; i < bestList.size(); i++) { FunctionInfo b = bestList.get(i); int index = (int) (b.bitsPerKey * 10 - 15); // double r = Math.log(b.generateNanos); double r = b.generateNanos; if (r < result[index]) { result[index] = r; config[index + index] = "" + r; config[index + index + 1] = b.averageBucketSize + "/" + b.leafSize; bestInfos[index] = b; } } for(FunctionInfo b : bestInfos) { if (b != null) { System.out.println(" " + b.bitsPerKey + " " + b.generateNanos); } } return config; } public static double getExpectedEvaluationSupplementalHashCalls(int leafSize, int averageBucketSize) { // System.out.println(" Estimated space for leafSize " + leafSize + " / // averageBucketSize " + averageBucketSize); // System.out.println(" Bucket sizes"); Settings s = new Settings(leafSize, averageBucketSize); double result = 0; for (int i = 0; i <= s.getMaxBucketSize(); i++) { double probBucketSize = Probability.getProbabilityOfBucketFallsIntoBinOfSize(averageBucketSize, i); if (probBucketSize <= 0) { continue; } double r = getExpectedEvaluationSupplementalHashCalls(s, i, 0); result += r * probBucketSize; } // System.out.println("averageBucketSize " + averageBucketSize + " // leafSize " + leafSize + " gen " + result); return result; } public static double getExpectedGenerationTime(int leafSize, int averageBucketSize) { // System.out.println(" Estimated space for leafSize " + leafSize + " / // averageBucketSize " + averageBucketSize); // System.out.println(" Bucket sizes"); Settings s = new Settings(leafSize, averageBucketSize); double result = 0; for (int i = 0; i <= s.getMaxBucketSize(); i++) { double probBucketSize = Probability.getProbabilityOfBucketSize(averageBucketSize, i); if (probBucketSize <= 0) { continue; } double r = getExpectedGenerationTime(s, i, 0); result += r * probBucketSize; } // System.out.println("averageBucketSize " + averageBucketSize + " // leafSize " + leafSize + " gen " + result); return result; } private static double getExpectedGenerationTime(Settings s, int size, int indent) { if (size <= 1) { return 0; } // String spaces = new String(new char[2 + indent * 2]).replace((char) // 0, ' '); if (size <= s.getLeafSize()) { return getExpectedHashFunctionCalls(size); } int split = s.getSplit(size); double result = getExpectedGenerationTime(s, size, indent, split); return result; } private static double getExpectedEvaluationSupplementalHashCalls(Settings s, int size, int indent) { if (size <= 1) { return 0; } // String spaces = new String(new char[2 + indent * 2]).replace((char) // 0, ' '); if (size <= s.getLeafSize()) { return 1; } int split = s.getSplit(size); double result = getExpectedEvaluationSupplementalHashCalls(s, size, indent, split); return result; } private static double getExpectedEvaluationSupplementalHashCalls(Settings s, int size, int indent, int split) { double result = 1; if (split < 0) { double p = -split / (double) size; result += p * getExpectedEvaluationSupplementalHashCalls(s, -split, indent + 1); result += (1 - p) * getExpectedEvaluationSupplementalHashCalls(s, size + split, indent + 1); } else { double p = 1.0 / split; for (int i = 0; i < split; i++) { result += p * getExpectedEvaluationSupplementalHashCalls(s, size / split, indent + 1); } } return result; } private static double getExpectedGenerationTime(Settings s, int size, int indent, int split) { double p = SpaceEstimator.getSplitProbability(size, split); double result = size * (1 / p); if (split < 0) { result += getExpectedGenerationTime(s, -split, indent + 1); result += getExpectedGenerationTime(s, size + split, indent + 1); } else { for (int i = 0; i < split; i++) { result += getExpectedGenerationTime(s, size / split, indent + 1); } } return result; } public static double getExpectedHashFunctionCalls(int size) { double averageTries = 1 / Probability.probabilitySplitIntoMSubsetsOfSizeN(size, 1); double a = 1, b = 1; double last = 0; double result = 0; for (int i = 1; i <= size; i++) { a *= size - (i - 1); b *= size; double p = 1 - (a / b); result += i * (p - last) * averageTries; last = p; } result += size; return result; } private static double simulateExpectedHashFunctionCalls(int leafSize) { Random r = new Random(1); int tries = 1000000 / leafSize; long calls = 0; int[] conflictAt = new int[leafSize]; for (int i = 0; i < tries; i++) { while (true) { int b = 0; int j = 0; for (; j < leafSize; j++) { calls++; int x = r.nextInt(leafSize); if ((b & (1 << x)) != 0) { conflictAt[j]++; break; } b |= 1 << x; } if (j == leafSize) { break; } } } int sum = 0; for (int x : conflictAt) { sum += x; } for (int i = 0; i < leafSize; i++) { System.out.println((i + 1) + " p " + (double) conflictAt[i] / sum); } return (double) calls / tries; } static double calcEstimatedHashCallsPerKey(long size, int split) { double p2 = size, p1 = split; return 0.3 * Math.pow(2.37, p1) * Math.pow(p2 / p1, 1 / (0.34 + (7 / Math.pow(p1, 2.1)))); } static long calcEstimatedHashCallsPerKey(int leafSize) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(leafSize, 1); return (long) (3.267 * (Math.pow(-1 / Math.log(1 - p), 1.0457) / leafSize)); } } ================================================ FILE: src/test/java/org/minperf/WikipediaTest.java ================================================ package org.minperf; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.HashSet; import org.junit.Assert; import org.minperf.utils.Text; /** * Test a from from Wikipedia. */ public class WikipediaTest { /** * Run just this test. * * @param a ignored */ public static void main(String... a) { try { if (!largeFileWithUniqueEntries(System.getProperty("user.home") + "/data/hash/mphf/" + "enwiki-20160305-all-titles.unique.txt")) { largeFile(System.getProperty("user.home") + "/data/hash/mphf/" + "enwiki-20160305-all-titles"); } // FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, list.size(), false); // System.out.println("random data: " + info.bitsPerKey + " bits/key"); } catch (IOException e) { throw new RuntimeException(e); } } private static boolean largeFileWithUniqueEntries(String fileName) throws IOException { if (!new File(fileName).exists()) { System.out.println("not found: " + fileName); return false; } RandomAccessFile f = new RandomAccessFile(fileName, "r"); byte[] data = new byte[(int) f.length()]; f.readFully(data); ArrayList list = new ArrayList(30 * 1024 * 1024); int end = Text.indexOf(data, 0, '\n'); Text t = new Text(data, 0, end); long time = System.currentTimeMillis(); while (true) { list.add(t); if (end >= data.length - 1) { break; } int start = end + 1; end = Text.indexOf(data, start, '\n'); t = new Text(data, start, end - start); long now = System.currentTimeMillis(); if (now - time > 2000) { System.out.println("size: " + list.size()); time = now; } } System.out.println("file: " + fileName); System.out.println("size: " + list.size()); int[] pairs = new int[] { // 5, 20, // 6, 12, // 6, 14, // 6, 16, // 6, 20, // 7, 16, // 7, 20, // 7, 24, // 8, 10, // 8, 18, // 8, 20, // 8, 24, // 8, 64, // 8, 96, // 8, 128, // 9, 12, // 9, 14, // 9, 16, // 9, 18, // 9, 20, // 9, 24, // 9, 28, // 9, 32, // 9, 64, // 10, 32, // 10, 64, // 10, 192, // 11, 32, // 11, 64, // 11, 256, // 12, 64, 5, 16, 6, 20, // 14, 192, // 14, 256, // 14, 512, // 15, 256, // slow // 15, 512, // slow // 15, 1024, // slow // 16, 1024, // slow }; for (int i = 0; i < pairs.length; i += 2) { int leafSize = pairs[i], averageBucketSize = pairs[i + 1]; test(list, leafSize, averageBucketSize); } // 1 thread // 5, 20, 2.2934250358455595, 1257, 334, // 6, 20, 2.2301333899505726, 1177, 338, // 7, 16, 2.2056644551315054, 1459, 316, // 7, 20, 2.1689277935290385, 1625, 325, // 7, 24, 2.097395632808568, 1537, 322, // 8, 10, 2.3722683340699433, 1990, 314, // 8, 18, 2.127560134412306, 2131, 310, // 8, 20, 2.146903893975849, 2270, 310, // 8, 24, 2.0783823125303242, 2244, 315, // 9, 12, 2.228793858569091, 3609, 302, // 9, 14, 2.157263044381975, 3573, 304, // 9, 16, 2.131986997469965, 3813, 307, // 9, 18, 2.088727997602901, 3888, 305, // 9, 20, 2.085344159281969, 4008, 304, // 9, 24, 2.0510659982789283, 4119, 312, // 9, 28, 2.006607237192625, 4283, 311, // 9, 32, 1.9651353563942207, 4473, 314, // 9, 64, 1.8612538090483068, 5012, 335, // 10, 32, 1.9459898065405232, 8674, 310, // 10, 64, 1.7997964108894018, 14669, 317, // 11, 32, 1.921768633858763, 19027, 302, // 11, 64, 1.7817167768394646, 26900, 318, // 12, 64, 1.7734789550806909, 54813, 310, // 13, 128, 1.6908858307695058, 179575, 338, // 13, 192, 1.6529698752547937, 199952, 362, // 14, 192, 1.6437121891329978, 402364, 372, // 14, 256, 1.6302684001174041, 404552, 401, // // test(list, 5, 10); // for (int leafSize = 5; leafSize < 10; leafSize++) { // test(list, leafSize, 10); // test(list, leafSize, 12); // test(list, leafSize, 14); // test(list, leafSize, 16); // test(list, leafSize, 18); // } // // test(list, 5, 256); // test(list, 6, 512); // test(list, 8, 1024); // test(list, 10, 2048); return true; } private static void largeFile(String fileName) throws IOException { if (!new File(fileName).exists()) { System.out.println("not found: " + fileName); return; } RandomAccessFile f = new RandomAccessFile(fileName, "r"); byte[] data = new byte[(int) f.length()]; f.readFully(data); HashSet set = new HashSet(40 * 1024 * 1024); int end = Text.indexOf(data, 0, '\n'); Text t = new Text(data, 0, end); long time = System.currentTimeMillis(); while (true) { set.add(t); if (end >= data.length - 1) { break; } int start = end + 1; end = Text.indexOf(data, start, '\n'); t = new Text(data, start, end - start); long now = System.currentTimeMillis(); if (now - time > 2000) { System.out.println("size: " + set.size()); time = now; } } System.out.println("file: " + fileName); System.out.println("size: " + set.size()); test(set, 8, 14); test(set, 8, 10); test(set, 8, 16); test(set, 8, 12); } private static void test(Collection set, int leafSize, int averageBucketSize) { long size = set.size(); long time = System.nanoTime(); byte[] desc = RecSplitBuilder. newInstance(new Text.UniversalTextHash()). leafSize(leafSize). averageBucketSize(averageBucketSize). generate(set).toByteArray(); time = System.nanoTime() - time; long generateNanos = time / size; // System.out.println("generate nanos: " + generateNanos); int bits = desc.length * 8; double bitsPerKey = (double) bits / set.size(); RecSplitEvaluator eval = RecSplitBuilder .newInstance(new Text.UniversalTextHash()).leafSize(leafSize) .averageBucketSize(averageBucketSize).buildEvaluator(new BitBuffer(desc)); long evaluateNanos = test(set, eval); System.out.println(" " + leafSize + ", " + averageBucketSize + ", " + bitsPerKey + ", " + generateNanos + ", " + evaluateNanos + ","); // System.out.println("evaluate"); // System.out.println(" % leafSize " + leafSize + " averageBucketSize " + averageBucketSize); // System.out.println(" (" + bitsPerKey + ", " + evaluateNanos + ")"); // System.out.println("generate"); // System.out.println(" % leafSize " + leafSize + " averageBucketSize " + averageBucketSize); // System.out.println(" (" + bitsPerKey + ", " + generateNanos + ")"); } private static long test(Collection set, RecSplitEvaluator eval) { BitSet known = new BitSet(); int size = set.size(); // verify for (T x : set) { int index = eval.evaluate(x); if (index > set.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } if (known.get(index)) { Assert.fail("duplicate entry: " + x + " " + index); } known.set(index); } // the the CPU cool try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // measure // Profiler prof = new Profiler().startCollecting(); long best = Long.MAX_VALUE; ArrayList list = new ArrayList(set); for (int i = 0; i < 10; i++) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } long evalNanos = System.nanoTime(); for (T x : list) { int index = eval.evaluate(x); if (index > list.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } } evalNanos = System.nanoTime() - evalNanos; long evaluateNanos = evalNanos / size; // System.out.println("evaluate: " + evaluateNanos); best = Math.min(best, evaluateNanos); } // System.out.println(prof.getTop(5)); return best; // System.out.println(prof.getTop(5)); } } ================================================ FILE: src/test/java/org/minperf/bdz/BDZTest.java ================================================ package org.minperf.bdz; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.BitSet; import java.util.HashSet; import org.junit.Assert; import org.junit.Test; import org.minperf.BitBuffer; import org.minperf.RandomizedTest; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * Tests the BDZ implementation. */ public class BDZTest { public static void main(String... args) { for (int size = 10; size < 10000000; size *= 10) { test(size); } testPerformance(1000000); } @Test public void test() { test(100000); } private static void testPerformance(int size) { HashSet set = RandomizedTest.createSet(size, 1); UniversalHash hash = new LongHash(); BitBuffer data = BDZ.generate(hash, set); int bitCount = data.position(); data.seek(0); BDZ bdz = BDZ.load(hash, data); assertEquals(bitCount, data.position()); BitSet test = new BitSet(); for (long x : set) { int i = bdz.evaluate(x); assertTrue(i >= 0 && i < size); assertFalse(test.get(i)); test.set(i); } int measureCount = 10; long evaluateNanos = System.nanoTime(); for (int i = 0; i < measureCount; i++) { for (Long x : set) { int index = bdz.evaluate(x); if (index > set.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } } } // System.out.println(prof.getTop(5)); evaluateNanos = (System.nanoTime() - evaluateNanos) / measureCount; System.out.println("size " + size + " bits/key: " + (double) bitCount / size + " evaluate " + (evaluateNanos / size) + " ns/key"); } private static void test(int size) { HashSet set = RandomizedTest.createSet(size, 1); UniversalHash hash = new LongHash(); BitBuffer data = BDZ.generate(hash, set); int bitCount = data.position(); System.out.println("size " + size + " bits/key: " + (double) bitCount / size); data.seek(0); BDZ bdz = BDZ.load(hash, data); assertEquals(bitCount, data.position()); BitSet test = new BitSet(); for (long x : set) { int i = bdz.evaluate(x); assertTrue(i >= 0 && i < size); assertFalse(test.get(i)); test.set(i); } } } ================================================ FILE: src/test/java/org/minperf/c/HashGenerator.java ================================================ package org.minperf.c; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.io.RandomAccessFile; import java.util.HashSet; import org.minperf.BitBuffer; import org.minperf.RecSplitBuilder; import org.minperf.RecSplitEvaluator; import org.minperf.Settings; import org.minperf.universal.StringHash; /** * A generator for the C version. */ public class HashGenerator { public static void main(String... args) throws IOException { String settingsFileName = "settings.bin"; String hashFileName = "hash.bin"; String keyFileName = "keys.txt"; String directory = "."; int leafSize = 8; int averageBucketSize = 18; for(int i=0; i ~/temp/keys.txt seq -f "%.20g" 1 1000000 > ~/temp/hash/keys.txt */ String fileName = directory + "/" + keyFileName; HashSet set = new HashSet(); System.out.println("Reading keys from " + fileName); LineNumberReader reader = new LineNumberReader(new BufferedReader( new FileReader(fileName))); while(true) { String line = reader.readLine(); if(line == null) { break; } set.add(line); } reader.close(); System.out.println("Generating MPHF with leafSize " + leafSize + ", averageBucketSize " + averageBucketSize + ", size " + set.size()); BitBuffer buff = RecSplitBuilder.newInstance(new StringHash()). leafSize(leafSize).averageBucketSize(averageBucketSize). eliasFanoMonotoneLists(false).generate(set); System.out.println("Storing MPHF with " + buff.position() / (double) set.size() + " bits/key"); storeBuffer(directory + "/" + hashFileName, buff); Settings s = new Settings(leafSize, averageBucketSize); storeSettings(s, directory + "/" + settingsFileName); buff.seek(0); RecSplitEvaluator evaluator = RecSplitBuilder.newInstance(new StringHash()). leafSize(leafSize).averageBucketSize(averageBucketSize). eliasFanoMonotoneLists(false).buildEvaluator(buff); long sum = 0; reader = new LineNumberReader(new BufferedReader( new FileReader(directory + "/" + keyFileName))); while(true) { String line = reader.readLine(); if(line == null) { break; } sum += evaluator.evaluate(line); } System.out.println("Sum of indexes: " + sum); } private static void storeSettings(Settings s, String fileName) throws IOException { BitBuffer buff = new BitBuffer(100000); int len = 4 * 1024; buff.writeEliasDelta(s.getLeafSize() + 1); buff.writeEliasDelta(s.getAverageBucketSize() + 1); buff.writeEliasDelta(len + 1); for (int i = 0; i < len; i++) { buff.writeEliasDelta(BitBuffer.foldSigned(s.getSplit(i)) + 1); buff.writeEliasDelta(s.getGolombRiceShift(i) + 1); } System.out.println("Storing settings to " + fileName); storeBuffer(fileName, buff); } private static void storeBuffer(String fileName, BitBuffer buff) throws IOException { RandomAccessFile f = new RandomAccessFile(fileName, "rw"); f.write(buff.toByteArray()); System.out.println("(file length: " + f.length() + " bytes)"); f.close(); } } ================================================ FILE: src/test/java/org/minperf/chd/CHD.java ================================================ package org.minperf.chd; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import org.minperf.BitBuffer; import org.minperf.Settings; import org.minperf.monotoneList.EliasFanoMonotoneList; import org.minperf.universal.UniversalHash; /** * This implementation supports holes, that is, the internal array is * potentially larger than the set size. * * @param the key type */ public class CHD { final UniversalHash hash; final int lambda; final BitBuffer buff; final double factor; int size; EliasFanoList list; int bucketCount; int targetLen; EliasFanoMonotoneList holes; // long hashCalls; CHD(UniversalHash hash, BitBuffer buff) { this(hash, buff, 6, 1.0); } CHD(UniversalHash hash, BitBuffer buff, int lambda, double factor) { this.hash = hash; this.buff = buff; this.lambda = lambda; this.factor = factor; } public void generate(Collection set) { int size = set.size(); buff.writeEliasDelta(size + 1); int bucketCount = (size + lambda - 1) / lambda; class Bucket { int id; ArrayList list = new ArrayList<>(); Bucket(int id) { this.id = id; } } ArrayList buckets = new ArrayList<>(bucketCount); for (int i = 0; i < bucketCount; i++) { buckets.add(new Bucket(i)); } int targetLen = (int) (size * factor); BitSet mapped = new BitSet(targetLen); for (T x : set) { long h = hash.universalHash(x, 0); // hashCalls++; int b = Settings.reduce((int) h, bucketCount); buckets.get(b).list.add(x); } Collections.sort(buckets, new Comparator() { @Override public int compare(Bucket o1, Bucket o2) { return Integer.compare(o2.list.size(), o1.list.size()); } }); int[] list = new int[bucketCount + 1]; int[] candidates = new int[buckets.get(0).list.size()]; for (int i = 0; i < bucketCount; i++) { Bucket bucket = buckets.get(i); if (bucket.list.size() == 0) { break; } int d = 0; while (true) { int j = 0; for (; j < bucket.list.size(); j++) { T x = bucket.list.get(j); long h = hash.universalHash(x, d); // hashCalls++; int p = Settings.reduce((int) h, targetLen); if (mapped.get(p)) { j--; break; } candidates[j] = p; mapped.set(p); } if (j == bucket.list.size()) { // found list[bucket.id + 1] = d; break; } // not found while (j >= 0) { mapped.clear(candidates[j]); j--; } d++; } } EliasFanoList.generate(list, buff); if (targetLen > size) { // TODO for guaranteed constant evaluate, // we would need a sparse rank or similar int[] holes = new int[targetLen - size]; for (int i = 0, j = 0; i < targetLen; i++) { if (!mapped.get(i)) { holes[j++] = i; } } long pos = buff.position(); EliasFanoMonotoneList.generate(holes, buff); pos = buff.position() - pos; } } public void load() { size = (int) buff.readEliasDelta() - 1; bucketCount = (size + lambda - 1) / lambda; list = EliasFanoList.load(buff); targetLen = (int) (size * factor); if (targetLen > size) { holes = EliasFanoMonotoneList.load(buff); } } public int evaluate(T x) { long h = hash.universalHash(x, 0); int b = Settings.reduce((int) h, bucketCount); int d = list.get(b + 1); h = hash.universalHash(x, d); int result = Settings.reduce((int) h, targetLen); if (targetLen > size) { result -= holesBelow(result); } return result; } private int holesBelow(int x) { int low = 0, high = targetLen - size - 1; while(low <= high) { int mid = (low + high) >>> 1; int y = holes.get(mid); if (y == x) { return mid + 1; } else if (y < x) { low = mid + 1; } else { high = mid - 1; } } return low; } } ================================================ FILE: src/test/java/org/minperf/chd/CHD2.java ================================================ package org.minperf.chd; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import org.minperf.BitBuffer; import org.minperf.Settings; import org.minperf.universal.UniversalHash; /** * This implementation does not use holes. * * @param the key type */ public class CHD2 { final UniversalHash hash; final int lambda; final int k; final BitBuffer buff; int size; int targetLen; EliasFanoList arrayList; int bucketCount; CHD2(UniversalHash hash, BitBuffer buff) { this(hash, buff, 6, 8); } CHD2(UniversalHash hash, BitBuffer buff, int lambda, int k) { this.hash = hash; this.buff = buff; this.lambda = lambda; this.k = k; } public void generate(Collection set) { int size = set.size(); int targetLen = (int) (size + k - 1) / k; buff.writeEliasDelta(size + 1); int bucketCount = (size + lambda - 1) / lambda; class Bucket { int id; ArrayList list = new ArrayList<>(); Bucket(int id) { this.id = id; } } ArrayList buckets = new ArrayList<>(bucketCount); for (int i = 0; i < bucketCount; i++) { buckets.add(new Bucket(i)); } int[] counters = new int[targetLen]; for (T x : set) { long h = hash.universalHash(x, 0); int b = Settings.reduce((int) h, bucketCount); buckets.get(b).list.add(x); } Collections.sort(buckets, new Comparator() { @Override public int compare(Bucket o1, Bucket o2) { return Integer.compare(o2.list.size(), o1.list.size()); } }); int[] array = new int[bucketCount + 1]; int[] candidates = new int[buckets.get(0).list.size()]; for (int i = 0; i < bucketCount; i++) { Bucket bucket = buckets.get(i); if (bucket.list.size() == 0) { break; } int d = 0; while (true) { int j = 0; for (; j < bucket.list.size(); j++) { T x = bucket.list.get(j); long h = hash.universalHash(x, d); int p = Settings.reduce((int) h, targetLen); if (counters[p] >= k) { j--; break; } candidates[j] = p; counters[p]++; } if (j == bucket.list.size()) { // found array[bucket.id + 1] = d; break; } // not found while (j >= 0) { counters[candidates[j]]--; j--; } d++; } } EliasFanoList.generate(array, buff); } public void load() { size = (int) buff.readEliasDelta() - 1; targetLen = (int) (size + k - 1) / k; bucketCount = (size + lambda - 1) / lambda; arrayList = EliasFanoList.load(buff); } public int evaluate(T x) { long h = hash.universalHash(x, 0); int b = Settings.reduce((int) h, bucketCount); int d = arrayList.get(b + 1); h = hash.universalHash(x, d); return Settings.reduce((int) h, targetLen); } } ================================================ FILE: src/test/java/org/minperf/chd/CHDTest.java ================================================ package org.minperf.chd; import java.util.BitSet; import java.util.HashMap; import java.util.Set; import org.junit.Assert; import org.minperf.BitBuffer; import org.minperf.RandomizedTest; import org.minperf.Settings; import org.minperf.SpaceEstimator; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; public class CHDTest { public static void main(String... args) { for(int lambda = 3; lambda <= 6; lambda++) { for (int size = 1000; size <= 100000; size *= 10) { for(double factor = 1.0; factor < 1.11; factor += 0.5) { testSize(size, lambda, factor); } } } // space for CHD-k + RecSplit int leafSize = 18; int bucketSize = 1024; Settings s = new Settings(leafSize, bucketSize); HashMap cache = new HashMap<>(); double space = SpaceEstimator.getExpectedBucketSpace(s, bucketSize, 0, cache); double bitsPerKey = space / bucketSize; System.out.println("space: " + bitsPerKey + " (just one bucket)"); for (int size = 1024; size <= 100000; size *= 2) { double p = testSizeK(size, 6, bucketSize); System.out.println("space for CHD-k + RecSplit: " + (bitsPerKey + p) + " bits/key"); } } private static double testSizeK(int size, int lambda, int k) { Set set = RandomizedTest.createSet(size, 1); return testK(set, k, lambda, Long.class); } @SuppressWarnings("unchecked") private static double testK(Set set, int k, int lambda, Class clazz) { int size = set.size(); BitBuffer buff = new BitBuffer(1000 + size * 1000); UniversalHash hash = null; if (clazz == Long.class) { hash = (UniversalHash) new LongHash(); } CHD2 chd = new CHD2(hash, buff, lambda, k); chd.generate(set); long totalBits = buff.position(); double bitsPerKey = (double) totalBits / size; // System.out.println("size " + size + " bits/key " + bitsPerKey); buff.seek(0); chd = new CHD2(hash, buff, lambda, k); chd.load(); verifyK(chd, set, k); return bitsPerKey; } private static void testSize(int size, int lambda, double factor) { Set set = RandomizedTest.createSet(size, 1); test(set, lambda, factor, Long.class); } @SuppressWarnings("unchecked") private static void test(Set set, int lambda, double factor, Class clazz) { int size = set.size(); BitBuffer buff = new BitBuffer(1000 + size * 1000); UniversalHash hash = null; if (clazz == Long.class) { hash = (UniversalHash) new LongHash(); } CHD chd = new CHD(hash, buff, lambda, factor); long time = System.nanoTime(); chd.generate(set); time = System.nanoTime() - time; long totalBits = buff.position(); System.out.println("size: " + size + " lambda: " + lambda + " factor: " + factor + " bits/key: " + (double) totalBits / size + // " hashCalls: " + chd.hashCalls + " ns/key: " + time / size); buff.seek(0); chd = new CHD(hash, buff, lambda, factor); chd.load(); verify(chd, set); } private static void verify(CHD eval, Set set) { BitSet known = new BitSet(); for (T x : set) { int index = eval.evaluate(x); if (index > set.size() || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } if (known.get(index)) { eval.evaluate(x); Assert.fail("duplicate entry: " + x + " " + index); } known.set(index); } } private static void verifyK(CHD2 eval, Set set, int k) { int[] count = new int[set.size() / k]; for (T x : set) { int index = eval.evaluate(x); if (index > count.length || index < 0) { Assert.fail("wrong entry: " + x + " " + index); } if (count[index] > k) { eval.evaluate(x); Assert.fail("too often: " + x + " " + index + " count=" + count[index]); } count[index]++; } } } ================================================ FILE: src/test/java/org/minperf/chd/EliasFanoList.java ================================================ package org.minperf.chd; import org.minperf.BitBuffer; import org.minperf.monotoneList.EliasFanoMonotoneList; public class EliasFanoList { private final int offset; private final BitBuffer buff; private final int bitsStart; private final EliasFanoMonotoneList borders; EliasFanoList(int offset, BitBuffer buff, int bitsStart, EliasFanoMonotoneList borders) { this.buff = buff; this.bitsStart = bitsStart; this.borders = borders; this.offset = offset; } public static EliasFanoList generate(int[] list, BitBuffer buffer) { int offset = Integer.MAX_VALUE; for(int x : list) { offset = Math.min(offset, x); } buffer.writeEliasDelta(offset + 1); int bitsCount = 0; int[] data = new int[1 + list.length]; int last = 0; BitBuffer bits = new BitBuffer(32 * list.length); for(int i=0; i>> 32); int to = (int) pair; int highest = 1 << (to - from); int x = (int) buff.readNumber(bitsStart + from, to - from); return (highest | x) + offset - 1; } } ================================================ FILE: src/test/java/org/minperf/chd/EliasFanoListTest.java ================================================ package org.minperf.chd; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.minperf.BitBuffer; public class EliasFanoListTest { @Test public void test() { int[] list = new int[10]; for (int i = 0; i < list.length; i++) { list[i] = i; } BitBuffer buffer = new BitBuffer(1000); EliasFanoList elist = EliasFanoList.generate(list, buffer); for (int i = 0; i < list.length; i++) { assertEquals("" + i, list[i], elist.get(i)); } int len = buffer.position(); buffer.seek(0); elist = EliasFanoList.load(buffer); assertEquals(len, buffer.position()); for (int i = 0; i < list.length; i++) { assertEquals("" + i, list[i], elist.get(i)); } } } ================================================ FILE: src/test/java/org/minperf/cuckoo/CuckooHashMap.java ================================================ package org.minperf.cuckoo; import java.util.Map; import java.util.Map.Entry; import org.minperf.Settings; import org.minperf.universal.UniversalHash; /** * A simple cuckoo hash map. * * @param the key type * @param the value type */ public class CuckooHashMap { private final int size; private final UniversalHash hash; private int hashIndex; private int maxLoop; private K[] keys; private V[] values; @SuppressWarnings("unchecked") public CuckooHashMap(Map map, UniversalHash hash) { this.hash = hash; size = map.size(); double e = 0.05; int r = (int) (size * (1 + e) + 1); maxLoop = (int) ((3 * Math.log(r) / Math.log(1 + e)) + 1); for (hashIndex = 0;; hashIndex += 2) { keys = (K[]) new Object[r * 2]; values = (V[]) new Object[r * 2]; if (tryAddAll(map)) { return; } } } private int index(K key, int id) { int offset; int x; if (id == 0) { offset = 0; x = hashIndex; } else { offset = keys.length / 2; x = hashIndex + 1; } return offset + Settings.reduce((int) hash.universalHash(key, x), keys.length / 2); } public V get(K key) { for (int i = 0; i < 2; i++) { int h = index(key, i); K k = keys[h]; if (k == null) { return null; } if (k.equals(key)) { return values[h]; } } return null; } private boolean tryAddAll(Map map) { for (Entry e : map.entrySet()) { if (!tryAdd(e.getKey(), e.getValue())) { return false; } } return true; } private boolean tryAdd(K key, V value) { for (int count = 0; count < 2 * maxLoop; count++) { int h = index(key, count & 1); K oldKey = keys[h]; V oldValue = values[h]; keys[h] = key; values[h] = value; if (oldKey == null) { return true; } key = oldKey; value = oldValue; } return false; } } ================================================ FILE: src/test/java/org/minperf/cuckoo/CuckooHashTest.java ================================================ package org.minperf.cuckoo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; import org.junit.Test; import org.minperf.PerformanceTest; import org.minperf.universal.StringHash; import org.minperf.universal.UniversalHash; /** * Tests for the cuckoo hash maps. */ public class CuckooHashTest { public static void main(String... args) { new CuckooHashTest().test(); new CuckooHashTest().testLongKey(); testPerf(); } private static void testPerf() { HashSet set = PerformanceTest.createSet(100000, 1); CuckooLongKeyHashSet ch = new CuckooLongKeyHashSet(set); ArrayList list = new ArrayList(set); long time = System.currentTimeMillis(); int sum = 0; for (int j = 0; j < 100; j++) { for (long x : list) { sum += ch.contains(x) ? 1 : 0; } } time = System.currentTimeMillis() - time; System.out.println("cuckoo " + time + " dummy " + sum); time = System.currentTimeMillis(); sum = 0; for (int j = 0; j < 100; j++) { for (long x : list) { sum += set.contains(x) ? 1 : 0; } } time = System.currentTimeMillis() - time; System.out.println("hashSet " + time + " dummy " + sum); } @Test public void test() { for (int size = 10; size < 1000000; size *= 2) { test(size); } } static void test(int size) { HashMap map = new HashMap(); for (int i = 0; i < size; i++) { map.put("k" + i, "v" + i); } UniversalHash hash = new StringHash(); CuckooHashMap ch = new CuckooHashMap( map, hash); for (Entry e : map.entrySet()) { String v = ch.get(e.getKey()); assertEquals(v, e.getValue()); } for (int i = 1; i < 10; i++) { assertTrue(ch.get("wrong-" + i) == null); } } @Test public void testLongKey() { for (int size = 10; size < 1000000; size *= 2) { testLongKey(size); } } static void testLongKey(int size) { HashSet set = PerformanceTest.createSet(size, 1); CuckooLongKeyHashSet ch = new CuckooLongKeyHashSet(set); for (long x : set) { assertTrue(ch.index(x) >= 0); assertTrue(ch.contains(x)); if (!set.contains(x + 1)) { assertTrue(ch.index(x + 1) < 0); assertFalse(ch.contains(x + 1)); } } } } ================================================ FILE: src/test/java/org/minperf/cuckoo/CuckooLongKeyHashSet.java ================================================ package org.minperf.cuckoo; import java.util.Set; /** * A cuckoo hash set for long entries. */ public class CuckooLongKeyHashSet { private final int size; private int hashIndex; private int maxLoop; private long[] entries; private int blockLength; public CuckooLongKeyHashSet(Set set) { size = set.size(); double e = 0.05; this.blockLength = (int) (size * (1 + e) + 1); maxLoop = (int) ((3 * Math.log(blockLength) / Math.log(1 + e)) + 1); for (hashIndex = 0;; hashIndex += 2) { entries = new long[blockLength * 2]; if (tryAddAll(set)) { return; } } } public int index(long key, int id) { int offset; int x; if (id == 0) { offset = 0; x = hashIndex; } else { offset = blockLength; x = hashIndex + 1; } return offset + supplementalHash(key, x, blockLength); } public boolean contains(long key) { if (entries[index(key, 0)] == key) { return true; } if (entries[index(key, 1)] == key) { return true; } return false; } public int index(long key) { for (int i = 0; i < 2; i++) { int h = index(key, i); long k = entries[h]; if (k == key) { return h; } } return -1; } public int arrayLength() { return entries.length; } private boolean tryAddAll(Set set) { for (long x : set) { if (!tryAdd(x)) { return false; } } return true; } private boolean tryAdd(long x) { for (int count = 0; count < 2 * maxLoop; count++) { int h = index(x, count & 1); long old = entries[h]; if (old == x) { return true; } entries[h] = x; if (old == 0) { return true; } x = old; } return false; } public static int supplementalHash(long hash, long index, int size) { // it would be better to use long, // but with some processors, 32-bit multiplication // seem to be much faster // (about 1200 ms for 32 bit, about 2000 ms for 64 bit) int x = (int) (Long.rotateLeft(hash, (int) index) ^ index); x = ((x >>> 16) ^ x) * 0x45d9f3b; x = ((x >>> 16) ^ x) * 0x45d9f3b; x = (x >>> 16) ^ x; return scaleInt(x, size); } private static int scaleInt(int x, int size) { // this is actually not completely uniform, // there is a small bias towards smaller numbers // possible speedup for the 2^n case: // return x & (size - 1); // division would also be faster // return x & (size - 1); return (x & (-1 >>> 1)) % size; } } ================================================ FILE: src/test/java/org/minperf/hash/MixTest.java ================================================ package org.minperf.hash; import static org.junit.Assert.assertEquals; import java.util.Random; import java.util.UUID; import org.junit.Test; public class MixTest { public static void main(String... args) { measureSupplementalHashCollisions128(1); measureSupplementalHashCollisions128(2); measureSupplementalHashSpeed128(); measureSupplementalHashCollisions(1); measureSupplementalHashCollisions(2); measureSupplementalHashSpeed(); } @Test public void inverse64() { Random r = new Random(1); for (int i = 0; i < 10000; i++) { long x = r.nextLong(); if ((x & 1) == 0) { continue; } long inverse = findInverse64(x); for (int j = 0; j < 10; j++) { long y = r.nextLong(); assertEquals("*" + x + " *" + inverse, y, y * x * inverse); } } } @Test public void random32() { Random r = new Random(1); for (int i = 0; i < 1000000; i++) { int x = r.nextInt(); assertEquals(x, Mix.unhash32(Mix.hash32(x))); } } @Test public void random64() { Random r = new Random(1); for (int i = 0; i < 1000000; i++) { long x = r.nextLong(); assertEquals(x, Mix.unhash64(Mix.hash64(x))); } } private static long modify(long x, int bit) { return x ^ (1L << bit); } private static UUID modify128(UUID x, int bit) { if (bit < 64) { return new UUID(x.getMostSignificantBits() ^ (1L << bit), x.getLeastSignificantBits()); } return new UUID(x.getMostSignificantBits(), x.getLeastSignificantBits() ^ (1L << (bit - 64))); } private static long findFirstSupplementalHashDifference(long a, long b, int dbit) { for (long index = 0; index < 10000; index++) { int xa = (int) supplementalHash(a, index); int xb = (int) supplementalHash(b, index); if ((xa & (1L << dbit)) != (xb & (1L << dbit))) { return index; } } return -1; } private static long findFirstSupplementalHashDifference128(UUID a, UUID b, int dbit) { for (long index = 0; index < 10000; index++) { int xa = (int) supplementalHash128(a, index); int xb = (int) supplementalHash128(b, index); if ((xa & (1L << dbit)) != (xb & (1L << dbit))) { return index; } } return -1; } private static void measureSupplementalHashCollisions(int changedBitsCount) { Random r = new Random(1); long sum = 0; long max = 0; int count = 10000; for (int i = 0; i < count; i++) { long a = r.nextLong(); for (int bit = 0; bit < 64; bit++) { int b1, b2; do { b1 = r.nextInt(64); b2 = r.nextInt(64); } while (b1 == b2); long b = modify(a, b1); if (changedBitsCount == 2) { b = modify(b, b2); } for (int dbit = 0; dbit < 64; dbit++) { long x = findFirstSupplementalHashDifference(a, b, dbit); if (x < 0) { System.out.println( "no difference for i=" + i + " dbit " + dbit + " change " + bit + " " + a + " " + b); } max = Math.max(max, x); sum += x; } } } System.out.println("bits: 64 changed: " + changedBitsCount + " average: " + (double) sum / count / 64 / 64 + " max: " + max); } private static void measureSupplementalHashCollisions128(int changedBitsCount) { Random r = new Random(1); long sum = 0; long max = 0; int count = 10000; for (int i = 0; i < count; i++) { UUID a = new UUID(r.nextLong(), r.nextLong()); for (int bit = 0; bit < 128; bit++) { int b1, b2; do { b1 = r.nextInt(128); b2 = r.nextInt(128); } while (b1 == b2); UUID b = modify128(a, b1); if (changedBitsCount == 2) { b = modify128(b, b2); } for (int dbit = 0; dbit < 128; dbit++) { long x = findFirstSupplementalHashDifference128(a, b, dbit); if (x < 0) { System.out.println( "no difference for i=" + i + " dbit " + dbit + " change " + bit + " " + a + " " + b); } max = Math.max(max, x); sum += x; } } } System.out.println("bits: 128 changed: " + changedBitsCount + " average: " + (double) sum / count / 128 / 128 + " max: " + max); } private static void measureSupplementalHashSpeed() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Random r = new Random(1); long sum = 0; long time = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { long x = r.nextLong(); for (int j = 0; j < 1000; j++) { sum += supplementalHash(x, j); } } time = System.currentTimeMillis() - time; System.out.println("bits: 64 time: " + time + " dummy: " + sum); } private static void measureSupplementalHashSpeed128() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Random r = new Random(1); long sum = 0; long time = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { long a = r.nextLong(); long b = r.nextLong(); UUID signature = new UUID(a, b); for (int j = 0; j < 1000; j++) { sum += supplementalHash128(signature, j); } } time = System.currentTimeMillis() - time; System.out.println("bits: 128 time: " + time + " dummy: " + sum); } private static long supplementalHash(long a, long index) { // bits: 64 changed: 1 average: 0.9989029052734375 max: 22 // bits: 64 changed: 2 average: 1.00027041015625 max: 23 // bits: 64 time: 7476 dummy: 6254326271697254890 // return LongHash.universalHash(a, index); // bits: 64 changed: 1 average: 1.05794130859375 max: 52 // bits: 64 changed: 2 average: 1.0269772216796875 max: 42 // bits: 64 time: 1293 dummy: -49479994468383 return Mix.supplementalHashWeyl(a, index); } private static long supplementalHash128(UUID signature, long index) { long a = signature.getMostSignificantBits(); long b = signature.getLeastSignificantBits(); // bits: 128 changed: 1 average: 1.0002647827148436 max: 25 // bits: 128 changed: 2 average: 1.0001899780273438 max: 24 // bits: 128 time: 14855 dummy: 4522406346767388702 // return LongHash.universalHash(a, index) ^ LongHash.universalHash(b, index); // bits: 128 changed: 1 average: 1.00045244140625 max: 23 // bits: 128 changed: 2 average: 1.0158296752929687 max: 28 // bits: 128 time: 2450 dummy: -5820939404663996947 // long x = a ^ Long.rotateLeft(b, (int) index) ^ index; // x = (x ^ (x >>> 32)) * 0x94d049bb133111ebL; // x = (x ^ (x >>> 32)) * 0xbf58476d1ce4e5b9L; // x = x ^ (x >>> 32); // return x; // bits: 128 changed: 1 average: 1.0089444580078124 max: 32 // bits: 128 changed: 2 average: 1.0058574096679687 max: 27 // bits: 128 time: 1616 dummy: 6194542897375344905 long x = (a * (index + 1)) + b; x = (x ^ (x >>> 30)) * 0xbf58476d1ce4e5b9L; x = (x ^ (x >>> 27)) * 0x94d049bb133111ebL; x = x ^ (x >>> 31); return x; } private static long findInverse64(long x) { long y = x; y = f64(x, y); y = f64(x, y); y = f64(x, y); y = f64(x, y); y = f64(x, y); return y; } private static long f64(long x, long y) { return y * (2 - y * x); } } ================================================ FILE: src/test/java/org/minperf/hem/HEM.java ================================================ package org.minperf.hem; import java.io.File; import java.util.HashSet; import java.util.PrimitiveIterator; import org.minperf.BitCodes; import org.minperf.FunctionInfo; import org.minperf.RandomizedTest; public class HEM { /** generate command line: - keys file (lines as text, or fixed number of bytes per key) - estimated (?) size -> to calculate signature size, chunk size - signature size (overrideable) - chunk size (overrideable) - number of keys to batch - option to either (a) merge duplicates (on clash), or (b) stop when finding a clash - defaults to merge? - how to do parallel key processing? only do first part, in parallel, with multiple key files - second step is: merge sort signature files (option to just do that, so 64:1 merge is possible) - also generate mphf (by streaming; chunk by chunk) - how to do parallel construction? by defining the chunk range, and merge MPHF at the end raw MPHF data format: * 64 bit: total size (number of keys) * 1 byte: number of bits per chunk (number of chunks is 2^n) * per chunk: * - 1 byte: signature hash (usually 0) * - chunk data (includes size of chunk) * - filler to full byte (so chunks can be easily concatenated) in-memory data format: * signature mask * chunk shift (chunk id = signature >>> shift) * array of evaluators * each evaluator knows the offset, and signature hash generate algorithm: * calc signature size (or use override) * calc number of chunks * read data, calc signatures, truncate to required size (truncate left part?) * sort & store signatures; one file per chunk (use uuid to allow concurrent generation) evaluation algorithm: * calc 128 bit signature * mask (to truncate, same as for generation) * shift to get chunk size target: <50 seconds for 1 billion keys page 7 50 ns / key? 24 threads 10^12 64-bit keys 3.7 bits/key 35.4 hours and required 637 GB RAM bit arrays ( 459 GB) the memory required for loading 20 billion keys in memory ( 178 GB). keys were loaded in memory when |Fi|  2% of total keys (i.e. when remaining number of keys to index was lower than 20 billion). The final MPHF occupied 3.71 bits per key. Query time (ns) around 216 ns ? xor-shift based hash function Retrieval and Perfect Hashing Using Fingerprinting Xorshift128* 2124 min 127440 sec 127440000000000 ns 1000000000000 keys 127.44 ns / key 24 threads, 1 thread might be 3058.56 ns / key 1000000000 35000000000 ns 35 ns / key, 8 threads; 280 ns / key 229000000000 ns, 1 thread; 229 ns / key */ public static void main(String... args) throws InterruptedException { // 3.7 bits/key // 250 ns / key with 1 thread // System.out.println(250 * 1000 * 1000000L / 1_000_000_000); for (int i = 0; i < 10; i++) { HashSet set = RandomizedTest.createSet(1000000, 1); for (int leafSize = 2; leafSize < 8; leafSize++) { for (int averageBucketSize = 4; averageBucketSize < 32; averageBucketSize *= 2) { FunctionInfo info = RandomizedTest.test(leafSize, averageBucketSize, set.size(), false, 1, true); System.out.println(info); } } } // test(args[0]); // test(args[1]); } private static void test(String fileName) throws InterruptedException { // dd if=/dev/urandom of=~/temp/hash/key64.bin bs=1048576 count=1024 // (13'017'359 bytes/sec) // int shift = 36; int shift; // dd if=/dev/urandom of=~/temp/hash/key64b.bin bs=1048576 count=4096 boolean varLong = false; int count = (int) (new File(fileName).length() / 8); double mean = Long.MAX_VALUE / count * 2; shift = BitCodes.calcBestGolombRiceShiftFromMean(mean); long[] data = new long[count]; long[] d2 = new long[count]; String diffsFileName = fileName + ".diffs"; for (int test = 0; test < 2; test++) { varLong ^= true; long time; System.out.println(""); System.out.println("test #" + test + " " + (varLong ? "varlong" : "golomb")); Thread.sleep(1000); time = System.nanoTime(); PrimitiveIterator.OfLong it = KeyReader.readSignaturesFromTextFile64(fileName); for (int i = 0; it.hasNext(); i++) { data[i] = it.nextLong(); } time = System.nanoTime() - time; System.out.println("read time: " + time / count + " ns/key, count=" + count); Thread.sleep(1000); time = System.nanoTime(); Sort.parallelSortUnsigned(data); // Sort.sortUnsignedSimple(data); time = System.nanoTime() - time; System.out.println("sort time: " + time / count + " ns/key"); SortedSignatures.FileWriter w = new SortedSignatures.FileWriter(diffsFileName); Thread.sleep(1000); time = System.nanoTime(); if (varLong) { w.writeDiffsVarLong(data); } else { w.writeDiffsGolombRice(data, shift); } w.close(); time = System.nanoTime() - time; System.out.println("diff write time: " + time / count + " ns/key"); System.out.println(" diff file size: " + new File(diffsFileName).length() / (double) count + " bytes/key"); Thread.sleep(1000); time = System.nanoTime(); SortedSignatures.FileIterator r = new SortedSignatures.FileIterator(diffsFileName); PrimitiveIterator.OfLong resultIterator; if (varLong) { resultIterator = r.iteratorVarLong(count); } else { resultIterator = r.iteratorGolombRice(count, shift); } for (int i = 0; i < count; i++) { d2[i] = resultIterator.nextLong(); } time = System.nanoTime() - time; System.out.println("diff read time: " + time / count + " ns/key"); for (int i = 0; i < d2.length; i++) { if (data[i] != d2[i]) { throw new AssertionError("" + i + " " + data[i] + "<>" + d2[i]); } } } } } ================================================ FILE: src/test/java/org/minperf/hem/MetaFile.java ================================================ package org.minperf.hem; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.Vector; import java.util.concurrent.ConcurrentMap; import org.junit.Assert; public class MetaFile implements ConcurrentMap { private final File file; private final TreeMap cache = new TreeMap(); private FileChannel channel; private FileLock lock; public MetaFile(String fileName) { this.file = new File(fileName); } public static void main(String... args) { String fileName = args[0]; MetaFile map = new MetaFile(fileName); MetaFile map2 = new MetaFile(fileName); map.clear(); map.put("hello", "world"); Assert.assertEquals("world", map2.get("hello")); } @Override public int size() { read(); return cache.size(); } @Override public boolean isEmpty() { read(); return cache.isEmpty(); } @Override public boolean containsKey(Object key) { read(); return cache.containsKey(key); } @Override public boolean containsValue(Object value) { read(); return cache.containsValue(value); } @Override public Collection values() { read(); return cache.values(); } @Override public Set keySet() { read(); return cache.keySet(); } @Override public Set> entrySet() { read(); return cache.entrySet(); } @Override public String get(Object key) { read(); return cache.get(key); } private void read() { try (FileInputStream in = new FileInputStream(file)) { FileChannel channel = in.getChannel(); try (FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) { Properties prop = new Properties(); prop.load(in); for(Entry e : prop.entrySet()) { cache.put(e.getKey().toString(), e.getValue().toString()); } } } catch (IOException e) { throw new RuntimeException(e); } } @SuppressWarnings("resource") private void lockForWriting() { try { channel = new RandomAccessFile(file, "rw").getChannel(); lock = channel.lock(); InputStream in = Channels.newInputStream(channel); Properties prop = new Properties(); prop.load(in); for(Entry e : prop.entrySet()) { cache.put(e.getKey().toString(), e.getValue().toString()); } } catch (IOException e) { throw new RuntimeException(e); } } private void writeAndUnlock() { try { channel.position(0); OutputStream out = Channels.newOutputStream(channel); Properties prop = new Properties() { private static final long serialVersionUID = 1L; @Override public synchronized Enumeration keys() { Vector v = new Vector(); for (Object o : keySet()) { v.add(o.toString()); } Collections.sort(v); return new Vector(v).elements(); } }; prop.putAll(cache); prop.store(out, null); out.flush(); channel.truncate(channel.position()); lock.release(); channel.close(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void clear() { lockForWriting(); try { cache.clear(); } finally { writeAndUnlock(); } } @Override public String remove(Object key) { lockForWriting(); try { return cache.remove(key); } finally { writeAndUnlock(); } } @Override public String put(String key, String value) { lockForWriting(); try { return cache.put(key, value); } finally { writeAndUnlock(); } } @Override public String putIfAbsent(String key, String value) { lockForWriting(); try { return cache.putIfAbsent(key, value); } finally { writeAndUnlock(); } } @Override public void putAll(Map map) { lockForWriting(); try { cache.putAll(map); } finally { writeAndUnlock(); } } @Override public boolean remove(Object key, Object value) { lockForWriting(); try { return cache.remove(key, value); } finally { writeAndUnlock(); } } @Override public boolean replace(String key, String oldValue, String newValue) { lockForWriting(); try { return cache.replace(key, oldValue, newValue); } finally { writeAndUnlock(); } } @Override public String replace(String key, String value) { lockForWriting(); try { return cache.replace(key, value); } finally { writeAndUnlock(); } } } ================================================ FILE: src/test/java/org/minperf/hem/RandomGenerator.java ================================================ package org.minperf.hem; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Random; import org.minperf.RandomizedTest; import org.minperf.hash.Mix; public class RandomGenerator { public static void main(String... args) { for(int test =0; test < 10; test++) { test(); } } private static void test() { int count = 1000000; long[] data = new long[count]; long time; time = System.nanoTime(); createRandomUniqueListSlow(data, 0); time = System.nanoTime() - time; System.out.println("slow: " + (time / count) + " ns/key " + sum(data)); time = System.nanoTime(); createRandomUniqueList(data, 0); time = System.nanoTime() - time; System.out.println("normal: " + (time / count) + " ns/key " + sum(data)); time = System.nanoTime(); createRandomUniqueListFast(data, 0); time = System.nanoTime() - time; System.out.println("fast: " + (time / count) + " ns/key " + sum(data)); } public static long sum(long[] data) { long x = 0; for(long y : data) { x += y; } return x; } public static void createRandomUniqueListSlow(long[] list, int seed) { int len = list.length; HashSet set = RandomizedTest.createSet(len, 1); int i = 0; for (long x : set) { list[i++] = x; } } public static void createRandomUniqueList(long[] list, int seed) { int len = list.length; Random r = new Random(seed); for (int i = 0; i < len; i++) { list[i] = r.nextLong(); } ArrayList duplicateIndexList = new ArrayList(); duplicateIndexList.clear(); Sort.parallelSortUnsigned(list); for (int i = 1; i < len; i++) { if (list[i - 1] == list[i]) { duplicateIndexList.add(i); } } if (duplicateIndexList.isEmpty()) { return; } outer: for (int s = 0;; s++) { long[] l2 = new long[duplicateIndexList.size()]; createRandomUniqueList(l2, s); for (long x : l2) { if (Arrays.binarySearch(list, x) >= 0) { continue outer; } } for (int i = 0; i < duplicateIndexList.size(); i++) { list[duplicateIndexList.get(i)] = l2[i]; } break; } Sort.parallelSortUnsigned(list); for (int i = 1; i < len; i++) { if (list[i - 1] == list[i]) { throw new AssertionError(); } } } public static void createRandomUniqueListFast(long[] list, int seed) { int len = list.length; for (int i = 0; i < len; i++) { list[i] = Mix.hash64(seed + i); } } } ================================================ FILE: src/test/java/org/minperf/hem/recsplit/TestFast.java ================================================ package org.minperf.hem.recsplit; import java.util.BitSet; import org.minperf.BitBuffer; import org.minperf.BitCodes; import org.minperf.Probability; import org.minperf.hem.RandomGenerator; public class TestFast { public static void main(String... args) { test(); System.out.println(); test(); } static void test() { for (int leafSize = 4; leafSize <= 6; leafSize++) { for (int averageBucketSize = 4; averageBucketSize <= 32; averageBucketSize *= 2) { int[] sizes = new int[64]; for (int size = 2; size <= leafSize; size++) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(size, 1); int k = BitCodes.calcBestGolombRiceShift(p); sizes[size] = k; } for (int size = leafSize + 1; size < 64; size++) { double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(2, size / 2); int k = BitCodes.calcBestGolombRiceShift(p); sizes[size] = k; } for (int len = 1048576; len <= 1048576; len *= 4) { long time; time = System.nanoTime(); long[] list = new long[len]; RandomGenerator.createRandomUniqueListFast(list, len); time = System.nanoTime() - time; // System.out.println("create " + time / len); Builder builder = new Builder().leafSize(leafSize).averageBucketSize(averageBucketSize); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } time = System.nanoTime(); BitBuffer buff = builder.generate(list); time = System.nanoTime() - time; System.out.print("leafSize " + leafSize + " avg " + averageBucketSize + " bits/key " + (double) buff.position() / len + " gen " + time / len); buff.seek(0); FastEvaluator eval = builder.evaluator(buff); BitSet s = new BitSet(); // TODO document: result is somewhat ascending parallel to // hash function - this might be good for some use cases (locality) long sum = 0; time = System.nanoTime(); for (long x : list) { int y = eval.evaluate(x); sum += y; } time = System.nanoTime() - time; System.out.println(" eval " + time / list.length + " dummy " + sum); for (long x : list) { int y = eval.evaluate(x); if (s.get(y) || y >= len) { throw new AssertionError("y=" + y + " len=" + len + " " + eval.evaluate(x)); } s.set(y); } } } } } } ================================================ FILE: src/test/java/org/minperf/hybrid/HybridTest.java ================================================ package org.minperf.hybrid; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.BitSet; import java.util.HashSet; import org.junit.Test; import org.minperf.BitBuffer; import org.minperf.RandomizedTest; import org.minperf.RecSplitBuilder; import org.minperf.RecSplitEvaluator; import org.minperf.Settings; import org.minperf.generator.ConcurrencyTool; import org.minperf.generator.Generator; import org.minperf.universal.LongHash; import org.minperf.universal.UniversalHash; /** * Tests the hybrid algorithm. */ public class HybridTest { public static void main(String... args) { for (int size = 10000; size < 10000000; size *= 10) { test(size); } } @Test public void test() { test(10000); } private static void test(int size) { HashSet set = RandomizedTest.createSet(size, 1); UniversalHash hash = new LongHash(); int leafSize = 10; for (int averageBucketSize = 32; averageBucketSize >= 8; averageBucketSize -= 4) { Settings settings = new Settings(leafSize, averageBucketSize); Generator generator; ConcurrencyTool pool = new ConcurrencyTool(8); generator = new Generator(pool, hash, settings, true, Integer.MAX_VALUE); BitBuffer buffer0 = generator.generate(set); int bitCount0 = buffer0.position(); BitBuffer buffer2 = RecSplitBuilder.newInstance(hash) .leafSize(leafSize).averageBucketSize(averageBucketSize).generate(set); int bitCount2 = buffer2.position(); System.out.println("size " + size + " averageBucketSize " + averageBucketSize + " hybrid " + (double) bitCount0 / size + " old " + +(double) bitCount2 / size); buffer2.seek(0); RecSplitEvaluator evaluatorOld = RecSplitBuilder.newInstance(hash) .leafSize(leafSize).averageBucketSize(averageBucketSize).buildEvaluator(buffer2); long time = System.nanoTime(); int sum = 0; for (int i = 0; i < 10; i++) { for (long x : set) { sum += evaluatorOld.evaluate(x); } } time = System.nanoTime() - time; System.out.println(time / 10 / size + " ns Old dummy " + sum); buffer0.seek(0); RecSplitEvaluator evaluator = new RecSplitEvaluator(buffer0, hash, settings, true); BitSet test = new BitSet(); for (long x : set) { int i = evaluator.evaluate(x); assertTrue(i >= 0 && i < size); assertFalse(test.get(i)); test.set(i); } time = System.nanoTime(); sum = 0; for (int i = 0; i < 10; i++) { for (long x : set) { sum += evaluator.evaluate(x); } } time = System.nanoTime() - time; System.out.println(time / 10 / size + " ns Hybrid dummy " + sum); } } } ================================================ FILE: src/test/java/org/minperf/medium/EstimateTimeForHugeSets.java ================================================ package org.minperf.medium; import java.math.BigInteger; import org.minperf.Probability; public class EstimateTimeForHugeSets { public static void main(String... args) { System.out.println("Read all entries, write signatures to a file, sort it, and construct the MPHF"); int bitsPerKey = 64; // 8 * 100; System.out.println("key size: " + formatBits(bitsPerKey)); // timing data for disk here: // http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html // RecSplit // (1.6139085584683803, 262655.0) // (1.647726743677378, 250957.0) // (1.6847065892037447, 104284.0) // (1.7663794926201724, 48849.0) // (1.9282185286288644, 5108.0) // (2.131266063511737, 1593.0) // CHD // % (2.501514530222621, 1899) // (2.2679004188762297, 2445) // (2.159221442493381, 3968) // (2.061488705059407, 13154) // (2.0043687601433935, 57080) // CHD // double mphfBitsPerKey = 2.159; // long mphfNsPerKey = 3968; double mphfBitsPerKey = 2.13; long mphfNsPerKey = 1593; // double mphfBitsPerKey = 1.928; // long mphfNsPerKey = 5108; // m4.4xlarge: 16 vCPUs, 64 GiB RAM, 2000 Mbps // m4.16xlarge: 64 vCPUs, 256 GiB RAM, 10,000 Mbps <== // 2.3 GHz Intel Xeon long keyReadBitsPerSecond = 10_000 * 1_000_000L; System.out.println("key data (HDD) reads: " + formatBits(keyReadBitsPerSecond) + "/s"); long signatureWriteBitsPerSecond = keyReadBitsPerSecond; System.out.println("signature writes: " + formatBits(signatureWriteBitsPerSecond) + "/s"); long signatureReadBitsPerSecond = keyReadBitsPerSecond; System.out.println("signature reads: " + formatBits(signatureReadBitsPerSecond) + "/s"); long ramBits = 64 * 8L * 1024 * 1024 * 1024; // no GPUs long mphfCPUCores = 64; System.out.println("RAM: " + formatBits(ramBits)); System.out.println("cpu cores: " + mphfCPUCores); System.out.println("MPHF bits/key: " + mphfBitsPerKey); System.out.println("MPHF ns/key: " + mphfNsPerKey); double probDuplicates = 1.0 / 64; // System.out.print("bits "); // int minProb = 16; // int maxProb = 32; // for (int x = minProb; x < maxProb; x *= 2) { // System.out.print(" 1/" + x); // } // System.out.println(); for (long size = 10_000_000_000L; size < 1_000_000_000_000L; size *= 10) { System.out.println(); System.out.printf("size: %,d \n", size); long keyBits = bitsPerKey * size; System.out.println("key size: " + formatBits(keyBits)); long readKeySeconds = keyBits / keyReadBitsPerSecond; System.out.println("time to read keys: " + formatSeconds(readKeySeconds)); long mphfBits = (long) (mphfBitsPerKey * size); long mphfSeconds = mphfNsPerKey * size / 1000000 / 1000 / mphfCPUCores; System.out.println("MPHF size: " + formatBits(mphfBits)); System.out.println("MPHF cpu time: " + formatSeconds(mphfSeconds)); long bestTime = Long.MAX_VALUE; double bestProb = 0; int bestBitsPerSignature = 0; for(int x = 2; x < 1000000; x*=2) { probDuplicates = 1.0 / x; int bitPerSignature = getBitsForProbability(size, probDuplicates); long signatureBits = bitPerSignature * size; long signaturesInMemory = ramBits / bitPerSignature; int signatureChunks = (int) ((size + signaturesInMemory - 1) / signaturesInMemory); long avgGap = BigInteger.ONE.shiftLeft(bitPerSignature). divide(BigInteger.valueOf(signaturesInMemory)).longValue(); int bitsPerGap = bitsPerGapEliasFano(avgGap); long entriesWithGap = ramBits / bitsPerGap; int gapChunks = (int) ((size + entriesWithGap - 1) / entriesWithGap); long signatureWriteSeconds = signatureBits / signatureWriteBitsPerSecond; long signatureReadSeconds = signatureBits / signatureReadBitsPerSecond; // concurrent signature writes and data reads // signatureWriteSeconds = 0; // concurrent signature reads and MPHF generation signatureReadSeconds = 0; long totalTimeSeconds = readKeySeconds + signatureWriteSeconds + signatureReadSeconds + mphfSeconds; long avgAddTime = getAverageAdditionalTime(totalTimeSeconds, probDuplicates); long avgTotalTime = totalTimeSeconds + avgAddTime; if (avgTotalTime < bestTime) { bestTime = avgTotalTime; bestProb = probDuplicates; bestBitsPerSignature = bitPerSignature; } if (x == 1024) { System.out.println(" probabilitxy of duplicates: " + probDuplicates); System.out.println(" bits per signature: " + bitPerSignature); System.out.println(" avg gap: " + avgGap); System.out.println(" bits per gap: " + bitsPerGap); System.out.println(" signature size: " + formatBits(signatureBits)); System.out.println(" signature chunks: " + signatureChunks); System.out.println(" gap chunks: " + gapChunks); System.out.println(" signature writes: " + formatSeconds(signatureWriteSeconds)); System.out.println(" signature reads: " + formatSeconds(signatureReadSeconds)); System.out.println(" total time (no duplicates): " + formatSeconds(totalTimeSeconds)); System.out.println(" total time (average for given probability of duplicates): " + formatSeconds(avgTotalTime)); } } System.out.println("best bits per signature: " + bestBitsPerSignature); System.out.println("best probabilitxy of duplicates: " + bestProb + " (1/" + (1.0 / bestProb) + ")"); System.out.println("best total time (average for given probability of duplicates): " + formatSeconds(bestTime)); // // available RAM in bits per key // long ramBits = (long) (3 * size); // System.out.println("size " + size); // for (int x = minProb; x < maxProb; x *= 2) { // int bits = getBitsForProbability(size, (double) 1 / x); // long entries = ramBits / bits; // int stripes = (int) (size / entries); // long avgGap = BigInteger.ONE.shiftLeft(bits).divide(BigInteger.valueOf(entries)).longValue(); // int bitsPerGap = bitsPerGapEliasFano(avgGap); // long entriesWithGap = ramBits / bitsPerGap; // int stripesWithGap = (int) (size / entriesWithGap); // System.out.println(" entries=" + entries + " bits=" + bits); // System.out.println(" avgGap=" + avgGap + " bitsPerGap=" + // bitsPerGap + " entriesWithGap=" + entriesWithGap + " stripes=" + stripes + " stripes2=" + stripesWithGap + ")"); // System.out.println(" writeNormal " + size * bits / 1024 / 1024 / 1024 / 8 + " GB"); // System.out.println(" writeGaps " + size * bitsPerGap / 1024 / 1024 / 1024 / 8 + " GB"); // } // System.out.print(" (" + (2 * size / 8 / 1024 / 1024 / 1024) + " GB MPHF)"); // System.out.println(); } } private static long getAverageAdditionalTime(long totalTimeSeconds, double probDuplicates) { long add = 0; while (totalTimeSeconds > 0) { totalTimeSeconds *= probDuplicates; add += totalTimeSeconds; } return add; } public static String formatBits(long bits) { long b = Math.abs(bits); long byt = 8, kib = byt * 1024, mib = kib * 1024; long gib = mib * 1024, tib = gib * 1024; String positive = String.format( " %d TiB %d GiB %d MiB %d KiB %d byte %d bit", b / tib, (b % tib) / gib, (b % gib) / mib, (b % mib) / kib, (b % kib) / byt, b % byt); positive = positive.replaceAll(" 0 [^ ]*", ""); return (bits < 0 ? "-" + positive : positive).trim(); } public static String formatSeconds(long seconds) { long absSeconds = Math.abs(seconds); String positive = String.format( "%d:%02d:%02d", absSeconds / 3600, (absSeconds % 3600) / 60, absSeconds % 60); return seconds < 0 ? "-" + positive : positive; } // // // // // if (bits <= 0) { // return bits + " bits"; // } // if (bits > 10 * bitsPerTB) { // return (bits + bitsPerTB - 1) / bitsPerTB + " TiB"; // } // if (bits > 10 * bitsPerGB) { // return (bits + bitsPerGB - 1) / bitsPerGB + " GiB"; // } // if (bits > 10 * bitsPerMB) { // return (bits + bitsPerMB - 1) / bitsPerMB + " MiB"; // } // if (bits > 10 * bitsPerKB) { // return (bits + bitsPerKB - 1) / bitsPerKB + " KiB"; // } // if (bits % 8 == 0) { // return (bits / 8) + " bytes"; // } // return bits + " bits"; // } public static void mainChunked(String... args) { int chunks = 10; System.out.println("Read in " + chunks + " chunks, append MPHF to file"); System.out.print("bits "); int minProb = 2; int maxProb = 4; for (int x = minProb; x < maxProb; x *= 2) { System.out.print(" 1/" + x); } System.out.println(); for (long size = 10_000_000_000L; size <= 1_000_000_000_000L; size *= 10) { // available RAM in bits per key long ramBits = (long) (4 * size); System.out.println("size " + size); for (int x = minProb; x < maxProb; x *= 2) { long size2 = size / chunks; int bits = getBitsForProbability(size2, (double) 1 / x); long entries = ramBits / bits; int stripes = (int) (size2 / entries); long avgGap = BigInteger.ONE.shiftLeft(bits).divide(BigInteger.valueOf(entries)).longValue(); int bitsPerGap = bitsPerGapEliasFano(avgGap); long entriesWithGap = ramBits / bitsPerGap; int stripesWithGap = (int) (size2 / entriesWithGap); System.out.println(" entries=" + entries + " bits=" + bits); System.out.println(" avgGap=" + avgGap + " bitsPerGap=" + bitsPerGap + " entriesWithGap=" + entriesWithGap + " stripes=" + stripes + " stripes2=" + stripesWithGap + ")"); System.out.println(" writeNormal " + size2 * bits / 1024 / 1024 / 1024 / 8 + " GB"); System.out.println(" writeGaps " + size2 * bitsPerGap / 1024 / 1024 / 1024 / 8 + " GB"); } System.out.print(" (" + (ramBits / 8 / 1024 / 1024 / 1024) + " GB RAM)"); System.out.print(" (" + (2 * size / 8 / 1024 / 1024 / 1024) + " GB MPHF)"); System.out.println(); } } public static void mainFullWithStripes(String... args) { System.out.println("Read all entries, write to striped files; merge and construct"); System.out.print("bits "); int minProb = 16; int maxProb = 32; for (int x = minProb; x < maxProb; x *= 2) { System.out.print(" 1/" + x); } System.out.println(); for (long size = 10_000_000_000L; size <= 1_000_000_000_000L; size *= 10) { // available RAM in bits per key long ramBits = (long) (3 * size); System.out.println("size " + size); for (int x = minProb; x < maxProb; x *= 2) { int bits = getBitsForProbability(size, (double) 1 / x); long entries = ramBits / bits; int stripes = (int) (size / entries); long avgGap = BigInteger.ONE.shiftLeft(bits).divide(BigInteger.valueOf(entries)).longValue(); int bitsPerGap = bitsPerGapEliasFano(avgGap); long entriesWithGap = ramBits / bitsPerGap; int stripesWithGap = (int) (size / entriesWithGap); System.out.println(" entries=" + entries + " bits=" + bits); System.out.println(" avgGap=" + avgGap + " bitsPerGap=" + bitsPerGap + " entriesWithGap=" + entriesWithGap + " stripes=" + stripes + " stripes2=" + stripesWithGap + ")"); System.out.println(" writeNormal " + size * bits / 1024 / 1024 / 1024 / 8 + " GB"); System.out.println(" writeGaps " + size * bitsPerGap / 1024 / 1024 / 1024 / 8 + " GB"); } System.out.print(" (" + (2 * size / 8 / 1024 / 1024 / 1024) + " GB MPHF)"); System.out.println(); } } public static int getBitsForProbability(long size, double d) { for (int bits = 1;; bits++) { double p = Probability.probabilityOfDuplicates(size, bits); if (p < d) { return bits; } } } private static int bitsPerGapEliasFano(long avgGap) { long len = 1; long max = len * avgGap; int lowBitCount = 64 - Long.numberOfLeadingZeros(Long.highestOneBit(max / len)); long x = len + (max >>> lowBitCount); return (int) ((x + lowBitCount * len) / len) + 1; } } ================================================ FILE: src/test/java/org/minperf/medium/EstimateTwoBillionEntries.java ================================================ package org.minperf.medium; import java.util.BitSet; import java.util.Random; public class EstimateTwoBillionEntries { public static void main(String... args) { long count = 2_000_000_000L; long memGBInBits = 1 * 1024L * 1024 * 1024 * 8; System.out.println("one gb, bits: " + memGBInBits); System.out.println("bitField " + count / 8 / 1024 / 1024 + " mb"); double mphfBitsPerKey = 1.8; long mphfBits = (long) (mphfBitsPerKey * count); System.out.println("mphf bits " + mphfBits + " " + (mphfBits / 8 / 1024 / 1024) + " mb"); long remainBits = memGBInBits - mphfBits; System.out.println("remain " + remainBits); double p0 = probDuplicate((int) (count / 32), 64); System.out.println("p0=" + (1 + p0)); int steps = 32; long perStep = (int) ((count + steps - 1) / steps); long bitsPerStep = perStep * steps; System.out.println("steps: " + steps + " perStep: " + perStep + " total " + (long) perStep * steps); int bitsBuild = 64; // (int) (remainBits / perStep); System.out.println("bits per step " + bitsPerStep + " " + bitsPerStep / 8 / 1024 / 1024 + " mb"); System.out.println("mphf per step " + mphfBitsPerKey * perStep + " " + mphfBitsPerKey * perStep / 8 / 1024 / 1024 + " mb" ); double p = probDuplicate((int) perStep, bitsBuild); System.out.println("p of duplicate =" + p); int segmentsDone = 0; int i = 0; while (segmentsDone < steps) { long mem = (long) (memGBInBits - (segmentsDone + 1) * perStep * mphfBitsPerKey); long segments = mem / (bitsBuild * perStep); System.out.println("mem: " + mem / 1024 / 1024 / 8 + " mb, segments: " + segments); segmentsDone += segments; i++; } System.out.println("i=" + i); // 238 + 14 = 252; // 1024; 4, -> // 968; 3, -> // 926; 3, -> // 884, 3 -> } static double testMultipleLists(long size, double factor) { Random r = new Random(1); // System.out.println("loop " + loop + " remaining: " + size + " factor " + factor); BitSet set = new BitSet((int) (size * factor)); BitSet duplicates = new BitSet((int) (size * factor)); for(int i=0; i { /** * Calculate the universal hash of the string modulo the given value. * * @param x the key * @param index the universal hash index * @param mod the modulo * @return a value between 0 (including) and the modulo (excluding) */ static int universalHash(UniversalHash hash, T x, int index, int mod) { return (int) Math.abs(hash.universalHash(x, index) % mod); } /** * The generator class (not really a class, just a collection of static * methods). */ public static class Generator { private final UniversalHash hash; private final Settings settings; public Generator(UniversalHash hash, Settings settings) { this.hash = hash; this.settings = settings; } /** * Generate the MPHF description for the given set. * * @param set the set * @return the description, in the form of an integer array * (uncompressed) */ public int[] generate(Set set) { int averageBucketSize = settings.getAverageBucketSize(); int bucketCount = (set.size() + averageBucketSize - 1) / averageBucketSize; List> buckets = partition(set, bucketCount); List> bucketData = new ArrayList<>(); int bucketsSize = 0; for (int i = 0; i < bucketCount; i++) { ArrayList bucket = new ArrayList<>(); processRecursively(buckets.get(i), bucket); bucketData.add(bucket); bucketsSize += bucket.size(); } int[] description = new int[1 + 2 * bucketCount + bucketsSize]; description[0] = set.size(); int pos = 1 + 2 * bucketCount, size = 0, d = 0; for (int i = 0; i < bucketCount; i++) { List b = bucketData.get(i); d += b.size(); description[1 + i] = d; size += buckets.get(i).size(); description[1 + bucketCount + i] = size; for (int j = 0; j < b.size(); j++) { description[pos++] = b.get(j); } } return description; } /** * Partition the set. * * @param set the input set * @param bucketCount the number of buckets * @return the list of buckets, each one with a set */ List> partition(Set set, int bucketCount) { List> buckets = new ArrayList<>(); for (int i = 0; i < bucketCount; i++) { buckets.add(new LinkedHashSet()); } for (T x : set) { int bucketId = universalHash(hash, x, 0, bucketCount); buckets.get(bucketId).add(x); } return buckets; } /** * Process a set recursively. * * @param set the set * @param description the description (uncompressed) */ void processRecursively(Set set, List description) { if (set.size() <= 1) { return; } int leafSize = settings.getLeafSize(); if (set.size() <= leafSize) { for (int x = 0;; x++) { if (canMapDirectly(set, x)) { description.add(x); return; } } } for (int x = 0;; x++) { List> list = trySplit(set, x); if (list != null) { description.add(x); for (Set s : list) { processRecursively(s, description); } return; } } } /** * Whether the given set can be mapped directly at this index. * * @param set the set * @param index the universal hash index * @return true if yes */ boolean canMapDirectly(Set set, int index) { Set used = new HashSet(); for (T s : set) { used.add(universalHash(hash, s, index, set.size())); } return used.size() == set.size(); } /** * Try to split the set into two subsets of equal size (the size of the * first subset must be size / 2). * * @param set the set * @param index the universal hash index * @return null if it didn't work out, or the list splitting worked */ List> trySplit(Set set, int index) { int setSize = set.size(); int split = settings.getSplit(setSize); int splitBy, firstSize, otherSize; if (split < 0) { splitBy = 2; firstSize = -split; otherSize = setSize - firstSize; } else { splitBy = split; firstSize = setSize - (setSize / splitBy) * (splitBy - 1); otherSize = setSize / splitBy; } List> list = new ArrayList<>(splitBy); for (int i = 0; i < splitBy; i++) { list.add(new LinkedHashSet()); } if (firstSize == otherSize) { for (T s : set) { list.get(universalHash(hash, s, index, splitBy)).add(s); } } else { for (T s : set) { int x = universalHash(hash, s, index, setSize) >= firstSize ? 1 : 0; list.get(x).add(s); } } for (int i = 0; i < list.size() - 1; i++) { int s2 = (i == 0) ? firstSize : otherSize; if (list.get(i).size() != s2) { return null; } } return list; } } /** * The evaluator class (not really a class, just a collection of static * methods). */ public static class Evaluator { private final UniversalHash hash; private final Settings settings; private final int[] description; Evaluator(UniversalHash hash, Settings settings, int[] description) { this.hash = hash; this.settings = settings; this.description = description; } /** * Evaluate the MPHF for the given key. * * @param x the key * @param description the MPHF description * @return the return of the function */ public int evaluate(T x) { //System.out.println("eval " + x); int totalSize = description[0]; int averageBucketSize = settings.getAverageBucketSize(); int leafSize = settings.getLeafSize(); int bucketCount = (totalSize + averageBucketSize - 1) / averageBucketSize; int bucketId = universalHash(hash, x, 0, bucketCount); int pos = 1 + 2 * bucketCount, offset = 0; if (bucketId > 0) { pos += description[1 + (bucketId - 1)]; offset = description[1 + bucketCount + (bucketId - 1)]; } int setSize = description[1 + bucketCount + bucketId] - offset; while (setSize > 1) { int index = description[pos++]; //System.out.println(" setSize " + setSize + " offset " + offset + " index " + index); if (setSize <= leafSize) { return offset + universalHash(hash, x, index, setSize); } int split = settings.getSplit(setSize); int splitBy, firstSize, otherSize; if (split < 0) { splitBy = 2; firstSize = -split; otherSize = setSize - firstSize; } else { splitBy = split; firstSize = setSize - (setSize / splitBy) * (splitBy - 1); otherSize = setSize / splitBy; } int p; if (firstSize == otherSize) { p = universalHash(hash, x, index, splitBy) % splitBy; } else { p = universalHash(hash, x, index, setSize) >= firstSize ? 1 : 0; } //System.out.println(" p=" + p); for (int i = 0; i < p; i++) { int s2 = (i == 0) ? firstSize : otherSize; offset += s2; pos = skip(description, s2, pos); } setSize = (p == 0) ? firstSize : otherSize; } return offset; } /** * Skip the description of a subset. * * @param description the MPHF description * @param setSize the size of the set to skip * @param pos the current position in the description list * @return the new position in the description list */ int skip(int[] description, int setSize, int pos) { int leafSize = settings.getLeafSize(); if (setSize <= 1) { return pos; } else if (setSize <= leafSize) { return pos + 1; } pos++; int split = settings.getSplit(setSize); int splitBy, firstSize, otherSize; if (split < 0) { splitBy = 2; firstSize = -split; otherSize = setSize - firstSize; } else { splitBy = split; firstSize = setSize - (setSize / splitBy) * (splitBy - 1); otherSize = setSize / splitBy; } for (int i = 0; i < splitBy; i++) { int s2 = (i == 0) ? firstSize : otherSize; pos = skip(description, s2, pos); } return pos; } } } ================================================ FILE: src/test/java/org/minperf/medium/MediumTest.java ================================================ package org.minperf.medium; import java.util.HashSet; import java.util.Random; import java.util.Set; import org.junit.Assert; import org.minperf.BitBuffer; import org.minperf.FunctionInfo; import org.minperf.RandomizedTest; import org.minperf.Settings; import org.minperf.chd.EliasFanoList; import org.minperf.monotoneList.EliasFanoMonotoneList; import org.minperf.universal.LongHash; public class MediumTest { public static void main(String... args) { compareSpace(); test(); } private static void compareSpace() { Set set = new HashSet(); int size = 10000; Random r = new Random(size); while (set.size() < size) { set.add(r.nextLong()); } LongHash hash = new LongHash(); for(int leafSize = 3; leafSize < 12; leafSize++) { for(int avgBucketSize = 10; avgBucketSize < 2000; avgBucketSize *= 1.5) { Settings settings = new Settings(leafSize, avgBucketSize); MediumRecSplit.Generator gen = new MediumRecSplit.Generator(hash, settings); int[] description = gen.generate(set); BitBuffer buff = compressBucketData(settings, true, true, description); int bitCountMonotone = buff.position(); buff.seek(0); int[] d2 = expandBucketData(settings, true, buff); Assert.assertEquals(description.length, d2.length); Assert.assertArrayEquals(description, d2); buff = compressBucketData(settings, true, false, description); int bitCountMonotoneMin = buff.position(); buff = compressBucketData(settings, false, true, description); int bitCountList = buff.position(); buff.seek(0); d2 = expandBucketData(settings, false, buff); Assert.assertEquals(description.length, d2.length); Assert.assertArrayEquals(description, d2); buff = compressBucketData(settings, false, false, description); int bitCountListMin = buff.position(); double efMonotone= (double) bitCountMonotone / size; double efList = (double) bitCountList / size; double efMonotoneMin = (double) bitCountMonotoneMin / size; double efListMin = (double) bitCountListMin / size; FunctionInfo info = RandomizedTest.test(leafSize, avgBucketSize, size, false); double regular = info.bitsPerKey; System.out.println("leafSize " + leafSize + " avg " + avgBucketSize + " regular " + regular + " efm " + efMonotoneMin + " .. " + efMonotone + " efl " + efListMin + " .. " + efList); } } } private static BitBuffer compressBucketData(Settings settings, boolean monotone, boolean includeStart, int[] description) { int size = description[0]; int averageBucketSize = settings.getAverageBucketSize(); int bucketCount = (size + averageBucketSize - 1) / averageBucketSize; int bucketPos = 1 + 2 * bucketCount; int[] bucketData = new int[description.length - bucketPos]; System.arraycopy(description, bucketPos, bucketData, 0, bucketData.length); BitBuffer buff = new BitBuffer(1000 + 10 * size); buff.writeEliasDelta(1 + size); buff.writeEliasDelta(1 + description.length); int[] bucketStart = new int[bucketCount]; System.arraycopy(description, 1, bucketStart, 0, bucketCount); int[] bucketOffsets = new int[bucketCount]; System.arraycopy(description, 1 + bucketCount, bucketOffsets, 0, bucketCount); EliasFanoMonotoneList.generate(bucketStart, buff); if (includeStart) { EliasFanoMonotoneList.generate(bucketOffsets, buff); } if (monotone) { eliasFanoList2(bucketData, buff); } else { EliasFanoList.generate(bucketData, buff); } return buff; } private static int[] expandBucketData(Settings settings, boolean monotone, BitBuffer buff) { int size = (int) (buff.readEliasDelta() - 1); int descriptionSize = (int) (buff.readEliasDelta() - 1); int averageBucketSize = settings.getAverageBucketSize(); int bucketCount = (size + averageBucketSize - 1) / averageBucketSize; int[] description = new int[descriptionSize]; description[0] = size; EliasFanoMonotoneList bucketStartList = EliasFanoMonotoneList.load(buff); EliasFanoMonotoneList bucketOffsetsList = EliasFanoMonotoneList.load(buff); for (int i = 0; i < bucketCount; i++) { description[1 + i] = bucketStartList.get(i); description[1 + bucketCount + i] = bucketOffsetsList.get(i); } int bucketPos = 1 + 2 * bucketCount; int[] bucketData = new int[description.length - bucketPos]; if (monotone) { EliasFanoMonotoneList l = EliasFanoMonotoneList.load(buff); for (int i = 0; i < bucketData.length; i++) { bucketData[i] = l.get(i) - (i == 0 ? 0 : l.get(i - 1)); } } else { EliasFanoList l = EliasFanoList.load(buff); for (int i = 0; i < bucketData.length; i++) { bucketData[i] = l.get(i); } } System.arraycopy(bucketData, 0, description, bucketPos, bucketData.length); return description; } private static void eliasFanoList2(int[] bucketData, BitBuffer buff) { int sum = 0; // System.out.println(Arrays.toString(bucketData)); for (int i = 0; i < bucketData.length; i++) { int x = bucketData[i]; bucketData[i] += sum; sum += x; } // System.out.println(Arrays.toString(bucketData)); EliasFanoMonotoneList.generate(bucketData, buff); } public static void test() { Set set = new HashSet(); int size = 100000; Random r = new Random(size); while (set.size() < size) { set.add(r.nextLong()); } LongHash hash = new LongHash(); Settings settings = new Settings(8, 100); MediumRecSplit.Generator gen = new MediumRecSplit.Generator(hash, settings); int[] data = gen.generate(set); HashSet used = new HashSet(); MediumRecSplit.Evaluator eval = new MediumRecSplit.Evaluator(hash, settings, data); for (Long x : set) { int e = eval.evaluate(x); if (!used.add(e)) { e = eval.evaluate(x); throw new AssertionError(); } } } } ================================================ FILE: src/test/java/org/minperf/medium/PartitionIntoSimilarSizedSets.java ================================================ package org.minperf.medium; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import org.minperf.utils.RandomSetGeneratorSlow; public class PartitionIntoSimilarSizedSets { public static void main(String... args) { for (long size = 100; size <= 1000000000L; size *= 2) { test(size); } } private static void test(long size) { size *= 11; Iterable set = randomSet(size, size); long time = System.nanoTime(); partition(set, size, 11, 22); time = System.nanoTime() - time; time /= size; System.out.println("size " + size + " " + time + " ns/key"); } private static void partition(Iterable set, long size, int average, int max) { int bucketCount = (int) ((size + average - 1) / average); if (bucketCount * average != size) { throw new RuntimeException("TODO only exact average bucket sizes supported right now"); } ArrayList> buckets = new ArrayList<>(); for(int i=0; i()); } for (long x : set) { int bucketId = reduce((int) x, bucketCount); buckets.get(bucketId).initial.add(x); } ArrayList> largeBuckets = new ArrayList<>(); for (int i = 0; i < bucketCount; i++) { Bucket b = buckets.get(i); if (b.initial.size() > average) { largeBuckets.add(b); } } Collections.sort(largeBuckets, new Comparator>() { @Override public int compare(Bucket o1, Bucket o2) { return -Integer.compare(o1.initial.size(), o2.initial.size()); } }); for(Bucket b : largeBuckets) { if (b.initial.size() <= average) { break; } distribute(buckets, average, b); } // // int[] indexList = new int[bucketCount]; // int maxIndex = 0; // for (int i = 0; i < largeBuckets.size(); i++) { // ArrayList list = largeBuckets.get(i); // int[] sizes; // for (int index = 1;; index++) { // boolean fail = false; // sizes = new int[bucketCount]; // for (long x : list) { // int y = supplementalHash(x, index); // int b; //// if ((y & 1) == 0) { //// b = reduce((int) x, bucketCount); //// } else { // b = reduce(y, bucketCount); //// } // if (available[b] - sizes[b] >= 0) { // sizes[b]++; // } else { // fail = true; // break; // } // } // if (!fail) { // indexList[i] = index; // maxIndex = Math.max(maxIndex, index); // break; // } // } // for (int j = 0; j < bucketCount; j++) { // available[j] -= sizes[j]; // } // } // System.out.println("max index: " + maxIndex); } private static void distribute(ArrayList> buckets, int targetSize, Bucket b) { b.moveProbability = 2 * (b.initial.size() + b.foreign.size() - targetSize); while (true) { ArrayList move = new ArrayList<>(); while (true) { for (long x : b.initial) { int h = supplementalHash(x, b.moveIndex); int y = reduce(h, 2 * targetSize); if (y >= b.moveProbability) { continue; } move.add(x); } if (b.initial.size() - move.size() + b.foreign.size() == targetSize) { break; } b.moveIndex++; } Bucket bestNext = b; for (long x : move) { int h = supplementalHash(x, b.moveIndex); int id = reduce(h, buckets.size()); Bucket b2 = buckets.get(id); if (b2.moveIndex < b.moveIndex) { bestNext = b2; } } if (bestNext != b) { for (long x : move) { int h = supplementalHash(x, b.moveIndex); int id = reduce(h, buckets.size()); Bucket b2 = buckets.get(id); b2.foreign.add(x); if (b2.moveIndex < b.moveIndex) { bestNext = b2; } } } } } public static int supplementalHash(long hash, long index) { int x = (int) (Long.rotateLeft(hash, (int) index) ^ index); x = ((x >>> 16) ^ x) * 0x45d9f3b; x = ((x >>> 16) ^ x) * 0x45d9f3b; x = (x >>> 16) ^ x; return x; } static Iterable randomSet(long size, long seed) { // System.out.println(" random set " + size); // Random r = new Random(seed); // long time = System.nanoTime(); // HashSet set = new HashSet(size); // while (set.size() < size) { // set.add(r.nextLong()); // } Iterable set = RandomSetGeneratorSlow.randomSequence(size); // long sum = 0; // for(long x : set) { // sum += x; // } // time = System.nanoTime() - time; // System.out.println(size + " " + time / size + " ns/key " + sum); return set; } public static int reduce(int hash, int n) { // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ return (int) (((hash & 0xffffffffL) * n) >>> 32); } static class Bucket { // the list of entries that are initially mapped to this bucket ArrayList initial = new ArrayList<>(); // the list mapped after re-distribution ArrayList foreign = new ArrayList<>(); ArrayList moved = new ArrayList<>(); // the probability to move entries initially mapped to a different bucket int moveProbability; // the number of times entries were tried to move int moveIndex; } } ================================================ FILE: src/test/java/org/minperf/medium/RecSplitEliasFano.java ================================================ package org.minperf.medium; import org.minperf.BitCodes; import org.minperf.Probability; public class RecSplitEliasFano { public static void main(String... args) { // can we say the maximum meaningful bucket size (for leafSize 6 - x) is 2048? // and for bucket size > 370, splitting by two doesn't save much space - instead just store size of first subset // int size = 10; // HashSet set = RandomizedTest.createSet(size, size); for(int leafSize = 2; leafSize < 16; leafSize++) { // int leafSize = 4; double p = Probability.probabilitySplitIntoMSubsetsOfSizeN(leafSize, 1); System.out.println(leafSize + " direct map, p=" + p); for (int i = leafSize;; i = (int) ((i * 1.2) + 1)) { double bitsForSize = (32.0 - Integer.numberOfLeadingZeros(1 + i)) / i; double p2 = Probability.probabilitySplitIntoMSubsetsOfSizeN(2, (i / 2) & 0xffffe); int k = BitCodes.calcBestGolombRiceShift(p2); double bitsForRice = BitCodes.calcAverageRiceGolombBits(k, p2) / i; if (Math.abs(bitsForSize - bitsForRice) < 0.01) { // if (Math.abs(2 * bitsForSize - bitsForRice) < 0.01) { System.out.println(" " + i + " bits for 0.." + i + ": " + bitsForSize + " riceBits: " + bitsForRice); System.out.println(" split into 2 subsets of size " + i + ": p=" + p2); break; } if (p2 < p) { System.out.println(" split into 2 subsets of size " + i + ": p=" + p2); break; } } } } } ================================================ FILE: src/test/java/org/minperf/medium/SimulateProbFallIntoLarge.java ================================================ package org.minperf.medium; import java.util.Random; public class SimulateProbFallIntoLarge { public static void main(String... args) { int avgSize = 10; Random r = new Random(0); for (int maxSize = 1; maxSize < 20; maxSize++) { System.out.println("maxSize=" + maxSize); for (int size = 100000; size <= 10000000; size *= 10) { for (int test = 0; test < 10; test++) { int bucketCount = size / avgSize; int[] bucketSizes = new int[bucketCount]; for (int i = 0; i < size; i++) { bucketSizes[r.nextInt(bucketCount)]++; } int large = 0; for (int i = 0; i < bucketCount; i++) { if (bucketSizes[i] > maxSize) { large += bucketSizes[i]; } } System.out .println(" size=" + size + " large=" + large + " " + 100. / size * large + "% in large"); } } } } } ================================================ FILE: src/test/java/org/minperf/medium/TestBBHash.java ================================================ package org.minperf.medium; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.HashSet; import org.minperf.RandomizedTest; import org.minperf.Settings; import org.minperf.universal.LongHash; public class TestBBHash { public static void main(String... args) { for (long size = 1_000_000_000_000L; size <= 1_000_000_000_000L; size *= 10) { double probDuplicates = 1.0 / 100; int bitPerSignature = EstimateTimeForHugeSets.getBitsForProbability(size, probDuplicates); System.out.println( size + " 1:" + 1 / probDuplicates + " prob of a duplicate bits/signature: " + bitPerSignature); long signatureBits = bitPerSignature * size; System.out.println(" signature size: " + EstimateTimeForHugeSets.formatBits(signatureBits)); } for (int len = 10; len <= 100000000; len *= 10) { test(len); } } private static void test(int size) { System.out.println("size " + size); HashSet set = RandomizedTest.createSet(size, 1); TestBBHash t = new TestBBHash(); t.generate(set); System.out.println("cardinality " + t.bits.cardinality()); } BitSet bits; ArrayList remainder; private static final double LEVEL_BITS = 1 / Math.log(2); public void generate(Collection set) { int size = set.size(); bits = new BitSet((int) (2 * size * LEVEL_BITS)); BitSet duplicates = new BitSet((int) (size * LEVEL_BITS)); remainder = new ArrayList(); int offset = 0; int level = 0; int len = (int) (LEVEL_BITS * size); int lastLen = len; int lastOffset = 0; boolean newDuplicates = false; do { newDuplicates = false; for(long x : set) { boolean wasAdded = false; if (level > 0) { wasAdded = true; long hash = LongHash.universalHash(x, level - 1); int index = lastOffset + Settings.reduce((int) hash, lastLen); wasAdded = !duplicates.get(index); } if (wasAdded) { continue; } long hash = LongHash.universalHash(x, level); int index = offset + Settings.reduce((int) hash, len); if (duplicates.get(index)) { // nothing to do } else if (bits.get(index)) { bits.clear(index); duplicates.set(index); newDuplicates = true; } else { bits.set(index); } } lastOffset = offset; offset += len; lastLen = len; len /= 2; level++; System.out.println("level " + level + " len " + len + " cardinality " + bits.cardinality() + " duplicates " + duplicates.cardinality()); } while (newDuplicates); } } ================================================ FILE: src/test/java/org/minperf/monotoneList/FenwickTreeMonotoneList.java ================================================ package org.minperf.monotoneList; /** * A monotone list that uses a fenwick tree. */ public class FenwickTreeMonotoneList extends MonotoneList { private final int[] array; FenwickTreeMonotoneList(int[] array) { this.array = array; } public static FenwickTreeMonotoneList generate(int[] data) { int[] array = new int[data.length]; int last = 0; for (int i = 0; i < data.length; i++) { int x = data[i]; add(array, i, x - last); last = x; } for (int i = 0; i < data.length; i++) { if (sum(array, i) != data[i]) { throw new AssertionError(); } } return new FenwickTreeMonotoneList(array); } private static void add(int[] array, int index, int value) { int len = array.length; index++; while (index <= len) { array[index - 1] += value; index += index & -index; } } private static int sum(int[] array, int index) { int sum = 0; index++; while (index > 0) { sum += array[index - 1]; index -= index & -index; } return sum; } @Override public int get(int i) { return sum(array, i); } @Override public long getPair(int i) { return (((long) get(i)) << 32) | get(i + 1); } } ================================================ FILE: src/test/java/org/minperf/monotoneList/MonotoneListTest.java ================================================ package org.minperf.monotoneList; import static org.junit.Assert.assertEquals; import java.util.Random; import org.junit.Test; import org.minperf.BitBuffer; /** * Test the MonotoneList implementations. */ public class MonotoneListTest { public static void main(String... args) { testPerformance(); testPerformance(); testPerformance(); testBestSize(100, 8, true); testBestSize(100, 8, false); new MonotoneListTest().testSaving(); } private static void testPerformance() { int bucketSize = 16, size = 1000000; test(bucketSize, size, false); test(bucketSize, size, true); } @Test public void test() { for (int bucketSize = 8; bucketSize < 256; bucketSize *= 2) { for (int size = 100; size <= 1000000; size *= 10) { if (size >= bucketSize) { test(bucketSize, size, true); test(bucketSize, size, false); } } } } public void testSaving() { for (int bucketSize = 8; bucketSize < 256; bucketSize *= 2) { for (int size = 100; size <= 100000000; size *= 10) { if (size >= bucketSize) { test(bucketSize, size, true); test(bucketSize, size, false); } } } } private static void testBestSize(int bucketSize, int size, boolean eliasFano) { int bucketCount = size / bucketSize; int[] sizes = randomSizes(size, bucketCount); int[] posList = posList(sizes); int bestLen = Integer.MAX_VALUE; String best = ""; BitBuffer buffer = new BitBuffer(100 * bucketCount); MonotoneList.generate(posList, buffer, eliasFano); assertEquals(MonotoneList.getSize(posList, eliasFano), buffer.position()); int len2 = buffer.position(); for (int shift1 = 2; shift1 <= 16; shift1++) { for (int shift2 = 1; shift2 <= shift1; shift2++) { for (int factor1 = 4; factor1 < 256; factor1 *= 2) { for (int factor2 = 2; factor2 <= factor1; factor2 *= 2) { buffer = new BitBuffer(100 * bucketCount); // MultiStageMonotoneList.SHIFT1 = shift1; // MultiStageMonotoneList.SHIFT2 = shift2; // MultiStageMonotoneList.FACTOR1 = factor1; // MultiStageMonotoneList.FACTOR2 = factor2; MultiStageMonotoneList list = MultiStageMonotoneList .generate(posList, buffer); for (int i = 0; i < posList.length; i++) { assertEquals("i:" + i, posList[i], list.get(i)); } int len = buffer.position(); if (len < bestLen) { best = " shift " + shift1 + "/" + shift2 + " factor " + factor1 + "/" + factor2 + " len " + len + " len2 " + len2; bestLen = len; } } } } } System.out.println(size + " " + best + " " + len2); } private static void test(int bucketSize, int size, boolean eliasFano) { int bucketCount = size / bucketSize; int[] sizes = randomSizes(size, bucketCount); int[] posList = posList(sizes); int[] expected = expectedPosList(size, bucketCount); int[] offsets = offsets(posList, expected); int min = min(offsets), max = max(offsets); int entryBits = 32 - Integer.numberOfLeadingZeros(-min + max); int diff = min(sizes); int[] sizeOffsets = plus(sizes, -diff); int[] posList2 = posList(sizeOffsets); // System.out.println(size + " avg gap: " + (double) posList2[posList2.length - 1] / posList2.length); for (int i = 0; i < bucketCount; i++) { assertEquals(posList[i], posList2[i] + i * diff); } BitBuffer buffer = new BitBuffer(1000 + 100 * size); MonotoneList list = MonotoneList.generate(posList2, buffer, eliasFano); assertEquals(MonotoneList.getSize(posList2, eliasFano), buffer.position()); int bitCount = buffer.position(); double oldBits = (double) (entryBits * bucketCount) / size; double newBits = (double) bitCount / size; System.out.println("bucketSize " + bucketSize + " bucketCount " + bucketCount + " old " + oldBits + " (" + entryBits + "*" + bucketCount + ") new " + newBits + " saving " + (oldBits - newBits) + " " + (eliasFano ? "elias-fano" : "fast")); for (int i = 0; i < bucketCount; i++) { assertEquals("i: " + i, posList2[i], list.get(i)); } buffer.seek(0); list = MonotoneList.load(buffer, eliasFano); buffer.seek(0); MonotoneList list2 = MonotoneList.load(buffer, eliasFano); assertEquals(bitCount, buffer.position()); for (int i = 0; i < bucketCount; i++) { assertEquals(posList2[i], list.get(i)); } for (int i = 0; i < bucketCount - 1; i++) { int a = list.get(i); int b = list.get(i + 1); long ab = list.getPair(i); assertEquals(((long) a << 32) + b, ab); } Random r = new Random(1); // memory access elsewhere byte[] data = new byte[bucketCount * 8]; int[] readList = new int[bucketCount]; r.nextBytes(data); for (int i = 1; i < bucketCount; i++) { readList[i] = r.nextInt(bucketCount - 1); } int dummy = 0; long time = System.nanoTime(); for (int test = 0; test < 10; test++) { for (int i = 0; i < bucketCount - 1; i++) { int x = readList[i]; dummy += list.get(x); dummy += list2.get(x); } } time = System.nanoTime() - time; if (bucketCount > 1) { System.out.println("Time: " + time / 1000000 + " ms; " + time / 10 / 2 / (bucketCount - 1) + " ns/key dummy " + dummy); } } private static int[] randomSizes(int size, int bucketCount) { Random r = new Random(size + bucketCount); int[] sizes = new int[bucketCount]; for (int i = 0; i < size; i++) { sizes[r.nextInt(bucketCount)]++; } return sizes; } private static int[] expectedPosList(int size, int bucketCount) { int[] sizes = new int[bucketCount]; for (int i = 0; i < bucketCount; i++) { sizes[i] = (int) ((long) size * i / bucketCount); } return sizes; } private static int[] offsets(int[] got, int[] expected) { int[] offsets = new int[got.length]; for (int i = 0; i < offsets.length; i++) { offsets[i] = got[i] - expected[i]; } return offsets; } private static int min(int[] sizes) { int min = Integer.MAX_VALUE; for (int x : sizes) { min = Math.min(min, x); } return min; } private static int max(int[] sizes) { int max = Integer.MIN_VALUE; for (int x : sizes) { max = Math.max(max, x); } return max; } private static int[] plus(int[] list, int plus) { int[] list2 = new int[list.length]; for (int i = 0; i < list.length; i++) { list2[i] = list[i] + plus; } return list2; } private static int[] posList(int[] sizes) { int[] posList = new int[sizes.length]; int p = 0; for (int i = 0; i < sizes.length; i++) { posList[i] = p; p += sizes[i]; } return posList; } } ================================================ FILE: src/test/java/org/minperf/rank/Rank9Test.java ================================================ package org.minperf.rank; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.BitSet; import java.util.Random; import org.junit.Test; import org.minperf.hash.Mix; /** * Test the simple rank/select implementation. */ public class Rank9Test { public static void main(String... args) { new Rank9Test().test(); new Rank9Test().testPerformance(); } private void testPerformance() { for (int size = 1 << 20; size > 0; size *= 2) { System.out.println(); System.out.println("Size: " + size); long time = System.nanoTime(); BitSet set = new BitSet(size); for (int i = 0; i < size;) { long x = Mix.hash64(i); for (int j = 0; j < 64; j++, i++) { set.set(i, (x & 1) == 1); x >>>= 1; } } time = (System.nanoTime() - time) / size; System.out.println("Construct bitset: " + time + " ns/bit cardinality: " + set.cardinality()); time = System.nanoTime(); Rank9 rank = new Rank9(set, size); time = (System.nanoTime() - time) / size; System.out.println("Construct rank: " + time + " ns/bit"); int[] list = new int[size]; for (int i = 0; i < size; i++) { list[i] = (int) Mix.hash64(i * 0xc4ceb9fe1a85ec53L) & (size - 1); } time = System.nanoTime(); int count = 0; for (int x : list) { count += rank.get(x) != 0 ? 1 : 0; } time = (System.nanoTime() - time) / size; System.out.println("Randomized get: " + time + " ns/bit; results: " + count); time = System.nanoTime(); long sum = 0; for (int x : list) { sum += rank.rank(x); } time = (System.nanoTime() - time) / size; System.out.println("Randomized rank: " + time + " ns/bit; result: " + sum); time = System.nanoTime(); count = 0; sum = 0; for (int x : list) { count += rank.get(x) != 0 ? 1 : 0; sum += rank.rank(x); } time = (System.nanoTime() - time) / size; System.out.println("Randomized rank, then get: " + time + " ns/bit; results: " + count + " " + sum); time = System.nanoTime(); count = 0; sum = 0; for (int x : list) { long rankAndGet = rank.rankAndGet(x); count += rankAndGet & 1; sum += rankAndGet >>> 1; } time = (System.nanoTime() - time) / size; System.out.println("Randomized rankAndGet: " + time + " ns/bit; results: " + count + " " + sum); } } @Test public void test() { for (int size = 0; size < 2000; size++) { test(size); } for (int size = 64; size < 1024 * 1024; size *= 2) { test(size); } } private static void test(int size) { testFull(size); testRandom(size); testEmpty(size); } private static void testEmpty(int size) { BitSet set = new BitSet(); set.set(0, size, false); Rank9 rank = new Rank9(set, size); assertEquals(0, rank.rank(0)); for (int j = 0; j < size; j++) { assertEquals(0L, rank.rank(j)); } } private static void testRandom(int size) { BitSet set = new BitSet(); Random r = new Random(size); for (int i = 0; i < size / 10; i++) { while (true) { int x = r.nextInt(size); if (!set.get(x)) { set.set(x); break; } } } Rank9 rank = new Rank9(set, size); assertEquals(0, rank.rank(0)); int x = 0; for (int j = 0; j < size; j++) { assertEquals(x, rank.rank(j)); assertEquals(x, rank.rankAndGet(j) >>> 1); if (set.get(j)) { assertTrue(rank.get(j) != 0); assertTrue((rank.rankAndGet(j) & 1) != 0); x++; } else { assertFalse(rank.get(j) != 0); assertFalse((rank.rankAndGet(j) & 1) != 0); } } } private static void testFull(int size) { BitSet set = new BitSet(); set.set(0, size, true); Rank9 rank = new Rank9(set, size); assertEquals(0, rank.rank(0)); for (int j = 1; j < size; j++) { assertEquals(j, rank.rank(j)); } } } ================================================ FILE: src/test/java/org/minperf/rank/RankTest.java ================================================ package org.minperf.rank; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.BitSet; import java.util.Random; import org.junit.Test; import org.minperf.BitBuffer; /** * Test the simple rank/select implementation. */ public class RankTest { public static void main(String... args) { new RankTest().test(); } @Test public void test() { int maxOverhead = 0; for (int i = 1; i < 1000; i++) { BitBuffer buffer = new BitBuffer(8000); BitSet set = new BitSet(i + 1); set.set(0, i); VerySimpleRank rank = VerySimpleRank.generate(set, buffer); maxOverhead = Math.max(maxOverhead, rank.getOverhead()); int read = rank.getReadBits(); double readFactor = read / (Math.log(i) / Math.log(2)); if (i > 10 && readFactor > 4) { fail(); } } assertTrue(maxOverhead < 32); for (int size = 0; size < 2000; size++) { test(size); } for (int size = 64; size < 1024 * 1024; size *= 2) { test(size); } } private static void test(int size) { testFull(size); testRandom(size); testEmpty(size); } private static void testEmpty(int size) { BitSet set = new BitSet(); set.set(0, size, false); BitBuffer buffer = new BitBuffer(100 + size * 2); VerySimpleRank rank = VerySimpleRank.generate(set, buffer); rank = reopen(rank, buffer); assertEquals(0, rank.rank(0)); for (int j = 0; j < size; j++) { assertEquals(0L, rank.rank(j)); } assertEquals(-1, rank.select(1)); } private static void testRandom(int size) { BitSet set = new BitSet(); Random r = new Random(size); for (int i = 0; i < size / 10; i++) { while (true) { int x = r.nextInt(size); if (!set.get(x)) { set.set(x); break; } } } BitBuffer buffer = new BitBuffer(100 + size * 2); VerySimpleRank rank = VerySimpleRank.generate(set, buffer); rank = reopen(rank, buffer); assertEquals(0, rank.rank(0)); int x = 0; for (int j = 0; j < size; j++) { assertEquals(x, rank.rank(j)); if (set.get(j)) { assertTrue(rank.get(j)); assertEquals(j, rank.select(x)); x++; } else { assertFalse(rank.get(j)); } } } private static void testFull(int size) { BitSet set = new BitSet(); set.set(0, size, true); BitBuffer buffer = new BitBuffer(100 + size * 2); VerySimpleRank rank = VerySimpleRank.generate(set, buffer); rank = reopen(rank, buffer); assertEquals(0, rank.rank(0)); for (int j = 1; j < size; j++) { assertEquals(j, rank.rank(j)); assertEquals(j, rank.select(j)); } } private static VerySimpleRank reopen(VerySimpleRank rank, BitBuffer buffer) { int bitsUsed = buffer.position(); int size = rank.getSize(); BitBuffer b2 = new BitBuffer(bitsUsed); b2.write(buffer); b2.seek(0); VerySimpleRank result = VerySimpleRank.load(b2); assertEquals(bitsUsed, b2.position()); assertEquals(size, rank.getSize()); return result; } } ================================================ FILE: src/test/java/org/minperf/select/SelectTest.java ================================================ package org.minperf.select; import static org.junit.Assert.assertEquals; import java.util.BitSet; import java.util.Random; import org.junit.Test; import org.minperf.BitBuffer; import org.minperf.rank.VerySimpleRank; /** * Test the simple select implementation. */ public class SelectTest { public static void main(String... args) { System.out.println("FastSelect performance test"); for (int i = 0; i < 4; i++) { testPerformance(); } System.out.println("Select int/long performance test"); for (int i = 0; i < 4; i++) { testLongSelectPerformance(); } } @Test public void testBitInInt() { Random r = new Random(1); for (int n = 0; n < 64; n++) { for (int i = 0; i < 100; i++) { if (Long.bitCount(i) < n + 1) { continue; } int a = VerySimpleSelect.selectBitSlow(i, n); int b = VerySimpleSelect.selectBitLong(i, n); int c = VerySimpleSelect.selectBit(i, n); int d = VerySimpleSelect.selectBitReverse(Integer.reverse(i), n); assertEquals(a, b); assertEquals(a, c); assertEquals(a, d); } for (int i = 0; i < 100; i++) { int x = r.nextInt(); if (Integer.bitCount(i) < n + 1) { continue; } int a = VerySimpleSelect.selectBitSlow(x, n); int b = VerySimpleSelect.selectBitLong(x & 0xffffffffL, n); int c = VerySimpleSelect.selectBitLongReverse( Long.reverse(x & 0xffffffffL), n); int d = VerySimpleSelect.selectBit(x, n); int e = VerySimpleSelect.selectBitReverse(Integer.reverse(x), n); assertEquals(a, b); assertEquals(a, c); assertEquals(a, d); assertEquals(a, e); } for (int i = 0; i < 100; i++) { long x = r.nextLong(); if (Long.bitCount(x) < n + 1) { continue; } int a = VerySimpleSelect.selectBitSlow(x, n); int b = VerySimpleSelect.selectBitLong(x, n); assertEquals(a, b); } } } private static void testLongSelectPerformance() { Random r = new Random(1); int[] values = new int[1024 * 1024]; int[] list = new int[1024 * 1024]; int tests = 100; for (int i = 0; i < 1024 * 1024; i++) { values[i] = r.nextInt(); list[i] = r.nextInt(Integer.bitCount(values[i])); } int sum = 0; long time = System.currentTimeMillis(); for (int test = 0; test < tests; test++) { for (int i = 0; i < 1024 * 1024; i++) { sum += VerySimpleSelect.selectBit(values[i], list[i]); } } time = System.currentTimeMillis() - time; System.out.println("new sum: " + sum + " " + time); } @Test public void testSelect() { testSelect(1000, 5); for (int size = 1; size <= 100000; size *= 10) { for (int fill = 0; fill <= 100; fill += 5) { testSelect(size, fill); } } } private static void testSelect(int size, int fill) { Random r = new Random(fill); BitSet set = new BitSet(); int count = fill * size / 100; for (int i = 0; i < count; i++) { int x = r.nextInt(size); set.set(x); } BitBuffer buffer = new BitBuffer(10000 + 10 * set.size()); Select select = Select.generate(set, buffer); int p1 = buffer.position(); assertEquals(p1, Select.getSize(set)); buffer.seek(0); select = Select.load(buffer); int p2 = buffer.position(); assertEquals(p1, p2); BitBuffer buffer2 = new BitBuffer(10000 + 10 * set.size()); VerySimpleRank rank = VerySimpleRank.generate(set, buffer2); p1 = buffer.position(); buffer2.seek(0); rank = VerySimpleRank.load(buffer2); p2 = buffer.position(); assertEquals(p1, p2); for (int i = 0, j = 0; i < set.length(); i++) { if (set.get(i)) { int x = (int) rank.select(j); assertEquals(i, x); x = (int) select.select(j); assertEquals(i, x); j++; } } int cardinality = set.cardinality(); for (int j = 0; j < cardinality - 1; j++) { int x1 = (int) select.select(j); int x2 = (int) select.select(j + 1); long x12 = select.selectPair(j); assertEquals(x1, x12 >>> 32); assertEquals(x2, (int) x12); } } private static void testPerformance() { int size = 1000000; int blockSize = 16; int[] count = new int[size]; Random r = new Random(1); for (int i = 0; i < size * blockSize; i++) { count[r.nextInt(size)]++; } int[] sumCount = new int[size]; int sum = 0; for (int i = 0; i < size; i++) { sumCount[i] = sum; sum += count[i]; } int max = sum; int len = size; int lowBitCount = 32 - Integer.numberOfLeadingZeros(Integer .highestOneBit(max / len)); BitSet set = new BitSet(); for (int i = 0; i < len; i++) { int x = i + (sumCount[i] >>> lowBitCount); set.set(x); } // System.out.println("set: " + set); BitBuffer buffer = new BitBuffer(10 * set.size()); Select select = Select.generate(set, buffer); int bitCount = buffer.position(); assertEquals(bitCount, Select.getSize(set)); System.out.println("bits/key fast: " + ((double) bitCount / size)); long time; time = System.nanoTime(); // Profiler prof = new Profiler().startCollecting(); for (int k = 0; k < 10; k++) { for (int i = 0, j = 0; i < set.length(); i++) { if (set.get(i)) { int x = (int) select.select(j); // System.out.println("select(" + j + ")=" + x); if (x != i) { System.out.println("WRONG: select(" + j + ") = " + x + " ; expected i = " + i + " j=" + j); // x = (int) select.select(j); // throw new AssertionError("exp " + i + " got " + x + // " at select(" + j + ")"); } j++; } } } // System.out.println(prof.getTop(5)); time = System.nanoTime() - time; System.out.println("time: " + (time / 10 / set.length())); buffer = new BitBuffer(10 * set.size()); VerySimpleRank rank = VerySimpleRank.generate(set, buffer); bitCount = buffer.position(); System.out.println("bits/key rank: " + ((double) bitCount / size)); time = System.nanoTime(); for (int k = 0; k < 10; k++) { for (int i = 0, j = 0; i < set.length(); i++) { if (set.get(i)) { int x = (int) rank.select(j); if (x != i) { throw new AssertionError(); // System.out.println("wrong: select(" + j + ") = " + x // + " ; expected i=" + i + " j=" + j); } j++; } } } time = System.nanoTime() - time; System.out.println("time: " + (time / 10 / set.length())); } } ================================================ FILE: src/test/java/org/minperf/simple/LongCollection.java ================================================ package org.minperf.simple; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import org.minperf.BitBuffer; import org.minperf.RecSplitBuilder; import org.minperf.universal.LongHash; public class LongCollection implements Collection { public static void main(String... args) { long[] data = new long[63_000_000]; // Random r = new Random(1); for (int i = 0; i < data.length; i++) { data[i] = i * 2; // r.nextLong(); } Arrays.sort(data); for (int i = 1; i < data.length; i++) { if (data[i - 1] == data[i]) { System.out.println("duplicate!"); } } LongCollection collection = new LongCollection(data); System.out.println("generating..."); RecSplitBuilder builder = RecSplitBuilder.newInstance(new LongHash()); builder.leafSize(7); builder.averageBucketSize(1000); builder.maxChunkSize(1_000_000); long time = System.currentTimeMillis(); BitBuffer buff = builder.generate(collection); System.out.println((double) buff.position() / data.length + " bits/key"); time = System.currentTimeMillis() - time; System.out.println(time + " ms"); } private final long[] data; LongCollection(long[] data) { this.data = data; } @Override public int size() { return data.length; } @Override public boolean isEmpty() { return data.length == 0; } @Override public boolean contains(Object o) { if (o == null) { return false; } else if (!(o instanceof Long)) { return false; } long x = (Long) o; for(long y : data) { if (y == x) { return true; } } return false; } @Override public Iterator iterator() { return new Iterator() { int pos; @Override public boolean hasNext() { return pos < data.length; } @Override public Long next() { return data[pos++]; } }; } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public T[] toArray(T[] a) { throw new UnsupportedOperationException(); } @Override public boolean add(Long e) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } } ================================================ FILE: src/test/java/org/minperf/simple/SimpleRecSplit.java ================================================ package org.minperf.simple; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.minperf.universal.StringHash; /** * A very simple implementation of the RecSplit algorithm. Compression is not * used. */ public class SimpleRecSplit { /** * The average bucket size. */ static final int AVERAGE_BUCKET_SIZE = 100; /** * The maximum size of a leaf. */ static final int LEAF_SIZE = 8; /** * The universal hash function. */ static final StringHash UNIVERSAL_HASH = new StringHash(); /** * Calculate the universal hash of the string modulo the given value. * * @param x the key * @param index the universal hash index * @param mod the modulo * @return a value between 0 (including) and the modulo (excluding) */ static int universalHash(String x, int index, int mod) { return (int) Math.abs(UNIVERSAL_HASH.universalHash(x, index) % mod); } /** * The generator class (not really a class, just a collection of static * methods). */ public static class Generator { /** * Generate the MPHF description for the given set. * * @param set the set * @return the description, in the form of an integer array * (uncompressed) */ public static int[] generate(Set set) { int bucketCount = 1 + (set.size() / AVERAGE_BUCKET_SIZE); List> buckets = partition(set, bucketCount); List> bucketData = new ArrayList<>(); int bucketsSize = 0; for (int i = 0; i < bucketCount; i++) { ArrayList bucket = new ArrayList<>(); processRecursively(buckets.get(i), bucket); bucketData.add(bucket); bucketsSize += bucket.size(); } int[] description = new int[1 + 2 * bucketCount + bucketsSize]; description[0] = set.size(); int pos = 1 + 2 * bucketCount, size = 0, d = 0; for (int i = 0; i < bucketCount; i++) { List b = bucketData.get(i); d += b.size(); description[1 + i] = d; size += buckets.get(i).size(); description[1 + bucketCount + i] = size; for (int j = 0; j < b.size(); j++) { description[pos++] = b.get(j); } } return description; } /** * Partition the set. * * @param set the input set * @param bucketCount the number of buckets * @return the list of buckets, each one with a set */ static List> partition(Set set, int bucketCount) { List> buckets = new ArrayList>(); for (int i = 0; i < bucketCount; i++) { buckets.add(new LinkedHashSet()); } for (String x : set) { int bucketId = universalHash(x, 0, bucketCount); buckets.get(bucketId).add(x); } return buckets; } /** * Process a set recursively. * * @param set the set * @param description the description (uncompressed) */ static void processRecursively(Set set, List description) { if (set.size() <= 1) { return; } if (set.size() <= LEAF_SIZE) { for (int x = 0;; x++) { if (canMapDirectly(set, x)) { description.add(x); return; } } } for (int x = 0;; x++) { List> list = trySplit(set, x); if (list != null) { description.add(x); for (Set s : list) { processRecursively(s, description); } return; } } } /** * Whether the given set can be mapped directly at this index. * * @param set the set * @param index the universal hash index * @return true if yes */ static boolean canMapDirectly(Set set, int index) { Set used = new HashSet(); for (String s : set) { used.add(universalHash(s, index, set.size())); } return used.size() == set.size(); } /** * Try to split the set into two subsets of equal size (the size of the * first subset must be size / 2). * * @param set the set * @param index the universal hash index * @return null if it didn't work out, or the list splitting worked */ static List> trySplit(Set set, int index) { List> list = new ArrayList<>(2); for (int i = 0; i < 2; i++) { list.add(new LinkedHashSet()); } for (String s : set) { list.get(universalHash(s, index, 2)).add(s); } return (list.get(0).size() == set.size() / 2) ? list : null; } } /** * The evaluator class (not really a class, just a collection of static * methods). */ public static class Evaluator { /** * Evaluate the MPHF for the given key. * * @param x the key * @param description the MPHF description * @return the return of the function */ public static int evaluate(String x, int[] description) { int totalSize = description[0]; int bucketCount = 1 + totalSize / AVERAGE_BUCKET_SIZE; int bucketId = universalHash(x, 0, bucketCount); int pos = 1 + 2 * bucketCount, offset = 0; if (bucketId > 0) { pos += description[1 + (bucketId - 1)]; offset = description[1 + bucketCount + (bucketId - 1)]; } int setSize = description[1 + bucketCount + bucketId] - offset; while (setSize > 1) { int index = description[pos++]; if (setSize <= LEAF_SIZE) { return offset + universalHash(x, index, setSize); } int p = universalHash(x, index, 2); if (p % 2 == 0) { setSize = setSize / 2; } else { offset += setSize / 2; pos = skip(description, setSize / 2, pos); setSize -= setSize / 2; } } return offset; } /** * Skip the description of a subset. * * @param description the MPHF description * @param setSize the size of the set to skip * @param pos the current position in the description list * @return the new position in the description list */ static int skip(int[] description, int setSize, int pos) { if (setSize <= 1) { return pos; } else if (setSize <= LEAF_SIZE) { return pos + 1; } pos = skip(description, setSize / 2, pos + 1); return skip(description, setSize - setSize / 2, pos); } } } ================================================ FILE: src/test/java/org/minperf/simple/SimpleTest.java ================================================ package org.minperf.simple; import java.time.Month; import java.time.format.TextStyle; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Set; import org.minperf.Probability; import org.minperf.universal.StringHash; public class SimpleTest { public static void main(String... args) { example(); printProbabilityOfLargeBucket(); printAbcKeySample(); printMonthSample(); test(); } private static void compareSpace() { Set set = new HashSet<>(Arrays.asList("a", "b", "c", "d", "e")); int[] desc = SimpleRecSplit.Generator.generate(set); for(String s : set) { System.out.println(s + " -> " + SimpleRecSplit.Evaluator.evaluate(s, desc)); } } private static void example() { Set set = new HashSet<>(Arrays.asList("a", "b", "c", "d", "e")); int[] desc = SimpleRecSplit.Generator.generate(set); for(String s : set) { System.out.println(s + " -> " + SimpleRecSplit.Evaluator.evaluate(s, desc)); } } private static void printProbabilityOfLargeBucket() { int average = 100; int max = 2000; double p = Probability.probabilityLargeBucket2(average, max); double p2 = Probability.probabilityLargeBucket(average, max); double p3 = 1.0; for (int i = 0; i < max; i++) { double ps = Probability.getProbabilityOfBucketFallsIntoBinOfSize(average, i); p3 -= ps; if ((ps == 0 && i > average) || p3 <= 0) { System.out.println("not measurable at bucket size " + i); break; } } System.out.println("Probability of large bucket size: " + p + " / " + p2 + " / " + p3); } private static void printAbcKeySample() { StringHash hash = new StringHash(); String[] list = new String[] { "a", "b", "c", "d" }; for (String s : list) { System.out.print(s + ": "); for (int i = 0; i < 20; i++) { long x = hash.universalHash(s, i) & 3; System.out.print(x + "\t"); } System.out.println(); } } private static void printMonthSample() { Set set = new LinkedHashSet(); for(int i=1; i<=12; i++) { String s = Month.of(i).getDisplayName(TextStyle.SHORT, Locale.ENGLISH).toLowerCase(); set.add(s); } System.out.println(SimpleRecSplit.Generator.partition(set, 4)); set = SimpleRecSplit.Generator.partition(set, 4).get(0); for (int index = 0;; index++) { List> list = SimpleRecSplit.Generator.trySplit(set, index); if (list != null) { System.out.println(index + " " + list); for (int i = 0; i < list.size(); i++) { Set s = list.get(i); System.out.println(s); for (int i2 = 0;; i2++) { if (SimpleRecSplit.Generator.canMapDirectly(s, i2)) { System.out.println("index: " + i2); for (String x : s) { System.out.println(x + " " + SimpleRecSplit.universalHash(x, i2, s.size())); } break; } } } break; } } } public static void test() { Set set = new HashSet(); for (int i = 0; i < 100000; i++) { set.add("Hello " + i); } int[] data = SimpleRecSplit.Generator.generate(set); HashSet used = new HashSet(); for (String x : set) { int e = SimpleRecSplit.Evaluator.evaluate(x, data); if (!used.add(e)) { e = SimpleRecSplit.Evaluator.evaluate(x, data); throw new AssertionError(); } } } } ================================================ FILE: src/test/java/org/minperf/simple/recsplit.md ================================================ ## Minimal Perfect Hashing with RecSplit ### Use Cases This could be a Facebook interview question: Your task is to find which users didn't ever create a post with an emoji. You have the list of users (2 billion), which you can scan at most 40 times, and a huge list of posts (user and text), which you can scan only once. Also, you have one computer, with 1 GB of RAM. And no external disk, so it has to be done all in RAM. Within two days. Can you do it? How? Of course you need one bit per user. That is 240 MB. So that leaves around 3 bit per user, which is not enough for a regular hash table. So the answer is, you use minimal perfect hashing. This needs just 2 bits for the index (a bit more during construction). Some use cases for minimal perfect hashing are: * Page Rank: there are billions of web sites (URLs or host names), and for each entry, you need a few bits for a counter to calculate the rank. * Garbage collection in a de-duplication store: billion of keys, each entry needs a one-bit marker. * Eye-based tracking: each word has many thousand "near matches" (tracking is very inaccurate) For the above use cases, you could use a database of some kind, but that requires much more space and I/O. In addition to the above, minimum perfect hash tables are sometimes used in compilers, to index entries in a "switch" statement. The main benefit is that case is the speed of hash functions, and not the small size of the index. ### Minimal Perfect Hashing What is minimal perfect hashing exactly, and how is it different from regular hashing? A regular hash function can have collisions, meaning multiple keys can hash to the same code. That means in a regular hash table, we need to keep the keys, so that we can resolve possible conflicts. For a fixed set of keys, it is possible, with some effort, to find a perfect hash function (PHF), that is a hash function that has no collisions for the given set: such a PHF maps n keys to m distinct values, where m >= n. With such a PHF, we can save space, as we don't necessarily need to keep the keys to resolve conflicts. We can save even more space if there are no gaps; for that we need to find a minimal perfect hash function (MPHF), that is a hash function that maps the n keys to n distinct values. This only needs around 2 bits per key. The main disadvantage of a minimal perfect hash functions is that it requires a fixed, pre-defined set of keys (even though there exist techniques that support modifications, under the name Dynamic Perfect Hashing). Then, not storing the key means you must only do lookups by a key that are in the set, or that false positives are OK. For example for garbage collection, there are simply no lookups by keys not in the set. You can detect with high probability if a key is not in the set, for example using a Bloom filter, or, even faster and more space saving, by keeping hash fingerprints, for example 8 bit per key for a detection rate of 99%. ### Universal Hashing How can we construct a (minimal) perfect hash function? A simple way, which unfortunately only works for small sets, is to use a technique called "Universal Hashing". This is basically a seeded hash function. As an example, a regular hash function is (Java code): public static int hashCode(byte[] a) { int result = 1; for (byte element : a) result = 31 * result + element; return result; } A seeded hash function, on the other hand, is something like this: public static int universalHash(byte[] a, int index) { int result = index; for (byte element : a) result = 31 * result + element + index; return result; } This is a sample implementation; it is not very "random". It is better to use a different algorithms, such as seeded Murmur Hash, or SpookyHash, or SipHash. Assuming we use this hash function for the keys "a", "b", "c", and "d", we get the following results for the formula `universalHash(k, index) mod 4`: Index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 ------ | - | - | - | - | - | - | - | - Key: "a" | 1 | 2 | 3 | 2 | 3 | 2 | 3 | 2 Key: "b" | 0 | 1 | 2 | 2 | 2 | 2 | 2 | 1 Key: "c" | 0 | 3 | 2 | 2 | 3 | 1 | 1 | 0 Key: "d" | 2 | 1 | 2 | 2 | 0 | 1 | 0 | 0 We see that at index 6, the result for each key is different. Therefore, `f(k) = universalHash(k, 6) mod 4` is actually a MPHF (minimal perfect hash function) for these keys! The only thing we need to store is the set size, and the index (the seed value). Finding a MPHF for a set in this way is brute force: find the first index where `universalHash(key, index) mod size` has no collision. This works well for small sets, but quickly get very slow. The challenge is to support huge sets, O(n) generation, and constant time evaluation. All existing algorithms for "large" MPHFs internally use universal hashing. ### Best Algorithms There is a proven theoretical lower bound for MPHFs, which is around 1.44 bits per key. The best algorithms that work well for large sets (that is, can generate an MPHF in linear time and space, and can evaluate it in constant time) are: * RecSplit: around 1.8 bits/key * CHD: around 2.1 bits/key * BDZ: around 2.7 bits/key This is assuming somewhat reasonable generation time; both RecSplit and CHD can approach the theoretical lower bound given enough time. ### RecSplit Algorithm Explained We now explain how the RecSplit algorithm works. The algorithm has three phases: partitioning, bucket processing, and storing. ### Partitioning The first phase partition the n keys into, let's say, n/100 buckets. That means the average bucket size is about 100. After this is done, buckets with more than, say, 2000 keys are merged and processed with a different fallback algorithm, for example BDZ. This makes RecSplit a hybrid algorithm in theory, however the probability of this is so low that this case can not occur in reality, except if the hash function is broken. Having this fallback guarantees that generation is strictly O(n), and lookups are strictly O(1). As an example, say we want to partition the set of 12 month name abbreviations, `jan`,`feb`,..., `dec`, into 4 buckets, numbered 0 to 3. That would be an average bucket size of 4. Say the method `universalHash(k, 0) modulo 4` maps the keys `feb`, `mar`, `may`,`jun`, `jul`, `dec` to bucket 0. The keys `jan`, `nov` go to bucket 1, the keys `apr`, `aug`, `sep`, `oct` go to bucket 2, and bucket 3 is empty. the overflow is empty as there are no overly large buckets. ### Bucket Processing In this phase, the entries of each bucket are split into small sets until they can be processed using the brute force algorithm described in Universal Hashing above. There are three cases: * Sets of size 0 and 1 are not further processed. * Set up to size 8 are processed using the brute force algorithm described in Universal Hashing above. We store the index. * Larger sets of size s are split as follows: in a brute force loop, we find the first x such that `universalHash(key, index) modulo 2` is 0 in half the cases (therefore splits into two subsets, the first one of size s/2, and the second one the rest). We store that index, and then process the subsets recursively. The bucket description is then the list of indexes that split the set. As an example, say the bucket contains `feb`, `mar`, `may`,`jun`, `jul`, `dec` (bucket 0 from above). Instead of using the limit 8 as described above, we use the limit 4. We are looking for an x so that `universalHash(key, x) modulo 2` splits the set into two subsets of size 3 each. (For an odd set size, the first subset would have one entry more than the second). The first x that does that is 3, and we have the two subsets `feb`, `mar`, `jun` and `may`, `jul`, `dec`. Now we process the subsets recursively. We find that `universalHash(key, 6) modulo 3` maps the first subset without conflict, and `universalHash(key, 0) modulo 3` maps the second subset. The bucket description is therefore 3, 6, 0. ### Storing In order to use less than 2 bits per key, some compression is needed. First, the size of the complete set is stored using the Elias Delta code. This needs few bits for small values, and more bits for larger values. Then, the bucket metadata is stored, in the form of two lists: This first list contains the number of entries per bucket, the second one the positions of the bucket descriptions. The lists are stored in the form of Elias-Fano monotone lists. Last, each bucket description is stored. The indexes are stored using the Rice code, so that small indexes use less bits that large ones. ### Observations For an MPHF size of less than 2 bits/key, most time of the RecSplit algorithm is spend in the Bucket Processing phase. That part is easy to parallelize, as buckets are independent of each other. It also allows to generate the MPHF incrementally if needed, without having to keep it fully in memory all the time. Time can be traded for space. If larger sets are processed directly (without splitting them first into smaller subsets), then the space used shrinks, even below 1.52 bits per key. However, this will slow down generation a lot. ### Perfect Hashing and k-Perfect Hashing Non-minimal perfect hashing allows for gaps, that is it maps n keys to a value 0..m where m > n, still without conflicts. The algorithm described above can be easily modified for this case, all that is needed is to change the the last stage, how sets up to size 8 are processed, to use a larger modulo (for example modulo 9, in which case m is 1.125n). This reduces space usage and speeds up generation. k-perfect hashing allows for conflicts, so that at most k keys map to the same value. The algorithm described can also be modified for this case, by changing the last stage to for example use modulo 4. ### Comparison to CHD and BDZ For the most space saving competing algorithms, CHD and BDZ, generation is hard to parallelize for two reasons: buckets need to be processed in a certain order, and processing a bucket depends on the result of processing all previous buckets. Generation using the RecSplit algorithm, on the other hand, is very easy to parallelize, because buckets can be processed concurrently, in any order, and independently of each other. Both CHD and BDZ need to keep the complete set in memory during generation, while the RecSplit algorithm can work incrementally (only keep, as an example, 1% of all buckets in memory at a time). According to our tests, the RecSplit algorithm is faster than both CHD and BDZ for both generation and evaluation, if configured for the same space usage. ================================================ FILE: src/test/java/org/minperf/tools/Dump.java ================================================ package org.minperf.tools; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import org.minperf.BitBuffer; import org.minperf.RecSplitBuilder; import org.minperf.generator.Generator; import org.minperf.universal.LongHash; /** * Generate a MPHF and store it in a file. */ public class Dump { public static void main(String... args) throws IOException { if (args.length != 4) { System.out.println("Usage: java " + Dump.class.getName() + " "); System.out.println(" n: number of keys"); System.out.println(" bucket size: average bucket size"); System.out.println(" leaf size: size of a leaf"); System.out.println(" file name: target file name"); return; } int n = Integer.parseInt(args[0]); int avgBucketSize = Integer.parseInt(args[1]); int leafSize = Integer.parseInt(args[2]); String fileName = args[3]; LongHash hash = new LongHash(); ArrayList list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add((long) i); } for (int test = 0; test < 5; test++) { System.out.println("test #" + test); long start = System.nanoTime(); BitBuffer buff = RecSplitBuilder.newInstance(hash). leafSize(leafSize). parallelism(1). averageBucketSize(avgBucketSize). eliasFanoMonotoneLists(true). generate(list); long time = System.nanoTime() - start; long nsPerKey = time / n; System.out.println(nsPerKey + " ns/key"); FileOutputStream out = new FileOutputStream(fileName); byte[] data = buff.toByteArray(); System.out.println(data.length * 8. / n + " bits/key"); out.write(data); // System.out.println("split_evals: " + Generator.num_split_evals); // System.out.println("split_count: " + Generator.num_split_count); // System.out.println("evals/split: " + (double) Generator.num_split_evals / Generator.num_split_count); // System.out.println("bij_evals: " + Arrays.toString(Generator.num_bij_evals)); // System.out.println("bij_counts: " + Arrays.toString(Generator.num_bij_counts)); // for(int i=0; i<20; i++) { // if (Generator.num_bij_counts[i] > 0) // System.out.println("evals/bij: " + i + " " + (double) Generator.num_bij_evals[i] / Generator.num_bij_counts[i]); // } out.close(); } } } ================================================ FILE: src/test/java/org/minperf/tools/Load.java ================================================ package org.minperf.tools; import java.io.IOException; import java.io.RandomAccessFile; import java.util.BitSet; import org.minperf.BitBuffer; import org.minperf.RecSplitBuilder; import org.minperf.RecSplitEvaluator; import org.minperf.universal.LongHash; /** * Read a MPHF from a file and evaluate it. */ public class Load { public static void main(String... args) throws IOException { if (args.length != 4) { System.out.println("Usage: java " + Load.class.getName() + " "); System.out.println(" n: number of keys"); System.out.println(" bucket size: average bucket size"); System.out.println(" leaf size: size of a leaf"); System.out.println(" file name: target file name"); return; } int n = Integer.parseInt(args[0]); int avgBucketSize = Integer.parseInt(args[1]); int leafSize = Integer.parseInt(args[2]); String fileName = args[3]; LongHash hash = new LongHash(); RandomAccessFile f = new RandomAccessFile(fileName, "r"); byte[] data = new byte[(int) f.length()]; f.readFully(data); f.close(); RecSplitEvaluator eval = RecSplitBuilder.newInstance(hash). leafSize(leafSize). averageBucketSize(avgBucketSize). eliasFanoMonotoneLists(true). buildEvaluator(new BitBuffer(data)); for (int test = 0; test < 5; test++) { System.out.println("test #" + test); long start = System.nanoTime(); BitSet set = new BitSet(); for (int i = 0; i < n; i++) { int x = eval.evaluate((long) i); if (set.get(x)) { throw new AssertionError("duplicate key: " + i); } set.set(x); } long time = System.nanoTime() - start; long nsPerKey = time / n; System.out.println(nsPerKey + " ns/key"); } } } ================================================ FILE: src/test/java/org/minperf/utils/FastDigitFromNumberExtraction.java ================================================ package org.minperf.utils; import java.util.Random; /** * Utility class to quickly extract a single base-3 digit from an integer. This * can be useful to build an array of numbers ranging 0..2. This could help * writing a fast and space-saving BDZ implementation (BDZ needs an array of * numbers 0..2). Could be changed to other bases. */ public class FastDigitFromNumberExtraction { public static void main(String... args) { test(); test(); test(); test(); } private static void test() { int base = 3; int max = getMaxArraySize(base); int[] data = new int[max]; Random r = new Random(1); int size = 1000000; int[] testData = new int[size]; int[] testIndexes = new int[size]; for (int i = 0; i < max; i++) { data[i] = BASE - 1; } int maxValue = convertToBase3(data); for(int test = 0; test <= maxValue; test++) { for(int i=0; i>> 8]; // dummy += getLoop(x, ix & 0xff); // } // time = System.nanoTime() - time; // System.out.println("loop " + time / size + " ns/key dummy " + dummy); time = System.nanoTime(); dummy = 0; for (int ix : testIndexes) { int x = testData[ix >>> 8]; dummy += extractDivMod(x, ix & 0xff); } time = System.nanoTime() - time; System.out.println("divMod " + time / size + " ns/key dummy " + dummy); time = System.nanoTime(); dummy = 0; for (int ix : testIndexes) { int x = testData[ix >>> 8]; dummy += extractMulMod(x, ix & 0xff); } time = System.nanoTime() - time; System.out.println("multiplyMod " + time / size + " ns/key dummy " + dummy); time = System.nanoTime(); dummy = 0; for (int ix : testIndexes) { int x = testData[ix >>> 8]; dummy += extractMulMul(x, ix & 0xff); } time = System.nanoTime() - time; System.out.println("multiplyMultiply " + time / size + " ns/key dummy " + dummy); System.out.println(); } final static int BASE = 3; final static int MAX = 20; final static long[] DIV; final static long[] MX; final static int[] S2S; static { int max = 19; DIV = new long[max + 1]; MX = new long[max + 1]; S2S = new int[max + 1]; long x = 1; for (int i = 0; i <= max; i++) { DIV[i] = x; int l = 64 - Long.numberOfLeadingZeros(x); long mx = ((1L << 32) * ((1L << l) - (int) x) / (int) x) + 1; int s2 = Math.max(l - 1, 0); S2S[i] = s2; MX[i] = mx; // 0xffffffffL / x + 1; x *= BASE; } } static int getMaxArraySize(int base) { long x = base; for (int i = 1;; i++) { long x2 = x * base; if (x2 > (1L << 32)) { return i; } x = x2; } } static int convertToBase3(int[] data) { int x = 0; for (int i = 0; i < MAX; i++) { x *= BASE; x += data[i]; } return x; } /** * Extract a base-3 number using a loop with division operations, and one modulo * operation. * * @param x he number * @param index the digit id * @return the number */ static int extractDivLoop(int x, int index) { long y = x & ((1L << 32) - 1); for (int i = 0; i < MAX - index - 1; i++) { y /= BASE; } return (int) (y % BASE); } /** * Extract a base-3 number using one division and one modulo operation. * * @param x he number * @param index the digit id * @return the number */ static int extractDivMod(int x, int index) { long y = x & ((1L << 32) - 1); y /= DIV[MAX - index - 1]; return (int) (y % BASE); } static long div3(long y, int index) { long mx = MX[index]; int s1 = 1; int s2 = S2S[index]; long t1 = (y * mx) >>> 32; long q = ((t1 + ((y - t1) >> s1)) >> s2); return q; } /** * Extract a base-3 number using one multiplication and one modulo operation. * * @param x the number * @param index the digit id * @return the number */ static int extractMulMod(int x, int index) { long d = div3(x & 0xffffffffL, MAX - index - 1); return (int) (d % BASE); } private static final long[] MULTIPLY_TWICE = new long[20]; static { long m = 1; for (int i = 0; i < 20; i++) { m *= 3; MULTIPLY_TWICE[19 - i] = (0x3fffffffffffffffL / m) + 1; } } /** * Extract a base-3 number using two multiplications. * * @param x the number * @param index the digit id * @return the number */ static int extractMulMul(int x, int index) { long xx = x & 0xffffffffL; xx *= MULTIPLY_TWICE[index]; xx = xx & 0x3fffffffffffffffL; xx *= 3; xx >>>= 62; return (int) xx; } } ================================================ FILE: src/test/java/org/minperf/utils/TextTest.java ================================================ package org.minperf.utils; import static org.junit.Assert.assertEquals; import org.junit.Test; /** * Tests the text class. */ public class TextTest { @Test public void test() { Text text = new Text("hello world".getBytes(), 0, 11); assertEquals("hello world", text.toString()); assertEquals("hello", text.subSequence(0, 5).toString()); assertEquals("world", text.subSequence(6, 6 + 5).toString()); assertEquals('h', text.charAt(0)); } }