package fj; import static fj.Function.compose; import fj.data.Array; import fj.data.Either; import fj.data.List; import fj.data.NonEmptyList; import fj.data.Option; 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; /** * Produces a hash code for an object which should attempt uniqueness. * * @version %build.number% */ public final class Hash { private final F f; private Hash(final F 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 Hash comap(final F g) { return new 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 Hash hash(final F f) { return new Hash(f); } /** * A hash that uses {@link Object#hashCode()}. * * @return A hash that uses {@link Object#hashCode()}. */ public static Hash anyHash() { return new Hash(new F() { public Integer f(final A a) { return a.hashCode(); } }); } /** * A hash instance for the boolean type. */ public static final Hash booleanHash = anyHash(); /** * A hash instance for the byte type. */ public static final Hash byteHash = anyHash(); /** * A hash instance for the char type. */ public static final Hash charHash = anyHash(); /** * A hash instance for the double type. */ public static final Hash doubleHash = anyHash(); /** * A hash instance for the float type. */ public static final Hash floatHash = anyHash(); /** * A hash instance for the int type. */ public static final Hash intHash = anyHash(); /** * A hash instance for the long type. */ public static final Hash longHash = anyHash(); /** * A hash instance for the short type. */ public static final Hash shortHash = anyHash(); /** * A hash instance for the String type. */ public static final Hash stringHash = anyHash(); /** * A hash instance for the {@link StringBuffer} type. */ public static final Hash stringBufferHash = new Hash(new F() { public Integer f(final StringBuffer 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 stringBuilderHash = new Hash(new F() { public Integer f(final StringBuilder 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 Either. * @param hb Hash the right side of Either. * @return A hash instance for the {@link Either} type. */ public static Hash> eitherHash(final Hash ha, final Hash hb) { return new Hash>(new F, Integer>() { public Integer f(final Either e) { return 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 Validation. * @param hb Hash the succeeding side of Validation. * @return A hash instance for the {@link Validation} type. */ public static Hash> validationHash(final Hash ha, final Hash hb) { return eitherHash(ha, hb).comap(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 Hash> listHash(final Hash ha) { return new Hash>(new F, Integer>() { public Integer f(final List as) { final int p = 419; int r = 239; List 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 Hash> nonEmptyListHash(final Hash ha) { return listHash(ha).comap(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 Hash> optionHash(final Hash ha) { return new Hash>(new F, Integer>() { public Integer f(final Option o) { return o.isNone() ? 0 : ha.hash(o.some()); } }); } /** * 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 Hash> streamHash(final Hash ha) { return new Hash>(new F, Integer>() { public Integer f(final Stream as) { final int p = 419; int r = 239; Stream 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 Hash> arrayHash(final Hash ha) { return new Hash>(new F, Integer>() { public Integer f(final Array 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 Hash> treeHash(final Hash ha) { return streamHash(ha).comap(Tree.flatten_()); } /** * 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 Hash> p1Hash(final Hash ha) { return ha.comap(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 Hash> p2Hash(final Hash ha, final Hash hb) { return new Hash>(new F, Integer>() { public Integer f(final P2 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 Hash> p3Hash(final Hash ha, final Hash hb, final Hash hc) { return new Hash>(new F, Integer>() { public Integer f(final P3 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 Hash> p4Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd) { return new Hash>(new F, Integer>() { public Integer f(final P4 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 Hash> p5Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he) { return new Hash>(new F, Integer>() { public Integer f(final P5 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 Hash> p6Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he, final Hash hf) { return new Hash>(new F, Integer>() { public Integer f(final P6 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 Hash> p7Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he, final Hash hf, final Hash hg) { return new Hash>(new F, Integer>() { public Integer f(final P7 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 Hash> p8Hash(final Hash ha, final Hash hb, final Hash hc, final Hash hd, final Hash he, final Hash hf, final Hash hg, final Hash hh) { return new Hash>(new F, Integer>() { public Integer f(final P8 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 Hash> v2Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v3Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v4Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v5Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v6Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v7Hash(final Hash ea) { return streamHash(ea).comap(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 Hash> v8Hash(final Hash ea) { return streamHash(ea).comap(V8.toStream_()); } }