package fj.data.properties;
import fj.P;
import fj.P2;
import fj.data.Seq;
import fj.test.reflect.CheckParams;
import fj.test.runner.PropertyTestRunner;
import fj.test.Gen;
import fj.test.Property;
import org.junit.runner.RunWith;
import static fj.Function.identity;
import static fj.test.Arbitrary.*;
import static fj.test.Property.implies;
import static fj.test.Property.prop;
import static fj.test.Property.property;
@RunWith(PropertyTestRunner.class)
@CheckParams(maxSize = 10000)
public class SeqProperties {
private static final Gen<P2<Seq<Integer>, Integer>> arbSeqWithIndex = arbSeq(arbInteger)
.filter(Seq::isNotEmpty)
.bind(seq -> Gen.choose(0, seq.length() - 1).map(i -> P.p(seq, i)));
public Property consHead() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.cons(n).head().equals(n)));
}
public Property consLength() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.cons(n).length() == seq.length() + 1));
}
public Property snocLast() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.snoc(n).last().equals(n)));
}
public Property snocLength() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.snoc(n).length() == seq.length() + 1));
}
public Property appendEmptyLeft() {
return property(arbSeq(arbInteger), seq -> prop(Seq.<Integer>empty().append(seq).equals(seq)));
}
public Property appendEmptyRight() {
return property(arbSeq(arbInteger), seq -> prop(seq.append(Seq.empty()).equals(seq)));
}
public Property appendLength() {
return property(arbSeq(arbInteger), arbSeq(arbInteger), (seq1, seq2) ->
prop(seq1.append(seq2).length() == seq1.length() + seq2.length()));
}
public Property consNotEmpty() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(!seq.cons(n).isEmpty()));
}
public Property snocNotEmpty() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(!seq.snoc(n).isEmpty()));
}
public Property appendSingleLeft() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(Seq.single(n).append(seq).equals(seq.cons(n))));
}
public Property appendSingleRight() {
return property(arbSeq(arbInteger), arbInteger, (seq, n) -> prop(seq.append(Seq.single(n)).equals(seq.snoc(n))));
}
public Property splitLength() {
return property(arbSeq(arbInteger), arbInteger, (seq, i) -> prop(seq.length() == seq.split(i)._1().length() + seq.split(i)._2().length()));
}
public Property tailLength() {
return property(arbSeq(arbInteger), seq ->
implies(!seq.isEmpty(), () -> prop(seq.length() == 1 + seq.tail().length())));
}
public Property initLength() {
return property(arbSeq(arbInteger), seq ->
implies(!seq.isEmpty(), () -> prop(seq.length() == seq.init().length() + 1)));
}
public Property mapId() {
return property(arbSeq(arbInteger), seq -> prop(seq.map(identity()).equals(seq)));
}
@CheckParams(minSize = 1)
public Property updateAndIndex() {
return property(arbSeqWithIndex, arbInteger, (pair, n) -> {
final Seq<Integer> seq = pair._1();
final int index = pair._2();
return prop(seq.update(index, n).index(index).equals(n));
});
}
@CheckParams(minSize = 1)
public Property delete() {
return property(arbSeqWithIndex, arbInteger, (pair, n) -> {
final Seq<Integer> seq = pair._1();
final int index = pair._2();
return prop(seq.delete(index).length() == seq.length() - 1);
});
}
public Property foldLeft() {
return property(arbSeq(Gen.value(1)), seq ->
prop(seq.foldLeft((acc, i) -> acc + i, 0) == seq.length()));
}
public Property foldRight() {
return property(arbSeq(Gen.value(1)), seq ->
prop(seq.foldRight((i, acc) -> acc + i, 0) == seq.length()));
}
public Property length() {
return property(arbList(arbInteger), list ->
prop(Seq.iterableSeq(list).length() == list.length())
);
}
}