/** * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com) * * 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 com.linkedin.pinot.core.util; import com.linkedin.pinot.core.indexsegment.utils.BitUtils; import java.util.BitSet; import java.util.Iterator; import java.util.Random; import java.util.TreeSet; import org.testng.Assert; import org.testng.annotations.Test; public class PinotDataCustomBitSetTest { @Test public void testSetBit() { final int LENGTH = 256; PinotDataCustomBitSet bitset = PinotDataCustomBitSet.withBitLength(LENGTH); for (int i = 0; i < LENGTH; i++) { if (i % 2 == 0) { bitset.setBit(i); } } for (int i = 0; i < LENGTH; i++) { if (i % 2 == 0) { Assert.assertTrue(bitset.isBitSet(i)); } else { Assert.assertFalse(bitset.isBitSet(i)); } } bitset.close(); } @Test public void testFindNthBitSet() { final int LENGTH = 256; PinotDataCustomBitSet bitset = PinotDataCustomBitSet.withBitLength(LENGTH); bitset.setBit(0); bitset.setBit(100); bitset.setBit(250); // System.out.println(bitset.findNthBitSetAfter(0, 1)); // System.out.println(bitset.findNthBitSetAfter(100, 1)); bitset.close(); } @Test public void testTurnOffNthLeftmostBits() { for(int value = 0; value < 256; ++value) { for(int bitsToTurnOff = 0; bitsToTurnOff < 8; ++bitsToTurnOff) { byte[] values = new byte[1]; values[0] = (byte) value; BitSet bitSet = BitSet.valueOf(values); // Turn off the bits in the bitSet int bitsToTurnOffInBitSet = bitsToTurnOff; while (0 < bitsToTurnOffInBitSet && !bitSet.isEmpty()) { bitSet.flip(bitSet.previousSetBit(7)); bitsToTurnOffInBitSet--; } values = bitSet.toByteArray(); int actual = BitUtils.turnOffNthLeftmostSetBits(value, bitsToTurnOff); if (values.length == 0) { Assert.assertEquals(actual, 0, "Value " + Integer.toBinaryString(value) + " with " + bitsToTurnOff + " bits to turn off => " + Integer.toBinaryString(actual)); } else { Assert.assertEquals(actual, values[0] & 0xFF, "Value " + Integer.toBinaryString(value) + " with " + bitsToTurnOff + " bits to turn off => " + Integer.toBinaryString(actual)); } } } } @Test public void testFindNthBitSetRandom() { final int ITERATIONS = 100000; final int LENGTH = 256; final int MAX_BITS_ON = 32; Random random = new Random(); for (int i = 0; i < ITERATIONS; i++) { PinotDataCustomBitSet bitset = PinotDataCustomBitSet.withBitLength(LENGTH); TreeSet<Integer> bitsOn = new TreeSet<Integer>(); int bitsToTurnOn = random.nextInt(MAX_BITS_ON); while (bitsOn.size() < bitsToTurnOn) { bitsOn.add(random.nextInt(LENGTH)); } TreeSet<Integer> bitsOnCopy = new TreeSet<Integer>(bitsOn); for (Integer indexOfBitToTurnOn : bitsOn) { bitset.setBit(indexOfBitToTurnOn); } int startSearchIndex = random.nextInt(LENGTH); // Discard all bits before or at the search index Iterator<Integer> bitsOnIterator = bitsOn.iterator(); while (bitsOnIterator.hasNext()) { Integer next = bitsOnIterator.next(); if (next <= startSearchIndex) { bitsOnIterator.remove(); } } // Discard all bits set on before the search ahead limit int nthBitToFind = random.nextInt(MAX_BITS_ON / 2) + 1; for (int j = 0; j < nthBitToFind - 1 && !bitsOn.isEmpty(); j++) { bitsOn.pollFirst(); } // Check the result against the expected index int expectedIndex; if (bitsOn.isEmpty()) { expectedIndex = -1; } else { expectedIndex = bitsOn.pollFirst(); } long nthBitSetAfter = bitset.findNthBitSetAfter(startSearchIndex, nthBitToFind); if (nthBitSetAfter != expectedIndex) { // System.out.println("Bits set " + bitsOnCopy + ", searching for " + nthBitToFind + "th bit from " + startSearchIndex); nthBitSetAfter = bitset.findNthBitSetAfter(startSearchIndex, nthBitToFind); } Assert.assertEquals(nthBitSetAfter, expectedIndex, "Bits set " + bitsOnCopy + ", searching for " + nthBitToFind + "th bit from " + startSearchIndex); bitset.close(); } } @Test public void testNthBit() { final int LENGTH = 256; for (int setBitIndex = 0; setBitIndex < LENGTH; ++setBitIndex) { for (int searchStartIndex = 0; searchStartIndex < LENGTH; ++searchStartIndex) { PinotDataCustomBitSet bitset = PinotDataCustomBitSet.withBitLength(LENGTH); bitset.setBit(setBitIndex); long foundSetBitIndex = bitset.nextSetBit(searchStartIndex); if (searchStartIndex <= setBitIndex) { Assert.assertEquals(foundSetBitIndex, setBitIndex, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } else { Assert.assertEquals(foundSetBitIndex, -1, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } bitset.close(); } } } @Test public void testNthBitWithConfusingBit() { final int LENGTH = 256; final int CONFUSING_BIT_RANGE = 8; for (int setBitIndex = 0; setBitIndex < LENGTH - CONFUSING_BIT_RANGE; ++setBitIndex) { for (int confusingBitOffset = 1; confusingBitOffset < CONFUSING_BIT_RANGE; ++confusingBitOffset) { for (int searchStartIndex = 0; searchStartIndex < LENGTH; ++searchStartIndex) { int confusingBitIndex = setBitIndex + confusingBitOffset; PinotDataCustomBitSet bitset = PinotDataCustomBitSet.withBitLength(LENGTH); bitset.setBit(setBitIndex); bitset.setBit(confusingBitIndex); long foundSetBitIndex = bitset.nextSetBit(searchStartIndex); if (searchStartIndex <= setBitIndex) { Assert.assertEquals(foundSetBitIndex, setBitIndex, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } else if (searchStartIndex <= confusingBitIndex) { Assert.assertEquals(foundSetBitIndex, confusingBitIndex, "Found bit at index " + foundSetBitIndex + " while it was set at " + confusingBitIndex + " searching from " + searchStartIndex); } else { Assert.assertEquals(foundSetBitIndex, -1, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } bitset.close(); } } } } @Test public void testNthBitFixed() { final int LENGTH = 256; int setBitIndex = 8; int searchStartIndex = 1; PinotDataCustomBitSet bitSet = PinotDataCustomBitSet.withBitLength(LENGTH); bitSet.setBit(setBitIndex); long foundSetBitIndex = bitSet.nextSetBit(searchStartIndex); if (searchStartIndex <= setBitIndex) { Assert.assertEquals(foundSetBitIndex, setBitIndex, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } else { Assert.assertEquals(foundSetBitIndex, -1, "Found bit at index " + foundSetBitIndex + " while it was set at " + setBitIndex + " searching from " + searchStartIndex); } bitSet.close(); } }