Skip to content

Commit

Permalink
Introduce .first and .last to 64 implementation RoaringBitmap#583
Browse files Browse the repository at this point in the history
  • Loading branch information
blacelle committed Nov 13, 2022
1 parent bdd66cb commit 6e0fc65
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.NoSuchElementException;

import org.roaringbitmap.Container;
import org.roaringbitmap.art.Art;
import org.roaringbitmap.art.ContainerIterator;
import org.roaringbitmap.art.Containers;
import org.roaringbitmap.art.KeyIterator;
import org.roaringbitmap.art.LeafNodeIterator;
import org.roaringbitmap.art.Node;
import org.roaringbitmap.art.*;

public class HighLowContainer {

Expand Down Expand Up @@ -111,6 +108,44 @@ public boolean isEmpty() {
return art.isEmpty();
}

private void assertNonEmpty() {
if(isEmpty()) {
throw new NoSuchElementException("Empty HighLowContainer");
}
}

/**
* Gets the first value in the array
* @return the first value in the array
* @throws NoSuchElementException if empty
*/
public long first() {
assertNonEmpty();

LeafNode firstNode = highKeyLeafNodeIterator(false).next();
long containerIdx = firstNode.getContainerIdx();
Container container = getContainer(containerIdx);
byte[] high = firstNode.getKeyBytes();
int low = container.first();
return LongUtils.toLong(high, low);
}

/**
* Gets the last value in the array
* @return the last value in the array
* @throws NoSuchElementException if empty
*/
public long last() {
assertNonEmpty();

LeafNode firstNode = highKeyLeafNodeIterator(true).next();
long containerIdx = firstNode.getContainerIdx();
Container container = getContainer(containerIdx);
byte[] high = firstNode.getKeyBytes();
int low = container.last();
return LongUtils.toLong(high, low);
}

/**
* serialize into the ByteBuffer in little endian
* @param buffer the ByteBuffer should be large enough to hold the data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.io.DataOutput;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.PrimitiveIterator;
import java.util.Spliterator;
import java.util.Spliterators;
Expand Down Expand Up @@ -142,6 +143,22 @@ public default LongStream reverseStream() {
*/
public long select(long j);

/**
* Get the first (smallest) integer in this RoaringBitmap,
* that is, returns the minimum of the set.
* @return the first (smallest) integer
* @throws NoSuchElementException if empty
*/
long first();

/**
* Get the last (largest) integer in this RoaringBitmap,
* that is, returns the maximum of the set.
* @return the last (largest) integer
* @throws NoSuchElementException if empty
*/
long last();

/**
* Serialize this bitmap.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public static char lowPart(long num) {
* @param low the low 16 bit
* @return the long data
*/
public static long toLong(byte[] high, char low) {
public static long toLong(byte[] high, int low) {
byte byte6 = (byte) (low >>> 8 & 0xFFL);
byte byte7 = (byte) low;
return (high[0] & 0xFFL) << 56
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;

import org.roaringbitmap.*;
Expand Down Expand Up @@ -127,6 +128,28 @@ private long throwSelectInvalidIndex(long j) {
"select " + j + " when the cardinality is " + this.getLongCardinality());
}

/**
* Get the first (smallest) integer in this RoaringBitmap,
* that is, returns the minimum of the set.
* @return the first (smallest) integer
* @throws NoSuchElementException if empty
*/
@Override
public long first() {
return highLowContainer.first();
}

/**
* Get the last (largest) integer in this RoaringBitmap,
* that is, returns the maximum of the set.
* @return the last (largest) integer
* @throws NoSuchElementException if empty
*/
@Override
public long last() {
return highLowContainer.last();
}

/**
* For better performance, consider the Use the {@link #forEach forEach} method.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1454,4 +1454,24 @@ public void flip(final long x) {

invalidateAboveHigh(high);
}

private void assertNonEmpty() {
if(isEmpty()) {
throw new NoSuchElementException("Empty " + this.getClass().getSimpleName());
}
}

@Override
public long first() {
Map.Entry<Integer, BitmapDataProvider> firstEntry = highToBitmap.firstEntry();

return RoaringIntPacking.pack(firstEntry.getKey(), firstEntry.getValue().first());
}

@Override
public long last() {
Map.Entry<Integer, BitmapDataProvider> lastEntry = highToBitmap.lastEntry();

return RoaringIntPacking.pack(lastEntry.getKey(), lastEntry.getValue().last());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.function.Executable;
import static org.roaringbitmap.Util.toUnsignedLong;

import com.google.common.primitives.Ints;
Expand All @@ -33,6 +32,7 @@
import static org.roaringbitmap.ValidationRangeConsumer.Value.PRESENT;
import org.roaringbitmap.art.LeafNode;
import org.roaringbitmap.art.LeafNodeIterator;
import org.roaringbitmap.buffer.MutableRoaringBitmap;

public class TestRoaring64Bitmap {

Expand Down Expand Up @@ -2201,4 +2201,50 @@ public void testIssue580() {
assertEquals(count, 7);

}

@Test
public void testEmptyFirst() {
assertThrows(NoSuchElementException.class, () -> new MutableRoaringBitmap().first());
}

@Test
public void testEmptyLast() {
assertThrows(NoSuchElementException.class, () -> new MutableRoaringBitmap().last());
}

@Test
public void testFirstLast_32b() {
Roaring64Bitmap rb = newDefaultCtor();

rb.add(2);
rb.add(4);
rb.add(8);
assertEquals(2, rb.first());
assertEquals(8, rb.last());
}

@Test
public void testFirstLast_64b() {
Roaring64Bitmap rb = newDefaultCtor();

rb.add(-128);
rb.add(-64);
rb.add(-32);
assertEquals(-128, rb.first());
assertEquals(-32, rb.last());
}

@Test
public void testFirstLast_32_64b() {
Roaring64Bitmap rb = newDefaultCtor();

rb.add(2);
rb.add(4);
rb.add(8);
rb.add(-128);
rb.add(-64);
rb.add(-32);
assertEquals(2, rb.first());
assertEquals(-32, rb.last());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1648,4 +1648,39 @@ public void testNaivelazyor_ImplicitRoaringBitmap() {
}
}

@Test
public void testFirstLast_32b() {
Roaring64NavigableMap rb = newDefaultCtor();

rb.add(2);
rb.add(4);
rb.add(8);
assertEquals(2, rb.first());
assertEquals(8, rb.last());
}

@Test
public void testFirstLast_64b() {
Roaring64NavigableMap rb = newDefaultCtor();

rb.add(-128);
rb.add(-64);
rb.add(-32);
assertEquals(-128, rb.first());
assertEquals(-32, rb.last());
}

@Test
public void testFirstLast_32_64b() {
Roaring64NavigableMap rb = newDefaultCtor();

rb.add(2);
rb.add(4);
rb.add(8);
rb.add(-128);
rb.add(-64);
rb.add(-32);
assertEquals(2, rb.first());
assertEquals(-32, rb.last());
}
}

0 comments on commit 6e0fc65

Please sign in to comment.