package fj.data.hamt;
import fj.Equal;
import fj.Ord;
import fj.P;
import fj.P3;
import fj.data.List;
import fj.data.Seq;
import fj.data.test.PropertyAssert;
import fj.function.Booleans;
import fj.test.Gen;
import fj.test.Property;
import fj.test.reflect.CheckParams;
import fj.test.runner.PropertyTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.math.BigInteger;
import static fj.Equal.bitSetSequal;
import static fj.Equal.booleanEqual;
import static fj.Equal.listEqual;
import static fj.Equal.stringEqual;
import static fj.Function.identity;
import static fj.data.hamt.BitSet.MAX_BIT_SIZE;
import static fj.data.hamt.BitSet.listBitSet;
import static fj.data.hamt.BitSet.longBitSet;
import static fj.test.Arbitrary.arbBoolean;
import static fj.test.Arbitrary.arbLong;
import static fj.test.Property.impliesBoolean;
import static fj.test.Property.prop;
import static fj.test.Property.property;
/**
* Created by maperr on 31/05/2016.
*/
@RunWith(PropertyTestRunner.class)
@CheckParams(maxSize = 10000)
public class BitSetProperties {
Property andTest() {
return property(arbLong, arbLong, (a, b) -> prop(longBitSet(a).and(longBitSet(b)).longValue() == (a & b)));
}
Property asStringTest() {
return property(arbLong, a -> prop(longBitSet(a).asString().equals(Long.toBinaryString(a))));
}
Property bitsToRightTest() {
return property(arbLong, arbBitSetSize, (a, i) ->
prop(
longBitSet(a).bitsToRight(i) ==
longBitSet(a).toList().reverse().take(i).filter(identity()).length()
));
}
Property longRoundTripTest() {
return property(arbNaturalLong, l -> prop(longBitSet(l).longValue() == l));
}
Property empty() {
return prop(BitSet.empty().isEmpty() && BitSet.empty().longValue() == 0);
}
Property generalEmptinessTest() {
return property(arbListBoolean, list ->
prop(list.dropWhile(Booleans.not).isEmpty() == listBitSet(list).isEmpty())
);
}
Property foldLeftTest() {
return property(arbLong, l -> prop(
BitSet.longBitSet(l).toList().dropWhile(b -> !b).foldLeft(
(acc, b) -> acc + 1, 0
) == BitSet.longBitSet(l).bitsUsed()
));
}
Property fromListTest() {
return property(arbListBoolean, l -> prop(listBitSet(l).toList().equals(l.dropWhile(b -> !b))));
}
Property fromStreamTest() {
return property(arbListBoolean, l -> prop(listBitSet(l).toStream().toList().equals(l.dropWhile(b -> !b))));
}
Property fromLongTest() {
return property(arbLong, l -> prop(BitSet.longBitSet(l).longValue() == l));
}
Property fromStringTest() {
Gen<String> g = arbListBoolean.map(l -> l.map(b -> Integer.toString(BitSet.toInt(b))).foldLeft((acc, s) -> acc + s, ""));
return property(g, (s) -> {
boolean zeroLength = s.isEmpty();
return Property.implies(!zeroLength, () -> {
long x = new BigInteger(s, 2).longValue();
long y = BitSet.stringBitSet(s).longValue();
return prop(x == y);
});
});
}
Gen<List<Boolean>> arbListBoolean = Gen.choose(0, MAX_BIT_SIZE).bind(i -> Gen.sequenceN(i, arbBoolean));
Property toListTest() {
return property(arbListBoolean, list -> {
List<Boolean> expected = list.dropWhile(Booleans.not);
List<Boolean> actual = listBitSet(list).toList();
return prop(Equal.listEqual(Equal.booleanEqual).eq(expected, actual));
});
}
Property clearTest() {
return property(arbLong, arbBitSetSize, (l, i) ->
prop(BitSet.longBitSet(l).clear(i).isSet(i) == false)
);
}
Property bitsUsedTest() {
return property(arbListBoolean, list -> prop(
list.dropWhile(b -> !b).length() ==
listBitSet(list).bitsUsed()
));
}
Property isSetTest() {
return property(arbNaturalLong, Gen.choose(0, MAX_BIT_SIZE),
(Long l, Integer i) -> prop(longBitSet(l).isSet(i) == ((l & (1L << i)) != 0))
);
}
Property stringBitSet() {
return property(arbNaturalLong, l ->
prop(BitSet.stringBitSet(BitSet.longBitSet(l).asString()).longValue() == l)
);
}
Property notTest() {
return property(arbLong, l -> prop(longBitSet(l).not().longValue() == ~l));
}
Property orTest() {
return property(arbLong, arbLong, (x, y) -> prop(
longBitSet(x).or(longBitSet(y)).longValue() == (x | y)
));
}
Gen<List<Integer>> bitSetIndices(int n) {
return Gen.listOfSorted(Gen.choose(0, MAX_BIT_SIZE - 1), n, Ord.intOrd);
}
Property rangeTest() {
return property(arbNaturalLong, bitSetIndices(4), (x, list) -> {
int l = list.index(0);
int h = list.index(2);
int m = Math.max(l, Math.min(list.index(1), h - 1));
int vh = list.index(3);
BitSet bs1 = longBitSet(x);
BitSet bs2 = bs1.range(l, h);
boolean b =
bs1.isSet(m) == bs2.isSet(m - l) &&
bs2.isSet(vh - l) == false;
return prop(b);
});
}
Property setTest() {
return property(arbNaturalLong, arbBitSetSize, (l, i) -> prop(longBitSet(l).set(i).isSet(i)));
}
Property setBooleanTest() {
return property(arbNaturalLong, arbBitSetSize, arbBoolean, (l, i, b) -> prop(longBitSet(l).set(i, b).isSet(i) == b));
}
Property shiftLeftTest() {
return property(arbNaturalLong, arbBitSetSize, (l, i) -> {
BitSet bs = longBitSet(l);
boolean b = bs.shiftLeft(i).longValue() == (l << i);
return impliesBoolean(bs.bitsUsed() + i < MAX_BIT_SIZE, b);
});
}
Property shiftRightTest() {
return property(arbNaturalLong, arbBitSetSize, (l, i) -> {
return prop(longBitSet(l).shiftRight(i).longValue() == (l >> i));
});
}
Property takeLowerTest() {
return property(arbNaturalLong, arbBitSetSize, (l, i) -> {
return prop(bitSetSequal.eq(longBitSet(l).takeLower(i), longBitSet(l).range(0, i)));
});
}
Property takeUpperTest() {
return property(arbNaturalLong, arbBitSetSize, (l, i) -> {
return prop(bitSetSequal.eq(longBitSet(l).takeUpper(i), longBitSet(l).range(MAX_BIT_SIZE, MAX_BIT_SIZE - i)));
});
}
Property toStreamTest() {
return property(arbNaturalLong, l -> {
return prop(listEqual(booleanEqual).eq(longBitSet(l).toList(), longBitSet(l).toStream().toList()));
});
}
Property xorTest() {
return property(arbNaturalLong, arbNaturalLong, (a, b) -> {
return prop(longBitSet(a).xor(longBitSet(b)).longValue() == (a ^ b));
});
}
static final Gen<Long> arbNaturalLong = Gen.choose(0, Long.MAX_VALUE);
static final Gen<Integer> arbBitSetSize = Gen.choose(0, MAX_BIT_SIZE - 1);
}