![JAR search and dependency download from the Maven repository](/logo.png)
com.github.tommyettinger.random.Jsf32Random 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 is
* Bob Jenkins' Small Fast Generator, using its 32-bit version. This generator has four {@code int} states and is
* similar to a 32-bit version of {@link WhiskerRandom} without using any multiplication. Like WhiskerRandom, if given
* a completely arbitrary seed, there is no guarantee of a minimum period, but unlike WhiskerRandom, 2 to the 32 (over 4
* billion) seeds have been checked to ensure they do not produce a generator with a period shorter than 2 to the 20
* (over 1 million).
*
* This algorithm hasn't been tested with ReMort, but
* it passes PractRand to
* 128 TB in M.E. O'Neill's testing, with two anomalies at 64TB and no other issues.
*
* This implements all optional methods in EnhancedRandom except {@link #skip(long)}.
*
* Based on this public-domain code by Bob Jenkins.
*/
public class Jsf32Random extends EnhancedRandom {
/**
* The first state; must be assigned by {@link #setSeed(long)} to be on a known-safe cycle.
*/
protected int stateA;
/**
* The second state; must be assigned by {@link #setSeed(long)} to be on a known-safe cycle.
*/
protected int stateB;
/**
* The third state; must be assigned by {@link #setSeed(long)} to be on a known-safe cycle.
*/
protected int stateC;
/**
* The fourth state; must be assigned by {@link #setSeed(long)} to be on a known-safe cycle.
*/
protected int stateD;
/**
* Creates a new Jsf32Random with a random state.
*/
public Jsf32Random() {
this((int)EnhancedRandom.seedFromMath());
}
/**
* Creates a new Jsf32Random 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 Jsf32Random(long seed) {
super(seed);
setSeed(seed);
}
/**
* Creates a new Jsf32Random with the given four states. All {@code int} values are technically permitted,
* but unless the generator is seeded with {@link #setSeed(long)} or the states given were copied exactly from
* a generator that had been seeded with setSeed(), the generator could (extremely rarely) have a short cycle.
*
* @param stateA any {@code int} value; no guarantees are provided that this will be on a known-safe cycle
* @param stateB any {@code int} value; no guarantees are provided that this will be on a known-safe cycle
* @param stateC any {@code int} value; no guarantees are provided that this will be on a known-safe cycle
* @param stateD any {@code int} value; no guarantees are provided that this will be on a known-safe cycle
*/
public Jsf32Random(int stateA, int stateB, int stateC, int stateD) {
super(stateA);
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
this.stateD = stateD;
}
@Override
public String getTag() {
return "JS3R";
}
/**
* 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.
* Unless each state given here was copied from Jsf32Random that had been seeded with {@link #setSeed(long)},
* there are no guarantees this will be on a known-good cycle after this call.
*
* @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;
}
}
/**
* This initializes all 4 states of the generator to random values based on the given seed.
* (2 to the 32) known-good initial generator states can be produced here, corresponding to
* all int seeds or long seeds in the range of an int. If you give any seed where
* {@code (seed != (int)seed)}, then this will produce a state that is not known to safe
* (though it could be, and most likely will be).
*
* @param seed the initial seed; may be any long
*/
@Override
public void setSeed (long seed) {
stateA = 0xF1EA5EED;
int y = (int) seed;
if(seed == y){
stateB = stateC = stateD = y;
for (int i = 0; i < 20; i++) {
nextInt();
}
return;
}
long x = seed;
x ^= x >>> 27;
x *= 0x3C79AC492BA7B653L;
x ^= x >>> 33;
x *= 0x1C69B3F74AC4AE35L;
stateB = (int)(x ^ x >>> 27);
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.
*
* @param stateD can be any long, but will be cast to an int before use
*/
public void setStateD (long stateD) {
this.stateD = (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.
*
* @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)stateD;
}
@Override
public long nextLong () {
int e = stateA - (stateB << 27 | stateB >>> 5);
stateA = stateB ^ (stateC << 17 | stateC >>> 15);
stateB = stateC + stateD;
stateC = stateD + e;
final int h = stateD = e + stateA;
e = stateA - (stateB << 27 | stateB >>> 5);
stateA = stateB ^ (stateC << 17 | stateC >>> 15);
stateB = stateC + stateD;
stateC = stateD + e;
final int l = stateD = e + stateA;
return (long) h << 32 | (l & 0xFFFFFFFFL);
}
@Override
public long previousLong () {
final int l = stateD;
int e = stateD - stateA;
final int h = stateD = stateC - e;
stateC = stateB - stateD;
stateB = stateA ^ (stateC << 17 | stateC >>> 15);
stateA = e + (stateB << 27 | stateB >>> 5);
e = stateD - stateA;
stateD = stateC - e;
stateC = stateB - stateD;
stateB = stateA ^ (stateC << 17 | stateC >>> 15);
stateA = e + (stateB << 27 | stateB >>> 5);
return (long) h << 32 | (l & 0xFFFFFFFFL);
}
@Override
public int previousInt () {
final int l = stateD;
final int e = stateD - stateA;
stateD = stateC - e;
stateC = stateB - stateD;
stateB = stateA ^ (stateC << 17 | stateC >>> 15);
stateA = e + (stateB << 27 | stateB >>> 5);
return l;
}
@Override
public int next (int bits) {
final int e = stateA - (stateB << 27 | stateB >>> 5);
stateA = stateB ^ (stateC << 17 | stateC >>> 15);
stateB = stateC + stateD;
stateC = stateD + e;
return (stateD = e + stateA) >>> (32 - bits);
}
@Override
public int nextInt () {
final int e = stateA - (stateB << 27 | stateB >>> 5);
stateA = stateB ^ (stateC << 17 | stateC >>> 15);
stateB = stateC + stateD;
stateC = stateD + e;
return stateD = e + stateA;
}
@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 Jsf32Random copy () {
return new Jsf32Random(stateA, stateB, stateC, stateD);
}
@Override
public boolean equals (Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Jsf32Random that = (Jsf32Random)o;
return stateA == that.stateA && stateB == that.stateB && stateC == that.stateC && stateD == that.stateD;
}
public String toString () {
return "Jsf32Random{" + "stateA=" + (stateA) + ", stateB=" + (stateB) + ", stateC=" + (stateC) + ", stateD=" + (stateD) + "}";
}
// public static void main(String[] args) {
// Jsf32Random random = new Jsf32Random(1L);
// long n0 = random.nextLong();
// long n1 = random.nextLong();
// long n2 = random.nextLong();
// long n3 = random.nextLong();
// long n4 = random.nextLong();
// long n5 = random.nextLong();
// long p5 = random.previousLong();
// long p4 = random.previousLong();
// long p3 = random.previousLong();
// long p2 = random.previousLong();
// long p1 = random.previousLong();
// long p0 = random.previousLong();
// System.out.println(n0 == p0);
// System.out.println(n1 == p1);
// System.out.println(n2 == p2);
// System.out.println(n3 == p3);
// System.out.println(n4 == p4);
// System.out.println(n5 == p5);
// System.out.println(n0 + " vs. " + p0);
// System.out.println(n1 + " vs. " + p1);
// System.out.println(n2 + " vs. " + p2);
// System.out.println(n3 + " vs. " + p3);
// System.out.println(n4 + " vs. " + p4);
// System.out.println(n5 + " vs. " + p5);
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy