Skip to content

Commit

Permalink
implement contains range (#237)
Browse files Browse the repository at this point in the history
* implement contains range

* avoid checking containers unless it's necessary
  • Loading branch information
richardstartin authored and lemire committed Mar 24, 2018
1 parent ebb736d commit a677b01
Show file tree
Hide file tree
Showing 23 changed files with 653 additions and 16 deletions.
12 changes: 12 additions & 0 deletions roaringbitmap/src/main/java/org/roaringbitmap/ArrayContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,18 @@ public boolean contains(final short x) {
return Util.unsignedBinarySearch(content, 0, cardinality, x) >= 0;
}

@Override
public boolean contains(int minimum, int supremum) {
int maximum = supremum - 1;
int start = Util.advanceUntil(content, -1, cardinality, (short)minimum);
int end = Util.advanceUntil(content, start - 1, cardinality, (short)maximum);
return start < cardinality
&& end < cardinality
&& end - start == maximum - minimum
&& content[start] == (short)minimum
&& content[end] == (short)maximum;
}


@Override
protected boolean contains(RunContainer runContainer) {
Expand Down
23 changes: 23 additions & 0 deletions roaringbitmap/src/main/java/org/roaringbitmap/BitmapContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,29 @@ public boolean contains(final short i) {
return (bitmap[x / 64] & (1L << x)) != 0;
}

@Override
public boolean contains(int minimum, int supremum) {
int start = minimum >>> 6;
int end = supremum >>> 6;
long first = ~((1L << minimum) - 1);
long last = ((1L << supremum) - 1);
if (start == end) {
return ((bitmap[end] & first & last) == (first & last));
}
if ((bitmap[start] & first) != first) {
return false;
}
if (end < bitmap.length && (bitmap[end] & last) != last) {
return false;
}
for (int i = start + 1; i < bitmap.length && i < end; ++i) {
if (bitmap[i] != -1L) {
return false;
}
}
return true;
}

@Override
protected boolean contains(BitmapContainer bitmapContainer) {
if((cardinality != -1) && (bitmapContainer.cardinality != -1)) {
Expand Down
8 changes: 8 additions & 0 deletions roaringbitmap/src/main/java/org/roaringbitmap/Container.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ public Container andNot(Container x) {
*/
public abstract boolean contains(short x);

/**
* Checks whether the container contains the entire range
* @param minimum the inclusive lower bound of the range
* @param supremum the exclusive upper bound of the range
* @return true if the container contains the range
*/
public abstract boolean contains(int minimum, int supremum);


/**
* Checks whether the container is a subset of this container or not
Expand Down
41 changes: 41 additions & 0 deletions roaringbitmap/src/main/java/org/roaringbitmap/RoaringBitmap.java
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,47 @@ public boolean contains(final int x) {
return c != null && c.contains(Util.lowbits(x));
}

/**
* Checks if the bitmap contains the range.
* @param minimum the inclusive lower bound of the range
* @param supremum the exclusive upper bound of the range
* @return whether the bitmap intersects with the range
*/
public boolean contains(long minimum, long supremum) {
rangeSanityCheck(minimum, supremum);
short firstKey = Util.highbits(minimum);
short lastKey = Util.highbits(supremum);
int span = Util.toIntUnsigned(lastKey) - Util.toIntUnsigned(firstKey);
int len = highLowContainer.size;
if (len < span) {
return false;
}
int begin = highLowContainer.getIndex(firstKey);
int end = highLowContainer.getIndex(lastKey);
end = end < 0 ? -end -1 : end;
if (begin < 0 || end - begin != span) {
return false;
}

int min = (short)minimum & 0xFFFF;
int sup = (short)supremum & 0xFFFF;
if (firstKey == lastKey) {
return highLowContainer.getContainerAtIndex(begin).contains(min, sup);
}
if (!highLowContainer.getContainerAtIndex(begin).contains(min, 1 << 16)) {
return false;
}
if (end < len && !highLowContainer.getContainerAtIndex(end).contains(0, sup)) {
return false;
}
for (int i = begin + 1; i < end; ++i) {
if (highLowContainer.getContainerAtIndex(i).getCardinality() != 1 << 16) {
return false;
}
}
return true;
}


/**
* Deserialize (retrieve) this bitmap.
Expand Down
19 changes: 19 additions & 0 deletions roaringbitmap/src/main/java/org/roaringbitmap/RunContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,25 @@ public boolean contains(short x) {
return false;
}

@Override
public boolean contains(int minimum, int supremum) {
int count = 0;
for (int i = 0; i < numberOfRuns(); ++i) {
int start = toIntUnsigned(getValue(i));
int length = toIntUnsigned(getLength(i));
int stop = start + length;
if (start >= supremum) {
break;
}
if (stop >= supremum) {
count += Math.max(0, supremum - start);
break;
}
count += Math.min(Math.max(0, stop - minimum), length);
}
return count >= supremum - minimum - 1;
}

@Override
protected boolean contains(RunContainer runContainer) {
int i1 = 0, i2 = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.roaringbitmap.ImmutableBitmapDataProvider;
import org.roaringbitmap.IntConsumer;
import org.roaringbitmap.IntIterator;
import org.roaringbitmap.PeekableIntIterator;
import org.roaringbitmap.PeekableShortIterator;
import org.roaringbitmap.RoaringBitmap;
import org.roaringbitmap.ShortIterator;
import org.roaringbitmap.Util;
import org.roaringbitmap.*;

/**
* ImmutableRoaringBitmap provides a compressed immutable (cannot be modified) bitmap. It is meant
Expand Down Expand Up @@ -995,6 +988,47 @@ public boolean contains(final int x) {
&& highLowContainer.containsForContainerAtIndex(index, BufferUtil.lowbits(x));
}

/**
* Checks if the bitmap contains the range.
* @param minimum the inclusive lower bound of the range
* @param supremum the exclusive upper bound of the range
* @return whether the bitmap intersects with the range
*/
public boolean contains(long minimum, long supremum) {
MutableRoaringBitmap.rangeSanityCheck(minimum, supremum);
short firstKey = BufferUtil.highbits(minimum);
short lastKey = BufferUtil.highbits(supremum);
int span = BufferUtil.toIntUnsigned(lastKey) - BufferUtil.toIntUnsigned(firstKey);
int len = highLowContainer.size();
if (len < span) {
return false;
}
int begin = highLowContainer.getIndex(firstKey);
int end = highLowContainer.getIndex(lastKey);
end = end < 0 ? -end -1 : end;
if (begin < 0 || end - begin != span) {
return false;
}

int min = (short)minimum & 0xFFFF;
int sup = (short)supremum & 0xFFFF;
if (firstKey == lastKey) {
return highLowContainer.getContainerAtIndex(begin).contains(min, sup);
}
if (!highLowContainer.getContainerAtIndex(begin).contains(min, 1 << 16)) {
return false;
}
if (end < len && !highLowContainer.getContainerAtIndex(end).contains(0, sup)) {
return false;
}
for (int i = begin + 1; i < end; ++i) {
if (highLowContainer.getContainerAtIndex(i).getCardinality() != 1 << 16) {
return false;
}
}
return true;
}

/**
* Checks whether the parameter is a subset of this RoaringBitmap or not
* @param subset the potential subset
Expand Down Expand Up @@ -1132,7 +1166,6 @@ public boolean intersects(long minimum, long supremum) {
.intersects((short) 0, (int)((supremum - 1) & 0xFFFF) + 1);
}


/**
* Returns the number of distinct integers added to the bitmap (e.g., number of bits set).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1675,6 +1675,18 @@ public boolean intersects(int minimum, int supremum) {
return index < cardinality && BufferUtil.toIntUnsigned(content.get(index)) < supremum;
}

@Override
public boolean contains(int minimum, int supremum) {
int maximum = supremum - 1;
int start = BufferUtil.advanceUntil(content, -1, cardinality, (short)minimum);
int end = BufferUtil.advanceUntil(content, start - 1, cardinality, (short)maximum);
return start < cardinality
&& end < cardinality
&& end - start == maximum - minimum
&& content.get(start) == (short)minimum
&& content.get(end) == (short)maximum;
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,29 @@ public boolean intersects(int minimum, int supremum) {
return false;
}

@Override
public boolean contains(int minimum, int supremum) {
int start = minimum >>> 6;
int end = supremum >>> 6;
long first = ~((1L << minimum) - 1);
long last = ((1L << supremum) - 1);
if (start == end) {
return ((bitmap.get(end) & first & last) == (first & last));
}
if ((bitmap.get(start) & first) != first) {
return false;
}
if (end < bitmap.limit() && (bitmap.get(end) & last) != last) {
return false;
}
for (int i = start + 1; i < bitmap.limit() && i < end; ++i) {
if (bitmap.get(i) != -1L) {
return false;
}
}
return true;
}

@Override
protected boolean contains(MappeableRunContainer runContainer) {
final int runCardinality = runContainer.getCardinality();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ public boolean contains(MappeableContainer subset) {
* @return true if the container intersects the range
*/
public abstract boolean intersects(int minimum, int supremum);

/**
* Checks whether the container contains the entire range
* @param minimum the inclusive lower bound of the range
* @param supremum the exclusive upper bound of the range
* @return true if the container contains the range
*/
public abstract boolean contains(int minimum, int supremum);

/**
* Fill the least significant 16 bits of the integer array, starting at index index, with the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2659,13 +2659,32 @@ protected boolean contains(MappeableBitmapContainer bitmapContainer) {
public boolean intersects(int minimum, int supremum) {
for (int i = 0; i < numberOfRuns(); ++i) {
if (BufferUtil.compareUnsigned(getValue(i), (short)minimum) >= 0
&& BufferUtil.toIntUnsigned(getValue(i)) < supremum) {
&& toIntUnsigned(getValue(i)) < supremum) {
return true;
}
}
return false;
}

@Override
public boolean contains(int minimum, int supremum) {
int count = 0;
for (int i = 0; i < numberOfRuns(); ++i) {
int start = toIntUnsigned(getValue(i));
int length = toIntUnsigned(getLength(i));
int stop = start + length;
if (start >= supremum) {
break;
}
if (stop >= supremum) {
count += Math.max(0, supremum - start);
break;
}
count += Math.min(Math.max(0, stop - minimum), length);
}
return count >= supremum - minimum - 1;
}


}

Expand Down
8 changes: 8 additions & 0 deletions roaringbitmap/src/test/java/org/roaringbitmap/Fuzzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static java.util.stream.Collectors.toList;
import static org.roaringbitmap.RandomisedTestData.randomBitmap;
import static org.roaringbitmap.Util.toUnsignedLong;

public class Fuzzer {

Expand Down Expand Up @@ -221,6 +222,13 @@ public void limitCardinalityXorCardinalityInvariance() {
+ RoaringBitmap.xorCardinality(rb, rb.limit(rb.getCardinality() / 2)));
}

@Test
public void containsRangeFirstLastInvariance() {
verifyInvariance(true,
rb -> RoaringBitmap.add(rb.clone(), toUnsignedLong(rb.first()), toUnsignedLong(rb.last()))
.contains(toUnsignedLong(rb.first()), toUnsignedLong(rb.last())));
}

@Test
public void intersectsRangeFirstLastInvariance() {
verifyInvariance(true, rb -> rb.intersects(rb.first(), rb.last()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.roaringbitmap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

Expand Down Expand Up @@ -81,6 +83,7 @@ public static TestDataSet testCase() {
}

OrderedWriter writer = new OrderedWriter();
private List<Long> ranges = new ArrayList<>();

public TestDataSet withRunAt(int key) {
assert key < 1 << 16;
Expand All @@ -100,9 +103,19 @@ public TestDataSet withBitmapAt(int key) {
return this;
}

public TestDataSet withRange(long minimum, long supremum) {
ranges.add(minimum);
ranges.add(supremum);
return this;
}

public RoaringBitmap build() {
writer.flush();
return writer.getUnderlying();
RoaringBitmap bitmap = writer.getUnderlying();
for (int i = 0; i < ranges.size(); i += 2) {
bitmap.add(ranges.get(i), ranges.get(i + 1));
}
return bitmap;
}
}
}

0 comments on commit a677b01

Please sign in to comment.