001    package fj.test;
002    
003    import fj.F;
004    import static fj.test.Gen.gen;
005    
006    import java.util.HashMap;
007    
008    /**
009     * A memoised generator variant. Stores generators that have already been computed for the given arguments.
010     *
011     * @version %build.number%<br>
012     *          <ul>
013     *          <li>$LastChangedRevision: 5 $</li>
014     *          <li>$LastChangedDate: 2008-12-06 16:49:43 +1000 (Sat, 06 Dec 2008) $</li>
015     *          <li>$LastChangedBy: tonymorris $</li>
016     *          </ul>
017     */
018    public final class Variant {
019      private static final HashMap<LongGen, Gen<?>> variantMemo = new HashMap<LongGen, Gen<?>>();
020    
021      private static final class LongGen {
022        private final long n;
023        private final Gen<?> gen;
024    
025        LongGen(final long n, final Gen<?> gen) {
026          this.n = n;
027          this.gen = gen;
028        }
029    
030        public boolean equals(final Object o) {
031          return o != null &&
032                 o.getClass() == LongGen.class &&
033                 n == ((LongGen)o).n &&
034                 gen == ((LongGen)o).gen;
035        }
036    
037        public int hashCode() {
038          final int p = 419;
039          int result = 239;
040          result = p * result + (int) (n ^ n >>> 32);
041          result = p * result + gen.hashCode();
042          return result;
043        }
044      }
045    
046      private Variant() {
047        throw new UnsupportedOperationException();
048      }
049    
050      /**
051       * Produces a generator that is independent of the given generator using the given value.
052       *
053       * @param n The value to produce the new generator from.
054       * @param g The generator to produce the new generator from.
055       * @return A generator that is independent of the given generator using the given value.
056       */
057      public static <A> Gen<A> variant(final long n, final Gen<A> g) {
058        final LongGen p = new LongGen(n, g);
059        final Gen<?> gx = variantMemo.get(p);
060        if(gx == null) {
061          final Gen<A> t = gen(new F<Integer, F<Rand, A>>() {
062            public F<Rand, A> f(final Integer i) {
063              return new F<Rand, A>() {
064                public A f(final Rand r) {
065                  return g.gen(i, r.reseed(n));
066                }
067              };
068            }
069          });
070          variantMemo.put(p, t);
071          return t;
072        } else return gen(new F<Integer, F<Rand, A>>() {
073          public F<Rand, A> f(final Integer i) {
074            return new F<Rand, A>() {
075              @SuppressWarnings({"unchecked"})
076              public A f(final Rand r) {
077                return (A)gx.gen(i, r);
078              }
079            };
080          }
081        });
082      }
083    
084      /**
085       * A curried version of {@link #variant(long, Gen)}.
086       *
087       * @param n The value to produce the new generator from.
088       * @return A curried version of {@link #variant(long, Gen)}.
089       */
090      public static <A> F<Gen<A>, Gen<A>> variant(final long n) {
091        return new F<Gen<A>, Gen<A>>() {
092          public Gen<A> f(final Gen<A> g) {
093            return variant(n, g);
094          }
095        };
096      }
097    }
098