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 static fj.Function.curry;

import fj.data.*;
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.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);
  }

  /**
   * 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 !eq(a1, a2);
  }

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

  /**
   * 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 f.f(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) {
    return equal(F1Functions.o(F1Functions.o(F1Functions.andThen(f), this.f), 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 equal(a1 -> a1::equals);
  }

  /**
   * 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 =
      equal(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 =
      equal(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 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 equal(e1 -> e2 -> 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).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) {
    return equal(a1 -> 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).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) {
    return equal(o1 -> o2 -> o1.isNone() && o2.isNone() ||
           o1.isSome() && o2.isSome() && ea.f.f(o1.some()).f(o2.some()));
  }

  public static  Equal> seqEqual(final Equal e) {
    return equal(s1 -> s2 -> streamEqual(e).eq(s1.toStream(), s2.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) {
    return equal(a1 -> 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 equal(a1 -> 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 Equal.equal(curry((t1, t2) -> ea.eq(t1.root(), t2.root()) && p1Equal(streamEqual(Equal.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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(p1 -> p2 -> 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 equal(
            p1 -> p2 -> 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).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) {
    return equal(curry((HList.HCons c1, HList.HCons c2) -> 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((Set a, Set b) -> streamEqual(e).eq(a.toStream(), b.toStream())));
  }

  public static  Equal> treeMapEqual(Equal k, Equal v) {
    return equal(t1 -> t2 -> streamEqual(p2Equal(k, v)).eq(t1.toStream(), t2.toStream()));
  }

  public static  Equal> writerEqual(Equal eq1, Equal eq2) {
    return equal(w1 -> w2 -> p2Equal(eq1, eq2).eq(w1.run(), w2.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);
  }

}