package fj; import static fj.Function.compose; import fj.data.*; 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; /** * Produces a hash code for an object which should attempt uniqueness. * * @version %build.number% */ public final class Hash<A> { private final F<A, Integer> f; private Hash(final F<A, Integer> f) { this.f = f; } /** * Compute the hash of the given value. * * @param a The value to compute the hash value for. * @return The hash value. */ public int hash(final A a) { return f.f(a); } /** * Maps the given function across this hash as a contra-variant functor. * * @param g The function to map. * @return A new hash. */ public <B> Hash<B> contramap(final F<B, A> g) { return hash(compose(f, g)); } /** * Construct a hash with the given hash function. * * @param f The function to construct the hash with. * @return A hash that uses the given function. */ public static <A> Hash<A> hash(final F<A, Integer> f) { return new Hash<>(f); } /** * A hash that uses {@link Object#hashCode()}. * * @return A hash that uses {@link Object#hashCode()}. */ public static <A> Hash<A> anyHash() { return hash(Object::hashCode); } /** * A hash instance for the <code>boolean</code> type. */ public static final Hash<Boolean> booleanHash = anyHash(); /** * A hash instance for the <code>byte</code> type. */ public static final Hash<Byte> byteHash = anyHash(); /** * A hash instance for the <code>char</code> type. */ public static final Hash<Character> charHash = anyHash(); /** * A hash instance for the <code>double</code> type. */ public static final Hash<Double> doubleHash = anyHash(); /** * A hash instance for the <code>float</code> type. */ public static final Hash<Float> floatHash = anyHash(); /** * A hash instance for the <code>int</code> type. */ public static final Hash<Integer> intHash = anyHash(); /** * A hash instance for the <code>long</code> type. */ public static final Hash<Long> longHash = anyHash(); /** * A hash instance for the <code>short</code> type. */ public static final Hash<Short> shortHash = anyHash(); /** * A hash instance for the <code>BigInteger</code> type. */ public static final Hash<BigInteger> bigintHash = anyHash(); /** * A hash instance for the <code>BigDecimal</code> type. */ public static final Hash<BigDecimal> bigdecimalHash = anyHash(); /** * A hash instance for the {@link Natural} type. */ public static final Hash<Natural> naturalHash = bigintHash.contramap(Natural::bigIntegerValue); /** * A hash instance for the <code>String</code> type. */ public static final Hash<String> stringHash = anyHash(); /** * A hash instance for the {@link StringBuffer} type. */ public static final Hash<StringBuffer> stringBufferHash = hash(sb -> { final int p = 419; int r = 239; for (int i = 0; i < sb.length(); i++) r = p * r + sb.charAt(i); return r; }); /** * A hash instance for the {@link StringBuilder} type. */ public static final Hash<StringBuilder> stringBuilderHash = hash(sb -> { final int p = 419; int r = 239; for (int i = 0; i < sb.length(); i++) r = p * r + sb.charAt(i); return r; }); /** * A hash instance for the {@link Either} type. * * @param ha Hash the left side of <code>Either</code>. * @param hb Hash the right side of <code>Either</code>. * @return A hash instance for the {@link Either} type. */ public static <A, B> Hash<Either<A, B>> eitherHash(final Hash<A> ha, final Hash<B> hb) { return hash(e -> e.isLeft() ? ha.hash(e.left().value()) : hb.hash(e.right().value())); } /** * A hash instance for the {@link Validation} type. * * @param ha Hash the failing side of <code>Validation</code>. * @param hb Hash the succeeding side of <code>Validation</code>. * @return A hash instance for the {@link Validation} type. */ public static <A, B> Hash<Validation<A, B>> validationHash(final Hash<A> ha, final Hash<B> hb) { return eitherHash(ha, hb).contramap(Validation.either()); } /** * A hash instance for the {@link List} type. * * @param ha A hash for the elements of the list. * @return A hash instance for the {@link List} type. */ public static <A> Hash<List<A>> listHash(final Hash<A> ha) { return hash(as -> { final int p = 419; int r = 239; List<A> aas = as; while (!aas.isEmpty()) { r = p * r + ha.hash(aas.head()); aas = aas.tail(); } return r; }); } /** * A hash instance for the {@link NonEmptyList} type. * * @param ha A hash for the elements of the non-empty list. * @return A hash instance for the {@link NonEmptyList} type. */ public static <A> Hash<NonEmptyList<A>> nonEmptyListHash(final Hash<A> ha) { return listHash(ha).contramap(NonEmptyList.toList_()); } /** * A hash instance for the {@link Option} type. * * @param ha A hash for the element of the optional value. * @return A hash instance for the {@link Option} type. */ public static <A> Hash<Option<A>> optionHash(final Hash<A> ha) { return hash(o -> o.isNone() ? 0 : ha.hash(o.some())); } public static <A> Hash<Seq<A>> seqHash(final Hash<A> h) { return hash(s -> streamHash(h).hash(s.toStream())); } public static <A> Hash<Set<A>> setHash(final Hash<A> h) { return hash(s -> streamHash(h).hash(s.toStream())); } /** * A hash instance for the {@link Stream} type. * * @param ha A hash for the elements of the stream. * @return A hash instance for the {@link Stream} type. */ public static <A> Hash<Stream<A>> streamHash(final Hash<A> ha) { return hash(as -> { final int p = 419; int r = 239; Stream<A> aas = as; while (!aas.isEmpty()) { r = p * r + ha.hash(aas.head()); aas = aas.tail()._1(); } return r; }); } /** * A hash instance for the {@link Array} type. * * @param ha A hash for the elements of the array. * @return A hash instance for the {@link Array} type. */ public static <A> Hash<Array<A>> arrayHash(final Hash<A> ha) { return hash(as -> { final int p = 419; int r = 239; for (int i = 0; i < as.length(); i++) { r = p * r + ha.hash(as.get(i)); } return r; }); } /** * A hash instance for the {@link Tree} type. * * @param ha A hash for the elements of the tree. * @return A hash instance for the {@link Tree} type. */ public static <A> Hash<Tree<A>> treeHash(final Hash<A> ha) { return streamHash(ha).contramap(Tree.flatten_()); } public static <K, V> Hash<TreeMap<K, V>> treeMapHash(final Hash<K> h, final Hash<V> v) { return hash(t -> streamHash(p2Hash(h, v)).hash(t.toStream())); } /** * A hash instance for a product-1. * * @param ha A hash for the first element of the product. * @return A hash instance for a product-1. */ public static <A> Hash<P1<A>> p1Hash(final Hash<A> ha) { return ha.contramap(P1.__1()); } /** * A hash instance for a product-2. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @return A hash instance for a product-2. */ public static <A, B> Hash<P2<A, B>> p2Hash(final Hash<A> ha, final Hash<B> hb) { return hash(p2 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p2._1()); r = p * r + hb.hash(p2._2()); return r; }); } /** * A hash instance for a product-3. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @return A hash instance for a product-3. */ public static <A, B, C> Hash<P3<A, B, C>> p3Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc) { return hash(p3 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p3._1()); r = p * r + hb.hash(p3._2()); r = p * r + hc.hash(p3._3()); return r; }); } /** * A hash instance for a product-4. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @param hd A hash for the fourth element of the product. * @return A hash instance for a product-4. */ public static <A, B, C, D> Hash<P4<A, B, C, D>> p4Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc, final Hash<D> hd) { return hash(p4 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p4._1()); r = p * r + hb.hash(p4._2()); r = p * r + hc.hash(p4._3()); r = p * r + hd.hash(p4._4()); return r; }); } /** * A hash instance for a product-5. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @param hd A hash for the fourth element of the product. * @param he A hash for the fifth element of the product. * @return A hash instance for a product-5. */ public static <A, B, C, D, E> Hash<P5<A, B, C, D, E>> p5Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc, final Hash<D> hd, final Hash<E> he) { return hash(p5 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p5._1()); r = p * r + hb.hash(p5._2()); r = p * r + hc.hash(p5._3()); r = p * r + hd.hash(p5._4()); r = p * r + he.hash(p5._5()); return r; }); } /** * A hash instance for a product-6. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @param hd A hash for the fourth element of the product. * @param he A hash for the fifth element of the product. * @param hf A hash for the sixth element of the product. * @return A hash instance for a product-6. */ public static <A, B, C, D, E, F$> Hash<P6<A, B, C, D, E, F$>> p6Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc, final Hash<D> hd, final Hash<E> he, final Hash<F$> hf) { return hash(p6 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p6._1()); r = p * r + hb.hash(p6._2()); r = p * r + hc.hash(p6._3()); r = p * r + hd.hash(p6._4()); r = p * r + he.hash(p6._5()); r = p * r + hf.hash(p6._6()); return r; }); } /** * A hash instance for a product-7. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @param hd A hash for the fourth element of the product. * @param he A hash for the fifth element of the product. * @param hf A hash for the sixth element of the product. * @param hg A hash for the seventh element of the product. * @return A hash instance for a product-7. */ public static <A, B, C, D, E, F$, G> Hash<P7<A, B, C, D, E, F$, G>> p7Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc, final Hash<D> hd, final Hash<E> he, final Hash<F$> hf, final Hash<G> hg) { return hash(p7 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p7._1()); r = p * r + hb.hash(p7._2()); r = p * r + hc.hash(p7._3()); r = p * r + hd.hash(p7._4()); r = p * r + he.hash(p7._5()); r = p * r + hf.hash(p7._6()); r = p * r + hg.hash(p7._7()); return r; }); } /** * A hash instance for a product-8. * * @param ha A hash for the first element of the product. * @param hb A hash for the second element of the product. * @param hc A hash for the third element of the product. * @param hd A hash for the fourth element of the product. * @param he A hash for the fifth element of the product. * @param hf A hash for the sixth element of the product. * @param hg A hash for the seventh element of the product. * @param hh A hash for the eighth element of the product. * @return A hash instance for a product-8. */ public static <A, B, C, D, E, F$, G, H> Hash<P8<A, B, C, D, E, F$, G, H>> p8Hash(final Hash<A> ha, final Hash<B> hb, final Hash<C> hc, final Hash<D> hd, final Hash<E> he, final Hash<F$> hf, final Hash<G> hg, final Hash<H> hh) { return hash(p8 -> { final int p = 419; int r = 239; r = p * r + ha.hash(p8._1()); r = p * r + hb.hash(p8._2()); r = p * r + hc.hash(p8._3()); r = p * r + hd.hash(p8._4()); r = p * r + he.hash(p8._5()); r = p * r + hf.hash(p8._6()); r = p * r + hg.hash(p8._7()); r = p * r + hh.hash(p8._8()); return r; }); } /** * A hash instance for a vector-2. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-2. */ public static <A> Hash<V2<A>> v2Hash(final Hash<A> ea) { return streamHash(ea).contramap(V2.toStream_()); } /** * A hash instance for a vector-3. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-3. */ public static <A> Hash<V3<A>> v3Hash(final Hash<A> ea) { return streamHash(ea).contramap(V3.toStream_()); } /** * A hash instance for a vector-4. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-4. */ public static <A> Hash<V4<A>> v4Hash(final Hash<A> ea) { return streamHash(ea).contramap(V4.toStream_()); } /** * A hash instance for a vector-5. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-5. */ public static <A> Hash<V5<A>> v5Hash(final Hash<A> ea) { return streamHash(ea).contramap(V5.toStream_()); } /** * A hash instance for a vector-6. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-6. */ public static <A> Hash<V6<A>> v6Hash(final Hash<A> ea) { return streamHash(ea).contramap(V6.toStream_()); } /** * A hash instance for a vector-7. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-7. */ public static <A> Hash<V7<A>> v7Hash(final Hash<A> ea) { return streamHash(ea).contramap(V7.toStream_()); } /** * A hash instance for a vector-8. * * @param ea A hash for the elements of the vector. * @return A hash instance for a vector-8. */ public static <A> Hash<V8<A>> v8Hash(final Hash<A> ea) { return streamHash(ea).contramap(V8.toStream_()); } }