package org.apache.lucene.util.packed; /** * 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. */ import org.apache.lucene.store.*; import org.apache.lucene.util.LuceneTestCase; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.io.IOException; public class TestPackedInts extends LuceneTestCase { private Random rnd; public void testBitsRequired() throws Exception { assertEquals(61, PackedInts.bitsRequired((long)Math.pow(2, 61)-1)); assertEquals(61, PackedInts.bitsRequired(0x1FFFFFFFFFFFFFFFL)); assertEquals(62, PackedInts.bitsRequired(0x3FFFFFFFFFFFFFFFL)); assertEquals(63, PackedInts.bitsRequired(0x7FFFFFFFFFFFFFFFL)); } public void testMaxValues() throws Exception { assertEquals("1 bit -> max == 1", 1, PackedInts.maxValue(1)); assertEquals("2 bit -> max == 3", 3, PackedInts.maxValue(2)); assertEquals("8 bit -> max == 255", 255, PackedInts.maxValue(8)); assertEquals("63 bit -> max == Long.MAX_VALUE", Long.MAX_VALUE, PackedInts.maxValue(63)); assertEquals("64 bit -> max == Long.MAX_VALUE (same as for 63 bit)", Long.MAX_VALUE, PackedInts.maxValue(64)); } public void testPackedInts() throws IOException { rnd = newRandom(); int num = 5 * RANDOM_MULTIPLIER; for (int iter = 0; iter < num; iter++) { long ceil = 2; for(int nbits=1;nbits<63;nbits++) { final int valueCount = 100+rnd.nextInt(500); final Directory d = newDirectory(rnd); IndexOutput out = d.createOutput("out.bin"); PackedInts.Writer w = PackedInts.getWriter( out, valueCount, nbits); final long[] values = new long[valueCount]; for(int i=0;i<valueCount;i++) { long v = rnd.nextLong() % ceil; if (v < 0) { v = -v; } values[i] = v; w.add(values[i]); } w.finish(); final long fp = out.getFilePointer(); out.close(); IndexInput in = d.openInput("out.bin"); PackedInts.Reader r = PackedInts.getReader(in); assertEquals(fp, in.getFilePointer()); for(int i=0;i<valueCount;i++) { assertEquals("index=" + i + " ceil=" + ceil + " valueCount=" + valueCount + " nbits=" + nbits + " for " + r.getClass().getSimpleName(), values[i], r.get(i)); } in.close(); in = d.openInput("out.bin"); PackedInts.ReaderIterator r2 = PackedInts.getReaderIterator(in); for(int i=0;i<valueCount;i++) { assertEquals("index=" + i + " ceil=" + ceil + " valueCount=" + valueCount + " nbits=" + nbits + " for " + r.getClass().getSimpleName(), values[i], r2.next()); } assertEquals(fp, in.getFilePointer()); in.close(); ceil *= 2; d.close(); } } } public void testControlledEquality() { final int VALUE_COUNT = 255; final int BITS_PER_VALUE = 8; List<PackedInts.Mutable> packedInts = createPackedInts(VALUE_COUNT, BITS_PER_VALUE); for (PackedInts.Mutable packedInt: packedInts) { for (int i = 0 ; i < packedInt.size() ; i++) { packedInt.set(i, i+1); } } assertListEquality(packedInts); } public void testRandomEquality() { final int[] VALUE_COUNTS = new int[]{0, 1, 5, 8, 100, 500}; final int MIN_BITS_PER_VALUE = 1; final int MAX_BITS_PER_VALUE = 64; rnd = newRandom(); for (int valueCount: VALUE_COUNTS) { for (int bitsPerValue = MIN_BITS_PER_VALUE ; bitsPerValue <= MAX_BITS_PER_VALUE ; bitsPerValue++) { assertRandomEquality(valueCount, bitsPerValue, rnd.nextLong()); } } } private void assertRandomEquality(int valueCount, int bitsPerValue, long randomSeed) { List<PackedInts.Mutable> packedInts = createPackedInts(valueCount, bitsPerValue); for (PackedInts.Mutable packedInt: packedInts) { try { fill(packedInt, (long)(Math.pow(2, bitsPerValue)-1), randomSeed); } catch (Exception e) { e.printStackTrace(System.err); fail(String.format( "Exception while filling %s: valueCount=%d, bitsPerValue=%s", packedInt.getClass().getSimpleName(), valueCount, bitsPerValue)); } } assertListEquality(packedInts); } private List<PackedInts.Mutable> createPackedInts( int valueCount, int bitsPerValue) { List<PackedInts.Mutable> packedInts = new ArrayList<PackedInts.Mutable>(); if (bitsPerValue <= 8) { packedInts.add(new Direct8(valueCount)); } if (bitsPerValue <= 16) { packedInts.add(new Direct16(valueCount)); } if (bitsPerValue <= 31) { packedInts.add(new Packed32(valueCount, bitsPerValue)); } if (bitsPerValue <= 32) { packedInts.add(new Direct32(valueCount)); } if (bitsPerValue <= 63) { packedInts.add(new Packed64(valueCount, bitsPerValue)); } packedInts.add(new Direct64(valueCount)); return packedInts; } private void fill(PackedInts.Mutable packedInt, long maxValue, long randomSeed) { Random rnd2 = new Random(randomSeed); maxValue++; for (int i = 0 ; i < packedInt.size() ; i++) { long value = Math.abs(rnd2.nextLong() % maxValue); packedInt.set(i, value); assertEquals(String.format( "The set/get of the value at index %d should match for %s", i, packedInt.getClass().getSimpleName()), value, packedInt.get(i)); } } private void assertListEquality( List<? extends PackedInts.Reader> packedInts) { assertListEquality("", packedInts); } private void assertListEquality( String message, List<? extends PackedInts.Reader> packedInts) { if (packedInts.size() == 0) { return; } PackedInts.Reader base = packedInts.get(0); int valueCount = base.size(); for (PackedInts.Reader packedInt: packedInts) { assertEquals(message + ". The number of values should be the same ", valueCount, packedInt.size()); } for (int i = 0 ; i < valueCount ; i++) { for (int j = 1 ; j < packedInts.size() ; j++) { assertEquals(String.format( "%s. The value at index %d should be the same for %s and %s", message, i, base.getClass().getSimpleName(), packedInts.get(j).getClass().getSimpleName()), base.get(i), packedInts.get(j).get(i)); } } } public void testSingleValue() throws Exception { Directory dir = newDirectory(newRandom()); IndexOutput out = dir.createOutput("out"); PackedInts.Writer w = PackedInts.getWriter(out, 1, 8); w.add(17); w.finish(); final long end = out.getFilePointer(); out.close(); IndexInput in = dir.openInput("out"); PackedInts.getReader(in); assertEquals(end, in.getFilePointer()); in.close(); dir.close(); } public void testSecondaryBlockChange() throws IOException { PackedInts.Mutable mutable = new Packed64(26, 5); mutable.set(24, 31); assertEquals("The value #24 should be correct", 31, mutable.get(24)); mutable.set(4, 16); assertEquals("The value #24 should remain unchanged", 31, mutable.get(24)); } }