001package squidpony.squidmath;
002
003/**
004 * A way to take an {@link IRNG} and get one or more random numbers from it to produce a double in some statistical
005 * distribution, such as Gaussian (also called the normal distribution), exponential, or various simpler schemes that
006 * don't have common mathematical names. An example of the last category is "spike" for a distribution that is very
007 * likely to be 0 and quickly drops off to being less likely for positive or negative results between 0 and -1 or 1, or
008 * "bathtub" for the "spike" distribution's fractional part from 0 to 1 (which is likely to be 0 or 1 and very unlikely
009 * to be near 0.5).
010 * Created by Tommy Ettinger on 11/23/2019.
011 */
012public interface IDistribution {
013    /**
014     * Gets a double between {@link #getLowerBound()} and {@link #getUpperBound()} that obeys this distribution.
015     * @param rng an IRNG, such as {@link RNG} or {@link GWTRNG}, that this will get one or more random numbers from
016     * @return a double within the range of {@link #getLowerBound()} and {@link #getUpperBound()}
017     */
018    double nextDouble(IRNG rng);
019
020    /**
021     * Gets the lower bound of the distribution. The documentation should specify whether the bound is inclusive or
022     * exclusive; if unspecified, it can be assumed to be inclusive (like {@link IRNG#nextDouble()}).
023     * @return the lower bound of the distribution
024     */
025    double getLowerBound();
026    /**
027     * Gets the upper bound of the distribution. The documentation should specify whether the bound is inclusive or
028     * exclusive; if unspecified, it can be assumed to be exclusive (like {@link IRNG#nextDouble()}).
029     * @return the upper bound of the distribution
030     */
031    double getUpperBound();
032    
033    abstract class SimpleDistribution implements IDistribution {
034
035        /**
036         * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite
037         * bounds) by getting the fractional component of a result from {@code otherDistribution}.
038         * @param otherDistribution any other IDistribution
039         * @return a new anonymous implementation of SimpleDistribution that gets the fractional part of {@code otherDistribution}.
040         */
041        public static SimpleDistribution fractionalDistribution(final IDistribution otherDistribution)
042        {
043            return new SimpleDistribution() {
044                @Override
045                public double nextDouble(IRNG rng) {
046                    final double v = otherDistribution.nextDouble(rng);
047                    return v - (v >= 0.0 ? (int) v : (int)v - 1);
048                }
049            };
050        }
051        /**
052         * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite
053         * bounds) by getting the fractional component of {@code offset} plus a result from {@code otherDistribution}.
054         * Using the offset allows distributions like {@link GaussianDistribution} to become centered halfway on 0.5,
055         * making the result of this distribution have a Gaussian-like peak on 0.5 instead of of peaking at the bounds
056         * when offset is 0.0.
057         * @param otherDistribution any other IDistribution
058         * @return a new anonymous implementation of SimpleDistribution that gets the fractional part of {@code otherDistribution}.
059         */
060        public static SimpleDistribution fractionalOffsetDistribution(final IDistribution otherDistribution, final double offset)
061        {
062            return new SimpleDistribution() {
063                @Override
064                public double nextDouble(IRNG rng) {
065                    final double v = otherDistribution.nextDouble(rng) + offset;
066                    return v - (v >= 0.0 ? (int) v : (int)v - 1);
067                }
068            };
069        }
070
071        /**
072         * Makes a new SimpleDistribution implementation given any IDistribution (typically one with large or infinite
073         * bounds) by simply clamping results that are below 0 to 0 and at least 1 to 0.9999999999999999 (the largest
074         * double less than 1.0 than can be represented). This will behave very oddly for distributions that are
075         * centered on 0.0.
076         * @param otherDistribution any other IDistribution
077         * @return a new anonymous implementation of SimpleDistribution that clamps {@code otherDistribution} in range.
078         */
079        public static SimpleDistribution clampedDistribution(final IDistribution otherDistribution)
080        {
081            return new SimpleDistribution() {
082                @Override
083                public double nextDouble(IRNG rng) {
084                    return Math.max(0.0, Math.min(0.9999999999999999, otherDistribution.nextDouble(rng)));
085                }
086            };
087        }
088
089        /**
090         * Gets the lower inclusive bound, which is 0.0.
091         *
092         * @return the lower inclusive bound of the distribution, 0.0
093         */
094        @Override
095        public double getLowerBound() {
096            return 0.0;
097        }
098
099        /**
100         * Gets the upper exclusive bound of the distribution, which is 1.0.
101         *
102         * @return the upper exclusive bound of the distribution, 1.0
103         */
104        @Override
105        public double getUpperBound() {
106            return 1.0;
107        }
108    }
109}