001package squidpony.squidmath;
002
003import squidpony.StringKit;
004
005import java.io.Serializable;
006
007/**
008 * A larger-period generator with 127 bits of state (two longs, one is always odd), a period of 2 to the 127, and what
009 * should be slightly better speed than the related OrbitRNG. It passes 32TB of PractRand testing with no anomalies or
010 * failures. The idea for this generator is to have a simple Weyl sequence (a counter with a large increment) for
011 * stateA, and at one point to multiply a value based on stateA by stateB, which is always odd. The trick is that stateB
012 * usually updates by adding a large even number, but about once every 10 billion generated numbers, decided by a
013 * comparison between a constant and stateA's value, it "shifts gears" and stays on the same value while stateA updates.
014 * <br>
015 * For some purposes you may want to instead consider {@link TangleRNG}, which also has two states (one odd) and uses a
016 * very similar algorithm, but it never "shifts gears," which drops its period down to 2 to the 64, makes it a
017 * {@link SkippingRandomness}, and should in theory speed it up (in practice, TangleRNG needs an extra step that actually
018 * makes it slower than GearRNG). An individual TangleRNG can't produce all possible long outputs and can produce some
019 * duplicates, but each pair of states for a TangleRNG has a different set of which outputs will be skipped and which
020 * will be duplicated. Since it would require months of solid number generation to exhaust the period of a TangleRNG, and
021 * that's the only time an output can be confirmed as skipped, it's probably fine for most usage to use many different
022 * TangleRNGs, all seeded differently. Other choices: you could use one {@link OrbitRNG} (which technically has a longer
023 * period, but some states produce very similar output), {@link DiverRNG} (if you don't mind that it never produces a
024 * duplicate output), {@link IsaacRNG} (if speed is less important but more secure output is), or Lathe64RNG, though all
025 * of those are probably slower than using one GearRNG object or even many TangleRNG objects.
026 * <br>
027 * The name comes from shifting gears; pretty straightforward here.
028 * <br>
029 * Created by Tommy Ettinger on 3/29/2020.
030 */
031public final class GearRNG implements RandomnessSource, Serializable {
032    private static final long serialVersionUID = 5L;
033    /**
034     * Can be any long value.
035     */
036    private long stateA;
037    /**
038     * Can be any odd long value.
039     */
040    private long stateB;
041
042    /**
043     * Creates a new generator seeded using Math.random.
044     */
045    public GearRNG() {
046        this((long) ((Math.random() - 0.5) * 0x10000000000000L)
047                ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L),
048                (long) ((Math.random() - 0.5) * 0x10000000000000L)
049                ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L));
050    }
051
052    public GearRNG(long seed) {
053        stateA = (seed = ((seed = (((seed * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ seed >>> 27) * 0xAEF17502108EF2D9L) ^ seed >>> 25;
054        stateB = ((seed = ((seed = (((seed * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ seed >>> 27) * 0xAEF17502108EF2D9L) ^ seed >>> 25) | 1L;
055    }
056
057    public GearRNG(final long seedA, final long seedB) {
058        stateA = seedA;
059        stateB = seedB | 1L;
060    }
061
062    /**
063     * Get the "A" part of the internal state as a long.
064     *
065     * @return the current internal state of this object.
066     */
067    public long getStateA() {
068        return stateA;
069    }
070
071    /**
072     * Set the "A" part of the internal state with a long.
073     *
074     * @param stateA any 64-bit long
075     */
076    public void setStateA(long stateA) {
077        this.stateA = stateA;
078    }
079
080    /**
081     * Get the "B" part of the internal state as a long.
082     *
083     * @return the current internal "B" state of this object.
084     */
085    public long getStateB() {
086        return stateB;
087    }
088
089    /**
090     * Set the "B" part of the internal state with a long; the lowest bit is always discarded and replaced with 1.
091     * That is, stateB is always an odd number, and if an even number is given it will be incremented.
092     *
093     * @param stateB any 64-bit long
094     */
095    public void setStateB(long stateB) {
096        this.stateB = stateB | 1L;
097    }
098
099
100    /**
101     * Using this method, any algorithm that might use the built-in Java Random
102     * can interface with this randomness source.
103     *
104     * @param bits the number of bits to be returned
105     * @return the integer containing the appropriate number of bits
106     */
107    @Override
108    public final int next(final int bits) {
109        final long s = (stateA += 0xC6BC279692B5C323L);
110        final long z = ((s < 0x800000006F17146DL) ? stateB : (stateB += 0x9479D2858AF899E6L)) * (s ^ s >>> 31);
111        return (int)(z ^ z >>> 25) >>> (32 - bits);
112    }
113    /**
114     * Using this method, any algorithm that needs to efficiently generate more
115     * than 32 bits of random data can interface with this randomness source.
116     * <p>
117     * Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
118     *
119     * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
120     */
121    @Override
122    public final long nextLong() {
123        final long s = (stateA += 0xC6BC279692B5C323L);
124        final long z = ((s < 0x800000006F17146DL) ? stateB : (stateB += 0x9479D2858AF899E6L)) * (s ^ s >>> 31);
125        return z ^ z >>> 25;
126    }
127    
128    /**
129     * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
130     * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
131     * copy the state so it isn't shared, usually, and produce a new value with the same exact state.
132     *
133     * @return a copy of this RandomnessSource
134     */
135    @Override
136    public GearRNG copy() {
137        return new GearRNG(stateA, stateB);
138    }
139    @Override
140    public String toString() {
141        return "GearRNG with stateA 0x" + StringKit.hex(stateA) + "L and stateB 0x" + StringKit.hex(stateB) + 'L';
142    }
143
144    @Override
145    public boolean equals(Object o) {
146        if (this == o) return true;
147        if (o == null || getClass() != o.getClass()) return false;
148
149        GearRNG gearRNG = (GearRNG) o;
150
151        return stateA == gearRNG.stateA && stateB == gearRNG.stateB;
152    }
153
154    @Override
155    public int hashCode() {
156        return (int) (31L * (stateA ^ (stateA >>> 32)) + (stateB ^ stateB >>> 32));
157    }
158    
159}