All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tommyettinger.random.Xoshiro128PlusPlusRandom Maven / Gradle / Ivy

/*
 * Copyright (c) 2022-2023 See AUTHORS file.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.github.tommyettinger.random;

/**
 * A random number generator that is optimized for performance on 32-bit machines and with Google Web Toolkit, this uses
 * no multiplication and is identical to the published xoshiro128++ algorithm when generating {@code int} values. When
 * generating {@code long} values, it is a little different. The trick this uses for generating 64-bit values is to
 * separately scramble states A and D to get the uppermost bits (this is what generating an int normally does), then to
 * scramble states B and C differently to get the lowermost bits. This means a call to {@link #nextLong()} advances the
 * state exactly as much as {@link #nextInt()}, which is an uncommon trait for mostly-32-bit-math generators.
 * 
* The actual speed of this is going to vary wildly depending on the platform being benchmarked. On GWT, which is the * main place where the performance of a random number generator might actually be a bottleneck in a game, this performs * very well, especially when producing {@code long} values. On desktop platforms, it is faster at generating * {@code int} values than {@code long}, which is to be expected for a 32-bit generator, but not as fast as some other * generators, like {@link ChopRandom}. *
* Xoshiro128PlusPlusRandom has a guaranteed period of {@code pow(2, 128) - 1}. For {@code int} outputs only, it is * 3-dimensionally equidistributed. For {@code long} outputs, equidistribution is unknown. It passes 64TB of PractRand * testing without anomalies (generating 64-bit numbers), and passes 2 to the 57.32 bytes of ReMort testing without * suspect results. ReMort is a very challenging test to pass for long sequences, so this result is encouraging, even if * the p-values dipped low in the middle of testing (they got better). This generator can be seen as a "safer * alternative" to {@link ChopRandom}, which is generally faster but has a lower minimum period and may have quality * issues that haven't yet been discovered. The Xoshiro family of generators is generally well-researched and most flaws * are already known and minor (with the PlusPlus scrambler, the only issue I know of is that this can't tolerate an * all-zero state). The way this generates 64-bit longs from a 32-bit generator is new and hasn't been tested by anyone * else, so it could have issues. *
* This implements all optional methods in EnhancedRandom except {@link #skip(long)}. *
* Based on this public-domain code by Vigna and Blackman. */ public class Xoshiro128PlusPlusRandom extends EnhancedRandom { /** * The first state; can be any int. */ protected int stateA; /** * The second state; can be any int. */ protected int stateB; /** * The third state; can be any int. */ protected int stateC; /** * The fourth state; can be any int. */ protected int stateD; /** * Creates a new Xoshiro128PlusPlusRandom with a random state. */ public Xoshiro128PlusPlusRandom() { this((int)EnhancedRandom.seedFromMath(), (int)EnhancedRandom.seedFromMath(), (int)EnhancedRandom.seedFromMath(), (int)EnhancedRandom.seedFromMath()); } /** * Creates a new Xoshiro128PlusPlusRandom with the given seed; all {@code long} values are permitted. * The seed will be passed to {@link #setSeed(long)} to attempt to adequately distribute the seed randomly. * * @param seed any {@code long} value */ public Xoshiro128PlusPlusRandom(long seed) { super(seed); setSeed(seed); } /** * Creates a new Xoshiro128PlusPlusRandom with the given four states; all {@code int} values are permitted. * These states will be used verbatim, unless all are 0 -- if they are all 0, then stateD is replaced with 1. * * @param stateA any {@code int} value * @param stateB any {@code int} value * @param stateC any {@code int} value * @param stateD any {@code int} value */ public Xoshiro128PlusPlusRandom(int stateA, int stateB, int stateC, int stateD) { super(stateA); this.stateA = stateA; this.stateB = stateB; this.stateC = stateC; this.stateD = (stateA|stateB|stateC|stateD) == 0 ? 1 : stateD; } @Override public String getTag() { return "XPPR"; } /** * This generator has 4 {@code int} states, so this returns 4. * * @return 4 (four) */ @Override public int getStateCount () { return 4; } /** * Gets the state determined by {@code selection}, as-is. The value for selection should be * between 0 and 3, inclusive; if it is any other value this gets state D as if 3 was given. * * @param selection used to select which state variable to get; generally 0, 1, 2, or 3 * @return the value of the selected state, which is an int that will be promoted to long */ @Override public long getSelectedState (int selection) { switch (selection) { case 0: return stateA; case 1: return stateB; case 2: return stateC; default: return stateD; } } /** * Sets one of the states, determined by {@code selection}, to the lower 32 bits of {@code value}, as-is. * Selections 0, 1, 2, and 3 refer to states A, B, C, and D, and if the selection is anything * else, this treats it as 3 and sets stateD. This always casts {@code value} to an int before using it. * If all four states would be 0 as a result of this call, it instead sets * the fourth part of the state to 1. * * @param selection used to select which state variable to set; generally 0, 1, 2, or 3 * @param value the exact value to use for the selected state, if valid */ @Override public void setSelectedState (int selection, long value) { switch (selection) { case 0: stateA = (int)value; break; case 1: stateB = (int)value; break; case 2: stateC = (int)value; break; default: stateD = (int)value; break; } if((stateA|stateB|stateC|stateD) == 0) stateD = 1; } /** * This initializes all 4 states of the generator to random values based on the given seed. * (2 to the 64) possible initial generator states can be produced here. This is not capable * of setting the full state to the only invalid value (all zeros). * * @param seed the initial seed; may be any long */ @Override public void setSeed (long seed) { long x = seed; x ^= x >>> 27; x *= 0x3C79AC492BA7B653L; x ^= x >>> 33; x *= 0x1C69B3F74AC4AE35L; stateA = (int)(x ^= x >>> 27); stateB = (int)(x >>> 32); x = (seed + 0x9E3779B97F4A7C15L); x ^= x >>> 27; x *= 0x3C79AC492BA7B653L; x ^= x >>> 33; x *= 0x1C69B3F74AC4AE35L; stateC = (int)(x ^= x >>> 27); stateD = (int)(x >>> 32); } public long getStateA () { return stateA; } /** * Sets the first part of the state by casting the parameter to an int. * * @param stateA can be any long, but will be cast to an int before use */ public void setStateA (long stateA) { this.stateA = (int)stateA; } public long getStateB () { return stateB; } /** * Sets the second part of the state by casting the parameter to an int. * * @param stateB can be any long, but will be cast to an int before use */ public void setStateB (long stateB) { this.stateB = (int)stateB; } public long getStateC () { return stateC; } /** * Sets the third part of the state by casting the parameter to an int. * * @param stateC can be any long, but will be cast to an int before use */ public void setStateC (long stateC) { this.stateC = (int)stateC; } public long getStateD () { return stateD; } /** * Sets the fourth part of the state by casting the parameter to an int. * If all four states would be 0 as a result of this call, it instead sets * the fourth part of the state to 1. * * @param stateD can be any long, but will be cast to an int before use */ public void setStateD (long stateD) { this.stateD = (stateA|stateB|stateC|(int)stateD) == 0 ? 1 : (int)stateD; } /** * Sets the state completely to the given four state variables, casting each to an int. * This is the same as calling {@link #setStateA(long)}, {@link #setStateB(long)}, * {@link #setStateC(long)}, and {@link #setStateD(long)} as a group. * If all four states would be 0 as a result of this call, it instead sets * the fourth part of the state to 1. * * @param stateA the first state; can be any long, but will be cast to an int before use * @param stateB the second state; can be any long, but will be cast to an int before use * @param stateC the third state; can be any long, but will be cast to an int before use * @param stateD the fourth state; can be any long, but will be cast to an int before use */ @Override public void setState (long stateA, long stateB, long stateC, long stateD) { this.stateA = (int)stateA; this.stateB = (int)stateB; this.stateC = (int)stateC; this.stateD = ((int)stateA|(int)stateB|(int)stateC|(int)stateD) == 0 ? 1 : (int)stateD; } @Override public long nextLong () { int h = (stateA + stateD); h = (h << 7 | h >>> 25) + stateA; int l = stateC - stateB; l = (l << 13 | l >>> 19) + stateC; int t = stateB << 9; stateC ^= stateA; stateD ^= stateB; stateB ^= stateC; stateA ^= stateD; stateC ^= t; stateD = (stateD << 11 | stateD >>> 21); return (long) h << 32 ^ (l & 0xFFFFFFFFL); } @Override public long previousLong () { stateD = (stateD << 21 | stateD >>> 11); // stateD has d ^ b int pa = stateA ^= stateD; // StateA has a stateC ^= stateB; // StateC has b ^ b << 9 stateC ^= stateC << 9; stateC ^= stateC << 18; // StateC has b stateB ^= stateA; // StateB has b ^ c int pc = stateC ^= stateB; // StateC has c int pb = stateB ^= stateC; // StateB has b int pd = stateD ^= stateB; // StateD has d pd = pa + pd; pd = (pd << 7 | pd >>> 25) + pa; pb = pc - pb; pb = (pb << 13 | pb >>> 19) + pc; return (long) pd << 32 | (pb & 0xFFFFFFFFL); } @Override public int previousInt () { stateD = (stateD << 21 | stateD >>> 11); // stateD has d ^ b int pa = stateA ^= stateD; // StateA has a stateC ^= stateB; // StateC has b ^ b << 9 stateC ^= stateC << 9; stateC ^= stateC << 18; // StateC has b stateB ^= stateA; // StateB has b ^ c stateC ^= stateB;// StateC has c stateB ^= stateC;// StateB has b int pd = stateD ^= stateB; // StateD has d pd = pa + pd; pd = (pd << 7 | pd >>> 25) + pa; return pd; } @Override public int next (int bits) { int result = (stateA + stateD); result = (result << 7 | result >>> 25) + stateA; int t = stateB << 9; stateC ^= stateA; stateD ^= stateB; stateB ^= stateC; stateA ^= stateD; stateC ^= t; stateD = (stateD << 11 | stateD >>> 21); return result >>> (32 - bits); } @Override public int nextInt () { int result = (stateA + stateD); result = (result << 7 | result >>> 25) + stateA; int t = stateB << 9; stateC ^= stateA; stateD ^= stateB; stateB ^= stateC; stateA ^= stateD; stateC ^= t; stateD = (stateD << 11 | stateD >>> 21); return result; } @Override public int nextInt (int bound) { return (int)(bound * (nextInt() & 0xFFFFFFFFL) >> 32) & ~(bound >> 31); } @Override public int nextSignedInt (int outerBound) { outerBound = (int)(outerBound * (nextInt() & 0xFFFFFFFFL) >> 32); return outerBound + (outerBound >>> 31); } @Override public void nextBytes (byte[] bytes) { for (int i = 0; i < bytes.length; ) {for (int r = nextInt(), n = Math.min(bytes.length - i, 4); n-- > 0; r >>>= 8) {bytes[i++] = (byte)r;}} } @Override public long nextLong (long inner, long outer) { final long rand = nextLong(); if (inner >= outer) return inner; final long randLow = rand & 0xFFFFFFFFL; final long randHigh = rand >>> 32; final long bound = outer - inner; final long boundLow = bound & 0xFFFFFFFFL; final long boundHigh = (bound >>> 32); return inner + (randHigh * boundLow >>> 32) + (randLow * boundHigh >>> 32) + randHigh * boundHigh; } @Override public long nextSignedLong (long inner, long outer) { if (outer < inner) { long t = outer; outer = inner + 1L; inner = t + 1L; } final long bound = outer - inner; final long rand = nextLong(); final long randLow = rand & 0xFFFFFFFFL; final long randHigh = rand >>> 32; final long boundLow = bound & 0xFFFFFFFFL; final long boundHigh = (bound >>> 32); return inner + (randHigh * boundLow >>> 32) + (randLow * boundHigh >>> 32) + randHigh * boundHigh; } @Override public boolean nextBoolean () { return nextInt() < 0; } @Override public float nextFloat () { return (nextInt() >>> 8) * 0x1p-24f; } @Override public float nextInclusiveFloat () { return (0x1000001L * (nextInt() & 0xFFFFFFFFL) >> 32) * 0x1p-24f; } @Override public Xoshiro128PlusPlusRandom copy () { return new Xoshiro128PlusPlusRandom(stateA, stateB, stateC, stateD); } /** * Jumps extremely far in the generator's sequence, such that it requires {@code Math.pow(2, 64)} calls to leap() to * complete a cycle through the generator's entire sequence. This can be used to create over 18 quintillion * substreams of this generator's sequence, each with a period of {@code Math.pow(2, 64)}. * @return the result of what nextLong() would return if it was called at the state this jumped to */ public long leap() { int s0 = 0; int s1 = 0; int s2 = 0; int s3 = 0; for(int b = 0; b < 32; b++) { if ((0x8764000b & 1 << b) != 0) { s0 ^= stateA; s1 ^= stateB; s2 ^= stateC; s3 ^= stateD; } nextInt(); } for(int b = 0; b < 32; b++) { if ((0xf542d2d3 & 1 << b) != 0) { s0 ^= stateA; s1 ^= stateB; s2 ^= stateC; s3 ^= stateD; } nextInt(); } for(int b = 0; b < 32; b++) { if ((0x6fa035c3 & 1 << b) != 0) { s0 ^= stateA; s1 ^= stateB; s2 ^= stateC; s3 ^= stateD; } nextInt(); } for(int b = 0; b < 32; b++) { if ((0x77f2db5b & 1 << b) != 0) { s0 ^= stateA; s1 ^= stateB; s2 ^= stateC; s3 ^= stateD; } nextInt(); } stateA = s0; stateB = s1; stateC = s2; stateD = s3; s3 = (s3 << 21 | s3 >>> 11); // s3 has d ^ b s0 ^= s3; // s0 has a s2 ^= s1; // s2 has b ^ b << 9 s2 ^= s2 << 9; s2 ^= s2 << 18; // s2 has b s1 ^= s0; // s1 has b ^ c s2 ^= s1; // s2 has c s1 ^= s2; // s1 has b s3 ^= s1; // s3 has d s3 = s0 + s3; s3 = (s3 << 7 | s3 >>> 25) + s0; s1 = s2 - s1; s1 = (s1 << 13 | s1 >>> 19) + s2; return (long) s3 << 32 ^ (s1 & 0xFFFFFFFFL); } @Override public boolean equals (Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Xoshiro128PlusPlusRandom that = (Xoshiro128PlusPlusRandom)o; return stateA == that.stateA && stateB == that.stateB && stateC == that.stateC && stateD == that.stateD; } public String toString () { return "Xoshiro128PlusPlusRandom{" + "stateA=" + (stateA) + ", stateB=" + (stateB) + ", stateC=" + (stateC) + ", stateD=" + (stateD) + "}"; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy