1 package org.apache.lucene.util;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.Random;
25 public class TestNumericUtils extends LuceneTestCase {
27 public void testLongConversionAndOrdering() throws Exception {
28 // generate a series of encoded longs, each numerical one bigger than the one before
30 for (long l=-100000L; l<100000L; l++) {
31 String act=NumericUtils.longToPrefixCoded(l);
34 assertTrue("actual bigger than last", last.compareTo(act) < 0 );
36 // test is back and forward conversion works
37 assertEquals("forward and back conversion should generate same long", l, NumericUtils.prefixCodedToLong(act));
43 public void testIntConversionAndOrdering() throws Exception {
44 // generate a series of encoded ints, each numerical one bigger than the one before
46 for (int i=-100000; i<100000; i++) {
47 String act=NumericUtils.intToPrefixCoded(i);
50 assertTrue("actual bigger than last", last.compareTo(act) < 0 );
52 // test is back and forward conversion works
53 assertEquals("forward and back conversion should generate same int", i, NumericUtils.prefixCodedToInt(act));
59 public void testLongSpecialValues() throws Exception {
60 long[] vals=new long[]{
61 Long.MIN_VALUE, Long.MIN_VALUE+1, Long.MIN_VALUE+2, -5003400000000L,
62 -4000L, -3000L, -2000L, -1000L, -1L, 0L, 1L, 10L, 300L, 50006789999999999L, Long.MAX_VALUE-2, Long.MAX_VALUE-1, Long.MAX_VALUE
64 String[] prefixVals=new String[vals.length];
66 for (int i=0; i<vals.length; i++) {
67 prefixVals[i]=NumericUtils.longToPrefixCoded(vals[i]);
69 // check forward and back conversion
70 assertEquals( "forward and back conversion should generate same long", vals[i], NumericUtils.prefixCodedToLong(prefixVals[i]) );
72 // test if decoding values as int fails correctly
74 NumericUtils.prefixCodedToInt(prefixVals[i]);
75 fail("decoding a prefix coded long value as int should fail");
76 } catch (NumberFormatException e) {
81 // check sort order (prefixVals should be ascending)
82 for (int i=1; i<prefixVals.length; i++) {
83 assertTrue( "check sort order", prefixVals[i-1].compareTo( prefixVals[i] ) < 0 );
86 // check the prefix encoding, lower precision should have the difference to original value equal to the lower removed bits
87 for (int i=0; i<vals.length; i++) {
88 for (int j=0; j<64; j++) {
89 long prefixVal=NumericUtils.prefixCodedToLong(NumericUtils.longToPrefixCoded(vals[i], j));
90 long mask=(1L << j) - 1L;
91 assertEquals( "difference between prefix val and original value for "+vals[i]+" with shift="+j, vals[i] & mask, vals[i]-prefixVal );
96 public void testIntSpecialValues() throws Exception {
98 Integer.MIN_VALUE, Integer.MIN_VALUE+1, Integer.MIN_VALUE+2, -64765767,
99 -4000, -3000, -2000, -1000, -1, 0, 1, 10, 300, 765878989, Integer.MAX_VALUE-2, Integer.MAX_VALUE-1, Integer.MAX_VALUE
101 String[] prefixVals=new String[vals.length];
103 for (int i=0; i<vals.length; i++) {
104 prefixVals[i]=NumericUtils.intToPrefixCoded(vals[i]);
106 // check forward and back conversion
107 assertEquals( "forward and back conversion should generate same int", vals[i], NumericUtils.prefixCodedToInt(prefixVals[i]) );
109 // test if decoding values as long fails correctly
111 NumericUtils.prefixCodedToLong(prefixVals[i]);
112 fail("decoding a prefix coded int value as long should fail");
113 } catch (NumberFormatException e) {
118 // check sort order (prefixVals should be ascending)
119 for (int i=1; i<prefixVals.length; i++) {
120 assertTrue( "check sort order", prefixVals[i-1].compareTo( prefixVals[i] ) < 0 );
123 // check the prefix encoding, lower precision should have the difference to original value equal to the lower removed bits
124 for (int i=0; i<vals.length; i++) {
125 for (int j=0; j<32; j++) {
126 int prefixVal=NumericUtils.prefixCodedToInt(NumericUtils.intToPrefixCoded(vals[i], j));
127 int mask=(1 << j) - 1;
128 assertEquals( "difference between prefix val and original value for "+vals[i]+" with shift="+j, vals[i] & mask, vals[i]-prefixVal );
133 public void testDoubles() throws Exception {
134 double[] vals=new double[]{
135 Double.NEGATIVE_INFINITY, -2.3E25, -1.0E15, -1.0, -1.0E-1, -1.0E-2, -0.0,
136 +0.0, 1.0E-2, 1.0E-1, 1.0, 1.0E15, 2.3E25, Double.POSITIVE_INFINITY
138 long[] longVals=new long[vals.length];
140 // check forward and back conversion
141 for (int i=0; i<vals.length; i++) {
142 longVals[i]=NumericUtils.doubleToSortableLong(vals[i]);
143 assertTrue( "forward and back conversion should generate same double", Double.compare(vals[i], NumericUtils.sortableLongToDouble(longVals[i]))==0 );
146 // check sort order (prefixVals should be ascending)
147 for (int i=1; i<longVals.length; i++) {
148 assertTrue( "check sort order", longVals[i-1] < longVals[i] );
152 public void testFloats() throws Exception {
153 float[] vals=new float[]{
154 Float.NEGATIVE_INFINITY, -2.3E25f, -1.0E15f, -1.0f, -1.0E-1f, -1.0E-2f, -0.0f,
155 +0.0f, 1.0E-2f, 1.0E-1f, 1.0f, 1.0E15f, 2.3E25f, Float.POSITIVE_INFINITY
157 int[] intVals=new int[vals.length];
159 // check forward and back conversion
160 for (int i=0; i<vals.length; i++) {
161 intVals[i]=NumericUtils.floatToSortableInt(vals[i]);
162 assertTrue( "forward and back conversion should generate same double", Float.compare(vals[i], NumericUtils.sortableIntToFloat(intVals[i]))==0 );
165 // check sort order (prefixVals should be ascending)
166 for (int i=1; i<intVals.length; i++) {
167 assertTrue( "check sort order", intVals[i-1] < intVals[i] );
171 // INFO: Tests for trieCodeLong()/trieCodeInt() not needed because implicitely tested by range filter tests
173 /** Note: The neededBounds Iterable must be unsigned (easier understanding what's happening) */
174 private void assertLongRangeSplit(final long lower, final long upper, int precisionStep,
175 final boolean useBitSet, final Iterable<Long> expectedBounds, final Iterable<Integer> expectedShifts
177 final OpenBitSet bits=useBitSet ? new OpenBitSet(upper-lower+1) : null;
178 final Iterator<Long> neededBounds = (expectedBounds == null) ? null : expectedBounds.iterator();
179 final Iterator<Integer> neededShifts = (expectedShifts == null) ? null : expectedShifts.iterator();
181 NumericUtils.splitLongRange(new NumericUtils.LongRangeBuilder() {
183 public void addRange(long min, long max, int shift) {
184 assertTrue("min, max should be inside bounds", min>=lower && min<=upper && max>=lower && max<=upper);
185 if (useBitSet) for (long l=min; l<=max; l++) {
186 assertFalse("ranges should not overlap", bits.getAndSet(l-lower) );
187 // extra exit condition to prevent overflow on MAX_VALUE
190 if (neededBounds == null || neededShifts == null)
192 // make unsigned longs for easier display and understanding
193 min ^= 0x8000000000000000L;
194 max ^= 0x8000000000000000L;
195 //System.out.println("0x"+Long.toHexString(min>>>shift)+"L,0x"+Long.toHexString(max>>>shift)+"L)/*shift="+shift+"*/,");
196 assertEquals( "shift", neededShifts.next().intValue(), shift);
197 assertEquals( "inner min bound", neededBounds.next().longValue(), min>>>shift);
198 assertEquals( "inner max bound", neededBounds.next().longValue(), max>>>shift);
200 }, precisionStep, lower, upper);
203 // after flipping all bits in the range, the cardinality should be zero
204 bits.flip(0,upper-lower+1);
205 assertTrue("The sub-range concenated should match the whole range", bits.isEmpty());
209 /** LUCENE-2541: NumericRangeQuery errors with endpoints near long min and max values */
210 public void testLongExtremeValues() throws Exception {
211 // upper end extremes
212 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 1, true, Arrays.asList(
213 0xffffffffffffffffL,0xffffffffffffffffL
217 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 2, true, Arrays.asList(
218 0xffffffffffffffffL,0xffffffffffffffffL
222 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 4, true, Arrays.asList(
223 0xffffffffffffffffL,0xffffffffffffffffL
227 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 6, true, Arrays.asList(
228 0xffffffffffffffffL,0xffffffffffffffffL
232 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 8, true, Arrays.asList(
233 0xffffffffffffffffL,0xffffffffffffffffL
237 assertLongRangeSplit(Long.MAX_VALUE, Long.MAX_VALUE, 64, true, Arrays.asList(
238 0xffffffffffffffffL,0xffffffffffffffffL
243 assertLongRangeSplit(Long.MAX_VALUE-0xfL, Long.MAX_VALUE, 4, true, Arrays.asList(
244 0xfffffffffffffffL,0xfffffffffffffffL
248 assertLongRangeSplit(Long.MAX_VALUE-0x10L, Long.MAX_VALUE, 4, true, Arrays.asList(
249 0xffffffffffffffefL,0xffffffffffffffefL,
250 0xfffffffffffffffL,0xfffffffffffffffL
255 // lower end extremes
256 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 1, true, Arrays.asList(
257 0x0000000000000000L,0x0000000000000000L
261 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 2, true, Arrays.asList(
262 0x0000000000000000L,0x0000000000000000L
266 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 4, true, Arrays.asList(
267 0x0000000000000000L,0x0000000000000000L
271 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 6, true, Arrays.asList(
272 0x0000000000000000L,0x0000000000000000L
276 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 8, true, Arrays.asList(
277 0x0000000000000000L,0x0000000000000000L
281 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE, 64, true, Arrays.asList(
282 0x0000000000000000L,0x0000000000000000L
287 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE+0xfL, 4, true, Arrays.asList(
288 0x000000000000000L,0x000000000000000L
292 assertLongRangeSplit(Long.MIN_VALUE, Long.MIN_VALUE+0x10L, 4, true, Arrays.asList(
293 0x0000000000000010L,0x0000000000000010L,
294 0x000000000000000L,0x000000000000000L
300 public void testRandomSplit() throws Exception {
301 long num = (long) atLeast(10);
302 for (long i=0; i < num; i++) {
303 executeOneRandomSplit(random);
307 private void executeOneRandomSplit(final Random random) throws Exception {
308 long lower = randomLong(random);
309 long len = random.nextInt(16384*1024); // not too large bitsets, else OOME!
310 while (lower + len < lower) { // overflow
313 assertLongRangeSplit(lower, lower + len, random.nextInt(64) + 1, true, null, null);
316 private long randomLong(final Random random) {
318 switch(random.nextInt(4)) {
320 val = 1L << (random.nextInt(63)); // patterns like 0x000000100000 (-1 yields patterns like 0x0000fff)
323 val = -1L << (random.nextInt(63)); // patterns like 0xfffff00000
326 val = random.nextLong();
329 val += random.nextInt(5)-2;
331 if (random.nextBoolean()) {
332 if (random.nextBoolean()) val += random.nextInt(100)-50;
333 if (random.nextBoolean()) val = ~val;
334 if (random.nextBoolean()) val = val<<1;
335 if (random.nextBoolean()) val = val>>>1;
341 public void testSplitLongRange() throws Exception {
342 // a hard-coded "standard" range
343 assertLongRangeSplit(-5000L, 9500L, 4, true, Arrays.asList(
344 0x7fffffffffffec78L,0x7fffffffffffec7fL,
345 0x8000000000002510L,0x800000000000251cL,
346 0x7fffffffffffec8L, 0x7fffffffffffecfL,
347 0x800000000000250L, 0x800000000000250L,
348 0x7fffffffffffedL, 0x7fffffffffffefL,
349 0x80000000000020L, 0x80000000000024L,
350 0x7ffffffffffffL, 0x8000000000001L
358 // the same with no range splitting
359 assertLongRangeSplit(-5000L, 9500L, 64, true, Arrays.asList(
360 0x7fffffffffffec78L,0x800000000000251cL
365 // this tests optimized range splitting, if one of the inner bounds
366 // is also the bound of the next lower precision, it should be used completely
367 assertLongRangeSplit(0L, 1024L+63L, 4, true, Arrays.asList(
368 0x800000000000040L, 0x800000000000043L,
369 0x80000000000000L, 0x80000000000003L
374 // the full long range should only consist of a lowest precision range; no bitset testing here, as too much memory needed :-)
375 assertLongRangeSplit(Long.MIN_VALUE, Long.MAX_VALUE, 8, false, Arrays.asList(
381 // the same with precisionStep=4
382 assertLongRangeSplit(Long.MIN_VALUE, Long.MAX_VALUE, 4, false, Arrays.asList(
388 // the same with precisionStep=2
389 assertLongRangeSplit(Long.MIN_VALUE, Long.MAX_VALUE, 2, false, Arrays.asList(
395 // the same with precisionStep=1
396 assertLongRangeSplit(Long.MIN_VALUE, Long.MAX_VALUE, 1, false, Arrays.asList(
402 // a inverse range should produce no sub-ranges
403 assertLongRangeSplit(9500L, -5000L, 4, false, Collections.<Long>emptyList(), Collections.<Integer>emptyList());
405 // a 0-length range should reproduce the range itsself
406 assertLongRangeSplit(9500L, 9500L, 4, false, Arrays.asList(
407 0x800000000000251cL,0x800000000000251cL
413 /** Note: The neededBounds Iterable must be unsigned (easier understanding what's happening) */
414 private void assertIntRangeSplit(final int lower, final int upper, int precisionStep,
415 final boolean useBitSet, final Iterable<Integer> expectedBounds, final Iterable<Integer> expectedShifts
417 final OpenBitSet bits=useBitSet ? new OpenBitSet(upper-lower+1) : null;
418 final Iterator<Integer> neededBounds = (expectedBounds == null) ? null : expectedBounds.iterator();
419 final Iterator<Integer> neededShifts = (expectedShifts == null) ? null : expectedShifts.iterator();
421 NumericUtils.splitIntRange(new NumericUtils.IntRangeBuilder() {
423 public void addRange(int min, int max, int shift) {
424 assertTrue("min, max should be inside bounds", min>=lower && min<=upper && max>=lower && max<=upper);
425 if (useBitSet) for (int i=min; i<=max; i++) {
426 assertFalse("ranges should not overlap", bits.getAndSet(i-lower) );
427 // extra exit condition to prevent overflow on MAX_VALUE
430 if (neededBounds == null)
432 // make unsigned ints for easier display and understanding
435 //System.out.println("0x"+Integer.toHexString(min>>>shift)+",0x"+Integer.toHexString(max>>>shift)+")/*shift="+shift+"*/,");
436 assertEquals( "shift", neededShifts.next().intValue(), shift);
437 assertEquals( "inner min bound", neededBounds.next().intValue(), min>>>shift);
438 assertEquals( "inner max bound", neededBounds.next().intValue(), max>>>shift);
440 }, precisionStep, lower, upper);
443 // after flipping all bits in the range, the cardinality should be zero
444 bits.flip(0,upper-lower+1);
445 assertTrue("The sub-range concenated should match the whole range", bits.isEmpty());
449 public void testSplitIntRange() throws Exception {
450 // a hard-coded "standard" range
451 assertIntRangeSplit(-5000, 9500, 4, true, Arrays.asList(
452 0x7fffec78,0x7fffec7f,
453 0x80002510,0x8000251c,
454 0x7fffec8, 0x7fffecf,
455 0x8000250, 0x8000250,
466 // the same with no range splitting
467 assertIntRangeSplit(-5000, 9500, 32, true, Arrays.asList(
468 0x7fffec78,0x8000251c
473 // this tests optimized range splitting, if one of the inner bounds
474 // is also the bound of the next lower precision, it should be used completely
475 assertIntRangeSplit(0, 1024+63, 4, true, Arrays.asList(
476 0x8000040, 0x8000043,
482 // the full int range should only consist of a lowest precision range; no bitset testing here, as too much memory needed :-)
483 assertIntRangeSplit(Integer.MIN_VALUE, Integer.MAX_VALUE, 8, false, Arrays.asList(
489 // the same with precisionStep=4
490 assertIntRangeSplit(Integer.MIN_VALUE, Integer.MAX_VALUE, 4, false, Arrays.asList(
496 // the same with precisionStep=2
497 assertIntRangeSplit(Integer.MIN_VALUE, Integer.MAX_VALUE, 2, false, Arrays.asList(
503 // the same with precisionStep=1
504 assertIntRangeSplit(Integer.MIN_VALUE, Integer.MAX_VALUE, 1, false, Arrays.asList(
510 // a inverse range should produce no sub-ranges
511 assertIntRangeSplit(9500, -5000, 4, false, Collections.<Integer>emptyList(), Collections.<Integer>emptyList());
513 // a 0-length range should reproduce the range itsself
514 assertIntRangeSplit(9500, 9500, 4, false, Arrays.asList(
515 0x8000251c,0x8000251c