com.github.tommyettinger.random.Xoshiro256MX3Random 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 guarantees 4-dimensional equidistribution (with the exception of the quartet with four
* zeroes in a row, every quartet of long results is produced exactly once over the period). This particular generator
* is nearly identical to {@link Xoshiro256StarStarRandom}, but instead of using the fast but weak StarStar "scrambler,"
* it runs output through the MX3 unary hash, which is slower but extremely strong. It has a period of
* (2 to the 256) - 1, which would take millennia to exhaust on current-generation hardware (at least).
* It can be considered stable, like the other EnhancedRandom implementations here. This isn't a cryptographic
* generator, but the only issue I know of with Xoshiro and the StarStar scrambler should be fully resolved here. The
* only invalid state is the one with 0 in each state variable, and this won't ever
* occur in the normal period of that contains all other states. You should generally seed this with {@link #setSeed(long)},
* rather than {@link #setState(long, long, long, long)}, because if you give similar states to the latter, it tends to
* produce severely flawed output on at least the low-order bits. This can't happen with setSeed().
*
* This implements all optional methods in EnhancedRandom except {@link #skip(long)}; it does implement
* {@link #previousLong()} without using skip().
*
* Xoshiro256** was written in 2018 by David Blackman and Sebastiano Vigna. You can consult their paper for technical details:
* PDF link here. The MX3 unary hash was written
* 2020 by Jon Maiga, GitHub repo here.
*/
public class Xoshiro256MX3Random extends EnhancedRandom {
/**
* The first state; can be any long, as long as all states are not 0.
*/
protected long stateA;
/**
* The second state; can be any long, as long as all states are not 0.
* This is the state that is scrambled and returned; if it is 0 before a number
* is generated, then the next number will be 0.
*/
protected long stateB;
/**
* The third state; can be any long, as long as all states are not 0.
*/
protected long stateC;
/**
* The fourth state; can be any long, as long as all states are not 0.
*/
protected long stateD;
/**
* Creates a new Xoshiro256MX3Random with a random state.
*/
public Xoshiro256MX3Random() {
super();
stateA = EnhancedRandom.seedFromMath();
stateB = EnhancedRandom.seedFromMath();
stateC = EnhancedRandom.seedFromMath();
stateD = EnhancedRandom.seedFromMath();
if ((stateA | stateB | stateC | stateD) == 0L)
stateD = 0x9E3779B97F4A7C15L;
}
/**
* Creates a new Xoshiro256MX3Random 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 Xoshiro256MX3Random(long seed) {
super(seed);
setSeed(seed);
}
/**
* Creates a new Xoshiro256MX3Random with the given four states; all {@code long} values are permitted.
* These states will be used verbatim, as long as they are not all 0. In that case, stateD is changed.
*
* @param stateA any {@code long} value
* @param stateB any {@code long} value
* @param stateC any {@code long} value
* @param stateD any {@code long} value
*/
public Xoshiro256MX3Random(long stateA, long stateB, long stateC, long stateD) {
super(stateA);
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
this.stateD = stateD;
if ((stateA | stateB | stateC | stateD) == 0L)
this.stateD = 0x9E3779B97F4A7C15L;
}
@Override
public String getTag() {
return "XMXR";
}
/**
* This generator has 4 {@code long} 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
*/
@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 {@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. If this would cause all states to be 0, it
* instead sets the selected state to 0x9E3779B97F4A7C15L.
*
* @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 = ((value | stateB | stateC | stateD) == 0L) ? 0x9E3779B97F4A7C15L : value;
break;
case 1:
stateB = ((stateA | value | stateC | stateD) == 0L) ? 0x9E3779B97F4A7C15L : value;
break;
case 2:
stateC = ((stateA | stateB | value | stateD) == 0L) ? 0x9E3779B97F4A7C15L : value;
break;
default:
stateD = ((stateA | stateB | stateC | value) == 0L) ? 0x9E3779B97F4A7C15L : value;
break;
}
}
/**
* 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, all with a different
* first value returned by {@link #nextLong()} (because {@code stateB} is guaranteed to be
* different for every different {@code seed}).
*
* @param seed the initial seed; may be any long
*/
@Override
public void setSeed (long seed) {
long x = (seed += 0x9E3779B97F4A7C15L);
x ^= x >>> 27;
x *= 0x3C79AC492BA7B653L;
x ^= x >>> 33;
x *= 0x1C69B3F74AC4AE35L;
stateA = x ^ x >>> 27;
x = (seed += 0x9E3779B97F4A7C15L);
x ^= x >>> 27;
x *= 0x3C79AC492BA7B653L;
x ^= x >>> 33;
x *= 0x1C69B3F74AC4AE35L;
stateB = x ^ x >>> 27;
x = (seed += 0x9E3779B97F4A7C15L);
x ^= x >>> 27;
x *= 0x3C79AC492BA7B653L;
x ^= x >>> 33;
x *= 0x1C69B3F74AC4AE35L;
stateC = x ^ x >>> 27;
x = (seed + 0x9E3779B97F4A7C15L);
x ^= x >>> 27;
x *= 0x3C79AC492BA7B653L;
x ^= x >>> 33;
x *= 0x1C69B3F74AC4AE35L;
stateD = x ^ x >>> 27;
}
public long getStateA () {
return stateA;
}
/**
* Sets the first part of the state.
*
* @param stateA can be any long
*/
public void setStateA (long stateA) {
this.stateA = stateA;
}
public long getStateB () {
return stateB;
}
/**
* Sets the second part of the state. Note that if you set this state to 0, the next random long (or most other types)
* will be 0, regardless of the other states.
*
* @param stateB can be any long
*/
public void setStateB (long stateB) {
this.stateB = stateB;
}
public long getStateC () {
return stateC;
}
/**
* Sets the third part of the state.
*
* @param stateC can be any long
*/
public void setStateC (long stateC) {
this.stateC = stateC;
}
public long getStateD () {
return stateD;
}
/**
* Sets the fourth part of the state.
*
* @param stateD can be any long
*/
public void setStateD (long stateD) {
this.stateD = stateD;
}
/**
* Sets the state completely to the given four state variables.
* 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
* @param stateB the second state; can be any long
* @param stateC the third state; can be any long
* @param stateD the fourth state; this will be returned as-is if the next call is to {@link #nextLong()}
*/
@Override
public void setState (long stateA, long stateB, long stateC, long stateD) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
this.stateD = stateD;
if ((stateA | stateB | stateC | stateD) == 0L)
this.stateD = 0x9E3779B97F4A7C15L;
}
@Override
public long nextLong () {
long result = stateB;
long t = stateB << 17;
stateC ^= stateA;
stateD ^= stateB;
stateB ^= stateC;
stateA ^= stateD;
stateC ^= t;
stateD = (stateD << 45 | stateD >>> 19);
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 29;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
return result ^ result >>> 29;
}
@Override
public int next (int bits) {
long result = stateB;
long t = stateB << 17;
stateC ^= stateA;
stateD ^= stateB;
stateB ^= stateC;
stateA ^= stateD;
stateC ^= t;
stateD = (stateD << 45 | stateD >>> 19);
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 29;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
return (int)((result ^ result >>> 29) >>> 64 - bits);
}
@Override
public long previousLong () {
stateD = (stateD << 19 | stateD >>> 45); // stateD has d ^ b
stateA ^= stateD; // StateA has a
stateC ^= stateB; // StateC has b ^ b << 17;
stateC ^= stateC << 17;
stateC ^= stateC << 34; // StateC has b
stateB ^= stateA; // StateB has b ^ c
stateC ^= stateB; // StateC has c;
long result = stateB ^= stateC; // StateB has b;
stateD ^= stateB; // StateD has d;
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 29;
result *= 0xBEA225F9EB34556DL;
result ^= result >>> 32;
result *= 0xBEA225F9EB34556DL;
return result ^ result >>> 29;
}
/**
* 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, 192)}.
* @return the result of what nextLong() would return if it was called at the state this jumped to
*/
public long leap()
{
long s0 = 0L;
long s1 = 0L;
long s2 = 0L;
long s3 = 0L;
for (long b = 0x76e15d3efefdcbbfL; b != 0L; b >>>= 1)
{
if ((1L & b) != 0L)
{
s0 ^= stateA;
s1 ^= stateB;
s2 ^= stateC;
s3 ^= stateD;
}
nextLong();
}
for (long b = 0xc5004e441c522fb3L; b != 0L; b >>>= 1)
{
if ((1L & b) != 0L)
{
s0 ^= stateA;
s1 ^= stateB;
s2 ^= stateC;
s3 ^= stateD;
}
nextLong();
}
for (long b = 0x77710069854ee241L; b != 0L; b >>>= 1)
{
if ((1L & b) != 0L)
{
s0 ^= stateA;
s1 ^= stateB;
s2 ^= stateC;
s3 ^= stateD;
}
nextLong();
}
for (long b = 0x39109bb02acbe635L; b != 0L; b >>>= 1)
{
if ((1L & b) != 0L)
{
s0 ^= stateA;
s1 ^= stateB;
s2 ^= stateC;
s3 ^= stateD;
}
nextLong();
}
stateA = s0;
stateB = s1;
stateC = s2;
stateD = s3;
s3 = (s3 << 19 | s3 >>> 45); // s3 has d ^ b
s0 ^= s3; // s0 has a
s2 ^= s1; // s2 has b ^ b << 17;
s2 ^= s2 << 17;
s2 ^= s2 << 34; // s2 has b
s1 ^= s0; // s1 has b ^ c
s2 ^= s1; // s2 has c;
s1 ^= s2; // StateB has b;
s1 ^= s1 >>> 32;
s1 *= 0xBEA225F9EB34556DL;
s1 ^= s1 >>> 29;
s1 *= 0xBEA225F9EB34556DL;
s1 ^= s1 >>> 32;
s1 *= 0xBEA225F9EB34556DL;
return s1 ^ s1 >>> 29;
}
@Override
public Xoshiro256MX3Random copy () {
return new Xoshiro256MX3Random(stateA, stateB, stateC, stateD);
}
@Override
public boolean equals (Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Xoshiro256MX3Random that = (Xoshiro256MX3Random)o;
if (stateA != that.stateA)
return false;
if (stateB != that.stateB)
return false;
if (stateC != that.stateC)
return false;
return stateD == that.stateD;
}
public String toString () {
return "Xoshiro256MX3Random{" + "stateA=" + (stateA) + "L, stateB=" + (stateB) + "L, stateC=" + (stateC) + "L, stateD=" + (stateD) + "L}";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy