package fj; import static fj.Function.curry; import fj.data.hlist.HList; import fj.data.Array; import fj.data.Either; import fj.data.LazyString; import fj.data.List; import fj.data.NonEmptyList; import fj.data.Option; import fj.data.Set; import fj.data.Stream; import fj.data.Tree; import fj.data.Validation; import fj.data.vector.V2; import fj.data.vector.V3; import fj.data.vector.V4; import fj.data.vector.V5; import fj.data.vector.V6; import fj.data.vector.V7; import fj.data.vector.V8; import java.math.BigInteger; import java.math.BigDecimal; /** * Tests for equality between two objects. * * @version %build.number% */ public final class Equal { private final F> f; private Equal(final F> f) { this.f = f; } /** * Returns true if the two given arguments are equal, false otherwise. * * @param a1 An object to test for equality against another. * @param a2 An object to test for equality against another. * @return true if the two given arguments are equal, false otherwise. */ public boolean eq(final A a1, final A a2) { return f.f(a1).f(a2); } /** * First-class equality check. * * @return A function that returns true if the two given arguments are equal. */ public F2 eq() { return new F2() { public Boolean f(final A a, final A a1) { return eq(a, a1); } }; } /** * Partially applied equality check. * * @param a An object to test for equality against another. * @return A function that returns true if the given argument equals the argument to this method. */ public F eq(final A a) { return new F() { public Boolean f(final A a1) { return eq(a, a1); } }; } /** * Maps the given function across this equal as a contra-variant functor. * * @param f The function to map. * @return A new equal. */ public Equal comap(final F f) { return equal(f.andThen().o(this.f).o(f)); } /** * Constructs an equal instance from the given function. * * @param f The function to construct the equal with. * @return An equal instance from the given function. */ public static Equal equal(final F> f) { return new Equal(f); } /** * Returns an equal instance that uses the {@link Object#equals(Object)} method to test for * equality. * * @return An equal instance that uses the {@link Object#equals(Object)} method to test for * equality. */ public static Equal anyEqual() { return new Equal(new F>() { public F f(final A a1) { return new F() { public Boolean f(final A a2) { return a1.equals(a2); } }; } }); } /** * An equal instance for the boolean type. */ public static final Equal booleanEqual = anyEqual(); /** * An equal instance for the byte type. */ public static final Equal byteEqual = anyEqual(); /** * An equal instance for the char type. */ public static final Equal charEqual = anyEqual(); /** * An equal instance for the double type. */ public static final Equal doubleEqual = anyEqual(); /** * An equal instance for the float type. */ public static final Equal floatEqual = anyEqual(); /** * An equal instance for the int type. */ public static final Equal intEqual = anyEqual(); /** * An equal instance for the BigInteger type. */ public static final Equal bigintEqual = anyEqual(); /** * An equal instance for the BigDecimal type. */ public static final Equal bigdecimalEqual = anyEqual(); /** * An equal instance for the long type. */ public static final Equal longEqual = anyEqual(); /** * An equal instance for the short type. */ public static final Equal shortEqual = anyEqual(); /** * An equal instance for the {@link String} type. */ public static final Equal stringEqual = anyEqual(); /** * An equal instance for the {@link StringBuffer} type. */ public static final Equal stringBufferEqual = new Equal(new F>() { public F f(final StringBuffer sb1) { return new F() { public Boolean f(final StringBuffer sb2) { if (sb1.length() == sb2.length()) { for (int i = 0; i < sb1.length(); i++) if (sb1.charAt(i) != sb2.charAt(i)) return false; return true; } else return false; } }; } }); /** * An equal instance for the {@link StringBuilder} type. */ public static final Equal stringBuilderEqual = new Equal(new F>() { public F f(final StringBuilder sb1) { return new F() { public Boolean f(final StringBuilder sb2) { if (sb1.length() == sb2.length()) { for (int i = 0; i < sb1.length(); i++) if (sb1.charAt(i) != sb2.charAt(i)) return false; return true; } else return false; } }; } }); /** * An equal instance for the {@link Either} type. * * @param ea Equality across the left side of {@link Either}. * @param eb Equality across the right side of {@link Either}. * @return An equal instance for the {@link Either} type. */ public static Equal> eitherEqual(final Equal ea, final Equal eb) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final Either e1) { return new F, Boolean>() { public Boolean f(final Either e2) { return e1.isLeft() && e2.isLeft() && ea.f.f(e1.left().value()).f(e2.left().value()) || e1.isRight() && e2.isRight() && eb.f.f(e1.right().value()).f(e2.right().value()); } }; } }); } /** * An equal instance for the {@link Validation} type. * * @param ea Equality across the failing side of {@link Validation}. * @param eb Equality across the succeeding side of {@link Validation}. * @return An equal instance for the {@link Validation} type. */ public static Equal> validationEqual(final Equal ea, final Equal eb) { return eitherEqual(ea, eb).comap(Validation.either()); } /** * An equal instance for the {@link List} type. * * @param ea Equality across the elements of the list. * @return An equal instance for the {@link List} type. */ public static Equal> listEqual(final Equal ea) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final List a1) { return new F, Boolean>() { public Boolean f(final List a2) { List x1 = a1; List x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { if (!ea.eq(x1.head(), x2.head())) return false; x1 = x1.tail(); x2 = x2.tail(); } return x1.isEmpty() && x2.isEmpty(); } }; } }); } /** * An equal instance for the {@link NonEmptyList} type. * * @param ea Equality across the elements of the non-empty list. * @return An equal instance for the {@link NonEmptyList} type. */ public static Equal> nonEmptyListEqual(final Equal ea) { return listEqual(ea).comap(NonEmptyList.toList_()); } /** * An equal instance for the {@link Option} type. * * @param ea Equality across the element of the option. * @return An equal instance for the {@link Option} type. */ public static Equal> optionEqual(final Equal ea) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final Option o1) { return new F, Boolean>() { public Boolean f(final Option o2) { return o1.isNone() && o2.isNone() || o1.isSome() && o2.isSome() && ea.f.f(o1.some()).f(o2.some()); } }; } }); } /** * An equal instance for the {@link Stream} type. * * @param ea Equality across the elements of the stream. * @return An equal instance for the {@link Stream} type. */ public static Equal> streamEqual(final Equal ea) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final Stream a1) { return new F, Boolean>() { public Boolean f(final Stream a2) { Stream x1 = a1; Stream x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { if (!ea.eq(x1.head(), x2.head())) return false; x1 = x1.tail()._1(); x2 = x2.tail()._1(); } return x1.isEmpty() && x2.isEmpty(); } }; } }); } /** * An equal instance for the {@link Array} type. * * @param ea Equality across the elements of the array. * @return An equal instance for the {@link Array} type. */ public static Equal> arrayEqual(final Equal ea) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final Array a1) { return new F, Boolean>() { public Boolean f(final Array a2) { if (a1.length() == a2.length()) { for (int i = 0; i < a1.length(); i++) { if (!ea.eq(a1.get(i), a2.get(i))) return false; } return true; } else return false; } }; } }); } /** * An equal instance for the {@link Tree} type. * * @param ea Equality across the elements of the tree. * @return An equal instance for the {@link Tree} type. */ public static Equal> treeEqual(final Equal ea) { return new Equal>(curry(new F2, Tree, Boolean>() { public Boolean f(final Tree t1, final Tree t2) { return ea.eq(t1.root(), t2.root()) && p1Equal(streamEqual(treeEqual(ea))).eq(t2.subForest(), t1.subForest()); } })); } /** * An equal instance for a product-1. * * @param ea Equality across the first element of the product. * @return An equal instance for a product-1. */ public static Equal> p1Equal(final Equal ea) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P1 p1) { return new F, Boolean>() { public Boolean f(final P1 p2) { return ea.eq(p1._1(), p2._1()); } }; } }); } /** * An equal instance for a product-2. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @return An equal instance for a product-2. */ public static Equal> p2Equal(final Equal ea, final Equal eb) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P2 p1) { return new F, Boolean>() { public Boolean f(final P2 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()); } }; } }); } /** * An equal instance for a product-3. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @return An equal instance for a product-3. */ public static Equal> p3Equal(final Equal ea, final Equal eb, final Equal ec) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P3 p1) { return new F, Boolean>() { public Boolean f(final P3 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()); } }; } }); } /** * An equal instance for a product-4. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @param ed Equality across the fourth element of the product. * @return An equal instance for a product-4. */ public static Equal> p4Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P4 p1) { return new F, Boolean>() { public Boolean f(final P4 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && ed.eq(p1._4(), p2._4()); } }; } }); } /** * An equal instance for a product-5. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @param ed Equality across the fourth element of the product. * @param ee Equality across the fifth element of the product. * @return An equal instance for a product-5. */ public static Equal> p5Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P5 p1) { return new F, Boolean>() { public Boolean f(final P5 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()); } }; } }); } /** * An equal instance for a product-6. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @param ed Equality across the fourth element of the product. * @param ee Equality across the fifth element of the product. * @param ef Equality across the sixth element of the product. * @return An equal instance for a product-6. */ public static Equal> p6Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee, final Equal ef) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P6 p1) { return new F, Boolean>() { public Boolean f(final P6 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6()); } }; } }); } /** * An equal instance for a product-7. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @param ed Equality across the fourth element of the product. * @param ee Equality across the fifth element of the product. * @param ef Equality across the sixth element of the product. * @param eg Equality across the seventh element of the product. * @return An equal instance for a product-7. */ public static Equal> p7Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee, final Equal ef, final Equal eg) { return new Equal>(new F, F, Boolean>>() { public F, Boolean> f(final P7 p1) { return new F, Boolean>() { public Boolean f(final P7 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6()) && eg.eq(p1._7(), p2._7()); } }; } }); } /** * An equal instance for a product-8. * * @param ea Equality across the first element of the product. * @param eb Equality across the second element of the product. * @param ec Equality across the third element of the product. * @param ed Equality across the fourth element of the product. * @param ee Equality across the fifth element of the product. * @param ef Equality across the sixth element of the product. * @param eg Equality across the seventh element of the product. * @param eh Equality across the eighth element of the product. * @return An equal instance for a product-8. */ public static Equal> p8Equal(final Equal ea, final Equal eb, final Equal ec, final Equal ed, final Equal ee, final Equal ef, final Equal eg, final Equal eh) { return new Equal>( new F, F, Boolean>>() { public F, Boolean> f(final P8 p1) { return new F, Boolean>() { public Boolean f(final P8 p2) { return ea.eq(p1._1(), p2._1()) && eb.eq(p1._2(), p2._2()) && ec.eq(p1._3(), p2._3()) && ed.eq(p1._4(), p2._4()) && ee.eq(p1._5(), p2._5()) && ef.eq(p1._6(), p2._6()) && eg.eq(p1._7(), p2._7()) && eh.eq(p1._8(), p2._8()); } }; } }); } /** * An equal instance for a vector-2. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-2. */ public static Equal> v2Equal(final Equal ea) { return streamEqual(ea).comap(V2.toStream_()); } /** * An equal instance for a vector-3. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-3. */ public static Equal> v3Equal(final Equal ea) { return streamEqual(ea).comap(V3.toStream_()); } /** * An equal instance for a vector-4. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-4. */ public static Equal> v4Equal(final Equal ea) { return streamEqual(ea).comap(V4.toStream_()); } /** * An equal instance for a vector-5. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-5. */ public static Equal> v5Equal(final Equal ea) { return streamEqual(ea).comap(V5.toStream_()); } /** * An equal instance for a vector-6. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-6. */ public static Equal> v6Equal(final Equal ea) { return streamEqual(ea).comap(V6.toStream_()); } /** * An equal instance for a vector-7. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-7. */ public static Equal> v7Equal(final Equal ea) { return streamEqual(ea).comap(V7.toStream_()); } /** * An equal instance for a vector-8. * * @param ea Equality across the elements of the vector. * @return An equal instance for a vector-8. */ public static Equal> v8Equal(final Equal ea) { return streamEqual(ea).comap(V8.toStream_()); } /** * An equal instance for lazy strings. */ public static final Equal eq = streamEqual(charEqual).comap(LazyString.toStream); /** * An equal instance for the empty heterogeneous list. */ public static final Equal hListEqual = anyEqual(); /** * An equal instance for heterogeneous lists. * * @param e Equality for the first element of the list. * @param l Equality for the rest of the list. * @return an equal instance for a heterogeneous list. */ public static > Equal> hListEqual(final Equal e, final Equal l) { return equal(curry(new F2, HList.HCons, Boolean>() { public Boolean f(final HList.HCons c1, final HList.HCons c2) { return e.eq(c1.head(), c2.head()) && l.eq(c1.tail(), c2.tail()); } })); } /** * Equal instance for sets. * * @param e Equality for the set elements. * @return An equal instance for sets. */ public static Equal> setEqual(final Equal e) { return equal(curry(new F2, Set, Boolean>() { public Boolean f(final Set a, final Set b) { return streamEqual(e).eq(a.toStream(), b.toStream()); } })); } }