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}