package io.vavr.collection;
import io.vavr.Function1;
import io.vavr.Tuple2;
import io.vavr.Tuple3;
import io.vavr.Value;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.IterableAssert;
import org.assertj.core.api.ObjectAssert;
import org.junit.Test;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import static java.util.stream.Collectors.toList;
import static io.vavr.Serializables.deserialize;
import static io.vavr.Serializables.serialize;
public class BitSetTest extends AbstractSortedSetTest {
private final static int MAX_BIT = 1_000_000;
private enum E {
V1, V2, V3
}
@Override
protected <T> IterableAssert<T> assertThat(Iterable<T> actual) {
return new IterableAssert<T>(actual) {
@Override
@SuppressWarnings("unchecked")
public IterableAssert<T> isEqualTo(Object obj) {
if (obj instanceof BitSet || actual instanceof BitSet) {
Assertions.assertThat(HashSet.ofAll(actual)).isEqualTo(HashSet.ofAll((Iterable<T>) obj));
} else {
super.isEqualTo(obj);
}
return this;
}
};
}
@Override
protected <T> ObjectAssert<T> assertThat(T actual) {
return new ObjectAssert<T>(actual) {
@Override
public ObjectAssert<T> isEqualTo(Object expected) {
if (actual instanceof Tuple2) {
final Tuple2<?, ?> t1 = (Tuple2<?, ?>) actual;
final Tuple2<?, ?> t2 = (Tuple2<?, ?>) expected;
assertThat((Iterable<?>) t1._1).isEqualTo(t2._1);
assertThat((Iterable<?>) t1._2).isEqualTo(t2._2);
return this;
} else if (actual instanceof Tuple3) {
final Tuple3<?, ?, ?> t1 = (Tuple3<?, ?, ?>) actual;
final Tuple3<?, ?, ?> t2 = (Tuple3<?, ?, ?>) expected;
assertThat((Iterable<?>) t1._1).isEqualTo(t2._1);
assertThat((Iterable<?>) t1._2).isEqualTo(t2._2);
assertThat((Iterable<?>) t1._3).isEqualTo(t2._3);
return this;
} else {
return super.isEqualTo(expected);
}
}
};
}
private <T> BitSet.Builder<T> bsBuilder() {
final Mapper<T> mapper = new Mapper<>();
return BitSet.withRelations(
(Function1<Integer, T> & Serializable) mapper::fromInt,
(Function1<T, Integer> & Serializable) mapper::toInt);
}
@Override
protected <T> Collector<T, ArrayList<T>, ? extends Traversable<T>> collector() {
return this.<T> bsBuilder().collector();
}
@Override
protected <T> BitSet<T> empty() {
return this.<T> bsBuilder().empty();
}
@Override
protected <T> BitSet<T> emptyWithNull() {
return empty();
}
@Override
protected boolean emptyShouldBeSingleton() {
return false;
}
@Override
protected <T> BitSet<T> of(T element) {
return this.<T> bsBuilder().of(element);
}
@Override
protected <T> BitSet<T> of(Comparator<? super T> comparator, T element) {
// comparator is not used
return this.<T> bsBuilder().of(element);
}
@SuppressWarnings("varargs")
@SafeVarargs
@Override
protected final <T> BitSet<T> of(Comparator<? super T> comparator, T... elements) {
// comparator is not used
return this.<T> bsBuilder().of(elements);
}
@SuppressWarnings("varargs")
@SafeVarargs
@Override
protected final <T> BitSet<T> of(T... elements) {
return this.<T> bsBuilder().of(elements);
}
@Override
protected boolean useIsEqualToInsteadOfIsSameAs() {
return true;
}
@Override
protected int getPeekNonNilPerformingAnAction() {
return 1;
}
@Override
protected <T> BitSet<T> ofAll(Iterable<? extends T> elements) {
return this.<T> bsBuilder().ofAll(elements);
}
@Override
protected <T extends Comparable<? super T>> BitSet<T> ofJavaStream(java.util.stream.Stream<? extends T> javaStream) {
return this.<T> bsBuilder().ofAll(javaStream);
}
@Override
protected BitSet<Boolean> ofAll(boolean... elements) {
return BitSet.ofAll(elements);
}
@Override
protected BitSet<Byte> ofAll(byte... elements) {
return BitSet.ofAll(elements);
}
@Override
protected BitSet<Character> ofAll(char... elements) {
return BitSet.ofAll(elements);
}
@Override
protected BitSet<Double> ofAll(double... elements) {
return this.<Double> bsBuilder().ofAll(Iterator.ofAll(elements));
}
@Override
protected BitSet<Float> ofAll(float... elements) {
return this.<Float> bsBuilder().ofAll(Iterator.ofAll(elements));
}
@Override
protected BitSet<Integer> ofAll(int... elements) {
return BitSet.ofAll(elements);
}
@Override
protected BitSet<Long> ofAll(long... elements) {
return BitSet.ofAll(elements);
}
@Override
protected BitSet<Short> ofAll(short... elements) {
return BitSet.ofAll(elements);
}
@Override
protected <T> BitSet<T> tabulate(int n, Function<? super Integer, ? extends T> f) {
return this.<T> bsBuilder().tabulate(n, f);
}
@Override
protected <T> BitSet<T> fill(int n, Supplier<? extends T> s) {
return this.<T> bsBuilder().fill(n, s);
}
@Override
protected BitSet<Character> range(char from, char toExclusive) {
return BitSet.range(from, toExclusive);
}
@Override
protected BitSet<Character> rangeBy(char from, char toExclusive, int step) {
return BitSet.rangeBy(from, toExclusive, step);
}
@Override
protected BitSet<Double> rangeBy(double from, double toExclusive, double step) {
return this.<Double> bsBuilder().ofAll(Iterator.rangeBy(from, toExclusive, step));
}
private static boolean isBadRange(int a, int b) {
return a < 0 || b < 0 || a > MAX_BIT || b > MAX_BIT;
}
private static boolean isBadRange(long a, long b) {
return a < 0 || b < 0 || a > MAX_BIT || b > MAX_BIT;
}
@Override
protected BitSet<Integer> range(int from, int toExclusive) {
if (isBadRange(from, toExclusive)) {
return this.<Integer> bsBuilder().ofAll(Iterator.range(from, toExclusive));
} else {
return BitSet.range(from, toExclusive);
}
}
@Override
protected BitSet<Integer> rangeBy(int from, int toExclusive, int step) {
if (isBadRange(from, toExclusive)) {
return this.<Integer> bsBuilder().ofAll(Iterator.rangeBy(from, toExclusive, step));
} else {
return BitSet.rangeBy(from, toExclusive, step);
}
}
@Override
protected BitSet<Long> range(long from, long toExclusive) {
if (isBadRange(from, toExclusive)) {
return this.<Long> bsBuilder().ofAll(Iterator.range(from, toExclusive));
} else {
return BitSet.range(from, toExclusive);
}
}
@Override
protected BitSet<Long> rangeBy(long from, long toExclusive, long step) {
if (isBadRange(from, toExclusive)) {
return this.<Long> bsBuilder().ofAll(Iterator.rangeBy(from, toExclusive, step));
} else {
return BitSet.rangeBy(from, toExclusive, step);
}
}
@Override
protected BitSet<Character> rangeClosed(char from, char toInclusive) {
return BitSet.rangeClosed(from, toInclusive);
}
@Override
protected BitSet<Character> rangeClosedBy(char from, char toInclusive, int step) {
return BitSet.rangeClosedBy(from, toInclusive, step);
}
@Override
protected BitSet<Double> rangeClosedBy(double from, double toInclusive, double step) {
return this.<Double> bsBuilder().ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
}
@Override
protected BitSet<Integer> rangeClosed(int from, int toInclusive) {
if (isBadRange(from, toInclusive)) {
return this.<Integer> bsBuilder().ofAll(Iterator.rangeClosed(from, toInclusive));
} else {
return BitSet.rangeClosed(from, toInclusive);
}
}
@Override
protected BitSet<Integer> rangeClosedBy(int from, int toInclusive, int step) {
if (isBadRange(from, toInclusive)) {
return this.<Integer> bsBuilder().ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
} else {
return BitSet.rangeClosedBy(from, toInclusive, step);
}
}
@Override
protected BitSet<Long> rangeClosed(long from, long toInclusive) {
if (isBadRange(from, toInclusive)) {
return this.<Long> bsBuilder().ofAll(Iterator.rangeClosed(from, toInclusive));
} else {
return BitSet.rangeClosed(from, toInclusive);
}
}
@Override
protected BitSet<Long> rangeClosedBy(long from, long toInclusive, long step) {
if (isBadRange(from, toInclusive)) {
return this.<Long> bsBuilder().ofAll(Iterator.rangeClosedBy(from, toInclusive, step));
} else {
return BitSet.rangeClosedBy(from, toInclusive, step);
}
}
// BitSet specific
@Test
public void testBitSet1() {
BitSet<Integer> bs = BitSet.empty();
bs = bs.add(2);
assertThat(bs.head()).isEqualTo(2);
assertThat(bs.length()).isEqualTo(1);
bs = bs.add(4);
assertThat(bs.head()).isEqualTo(2);
assertThat(bs.length()).isEqualTo(2);
bs = bs.remove(2);
assertThat(bs.head()).isEqualTo(4);
assertThat(bs.contains(2)).isFalse();
assertThat(bs.length()).isEqualTo(1);
bs = bs.remove(4);
assertThat(bs.isEmpty()).isTrue();
assertThat(bs.length()).isEqualTo(0);
}
@Test
public void testBitSet2() {
BitSet<Integer> bs = BitSet.empty();
bs = bs.add(2);
assertThat(bs.head()).isEqualTo(2);
assertThat(bs.add(2)).isSameAs(bs);
assertThat(bs.length()).isEqualTo(1);
bs = bs.add(70);
assertThat(bs.head()).isEqualTo(2);
assertThat(bs.add(2)).isSameAs(bs);
assertThat(bs.add(70)).isSameAs(bs);
assertThat(bs.length()).isEqualTo(2);
bs = bs.remove(2);
assertThat(bs.head()).isEqualTo(70);
assertThat(bs.contains(2)).isFalse();
assertThat(bs.length()).isEqualTo(1);
bs = bs.remove(70);
assertThat(bs.isEmpty()).isTrue();
assertThat(bs.length()).isEqualTo(0);
bs = bs.add(2);
bs = bs.add(70);
bs = bs.add(3);
assertThat(bs.length()).isEqualTo(3);
bs = bs.add(71);
assertThat(bs.length()).isEqualTo(4);
bs = bs.add(701);
assertThat(bs.length()).isEqualTo(5);
}
@Test
public void testBitSetN() {
BitSet<Integer> bs = BitSet.empty();
bs = bs.add(2);
assertThat(bs.head()).isEqualTo(2);
bs = bs.add(700);
assertThat(bs.head()).isEqualTo(2);
assertThat(bs.add(2)).isSameAs(bs);
assertThat(bs.add(700)).isSameAs(bs);
bs = bs.remove(2);
assertThat(bs.head()).isEqualTo(700);
assertThat(bs.contains(2)).isFalse();
bs = bs.remove(700);
assertThat(bs.isEmpty()).isTrue();
}
@Test
public void testFactories() {
assertThat(BitSet.of(7).contains(7)).isTrue(); // BitSet1, < 64
assertThat(BitSet.of(77).contains(77)).isTrue(); // BitSet2, < 2*64
assertThat(BitSet.of(777).contains(777)).isTrue(); // BitSetN, >= 2*64
assertThat(BitSet.ofAll(List.of(1).toJavaStream())).isEqualTo(BitSet.of(1));
assertThat(BitSet.fill(1, () -> 1)).isEqualTo(BitSet.of(1));
assertThat(BitSet.tabulate(1, i -> 1)).isEqualTo(BitSet.of(1));
}
@Test
public void shouldAllAll() {
assertThat(BitSet.empty().add(7).addAll(List.of(1, 2))).isEqualTo(BitSet.of(1, 2, 7));
assertThat(BitSet.empty().add(77).addAll(List.of(1, 2))).isEqualTo(BitSet.of(1, 2, 77));
assertThat(BitSet.empty().add(777).addAll(List.of(1, 2))).isEqualTo(BitSet.of(1, 2, 777));
}
@Test
public void shouldCollectInts() {
final Traversable<Integer> actual = java.util.stream.Stream.of(1, 2, 3).collect(BitSet.collector());
assertThat(actual).isEqualTo(of(1, 2, 3));
}
@Test
public void testEnums() {
BitSet<E> bs = BitSet.withEnum(E.class).empty();
bs = bs.add(E.V2);
assert bs.head() == E.V2;
bs = bs.add(E.V3);
assert bs.head() == E.V2;
bs = bs.remove(E.V2);
assert bs.head() == E.V3;
assert !bs.contains(E.V2);
assert bs.contains(E.V3);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAddNegativeElementToEmpty() {
BitSet.empty().add(-1);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAddNegativeElementToBitSet2() {
BitSet.empty().add(77).add(-1);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAddNegativeElementToBitSetN() {
BitSet.empty().add(777).add(-1);
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowAddNegativeElements() {
BitSet.empty().addAll(List.of(-1));
}
@Test(expected = IllegalArgumentException.class)
public void shouldThrowContainsNegativeElements() {
BitSet.empty().contains(-1);
}
@Test
public void shouldSerializeDeserializeNativeBitSet() {
final Object actual = deserialize(serialize(BitSet.of(1, 2, 3)));
final Object expected = BitSet.of(1, 2, 3);
assertThat(actual).isEqualTo(expected);
}
@Test
public void shouldSerializeDeserializeEnumBitSet() {
final Object actual = deserialize(serialize(BitSet.withEnum(E.class).of(E.V1, E.V2)));
final Object expected = BitSet.withEnum(E.class).of(E.V1, E.V2);
assertThat(actual).isEqualTo(expected);
}
@Test
public void shouldBehaveExactlyLikeAnotherBitSet() {
for (int i = 0; i < 10; i++) {
final Random random = getRandom(-1);
final java.util.BitSet mutableBitSet = new java.util.BitSet();
BitSet<Integer> functionalBitSet = BitSet.empty();
final int size = 5_000;
for (int j = 0; j < size; j++) {
/* Insert */
if (random.nextInt() % 3 == 0) {
assertMinimumsAreEqual(mutableBitSet, functionalBitSet);
final int value = random.nextInt(size);
mutableBitSet.set(value);
functionalBitSet = functionalBitSet.add(value);
}
assertMinimumsAreEqual(mutableBitSet, functionalBitSet);
/* Delete */
if (random.nextInt() % 5 == 0) {
if (!mutableBitSet.isEmpty()) { mutableBitSet.clear(mutableBitSet.nextSetBit(0)); }
if (!functionalBitSet.isEmpty()) { functionalBitSet = functionalBitSet.tail(); }
assertMinimumsAreEqual(mutableBitSet, functionalBitSet);
}
}
final Collection<Integer> oldValues = mutableBitSet.stream().sorted().boxed().collect(toList());
final Collection<Integer> newValues = functionalBitSet.toJavaList();
assertThat(oldValues).isEqualTo(newValues);
}
}
private void assertMinimumsAreEqual(java.util.BitSet oldSet, BitSet<Integer> newSet) {
assertThat(oldSet.isEmpty()).isEqualTo(newSet.isEmpty());
if (!newSet.isEmpty()) {
assertThat(oldSet.nextSetBit(0)).isEqualTo(newSet.head());
}
}
// -- toSortedSet
@Override
@Test
public void shouldConvertToSortedSetWithoutComparatorOnComparable() {
final Value<Integer> value = BitSet.of(3, 7, 1, 15, 0);
final Set<Integer> set = value.toSortedSet();
if (value.isSingleValued()) {
assertThat(set).isEqualTo(TreeSet.of(3));
} else {
assertThat(set).isEqualTo(TreeSet.of(0, 1, 3, 7, 15));
}
}
// -- toPriorityQueue
@Test
@Override
public void shouldConvertToPriorityQueueUsingImplicitComparator() {
final Value<Integer> value = BitSet.of(1, 3, 2);
final PriorityQueue<Integer> queue = value.toPriorityQueue();
if (value.isSingleValued()) {
assertThat(queue).isEqualTo(PriorityQueue.of(1));
} else {
assertThat(queue).isEqualTo(PriorityQueue.of(1, 2, 3));
}
}
@Test
@Override
public void shouldConvertToPriorityQueueUsingExplicitComparator() {
final Comparator<Integer> comparator = Comparator.naturalOrder();
final Value<Integer> value = BitSet.of(1, 3, 2);
final PriorityQueue<Integer> queue = value.toPriorityQueue(comparator);
if (value.isSingleValued()) {
assertThat(queue).isEqualTo(PriorityQueue.of(comparator, 1));
} else {
assertThat(queue).isEqualTo(PriorityQueue.of(comparator, 1, 2, 3));
}
}
// -- classes
private static class Mapper<T> implements Serializable {
private static final long serialVersionUID = 1L;
private final java.util.Map<Integer, T> fromIntMap = new java.util.HashMap<>();
private final java.util.Map<T, Integer> toIntMap = new java.util.HashMap<>();
private int nextValue = 0;
synchronized T fromInt(Integer i) {
if (i < nextValue) {
return fromIntMap.get(i);
} else {
throw new RuntimeException();
}
}
synchronized Integer toInt(T value) {
Integer i = toIntMap.get(value);
if (i == null) {
i = nextValue++;
toIntMap.put(value, i);
fromIntMap.put(i, value);
}
return i;
}
}
}