package fj; import fj.data.Array; import fj.data.Either; import fj.data.LazyString; import fj.data.List; import fj.data.Natural; import fj.data.NonEmptyList; import fj.data.Option; import fj.data.Seq; import fj.data.Set; import fj.data.Stream; import fj.data.Tree; import fj.data.TreeMap; import fj.data.Validation; import fj.data.Writer; import fj.data.hamt.BitSet; import fj.data.hlist.HList; 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.BigDecimal; import java.math.BigInteger; import static fj.Function.compose; import static fj.Function.constant; import static fj.Function.curry; /** * Tests for equality between two objects. * * @version %build.number% */ public final class Equal<A> { /** * Primitives functions of Equal: minimal definition and overridable methods. */ public interface Definition<A> { F<A, Boolean> equal(A a); default boolean equal(A a1, A a2) { return equal(a1).f(a2); } } /** * Primitives functions of Equal: alternative minimal definition and overridable methods. */ public interface AltDefinition<A> extends Definition<A> { @Override boolean equal(A a1, A a2); @Override default F<A, Boolean> equal(A a) { return a2 -> equal(a, a2); } } private final Definition<A> def; private Equal(final Definition<A> def) { this.def = def; } /** * Returns <code>true</code> if the two given arguments are equal, <code>false</code> otherwise. * * @param a1 An object to test for equality against another. * @param a2 An object to test for equality against another. * @return <code>true</code> if the two given arguments are equal, <code>false</code> otherwise. */ public boolean eq(final A a1, final A a2) { return def.equal(a1, a2); } /** * Returns <code>true</code> if the two given arguments are not equal, <code>false</code> otherwise. * * @param a1 An object to test for inequality against another. * @param a2 An object to test for inequality against another. * @return <code>true</code> if the two given arguments are not equal, <code>false</code> otherwise. */ public boolean notEq(final A a1, final A a2) { return !def.equal(a1, a2); } /** * First-class equality check. * * @return A function that returns <code>true</code> if the two given arguments are equal. */ public F2<A, A, Boolean> eq() { return def::equal; } /** * Partially applied equality check. * * @param a An object to test for equality against another. * @return A function that returns <code>true</code> if the given argument equals the argument to this method. */ public F<A, Boolean> eq(final A a) { return def.equal(a); } /** * Maps the given function across this equal as a contra-variant functor. * * @param f The function to map. * @return A new equal. */ public <B> Equal<B> contramap(final F<B, A> f) { Definition<A> eaDef = def; return equalDef(new Definition<B>(){ @Override public F<B, Boolean> equal(B b) { return compose(eaDef.equal(f.f(b)), f); } @Override public boolean equal(B b1, B b2) { return eaDef.equal(f.f(b1), f.f(b2)); } }); } /** * Constructs an equal instance from the given function. * * Java 8+ users: use {@link #equalDef(Definition)} instead. * * @param f The function to construct the equal with. * @return An equal instance from the given function. */ public static <A> Equal<A> equal(final F<A, F<A, Boolean>> f) { return new Equal<>(f::f); } /** * Constructs an equal instance from the given function. * * Java 8+ users: use {@link #equalDef(AltDefinition)} instead. * * @param f The function to construct the equal with. * @return An equal instance from the given function. */ public static <A> Equal<A> equal(final F2<A, A, Boolean> f) { return equalDef(f::f); } /** * Constructs an equal instance from the given definition. * * @param definition a definition of the equal instance. * @return An equal instance from the given function. */ public static <A> Equal<A> equalDef(final Definition<A> definition) { return new Equal<>(definition); } /** * Constructs an equal instance from the given (alternative) definition. * * @param definition a definition of the equal instance. * @return An equal instance from the given function. */ public static <A> Equal<A> equalDef(final AltDefinition<A> definition) { return new Equal<>(definition); } /** * 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 <A> Equal<A> anyEqual() { return equalDef(new Definition<A>() { @Override public F<A, Boolean> equal(A a) { return a::equals; } @Override public boolean equal(A a1, A a2) { return a1.equals(a2); } }); } /** * An equal instance for the <code>boolean</code> type. */ public static final Equal<Boolean> booleanEqual = anyEqual(); /** * An equal instance for the <code>byte</code> type. */ public static final Equal<Byte> byteEqual = anyEqual(); /** * An equal instance for the <code>char</code> type. */ public static final Equal<Character> charEqual = anyEqual(); /** * An equal instance for the <code>double</code> type. */ public static final Equal<Double> doubleEqual = anyEqual(); /** * An equal instance for the <code>float</code> type. */ public static final Equal<Float> floatEqual = anyEqual(); /** * An equal instance for the <code>int</code> type. */ public static final Equal<Integer> intEqual = anyEqual(); /** * An equal instance for the <code>BigInteger</code> type. */ public static final Equal<BigInteger> bigintEqual = anyEqual(); /** * An equal instance for the <code>BigDecimal</code> type. */ public static final Equal<BigDecimal> bigdecimalEqual = anyEqual(); /** * An equal instance for the <code>long</code> type. */ public static final Equal<Long> longEqual = anyEqual(); /** * An equal instance for the <code>short</code> type. */ public static final Equal<Short> shortEqual = anyEqual(); /** * An equal instance for the <code>Natural</code> type. */ public static final Equal<Natural> naturalEqual = bigintEqual.contramap(Natural::bigIntegerValue); /** * An equal instance for the {@link String} type. */ public static final Equal<String> stringEqual = anyEqual(); /** * An equal instance for the {@link StringBuffer} type. */ public static final Equal<StringBuffer> stringBufferEqual = equalDef((sb1, 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<StringBuilder> stringBuilderEqual = equalDef((sb1, 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 BitSet} type. */ public static final Equal<BitSet> bitSetSequal = equalDef((bs1, bs2) -> bs1.longValue() == bs2.longValue()); /** * 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 <A, B> Equal<Either<A, B>> eitherEqual(final Equal<A> ea, final Equal<B> eb) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; return equalDef(e1 -> e1.either( a1 -> Either.either_(eaDef.equal(a1), (B __) -> false), b1 -> Either.either_((A __)-> false, ebDef.equal(b1)) )); } /** * 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 <A, B> Equal<Validation<A, B>> validationEqual(final Equal<A> ea, final Equal<B> eb) { return eitherEqual(ea, eb).contramap(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 <A> Equal<List<A>> listEqual(final Equal<A> ea) { Definition<A> eaDef = ea.def; return equalDef((a1, a2) -> { List<A> x1 = a1; List<A> x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { if (!eaDef.equal(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 <A> Equal<NonEmptyList<A>> nonEmptyListEqual(final Equal<A> ea) { return listEqual(ea).contramap(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 <A> Equal<Option<A>> optionEqual(final Equal<A> ea) { Definition<A> eaDef = ea.def; return equalDef(o1 -> o1.option( Option.isNone_(), a1 -> Option.option_(false, eaDef.equal(a1)) )); } public static <A> Equal<Seq<A>> seqEqual(final Equal<A> e) { return streamEqual(e).contramap(Seq::toStream); } /** * 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 <A> Equal<Stream<A>> streamEqual(final Equal<A> ea) { Definition<A> eaDef = ea.def; return equalDef((a1, a2) -> { Stream<A> x1 = a1; Stream<A> x2 = a2; while (x1.isNotEmpty() && x2.isNotEmpty()) { if (!eaDef.equal(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 <A> Equal<Array<A>> arrayEqual(final Equal<A> ea) { Definition<A> eaDef = ea.def; return equalDef((a1, a2) -> { if (a1.length() == a2.length()) { for (int i = 0; i < a1.length(); i++) { if (!eaDef.equal(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 <A> Equal<Tree<A>> treeEqual(final Equal<A> ea) { Definition<A> eaDef = ea.def; return equalDef(new AltDefinition<Tree<A>>() { final Definition<P1<Stream<Tree<A>>>> subForestEqDef = p1Equal(streamEqual(equalDef(this))).def; @Override public boolean equal(Tree<A> t1, Tree<A> t2) { return eaDef.equal(t1.root(), t2.root()) && subForestEqDef.equal(t1.subForest(), t2.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 <A> Equal<P1<A>> p1Equal(final Equal<A> ea) { return ea.contramap(P1.__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 <A, B> Equal<P2<A, B>> p2Equal(final Equal<A> ea, final Equal<B> eb) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; return equalDef((p1, p2)-> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(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 <A, B, C> Equal<P3<A, B, C>> p3Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(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 <A, B, C, D> Equal<P4<A, B, C, D>> p4Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec, final Equal<D> ed) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; Definition<D> edDef = ed.def; return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && edDef.equal(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 <A, B, C, D, E> Equal<P5<A, B, C, D, E>> p5Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec, final Equal<D> ed, final Equal<E> ee) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; Definition<D> edDef = ed.def; Definition<E> eeDef = ee.def; return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && edDef.equal(p1._4(), p2._4()) && eeDef.equal(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 <A, B, C, D, E, F$> Equal<P6<A, B, C, D, E, F$>> p6Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec, final Equal<D> ed, final Equal<E> ee, final Equal<F$> ef) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; Definition<D> edDef = ed.def; Definition<E> eeDef = ee.def; Definition<F$> efDef = ef.def; return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(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 <A, B, C, D, E, F$, G> Equal<P7<A, B, C, D, E, F$, G>> p7Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec, final Equal<D> ed, final Equal<E> ee, final Equal<F$> ef, final Equal<G> eg) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; Definition<D> edDef = ed.def; Definition<E> eeDef = ee.def; Definition<F$> efDef = ef.def; Definition<G> egDef = eg.def; return equalDef((p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(p1._6(), p2._6()) && egDef.equal(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 <A, B, C, D, E, F$, G, H> Equal<P8<A, B, C, D, E, F$, G, H>> p8Equal(final Equal<A> ea, final Equal<B> eb, final Equal<C> ec, final Equal<D> ed, final Equal<E> ee, final Equal<F$> ef, final Equal<G> eg, final Equal<H> eh) { Definition<A> eaDef = ea.def; Definition<B> ebDef = eb.def; Definition<C> ecDef = ec.def; Definition<D> edDef = ed.def; Definition<E> eeDef = ee.def; Definition<F$> efDef = ef.def; Definition<G> egDef = eg.def; Definition<H> ehDef = eh.def; return equalDef( (p1, p2) -> eaDef.equal(p1._1(), p2._1()) && ebDef.equal(p1._2(), p2._2()) && ecDef.equal(p1._3(), p2._3()) && edDef.equal(p1._4(), p2._4()) && eeDef.equal(p1._5(), p2._5()) && efDef.equal(p1._6(), p2._6()) && egDef.equal(p1._7(), p2._7()) && ehDef.equal(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 <A> Equal<V2<A>> v2Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V3<A>> v3Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V4<A>> v4Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V5<A>> v5Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V6<A>> v6Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V7<A>> v7Equal(final Equal<A> ea) { return streamEqual(ea).contramap(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 <A> Equal<V8<A>> v8Equal(final Equal<A> ea) { return streamEqual(ea).contramap(V8.toStream_()); } /** * An equal instance for lazy strings. */ public static final Equal<LazyString> eq = streamEqual(charEqual).contramap(LazyString.toStream); /** * An equal instance for the empty heterogeneous list. */ public static final Equal<HList.HNil> 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 <E, L extends HList<L>> Equal<HList.HCons<E, L>> hListEqual(final Equal<E> e, final Equal<L> l) { Definition<E> eDef = e.def; Definition<L> lDef = l.def; return equalDef((c1, c2) -> eDef.equal(c1.head(), c2.head()) && lDef.equal(c1.tail(), c2.tail())); } /** * Equal instance for sets. * * @param e Equality for the set elements. * @return An equal instance for sets. */ public static <A> Equal<Set<A>> setEqual(final Equal<A> e) { return streamEqual(e).contramap(Set::toStream); } public static <K, V> Equal<TreeMap<K, V>> treeMapEqual(Equal<K> k, Equal<V> v) { return streamEqual(p2Equal(k, v)).contramap(TreeMap::toStream); } public static <A, B> Equal<Writer<A, B>> writerEqual(Equal<A> eq1, Equal<B> eq2) { return p2Equal(eq1, eq2).contramap(Writer::run); } /** * Helper method to implement {@link Object#equals(Object)} correctly. DO NOT USE it for any other purpose. * * @param clazz the class in which the {@link Object#equals(Object)} is implemented * @param self a reference to 'this' * @param other the other object of the comparison * @param equal an equal instance for the type of self (that use {@link #anyEqual()} if generic type). * @return true if self and other are equal */ @SuppressWarnings("unchecked") public static <A> boolean equals0(final java.lang.Class<? super A> clazz, final A self, final Object other, final Equal<A> equal) { return self == other || clazz.isInstance(other) && equal.eq(self, (A) other); } /** * Helper method to implement {@link Object#equals(Object)} correctly. DO NOT USE it for any other purpose. * * @param clazz the class in which the {@link Object#equals(Object)} is implemented * @param self a reference to 'this' * @param other the other object of the comparison * @param equal a lazy equal instance for the type (that use {@link #anyEqual()} if generic type).. * @return true if self and other are equal */ @SuppressWarnings("unchecked") public static <A> boolean equals0(final java.lang.Class<? super A> clazz, final A self, final Object other, final F0<Equal<A>> equal) { return self == other || clazz.isInstance(other) && equal.f().eq(self, (A) other); } }