All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.Equal Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
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 {

  /**
   * Primitives functions of Equal: minimal definition and overridable methods.
   */
  public interface Definition {

    F 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 extends Definition {

    @Override
    boolean equal(A a1, A a2);

    @Override
    default F equal(A a) {
      return a2 -> equal(a, a2);
    }
  }

  private final Definition def;

  private Equal(final Definition def) {
    this.def = def;
  }

  /**
   * 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 def.equal(a1, a2);
  }

  /**
   * Returns true if the two given arguments are not equal, false otherwise.
   *
   * @param a1 An object to test for inequality against another.
   * @param a2 An object to test for inequality against another.
   * @return true if the two given arguments are not equal, false otherwise.
   */
  public boolean notEq(final A a1, final A a2) {
    return !def.equal(a1, a2);
  }

  /**
   * First-class equality check.
   *
   * @return A function that returns true if the two given arguments are equal.
   */
  public F2 eq() {
    return def::equal;
  }

  /**
   * 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 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  Equal contramap(final F f) {
    Definition eaDef = def;
    return equalDef(new Definition(){
      @Override
      public F 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  Equal equal(final F> 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  Equal equal(final F2 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  Equal equalDef(final Definition 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  Equal equalDef(final AltDefinition 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  Equal anyEqual() {
    return equalDef(new Definition() {
      @Override
      public F equal(A a) {
        return a::equals;
      }

      @Override
      public boolean equal(A a1, 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 Natural type.
   */
  public static final Equal naturalEqual = bigintEqual.contramap(Natural::bigIntegerValue);

  /**
   * 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 =
      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 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 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  Equal> eitherEqual(final Equal ea, final Equal eb) {
    Definition eaDef = ea.def;
    Definition 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  Equal> validationEqual(final Equal ea, final Equal 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  Equal> listEqual(final Equal ea) {
    Definition eaDef = ea.def;
    return equalDef((a1, a2) -> {
      List x1 = a1;
      List 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  Equal> nonEmptyListEqual(final Equal 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  Equal> optionEqual(final Equal ea) {
    Definition eaDef = ea.def;
    return equalDef(o1 -> o1.option(
        Option.isNone_(),
        a1 -> Option.option_(false, eaDef.equal(a1))
    ));
  }

  public static  Equal> seqEqual(final Equal 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  Equal> streamEqual(final Equal ea) {
    Definition eaDef = ea.def;
    return equalDef((a1, a2) -> {
      Stream x1 = a1;
      Stream 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  Equal> arrayEqual(final Equal ea) {
    Definition 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  Equal> treeEqual(final Equal ea) {
    Definition eaDef = ea.def;
    return equalDef(new AltDefinition>() {

      final Definition>>> subForestEqDef = p1Equal(streamEqual(equalDef(this))).def;

      @Override
      public boolean equal(Tree t1, Tree 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  Equal> p1Equal(final Equal 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  Equal> p2Equal(final Equal ea, final Equal eb) {
    Definition eaDef = ea.def;
    Definition 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  Equal> p3Equal(final Equal ea, final Equal eb, final Equal ec) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition 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  Equal> p4Equal(final Equal ea, final Equal eb, final Equal ec,
                                                           final Equal ed) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition ecDef = ec.def;
    Definition 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  Equal> p5Equal(final Equal ea, final Equal eb,
                                                                 final Equal ec, final Equal ed,
                                                                 final Equal ee) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition ecDef = ec.def;
    Definition edDef = ed.def;
    Definition 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  Equal> p6Equal(final Equal ea, final Equal eb,
                                                                         final Equal ec, final Equal ed,
                                                                         final Equal ee, final Equal ef) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition ecDef = ec.def;
    Definition edDef = ed.def;
    Definition eeDef = ee.def;
    Definition 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  Equal> p7Equal(final Equal ea, final Equal eb,
                                                                               final Equal ec, final Equal ed,
                                                                               final Equal ee, final Equal ef,
                                                                               final Equal eg) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition ecDef = ec.def;
    Definition edDef = ed.def;
    Definition eeDef = ee.def;
    Definition efDef = ef.def;
    Definition 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  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) {
    Definition eaDef = ea.def;
    Definition ebDef = eb.def;
    Definition ecDef = ec.def;
    Definition edDef = ed.def;
    Definition eeDef = ee.def;
    Definition efDef = ef.def;
    Definition egDef = eg.def;
    Definition 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  Equal> v2Equal(final Equal 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  Equal> v3Equal(final Equal 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  Equal> v4Equal(final Equal 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  Equal> v5Equal(final Equal 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  Equal> v6Equal(final Equal 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  Equal> v7Equal(final Equal 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  Equal> v8Equal(final Equal ea) {
    return streamEqual(ea).contramap(V8.toStream_());
  }

  /**
   * An equal instance for lazy strings.
   */
  public static final Equal eq = streamEqual(charEqual).contramap(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) {
    Definition eDef = e.def;
    Definition 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  Equal> setEqual(final Equal e) {
    return streamEqual(e).contramap(Set::toStream);
  }

  public static  Equal> treeMapEqual(Equal k, Equal v) {
    return streamEqual(p2Equal(k, v)).contramap(TreeMap::toStream);
  }

  public static  Equal> writerEqual(Equal eq1, Equal 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  boolean equals0(final java.lang.Class clazz, final A self, final Object other, final Equal 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  boolean equals0(final java.lang.Class clazz, final A self, final Object other, final F0> equal) {
    return self == other || clazz.isInstance(other) && equal.f().eq(self, (A) other);
  }

}