001    package fj.test;
002    
003    import fj.F;
004    import fj.data.Option;
005    import static fj.data.Option.some;
006    
007    import static java.lang.Math.max;
008    import static java.lang.Math.min;
009    import java.util.Random;
010    
011    /**
012     * A random number generator.
013     *
014     * @version %build.number%<br>
015     *          <ul>
016     *          <li>$LastChangedRevision: 5 $</li>
017     *          <li>$LastChangedDate: 2008-12-06 16:49:43 +1000 (Sat, 06 Dec 2008) $</li>
018     *          <li>$LastChangedBy: tonymorris $</li>
019     *          </ul>
020     */
021    public final class Rand {
022      private final F<Option<Long>, F<Integer, F<Integer, Integer>>> f;
023      private final F<Option<Long>, F<Double, F<Double, Double>>> g;
024    
025      private Rand(final F<Option<Long>, F<Integer, F<Integer, Integer>>> f, final F<Option<Long>, F<Double, F<Double, Double>>> g) {
026        this.f = f;
027        this.g = g;
028      }
029    
030      /**
031       * Randomly chooses a value between the given range (inclusive).
032       *
033       * @param seed The seed to use for random generation.
034       * @param from The minimum value to choose.
035       * @param to   The maximum value to choose.
036       * @return A random value in the given range.
037       */
038      public int choose(final long seed, final int from, final int to) {
039        return f.f(some(seed)).f(from).f(to);
040      }
041    
042      /**
043       * Randomly chooses a value between the given range (inclusive).
044       *
045       * @param from The minimum value to choose.
046       * @param to   The maximum value to choose.
047       * @return A random value in the given range.
048       */
049      public int choose(final int from, final int to) {
050        return f.f(Option.<Long>none()).f(from).f(to);
051      }
052    
053      /**
054       * Randomly chooses a value between the given range (inclusive).
055       *
056       * @param seed The seed to use for random generation.
057       * @param from The minimum value to choose.
058       * @param to   The maximum value to choose.
059       * @return A random value in the given range.
060       */
061      public double choose(final long seed, final double from, final double to) {
062        return g.f(some(seed)).f(from).f(to);
063      }
064    
065      /**
066       * Randomly chooses a value between the given range (inclusive).
067       *
068       * @param from The minimum value to choose.
069       * @param to   The maximum value to choose.
070       * @return A random value in the given range.
071       */
072      public double choose(final double from, final double to) {
073        return g.f(Option.<Long>none()).f(from).f(to);
074      }
075    
076      /**
077       * Gives this random generator a new seed.
078       *
079       * @param seed The seed of the new random generator.
080       * @return A random generator with the given seed.
081       */
082      public Rand reseed(final long seed) {
083        return new Rand(new F<Option<Long>, F<Integer, F<Integer, Integer>>>() {
084          public F<Integer, F<Integer, Integer>> f(final Option<Long> old) {
085            return new F<Integer, F<Integer, Integer>>() {
086              public F<Integer, Integer> f(final Integer from) {
087                return new F<Integer, Integer>() {
088                  public Integer f(final Integer to) {
089                    return f.f(some(seed)).f(from).f(to);
090                  }
091                };
092              }
093            };
094          }
095        }, new F<Option<Long>, F<Double, F<Double, Double>>>() {
096          public F<Double, F<Double, Double>> f(final Option<Long> old) {
097            return new F<Double, F<Double, Double>>() {
098              public F<Double, Double> f(final Double from) {
099                return new F<Double, Double>() {
100                  public Double f(final Double to) {
101                    return g.f(some(seed)).f(from).f(to);
102                  }
103                };
104              }
105            };
106          }
107        });
108      }
109    
110      /**
111       * Constructs a random generator from the given functions that supply a range to produce a
112       * result.
113       *
114       * @param f The integer random generator.
115       * @param g The floating-point random generator.
116       * @return A random generator from the given functions that supply a range to produce a result.
117       */
118      public static Rand rand(final F<Option<Long>, F<Integer, F<Integer, Integer>>> f, final F<Option<Long>, F<Double, F<Double, Double>>> g) {
119        return new Rand(f, g);
120      }
121    
122    
123      private static final F<Long, Random> fr = new F<Long, Random>() {
124        public Random f(final Long x) {
125          return new Random(x);
126        }
127      };
128    
129      /**
130       * A standard random generator that uses {@link Random}.
131       */
132      public static final Rand standard = new Rand(new F<Option<Long>, F<Integer, F<Integer, Integer>>>() {
133        public F<Integer, F<Integer, Integer>> f(final Option<Long> seed) {
134          return new F<Integer, F<Integer, Integer>>() {
135            public F<Integer, Integer> f(final Integer from) {
136              return new F<Integer, Integer>() {
137                public Integer f(final Integer to) {
138                  final int f = min(from, to);
139                  final int t = max(from, to);
140                  return f + seed.map(fr).orSome(new Random()).nextInt(t - f + 1);
141                }
142              };
143            }
144          };
145        }
146      }, new F<Option<Long>, F<Double, F<Double, Double>>>() {
147        public F<Double, F<Double, Double>> f(final Option<Long> seed) {
148          return new F<Double, F<Double, Double>>() {
149            public F<Double, Double> f(final Double from) {
150              return new F<Double, Double>() {
151                public Double f(final Double to) {
152                  final double f = min(from, to);
153                  final double t = max(from, to);
154                  return seed.map(fr).orSome(new Random()).nextDouble() * (t - f) + f;
155                }
156              };
157            }
158          };
159        }
160      });
161    }