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