package fj.data.properties; import fj.*; import fj.data.Array; import fj.data.Either; import fj.test.Gen; import fj.test.Property; import fj.test.reflect.CheckParams; import fj.test.runner.PropertyTestRunner; import org.junit.runner.RunWith; import java.util.HashMap; import java.util.Map; import java.util.Set; import static fj.Equal.arrayEqual; import static fj.Equal.intEqual; import static fj.Function.compose; import static fj.Function.identity; import static fj.data.Array.array; import static fj.data.Array.empty; 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 ArrayProperties { private static final Equal<Array<Integer>> eq = arrayEqual(intEqual); private static final Gen<P2<Array<Integer>, Integer>> arbArrayWithIndex = arbArray(arbInteger) .filter(Array::isNotEmpty) .bind(array -> Gen.choose(0, array.length() - 1).map(i -> P.p(array, i))); public Property isEmpty() { return property(arbArray(arbInteger), array -> prop(array.isEmpty() != array.isNotEmpty())); } public Property isNotEmpty() { return property(arbArray(arbInteger), array -> prop(array.length() > 0 == array.isNotEmpty())); } public Property toOption() { return property(arbArray(arbInteger), array -> prop(array.toOption().isNone() || intEqual.eq(array.toOption().some(), array.get(0)))); } public Property toEither() { return property(arbArray(arbInteger), arbP1(arbInteger), (array, n) -> { final Either<Integer, Integer> e = array.toEither(n); return prop(e.isLeft() && intEqual.eq(e.left().value(), n._1()) || intEqual.eq(e.right().value(), array.get(0))); }); } public Property mapId() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.map(identity()), array))); } public Property mapCompose() { final F<Integer, Integer> f = x -> x + 3; final F<Integer, Integer> g = x -> x * 4; return property(arbArray(arbInteger), array -> prop(eq.eq(array.map(compose(f, g)), array.map(g).map(f)))); } public Property foreachDoEffect() { return property(arbArray(arbInteger), array -> { int[] acc = {0}; array.foreachDoEffect(x -> acc[0] += x); int acc2 = 0; for (int x : array) { acc2 += x; } return prop(intEqual.eq(acc[0], acc2)); }); } public Property filter() { final F<Integer, Boolean> predicate = (x -> x % 2 == 0); return property(arbArray(arbInteger), array -> prop(array.filter(predicate).forall(predicate))); } public Property filterLength() { final F<Integer, Boolean> predicate = (x -> x % 2 == 0); return property(arbArray(arbInteger), array -> prop(array.filter(predicate).length() <= array.length())); } public Property bindLeftIdentity() { final F<Integer, Array<Integer>> f = (i -> array(-i)); return property(arbArray(arbInteger), arbInteger, (array, i) -> prop(eq.eq(array(i).bind(f), f.f(i)))); } public Property bindRightIdentity() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.bind(Array::array), array))); } public Property bindAssociativity() { final F<Integer, Array<Integer>> f = x -> array(x + 3); final F<Integer, Array<Integer>> g = x -> array(x * 4); return property(arbArray(arbInteger), array -> prop(eq.eq(array.bind(f).bind(g), array.bind(i -> f.f(i).bind(g))))); } public Property foldRight() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.foldRight((i, s) -> array(i).append(s), empty()), array))); } public Property foldLeft() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.foldLeft((s, i) -> array(i).append(s), empty()), array.reverse().foldRight((i, s) -> array(i).append(s), empty())))); } public Property scans() { return property(arbArray(arbInteger), arbInteger, (array, z) -> { final F<Integer, F<Integer, Integer>> add = x -> y -> x + y; final Array<Integer> left = array.scanLeft(add, z); final Array<Integer> right = array.reverse().scanRight(add, z).reverse(); return prop(eq.eq(left, right)); }); } public Property scans1() { return property(arbArray(arbInteger), array -> implies(array.isNotEmpty(), () -> { final F<Integer, F<Integer, Integer>> add = x -> y -> x + y; final Array<Integer> left = array.scanLeft1(add); final Array<Integer> right = array.reverse().scanRight1(add).reverse(); return prop(eq.eq(left, right)); })); } @CheckParams(maxSize = 100) public Property sequence() { return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> prop(eq.eq(array1.sequence(array2), array1.bind(__ -> array2)))); } public Property reverseIdentity() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.reverse().reverse(), array))); } public Property reverse() { return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> prop(eq.eq(array1.append(array2).reverse(), array2.reverse().append(array1.reverse())))); } @CheckParams(minSize = 1) public Property reverseIndex() { return property(arbArrayWithIndex, p -> { final Array<Integer> array = p._1(); final Integer i = p._2(); return prop(intEqual.eq(array.reverse().get(i), array.get(array.length() - i - 1))); }); } public Property appendLeftIdentity() { return property(arbArray(arbInteger), array -> prop(eq.eq(Array.<Integer> empty().append(array), array))); } public Property appendRightIdentity() { return property(arbArray(arbInteger), array -> prop(eq.eq(array.append(empty()), array))); } public Property appendAssociativity() { return property(arbArray(arbInteger), arbArray(arbInteger), arbArray(arbInteger), (array1, array2, array3) -> prop(eq.eq(array1.append(array2).append(array3), array1.append(array2.append(array3))))); } public Property appendLength() { return property(arbArray(arbInteger), arbArray(arbInteger), (array1, array2) -> prop(array1.append(array2).length() == array1.length() + array2.length())); } public Property arrayLength() { return property(arbArray(arbInteger), array -> prop(array.length() == array.array().length)); } @CheckParams(minSize = 1) public Property index() { return property(arbArrayWithIndex, p -> { final Array<Integer> array = p._1(); final Integer i = p._2(); return prop(intEqual.eq(array.get(i), array.array(Integer[].class)[i])); }); } public Property forallExists() { return property(arbArray(arbInteger), array -> prop(array.forall(x -> x % 2 == 0) == !array.exists(x -> x % 2 != 0))); } public Property find() { return property(arbArray(arbInteger), array -> prop(array.find(x -> x % 2 == 0).forall(x -> x % 2 == 0))); } @CheckParams(maxSize = 500) public Property join() { return property(arbArray(arbArray(arbInteger)), (Array<Array<Integer>> array) -> prop(eq.eq(array.foldRight(Array::append, empty()), Array.join(array)))); } }