22
33import fj .F ;
44import fj .data .Option ;
5- import static fj .data .Option .some ;
65
6+ import java .util .Random ;
7+
8+ import static fj .data .Option .none ;
9+ import static fj .data .Option .some ;
710import static java .lang .Math .max ;
811import static java .lang .Math .min ;
9- import java .util .Random ;
1012
1113/**
1214 * A random number generator.
@@ -17,9 +19,17 @@ public final class Rand {
1719 private final F <Option <Long >, F <Integer , F <Integer , Integer >>> f ;
1820 private final F <Option <Long >, F <Double , F <Double , Double >>> g ;
1921
20- private Rand (final F <Option <Long >, F <Integer , F <Integer , Integer >>> f , final F <Option <Long >, F <Double , F <Double , Double >>> g ) {
22+ // TODO Change to F<Long,Rand> when rand(f,g) is removed
23+ private final Option <F <Long , Rand >> optOnReseed ;
24+
25+ private Rand (
26+ F <Option <Long >, F <Integer , F <Integer , Integer >>> f ,
27+ F <Option <Long >, F <Double , F <Double , Double >>> g ,
28+ Option <F <Long , Rand >> optOnReseed ) {
29+
2130 this .f = f ;
2231 this .g = g ;
32+ this .optOnReseed = optOnReseed ;
2333 }
2434
2535 /**
@@ -74,65 +84,97 @@ public double choose(final double from, final double to) {
7484 * @param seed The seed of the new random generator.
7585 * @return A random generator with the given seed.
7686 */
77- public Rand reseed (final long seed ) {
78- return new Rand (new F <Option <Long >, F <Integer , F <Integer , Integer >>>() {
79- public F <Integer , F <Integer , Integer >> f (final Option <Long > old ) {
80- return new F <Integer , F <Integer , Integer >>() {
81- public F <Integer , Integer > f (final Integer from ) {
82- return new F <Integer , Integer >() {
83- public Integer f (final Integer to ) {
84- return f .f (some (seed )).f (from ).f (to );
85- }
86- };
87- }
88- };
89- }
90- }, new F <Option <Long >, F <Double , F <Double , Double >>>() {
91- public F <Double , F <Double , Double >> f (final Option <Long > old ) {
92- return new F <Double , F <Double , Double >>() {
93- public F <Double , Double > f (final Double from ) {
94- return new F <Double , Double >() {
95- public Double f (final Double to ) {
96- return g .f (some (seed )).f (from ).f (to );
97- }
98- };
99- }
100- };
101- }
102- });
87+ public Rand reseed (long seed ) {
88+ return optOnReseed .<Rand >option (
89+ () -> {
90+ throw new IllegalStateException ("reseed() called on a Rand created with deprecated rand() method" );
91+ },
92+ onReseed -> onReseed .f (seed ));
10393 }
10494
10595 /**
10696 * Constructs a random generator from the given functions that supply a range to produce a
10797 * result.
98+ * <p>
99+ * Calling {@link #reseed(long)} on an instance returned from this method will
100+ * result in an exception being thrown. Use {@link #rand(F, F, F)} instead.
108101 *
109102 * @param f The integer random generator.
110103 * @param g The floating-point random generator.
111104 * @return A random generator from the given functions that supply a range to produce a result.
112105 */
113- public static Rand rand (final F <Option <Long >, F <Integer , F <Integer , Integer >>> f , final F <Option <Long >, F <Double , F <Double , Double >>> g ) {
114- return new Rand (f , g );
106+ // TODO Change Option<F<Long,Rand>> optOnReseed to F<Long,Road> onReseed when removing this method
107+ @ Deprecated
108+ public static Rand rand (
109+ F <Option <Long >, F <Integer , F <Integer , Integer >>> f ,
110+ F <Option <Long >, F <Double , F <Double , Double >>> g ) {
111+
112+ return new Rand (f , g , none ());
115113 }
116114
115+ /**
116+ * Constructs a reseedable random generator from the given functions that supply a range to produce a
117+ * result.
118+ *
119+ * @param f The integer random generator.
120+ * @param g The floating-point random generator.
121+ * @param onReseed Function to create a reseeded Rand.
122+ * @return A random generator from the given functions that supply a range to produce a result.
123+ */
124+ public static Rand rand (
125+ F <Option <Long >, F <Integer , F <Integer , Integer >>> f ,
126+ F <Option <Long >, F <Double , F <Double , Double >>> g ,
127+ F <Long , Rand > onReseed ) {
117128
118- private static final F <Long , Random > fr = new F <Long , Random >() {
119- public Random f (final Long x ) {
120- return new Random (x );
121- }
122- };
129+ return new Rand (f , g , some (onReseed ));
130+ }
123131
124132 /**
125133 * A standard random generator that uses {@link Random}.
126134 */
127- public static final Rand standard = new Rand (seed -> from -> to -> {
128- final int min = min (from , to );
129- final int max = max (from , to );
130- final Random random = seed .map (fr ).orSome (new Random ());
131- return (int ) ((random .nextLong () & Long .MAX_VALUE ) % (1L + max - min )) + min ;
132- }, seed -> from -> to -> {
133- final double min = min (from , to );
134- final double max = max (from , to );
135- final Random random = seed .map (fr ).orSome (new Random ());
136- return random .nextDouble () * (max - min ) + min ;
137- });
135+ public static final Rand standard = createStandard (new Random ());
136+
137+ private static Rand createStandard (Random defaultRandom ) {
138+ return rand (
139+ optSeed -> from -> to ->
140+ standardChooseInt (optSeed .<Random >option (() -> defaultRandom , Random ::new ), from , to ),
141+ optSeed -> from -> to ->
142+ standardChooseDbl (optSeed .<Random >option (() -> defaultRandom , Random ::new ), from , to ),
143+ newSeed -> createStandard (new Random (newSeed )));
144+ }
145+
146+ /*
147+ * Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (inclusive).
148+ */
149+ private static int standardChooseInt (Random random , int from , int to ) {
150+ int result ;
151+ if (from != to ) {
152+ int min = min (from , to );
153+ int max = max (from , to );
154+ long range = (1L + max ) - min ;
155+ long bound = Long .MAX_VALUE - (Long .MAX_VALUE % range );
156+ long r = random .nextLong () & Long .MAX_VALUE ;
157+ while (r >= bound ) {
158+ // Ensure uniformity
159+ r = random .nextLong () & Long .MAX_VALUE ;
160+ }
161+ result = (int ) ((r % range ) + min );
162+ } else {
163+ result = from ;
164+ }
165+ return result ;
166+ }
167+
168+ /*
169+ * Returns a uniformly distributed value between min(from,to) (inclusive) and max(from,to) (exclusive)
170+ *
171+ * In theory, this differs from the choose() contract, which specifies a closed interval.
172+ * In practice, the difference shouldn't matter.
173+ */
174+ private static double standardChooseDbl (Random random , double from , double to ) {
175+ double min = min (from , to );
176+ double max = max (from , to );
177+ return ((max - min ) * random .nextDouble ()) + min ;
178+ }
179+
138180}
0 commit comments