001package squidpony.squidmath;
002
003import java.io.Serializable;
004import java.util.Map;
005
006/**
007 * A class that wraps an RNG and allows different String keys to be associated with biases toward low or high results
008 * when a method is called that gets a number from the wrapped RNG. With this, you could make a category of "blessed" or
009 * "cursed" biases that, instead of using a uniform distribution that produces all numbers approximately with equal
010 * likelihood (with doubles between 0.0 and 1.0 averaging at 0.5), have different averages, like 0.7 for blessed or 0.3
011 * for cursed, when generating between 0.0 and 1.0. You could also use this to favor or disfavor the player for "easy
012 * mode" or "hard mode" categories of play.
013 * <br>
014 * The API allows you to associate an alternative average with a kind as a String, like "blessed to-hit" or "hard mode
015 * enemy damage", if you expect to use that number more than once and might want to tweak any averages by changing one
016 * number at a later point. You can also give an average as a double in place of a kind as a String, which avoids a
017 * HashMap lookup and lets you give flexibly-adjusted numbers, but does need more effort to change many values
018 * throughout a codebase if averages aren't all generated by a formula. You can also set the distribution in the
019 * constructor or by changing the public distribution field; you can use constants in this class, TRIANGULAR,
020 * EXPONENTIAL, TRUNCATED, SOFT_TRIANGULAR, and EXP_TRI (the average of EXPONENTIAL and TRIANGULAR), for different
021 * choices, with the default being EXP_TRI. Each one of these has different behavior regarding a preference toward
022 * extreme values; TRIANGULAR almost never produces very high or very low values, EXPONENTIAL frequently produces the
023 * highest or lowest values for high or low expected averages, respectively, TRUNCATED will simply never generate values
024 * that are too far from the average (otherwise it's uniform), SOFT_TRIANGULAR will produce a rounded version of
025 * TRIANGULAR's distribution with less of an angular peak and more frequent high and low values, and EXP_TRI will have
026 * something like a curve shape that may "collide" slightly with the upper bound if the average is high enough.
027 * <br>
028 * Credit for the technique used for the exponential modification to distributions goes to user pjs on StackOverflow,
029 * http://stackoverflow.com/a/17796997 .
030 * Credit should also be given to user vydd of the LispGames community, who made a visualization of the distribution
031 * changing as the expected average changed (at the time, the typical behavior of an exponential distribution looked
032 * like a bug, and the visualization showed that it was correct behavior). Derrick Creamer noticed how strange the
033 * exponential distribution would seem to most players, and that led to adding the simple triangular distribution.
034 * Created by Tommy Ettinger on 3/20/2016.
035 */
036public class RandomBias implements Serializable {
037    private OrderedMap<String, Double> biases;
038    public IRNG rng;
039    public int distribution = EXP_TRI;
040
041    /**
042     * A constant for a distribution that linearly increases in probability from a 0.0 chance of 0.0. to a 0.3333...
043     * chance of getting the expected average, then linearly decreases until it reaches a 0.0 chance of 1.0. Doesn't
044     * really support expected averages below 1/3 or above 2/3, due to how the triangular distribution works.
045     */
046    public static final int TRIANGULAR = 0,
047    /**
048     * A constant for a distribution that, for all values other than 0.5, will strongly favor either high or low
049     * results to push the odds in favor of a high or low expected average. This is probably not what most players
050     * expect, since it leads to massively more critical hits or failures if min or max results are counted as such.
051     */
052    EXPONENTIAL = 1,
053    /**
054     * Not like the other distributions; this is a constant for a distribution that simply truncates a random number's
055     * possible range to less than 1.0, and adjusts the minimum or maximum value so that the average is the desired one.
056     * This is a uniform random number generator, unlike the others which have a bias toward certain values; it simply
057     * cannot generate values outside a certain range, and the values within the range it generates are all equally
058     * likely. The range gets smaller the closer the expected average is to 0.0 or 1.0, with an expected average of 0.4
059     * producing values between 0.0 and 0.8, and an expected average of 0.9 producing values of 0.8 to 1.0 (in all
060     * cases, this is exclusive on the upper bound).
061     */
062    TRUNCATED = 2,
063    /**
064     * A constant for a distribution that averages two random floats, each with a triangular distribution (the same as
065     * what using the TRIANGULAR constant would produce, but the distribution becomes more curved when multiple random
066     * "dice rolls" are involved), to soften the point of the triangle and make very high or very low values appear
067     * somewhat more frequently, while the expected average appears less frequently. This should not be used to generate
068     * very large numbers, since the floats this uses lose precision after 24 bits, or about 16 million. It should
069     * produce very reasonable results for common values in games, like 0 to 100 or 0 to 20. Doesn't really support
070     * expected averages below 1/3 or above 2/3, due to how the triangular distribution works.
071     */
072    SOFT_TRIANGULAR = 3,
073    /**
074     * A constant for a distribution that averages two random floats, one with a triangular distribution (the same as
075     * what using the TRIANGULAR constant would produce), and one with an exponential distribution (the same as what
076     * using the EXPONENTIAL constant would produce) to soften the point of the triangle and make very high or very low
077     * values appear much more frequently, while the expected average appears somewhat less frequently. This should not
078     * be used to generate very large numbers, since the floats this uses lose precision after 24 bits, or about 16
079     * million. It should produce very reasonable results for common values in games, like 0 to 100 or 0 to 20. Has
080     * limited support for expected averages below 1/3 or above 2/3; unlike TRIANGULAR or SOFT_TRIANGULAR, expected
081     * averages outside that range will still affect the generated average due to the EXPONENTIAL distribution
082     * contributing half of the correction needed to match the expected average. An expected average of 5/6 will produce
083     * an approximate average with this of 3/4, as opposed to 2/3 (for pure TRIANGULAR) or 5/6 (for EXPONENTIAL).
084     */
085    EXP_TRI = 4,
086    /**
087     * "Bathtub-shaped" or "U-shaped" distribution (technically the arcsine distribution) that is significantly more
088     * likely to produce results at either extreme than it is to generate them in the center. The extremes in this case
089     * are the same as the truncated distribution, so not all values are possible unless the expected average is 0.5.
090     */
091    BATHTUB_TRUNCATED = 5;
092
093    private static final long serialVersionUID = 4245874924013134958L;
094
095    public RandomBias()
096    {
097        biases = new OrderedMap<>(32);
098        rng = new GWTRNG();
099    }
100    public RandomBias(IRNG rng)
101    {
102        this.rng = rng;
103        biases = new OrderedMap<>(32);
104    }
105    public RandomBias(IRNG rng, Map<String, Double> mapping)
106    {
107        this(rng, mapping, EXP_TRI);
108    }
109    public RandomBias(IRNG rng, Map<String, Double> mapping, int distribution) {
110        this.rng = rng;
111        this.distribution = distribution;
112        if (mapping == null) {
113            biases = new OrderedMap<>(32);
114        } else {
115            biases = new OrderedMap<>(mapping.size());
116            double exp;
117            for (Map.Entry<String, Double> kv : mapping.entrySet()) {
118                exp = kv.getValue();
119                if (exp <= 0) exp = 0.001;
120                if (exp >= 1) exp = 0.999;
121                biases.put(kv.getKey(), exp);
122            }
123        }
124    }
125
126    /**
127     * Adds a kind of bias that can be used to change the average of random numbers generated when specified with that
128     * kind.
129     * @param kind a String that will be used as a key in a Map; can be given later on to bias results using this key
130     * @param expectedAverage above 0.0 and below 1.0, with 0.5 as the normal average but other values are more useful.
131     * @return this for chaining
132     */
133    public RandomBias putBias(String kind, double expectedAverage)
134    {
135        if(expectedAverage <= 0) expectedAverage = 0.001;
136        if(expectedAverage >= 1) expectedAverage = 0.999;
137        biases.put(kind, expectedAverage);
138        return this;
139    }
140    /**
141     * Adds a number of kinds of bias that can be used to change the average of random numbers generated when specified
142     * with one of those kinds.
143     * @param mapping should have String keys that can be used later, and double values greater than 0 but less than 1.
144     * @return this for chaining
145     */
146    public RandomBias putBiases(Map<String, Double> mapping)
147    {
148        double exp;
149        for(Map.Entry<String, Double> kv : mapping.entrySet())
150        {
151            exp = kv.getValue();
152            if(exp <= 0) exp = 0.001;
153            if(exp >= 1) exp = 0.999;
154            biases.put(kv.getKey(), exp);
155        }
156        return this;
157    }
158
159    private double quantile(double expected)
160    {
161        switch (distribution)
162        {
163            case EXPONENTIAL: return exponentialQuantile(expected);
164            case TRUNCATED: return truncatedQuantile(expected);
165            case TRIANGULAR: return triangularQuantile(expected);
166            case SOFT_TRIANGULAR: return softQuantile(expected);
167            case BATHTUB_TRUNCATED: return bathtubTruncatedQuantile(expected);
168            default: return mixQuantile(expected);
169        }
170    }
171
172    private double triangularQuantile(double expected)
173    {
174        expected = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0));
175        double p = rng.nextDouble();
176        if(p < expected)
177            return Math.sqrt(expected * p);
178        if(p > expected)
179            return 1 - Math.sqrt((1 - expected) * (1 - p));
180        return expected;
181    }
182    private double truncatedQuantile(double expected)
183    {
184        if(expected >= 0.5)
185            return rng.nextDouble() * (1.0 - expected) * 2 + expected - (1.0 - expected);
186        return rng.nextDouble() * expected * 2;
187    }
188    private double bathtubQuantile(double expected)
189    {
190        expected = Math.sin(expected * Math.PI * 0.4999999966); // can't be 0.5 because it becomes inclusive on 1.0
191        return expected * expected;
192    }
193    private double bathtubTruncatedQuantile(double expected)
194    {
195        if(expected >= 0.5)
196            return bathtubQuantile(rng.nextDouble()) * (0.9999999999999999 - expected) * 2 + expected - (0.9999999999999999 - expected);
197        return bathtubQuantile(rng.nextDouble()) * expected * 2;
198    }
199
200    private double exponentialQuantile(double expected)
201    {
202        return 0.9999999999999999 - Math.pow( rng.nextDouble(), 1.0 / (1.0 - expected) - 1.0);
203    }
204
205    private double softQuantile(double expected)
206    {
207        expected = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0));
208        long pair = rng.nextLong();
209        float left = (pair >>> 40) * 0x1p-24f, right = (pair & 0xFFFFFFL) * 0x1p-24f;
210        double v;
211
212        if(left < expected)
213            v = Math.sqrt(expected * left);
214        else if(left > expected)
215            v = 1 - Math.sqrt((1 - expected) * (1 - left));
216        else
217            v = expected;
218        if(right < expected)
219            return (v + Math.sqrt(expected * right)) * 0.5;
220        if(right > expected)
221            return (v + 1 - Math.sqrt((1 - expected) * (1 - right))) * 0.5;
222        return expected;
223    }
224    private double mixQuantile(double expected)
225    {
226        double d2 = Math.max(0.001, Math.min(0.999, expected * 3.0 - 1.0)), v;
227        long pair = rng.nextLong();
228        float left = (pair >>> 40) * 0x1p-24f, right = (pair & 0xFFFFFFL) * 0x1p-24f;
229
230        if(left < d2)
231            v = Math.sqrt(d2 * left);
232        else if(left > d2)
233            v = 1 - Math.sqrt((1 - d2) * (1 - left));
234        else
235            v = d2;
236        return (Math.pow( right, 1.0 / expected - 1.0) + v) * 0.5;
237    }
238    /**
239     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
240     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
241     * expected average the kind was associated with. The returned number will be a positive long in either case, but
242     * not all long values are possible if this is biased, in part because of generating a double, which has less
243     * precision than long, and in part because some numbers need to be more common than others. If the kind is not in
244     * the map, this generates a positive long, using 63 bits instead of RNG's normal 64 bits since it never generates
245     * negative numbers.
246     * @param kind the kind of bias to look up
247     * @return a random 63-bit positive long, potentially influenced by the bias associated with kind, if present
248     */
249    public long biasedLong(String kind)
250    {
251        Double d = biases.get(kind);
252        if(d == null)
253            return rng.nextLong() >>> 1;
254        return (long)(quantile(d) * Long.MAX_VALUE);
255    }
256
257    /**
258     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
259     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
260     * expected average the kind was associated with. The returned number will be a long between 0 and bound (exclusive
261     * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all
262     * negative bounds). If the kind is not in the map, this generates a long between 0 and bound (exclusive on bound),
263     * even if bound is negative.
264     * @param kind the kind of bias to look up
265     * @param bound the outer bound, exclusive; can be negative
266     * @return a random long between 0 and bound, potentially influenced by the bias associated with kind, if present
267     */
268    public long biasedLong(String kind, long bound)
269    {
270        boolean n = bound < 0;
271        Double d = biases.get(kind);
272        if(d == null)
273            return n ? rng.nextLong(-bound) * -1 : rng.nextLong(bound);
274        return (long)(quantile(d) * bound);
275    }
276
277    /**
278     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
279     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
280     * expected average the kind was associated with. The returned number will be a double between 0.0 and 1.0
281     * (exclusive on 1.0). If the kind is not in the map, this generates a double using RNG and no further changes.
282     * @param kind the kind of bias to look up
283     * @return a random double between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
284     */
285    public double biasedDouble(String kind)
286    {
287        Double d = biases.get(kind);
288        if(d == null)
289            return rng.nextDouble();
290        return quantile(d);
291    }
292
293    /**
294     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
295     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
296     * expected average the kind was associated with. The returned number will be a double between 0 and bound (exclusive
297     * on bound), where bound can be negative (the same as RNG). If the kind is not in the map, this doesn't adjust the
298     * average, and acts exactly like RNG.
299     * @param kind the kind of bias to look up
300     * @param bound the outer bound, exclusive; can be negative
301     * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present
302     */
303    public double biasedDouble(String kind, double bound)
304    {
305        Double d = biases.get(kind);
306        if(d == null)
307            return rng.nextDouble(bound);
308        return quantile(d) * bound;
309    }
310
311    /**
312     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
313     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
314     * expected average the kind was associated with. The returned number will be a positive int in either case. If the
315     * kind is not in the map, this generates a positive int, using 31 bits instead of RNG's normal 32 bits since it
316     * never generates negative numbers.
317     * @param kind the kind of bias to look up
318     * @return a random 31-bit positive int, potentially influenced by the bias associated with kind, if present
319     */
320    public int biasedInt(String kind)
321    {
322        Double d = biases.get(kind);
323        if(d == null)
324            return rng.nextInt() >>> 1;
325        return (int)(quantile(d) * Integer.MAX_VALUE);
326    }
327
328    /**
329     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
330     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
331     * expected average the kind was associated with. The returned number will be an int between 0 and bound (exclusive
332     * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all
333     * negative bounds). If the kind is not in the map, this generates an int between 0 and bound (exclusive on bound),
334     * even if bound is negative.
335     * @param kind the kind of bias to look up
336     * @param bound the outer bound, exclusive; can be negative
337     * @return a random int between 0 and bound, potentially influenced by the bias associated with kind, if present
338     */
339    public int biasedInt(String kind, int bound)
340    {
341        boolean n = bound < 0;
342        Double d = biases.get(kind);
343        if(d == null)
344            return n ? rng.nextInt(-bound) * -1 : rng.nextInt(bound);
345        return (int)(quantile(d) * bound);
346    }
347
348    /**
349     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
350     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
351     * expected average the kind was associated with. The returned number will be a float between 0.0 and 1.0
352     * (exclusive on 1.0). If the kind is not in the map, this generates a float using RNG and no further changes.
353     * @param kind the kind of bias to look up
354     * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
355     */
356    public float biasedFloat(String kind)
357    {
358        Double d = biases.get(kind);
359        if(d == null)
360            return rng.nextFloat();
361        return (float) quantile(d);
362    }
363
364    /**
365     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
366     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
367     * expected average the kind was associated with. The returned number will be a float between 0 and bound (exclusive
368     * on bound), where bound can be negative. If the kind is not in the map, this doesn't adjust the average.
369     * @param kind the kind of bias to look up
370     * @param bound the outer bound, exclusive; can be negative
371     * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present
372     */
373    public float biasedFloat(String kind, float bound)
374    {
375        Double d = biases.get(kind);
376        if(d == null)
377            return rng.nextFloat() * bound;
378        return (float)(quantile(d) * bound);
379    }
380
381    /**
382     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
383     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
384     * expected average the kind was associated with. The returned boolean will be true if the random number (between
385     * 0.0 and 1.0, exclusive upper) is greater than or equal to 0.5. If the kind is not in the map, this generates a
386     * boolean using RNG and no further changes.
387     * @param kind the kind of bias to look up
388     * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
389     */
390    public boolean biasedBoolean(String kind)
391    {
392        Double d = biases.get(kind);
393        if(d == null)
394            return rng.nextBoolean();
395        return quantile(d) >= 0.5;
396    }
397    /**
398     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
399     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
400     * expected average the kind was associated with. The returned number will be an int between min and max (exclusive
401     * on max), where min and/or max can be negative, and the difference between the two can be either positive or
402     * negative. If the kind is not in the map, this doesn't adjust the average.
403     * @param kind the kind of bias to look up
404     * @param min the inner bound, inclusive; can be negative
405     * @param max the outer bound, exclusive; can be negative
406     * @return a random int between min and max, potentially influenced by the bias associated with kind, if present
407     */
408    public int biasedBetween(String kind, int min, int max)
409    {
410        return biasedInt(kind, max - min) + min;
411    }
412
413    /**
414     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
415     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
416     * expected average the kind was associated with. The returned number will be a long between min and max (exclusive
417     * on max), where min and/or max can be negative, and the difference between the two can be either positive or
418     * negative. If the kind is not in the map, this doesn't adjust the average.
419     * @param kind the kind of bias to look up
420     * @param min the inner bound, inclusive; can be negative
421     * @param max the outer bound, exclusive; can be negative
422     * @return a random long between min and max, potentially influenced by the bias associated with kind, if present
423     */
424    public long biasedBetween(String kind, long min, long max)
425    {
426        return biasedLong(kind, max - min) + min;
427    }
428
429    /**
430     * Looks up the given kind in the Map of biases this stores, and generates a random number using this object's RNG.
431     * If the kind is in the Map, this adjusts the generated number so it matches a distribution that would have the
432     * expected average the kind was associated with. The returned number will be a double between min and max
433     * (exclusive on max), where min and/or max can be negative, and the difference between the two can be either
434     * positive or negative. If the kind is not in the map, this doesn't adjust the average.
435     * @param kind the kind of bias to look up
436     * @param min the inner bound, inclusive; can be negative
437     * @param max the outer bound, exclusive; can be negative
438     * @return a random double between min and max, potentially influenced by the bias associated with kind, if present
439     */
440    public double biasedBetween(String kind, double min, double max)
441    {
442        return biasedDouble(kind, max - min) + min;
443    }
444
445
446    /**
447     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
448     * that would have the given expected average. The returned number will be a positive long in either case, but
449     * not all long values are possible if this is biased, in part because of generating a double, which has less
450     * precision than long, and in part because some numbers need to be more common than others.
451     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
452     * @return a random 63-bit positive long, potentially influenced by the bias associated with kind, if present
453     */
454    public long biasedLong(double expectedAverage)
455    {
456        if(expectedAverage <= 0) expectedAverage = 0.001;
457        if(expectedAverage >= 1) expectedAverage = 0.999;
458        return (long)(quantile(expectedAverage) * Long.MAX_VALUE);
459    }
460
461    /**
462     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
463     * that would have the given expected average. The returned number will be a long between 0 and bound (exclusive
464     * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all
465     * negative bounds).
466     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
467     * @param bound the outer bound, exclusive; can be negative
468     * @return a random long between 0 and bound, potentially influenced by the bias associated with kind, if present
469     */
470    public long biasedLong(double expectedAverage, long bound)
471    {
472        if(expectedAverage <= 0) expectedAverage = 0.001;
473        if(expectedAverage >= 1) expectedAverage = 0.999;
474        return (long)(quantile(expectedAverage) * bound);
475    }
476
477    /**
478     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
479     * that would have the given expected average. The returned number will be a double between 0.0 and 1.0 (exclusive
480     * on 1.0).
481     * @param expectedAverage the desired average
482     * @return a random double between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
483     */
484    public double biasedDouble(double expectedAverage)
485    {
486        if(expectedAverage <= 0) expectedAverage = 0.001;
487        if(expectedAverage >= 1) expectedAverage = 0.999;
488        return quantile(expectedAverage);
489    }
490
491    /**
492     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
493     * that would have the given expected average. The returned number will be a double between 0 and bound (exclusive
494     * on bound), where bound can be negative (the same as RNG).
495     * @param expectedAverage the desired average
496     * @param bound the outer bound, exclusive; can be negative
497     * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present
498     */
499    public double biasedDouble(double expectedAverage, double bound)
500    {
501        if(expectedAverage <= 0) expectedAverage = 0.001;
502        if(expectedAverage >= 1) expectedAverage = 0.999;
503        return quantile(expectedAverage) * bound;
504    }
505
506    /**
507     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
508     * that would have the given expected average. The returned number will be a positive int from 0 to (2 to the 31)-1
509     * in either case.
510     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
511     * @return a random 31-bit positive int, potentially influenced by the bias associated with kind, if present
512     */
513    public int biasedInt(double expectedAverage)
514    {
515        if(expectedAverage <= 0) expectedAverage = 0.001;
516        if(expectedAverage >= 1) expectedAverage = 0.999;
517        return (int)(quantile(expectedAverage) * Integer.MAX_VALUE);
518    }
519
520    /**
521     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
522     * that would have the given expected average. The returned number will be an int between 0 and bound (exclusive
523     * on bound), where bound can be negative (and this behavior is allowed even though RNG normally returns 0 for all
524     * negative bounds).
525     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
526     * @param bound the outer bound, exclusive; can be negative
527     * @return a random int between 0 and bound, potentially influenced by the bias associated with kind, if present
528     */
529    public int biasedInt(double expectedAverage, int bound)
530    {
531        if(expectedAverage <= 0) expectedAverage = 0.001;
532        if(expectedAverage >= 1) expectedAverage = 0.999;
533        return (int)(quantile(expectedAverage) * bound);
534    }
535
536    /**
537     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
538     * that would have the given expected average. The returned number will be a float between 0.0f and 1.0f (exclusive
539     * on 1.0f).
540     * @param expectedAverage the desired average
541     * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
542     */
543    public float biasedFloat(double expectedAverage)
544    {
545        if(expectedAverage <= 0) expectedAverage = 0.001;
546        if(expectedAverage >= 1) expectedAverage = 0.999;
547        return (float) quantile(expectedAverage);
548    }
549
550    /**
551     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
552     * that would have the given expected average. The returned number will be a float between 0f and bound (exclusive
553     * on bound), where bound can be negative.
554     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
555     * @param bound the outer bound, exclusive; can be negative
556     * @return a random double between 0 and bound, potentially influenced by the bias associated with kind, if present
557     */
558    public float biasedFloat(double expectedAverage, float bound)
559    {
560        if(expectedAverage <= 0) expectedAverage = 0.001;
561        if(expectedAverage >= 1) expectedAverage = 0.999;
562        return (float)(quantile(expectedAverage) * bound);
563    }
564
565    /**
566     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
567     * that would have the given expected average. The returned boolean will be true if the random number (between 0.0
568     * and 1.0, exclusive upper) is greater than or equal to 0.5.
569     * @param expectedAverage the desired probability of a true result, between 0.0 and 1.0
570     * @return a random float between 0.0 and 1.0, potentially influenced by the bias associated with kind, if present
571     */
572    public boolean biasedBoolean(double expectedAverage)
573    {
574        if(expectedAverage <= 0) expectedAverage = 0.001;
575        if(expectedAverage >= 1) expectedAverage = 0.999;
576        return quantile(expectedAverage) >= 0.5;
577    }
578    /**
579     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
580     * that would have the given expected average. The returned number will be an int between min and max (exclusive
581     * on max), where min and/or max can be negative, and the difference between the two can be either positive or
582     * negative.
583     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
584     * @param min the inner bound, inclusive; can be negative
585     * @param max the outer bound, exclusive; can be negative
586     * @return a random int between min and max, potentially influenced by the bias associated with kind, if present
587     */
588    public int biasedBetween(double expectedAverage, int min, int max)
589    {
590        return biasedInt(expectedAverage, max - min) + min;
591    }
592
593    /**
594     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
595     * that would have the given expected average. The returned number will be a long between min and max (exclusive
596     * on max), where min and/or max can be negative, and the difference between the two can be either positive or
597     * negative.
598     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
599     * @param min the inner bound, inclusive; can be negative
600     * @param max the outer bound, exclusive; can be negative
601     * @return a random long between min and max, potentially influenced by the bias associated with kind, if present
602     */
603    public long biasedBetween(double expectedAverage, long min, long max)
604    {
605        return biasedLong(expectedAverage, max - min) + min;
606    }
607
608    /**
609     * Generates a random number using this object's RNG and adjusts the generated number so it matches a distribution
610     * that would have the given expected average. The returned number will be a double between min and max (exclusive
611     * on max), where min and/or max can be negative, and the difference between the two can be either positive or
612     * negative.
613     * @param expectedAverage the desired average if the minimum value was 0.0 and the exclusive max was 1.0
614     * @param min the inner bound, inclusive; can be negative
615     * @param max the outer bound, exclusive; can be negative
616     * @return a random double between min and max, potentially influenced by the bias associated with kind, if present
617     */
618    public double biasedBetween(double expectedAverage, double min, double max)
619    {
620        return biasedDouble(expectedAverage, max - min) + min;
621    }
622
623    @Override
624    public String toString() {
625        return "RandomBias{" +
626                "biases=" + biases +
627                ", rng=" + rng +
628                ", distribution=" + distribution +
629                '}';
630    }
631
632}