Skip to content

Commit

Permalink
Add WhiskerRandom; Gradle 7.5.
Browse files Browse the repository at this point in the history
  • Loading branch information
tommyettinger committed Jul 16, 2022
1 parent ce507f9 commit 119cc25
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 7 deletions.
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\:https://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\:https://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
6 changes: 6 additions & 0 deletions gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"

# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi

# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
Expand Down
14 changes: 8 additions & 6 deletions gradlew.bat
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@rem limitations under the License.
@rem

@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
Expand All @@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

Expand All @@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Expand Down Expand Up @@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%

:mainEnd
if "%OS%"=="Windows_NT" endlocal
Expand Down
310 changes: 310 additions & 0 deletions src/main/java/com/github/tommyettinger/random/WhiskerRandom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
/*
* Copyright (c) 2022 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:https://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;

/**
* Typically the fastest generator here on recent JDKs, with a huge probable period but no minimum period guarantee.
* This generator is extremely similar to {@link FourWheelRandom}; they use the same operations except for an additional
* subtraction that FourWheelRandom uses. Removing this one operation, and changing the order of and constants used by
* the other operations, improves both quality and speed here. This can be considered stable, like the other
* EnhancedRandom implementations here. This generator is between 10% and 20% faster than FourWheelRandom on Java 16 and
* up, including on Graal but not counting Semeru JDKs (which seem to be generally slower for all microbenchmarks).
* <br>
* Testing performed should be sufficient, but more can always be done; this passes at least 64TB of PractRand without
* issues. This has been tested with Remortality, but not to the hundreds-of-PB threshold that FourWheelRandom required
* to fail, so it could still fail that test after an absurd amount of data has been tested. {@link StrangerRandom} is
* probably stronger, but not as fast; {@link TrimRandom} is possibly stronger and offers a guaranteed minimum period of
* 2 to the 64, but isn't as fast.
* <br>
* The algorithm used here has four states purely to exploit instruction-level parallelism; it isn't trying to extend the
* period of the generator beyond about 2 to the 64 (the expected bare minimum, though some cycles will likely be much
* longer). There's a complex tangle of dependencies across the four states, but it is possible to invert the generator
* given a full 256-bit state; this is vital for its period and quality.
* <br>
* It is strongly recommended that you seed this with {@link #setSeed(long)} instead of
* {@link #setState(long, long, long, long)}, because if you give sequential seeds to both setSeed() and setState(), the
* former will start off random, while the latter will start off with a correlation between the initial a and c and the
* results. The correlation should go away very quickly, though, probably in fewer than 10 generated numbers.
* <br>
* This implements all optional methods in EnhancedRandom except {@link #skip(long)}; it does implement
* {@link #previousLong()} without using skip().
*/
public class WhiskerRandom extends EnhancedRandom {
@Override
public String getTag() {
return "WhiR";
}

/**
* The first state; can be any long.
*/
protected long stateA;
/**
* The second state; can be any long.
*/
protected long stateB;
/**
* The third state; can be any long.
*/
protected long stateC;
/**
* The fourth state; can be any long.
*/
protected long stateD;

/**
* Creates a new WhiskerRandom with a random state.
*/
public WhiskerRandom() {
stateA = EnhancedRandom.seedFromMath();
stateB = EnhancedRandom.seedFromMath();
stateC = EnhancedRandom.seedFromMath();
stateD = EnhancedRandom.seedFromMath();
}

/**
* Creates a new WhiskerRandom 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 WhiskerRandom(long seed) {
setSeed(seed);
}

/**
* Creates a new WhiskerRandom with the given four states; all {@code long} values are permitted.
* These states will be used verbatim.
*
* @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 WhiskerRandom(long stateA, long stateB, long stateC, long stateD) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
this.stateD = stateD;
}

/**
* 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.
*
* @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;
break;
case 1:
stateB = value;
break;
case 2:
stateC = value;
break;
default:
stateD = 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.
*
* @param seed the initial seed; may be any long
*/
@Override
public void setSeed (long seed) {
stateA = seed ^ 0xC6BC279692B5C323L;
stateB = seed ^ ~0xC6BC279692B5C323L;
seed ^= seed >>> 32;
seed *= 0xBEA225F9EB34556DL;
seed ^= seed >>> 29;
seed *= 0xBEA225F9EB34556DL;
seed ^= seed >>> 32;
seed *= 0xBEA225F9EB34556DL;
seed ^= seed >>> 29;
stateC = ~seed;
stateD = seed;
}

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.
*
* @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; can be any long
*/
@Override
public void setState (long stateA, long stateB, long stateC, long stateD) {
this.stateA = stateA;
this.stateB = stateB;
this.stateC = stateC;
this.stateD = stateD;
}

@Override
public long nextLong () {
final long fa = stateA;
final long fb = stateB;
final long fc = stateC;
final long fd = stateD;
stateA = fd * 0xF1357AEA2E62A9C5L; // Considered good by Steele and Vigna, https://arxiv.org/abs/2001.05304v1
stateB = (fa << 44 | fa >>> 20);
stateC = fb + 0x9E3779B97F4A7C15L; // 2 to the 64 divided by the golden ratio
return stateD = fa ^ fc;
}

@Override
public long previousLong () {
final long fa = stateA;
final long fb = stateB;
final long fc = stateC;
final long fd = stateD;
stateA = (fb >>> 44 | fb << 20);
stateB = fc - 0x9E3779B97F4A7C15L;
stateC = stateA ^ fd;
return stateD = fa * 0x781494A55DAAED0DL; // modular multiplicative inverse of 0xF1357AEA2E62A9C5L
}

@Override
public int next (int bits) {
final long fa = stateA;
final long fb = stateB;
final long fc = stateC;
final long fd = stateD;
stateA = fd * 0xF1357AEA2E62A9C5L;
stateB = (fa << 44 | fa >>> 20);
stateC = fb + 0x9E3779B97F4A7C15L;
return (int)(stateD = fa ^ fc) >>> (32 - bits);
}

@Override
public WhiskerRandom copy () {
return new WhiskerRandom(stateA, stateB, stateC, stateD);
}

@Override
public boolean equals (Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;

WhiskerRandom that = (WhiskerRandom)o;

return stateA == that.stateA && stateB == that.stateB && stateC == that.stateC && stateD == that.stateD;
}

public String toString () {
return "WhiskerRandom{" + "stateA=" + (stateA) + "L, stateB=" + (stateB) + "L, stateC=" + (stateC) + "L, stateD=" + (stateD) + "L}";
}
}

0 comments on commit 119cc25

Please sign in to comment.