001package squidpony.squidmath;
002
003import squidpony.StringKit;
004
005import java.io.Serializable;
006
007/**
008 * A variant on {@link ThrustAltRNG} that gives up some speed to gain a much better period and the ability to produce
009 * all possible long values over that period. Its period is 2 to the 128, and it produces all long outputs with equal
010 * likelihood. OrbitRNG is close to ThrustAltRNG in implementation, and ThrustAltRNG passes PractRand and TestU01 just
011 * fine, but OrbitRNG should actually be more robust. For some purposes you may want to instead consider
012 * {@link TangleRNG}, which also has two states and uses a very similar algorithm, but it skips some work Orbit does and
013 * in doing so speeds up a lot and drops its period down to 2 to the 64. An individual TangleRNG can't produce all
014 * possible long outputs and can produce some duplicates, but each pair of states for a TangleRNG has a different set of
015 * which outputs will be skipped and which will be duplicated. Since it would require months of solid number generation
016 * to exhaust the period of a TangleRNG, and that's the only time an output can be confirmed as skipped, it's probably
017 * fine for some usage to use many different TangleRNGs, all seeded differently. In other cases you could use just one
018 * OrbitRNG; its period is large enough not to worry about exhausting it in a game, though it isn't quite as fast as
019 * TangleRNG. Orbit is extremely close in speed to a 64-bit variant on {@link Lathe32RNG}, and should have similarly
020 * high quality; it should be slightly slower than {@link XoRoRNG} but doesn't fail the statistical tests that XoRo
021 * fails rather badly.
022 * <br>
023 * The name comes from how the pair of states act like two planets orbiting a star at different rates, and also evokes
024 * the larger-scale period relative to {@link TangleRNG}.
025 * <br>
026 * Created by Tommy Ettinger on 7/9/2018.
027 */
028public final class OrbitRNG implements RandomnessSource, Serializable {
029    private static final long serialVersionUID = 4L;
030    /**
031     * Can be any long value.
032     */
033    public long stateA;
034    
035    public long stateB;
036
037    /**
038     * Creates a new generator seeded using Math.random.
039     */
040    public OrbitRNG() {
041        this((long) ((Math.random() - 0.5) * 0x10000000000000L)
042                ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L),
043                (long) ((Math.random() - 0.5) * 0x10000000000000L)
044                ^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L));
045    }
046
047    public OrbitRNG(long seed) {
048        stateA = (seed = ((seed = (((seed * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ seed >>> 27) * 0xAEF17502108EF2D9L) ^ seed >>> 25;
049        stateB = (seed = ((seed = (((seed * 0x632BE59BD9B4E019L) ^ 0x9E3779B97F4A7C15L) * 0xC6BC279692B5CC83L)) ^ seed >>> 27) * 0xAEF17502108EF2D9L) ^ seed >>> 25;
050    }
051
052    public OrbitRNG(final long seedA, final long seedB) {
053        stateA = seedA;
054        stateB = seedB;
055    }
056
057    /**
058     * Get the "A" part of the internal state as a long.
059     *
060     * @return the current internal state of this object.
061     */
062    public long getStateA() {
063        return stateA;
064    }
065
066    /**
067     * Set the "A" part of the internal state with a long.
068     *
069     * @param stateA any 64-bit long
070     */
071    public void setStateA(long stateA) {
072        this.stateA = stateA;
073    }
074
075    /**
076     * Get the "B" part of the internal state as a long.
077     *
078     * @return the current internal "B" state of this object.
079     */
080    public long getStateB() {
081        return stateB;
082    }
083
084    /**
085     * Set the "B" part of the internal state with a long.
086     *
087     * @param stateB any 64-bit long
088     */
089    public void setStateB(long stateB) {
090        this.stateB = stateB;
091    }
092
093
094    /**
095     * Using this method, any algorithm that might use the built-in Java Random
096     * can interface with this randomness source.
097     *
098     * @param bits the number of bits to be returned
099     * @return the integer containing the appropriate number of bits
100     */
101    @Override
102    public final int next(final int bits) {
103        final long s = (stateA += 0x6C8E9CF570932BD5L);
104        if(s == 0L)
105            stateB += 0x9E3779B97F4A7C15L;
106        final long z = (s ^ (s >>> 25)) * ((stateB += 0x9E3779B97F4A7C15L) | 1L);
107        return (int)(z ^ (z >>> 22)) >>> (32 - bits);
108    }
109    /**
110     * Using this method, any algorithm that needs to efficiently generate more
111     * than 32 bits of random data can interface with this randomness source.
112     * <p>
113     * Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
114     *
115     * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
116     */
117    @Override
118    public final long nextLong() {
119        final long s = (stateA += 0x6C8E9CF570932BD5L);
120        if(s == 0L)
121            stateB += 0x9E3779B97F4A7C15L;
122        final long z = (s ^ (s >>> 25)) * ((stateB += 0x9E3779B97F4A7C15L) | 1L);
123        return z ^ (z >>> 22);
124    }
125
126    /**
127     * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
128     * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
129     * copy the state so it isn't shared, usually, and produce a new value with the same exact state.
130     *
131     * @return a copy of this RandomnessSource
132     */
133    @Override
134    public OrbitRNG copy() {
135        return new OrbitRNG(stateA, stateB);
136    }
137    @Override
138    public String toString() {
139        return "OrbitRNG with stateA 0x" + StringKit.hex(stateA) + "L and stateB 0x" + StringKit.hex(stateB) + 'L';
140    }
141
142    @Override
143    public boolean equals(Object o) {
144        if (this == o) return true;
145        if (o == null || getClass() != o.getClass()) return false;
146
147        OrbitRNG orbitRNG = (OrbitRNG) o;
148
149        return stateA == orbitRNG.stateA && stateB == orbitRNG.stateB;
150    }
151
152    @Override
153    public int hashCode() {
154        return (int) (31L * (stateA ^ (stateA >>> 32)) + (stateB ^ stateB >>> 32));
155    }
156    
157//    public static void main(String[] args)
158//    {
159//        /*
160//        cd target/classes
161//        java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly sarong/ThrustAltRNG > ../../thrustalt_asm.txt
162//         */
163//        long seed = 1L;
164//        ThrustAltRNG rng = new ThrustAltRNG(seed);
165//
166//        for (int i = 0; i < 1000000007; i++) {
167//            seed += rng.nextLong();
168//        }
169//        System.out.println(seed);
170//    }
171
172}