package squidpony.squidmath;
import com.google.gwt.typedarrays.client.Float64ArrayNative;
import com.google.gwt.typedarrays.client.Float32ArrayNative;
import com.google.gwt.typedarrays.client.Int32ArrayNative;
import com.google.gwt.typedarrays.client.Int8ArrayNative;
import com.google.gwt.typedarrays.shared.Float64Array;
import com.google.gwt.typedarrays.shared.Float32Array;
import com.google.gwt.typedarrays.shared.Int32Array;
import com.google.gwt.typedarrays.shared.Int8Array;
/**
* Various numeric functions that are important to performance but need alternate implementations on GWT to obtain it.
* Super-sourced on GWT, but most things here are direct calls to JDK methods when on desktop or Android.
*/
public class NumberTools {
private static final Int8Array wba = Int8ArrayNative.create(8);
private static final Int32Array wia = Int32ArrayNative.create(wba.buffer(), 0, 2);
private static final Float32Array wfa = Float32ArrayNative.create(wba.buffer(), 0, 2);
private static final Float64Array wda = Float64ArrayNative.create(wba.buffer(), 0, 1);
public static long doubleToLongBits(final double value) {
wda.set(0, value);
return ((long)wia.get(1) << 32) | (wia.get(0) & 0xffffffffL);
}
public static long doubleToRawLongBits(final double value) {
wda.set(0, value);
return ((long)wia.get(1) << 32) | (wia.get(0) & 0xffffffffL);
}
public static double longBitsToDouble(final long bits) {
wia.set(1, (int)(bits >>> 32));
wia.set(0, (int)bits);
return wda.get(0);
}
public static int doubleToLowIntBits(final double value)
{
wda.set(0, value);
return wia.get(0);
}
public static int doubleToHighIntBits(final double value)
{
wda.set(0, value);
return wia.get(1);
}
public static int doubleToMixedIntBits(final double value)
{
wda.set(0, value);
return wia.get(0) ^ wia.get(1);
}
public static double setExponent(final double value, final int exponentBits)
{
wda.set(0, value);
wia.set(1, (wia.get(1) & 0xfffff) | exponentBits << 20);
return wda.get(0);
}
public static double bounce(final double value)
{
wda.set(0, value);
int s = wia.get(1) & 0xfffff, flip = -((s & 0x80000)>>19);
wia.set(1, ((s ^ flip) & 0xfffff) | 0x40100000);
wia.set(0, wia.get(0) ^ flip);
return wda.get(0) - 5.0;
}
public static float bounce(final float value)
{
wfa.set(0, value);
int s = wia.get(0) & 0x007fffff, flip = -((s & 0x00400000)>>22);
wia.set(0, ((s ^ flip) & 0x007fffff) | 0x40800000);
return wfa.get(0) - 5f;
}
public static double bounce(final long value)
{
int s = (int)(value>>>32&0xfffff), flip = -((s & 0x80000)>>19);
wia.set(1, ((s ^ flip) & 0xfffff) | 0x40100000);
wia.set(0, ((int)value) ^ flip);
return wda.get(0) - 5.0;
}
public static double bounce(final int valueLow, final int valueHigh)
{
int s = valueHigh & 0xfffff, flip = -((s & 0x80000)>>19);
wia.set(1, ((s ^ flip) & 0xfffff) | 0x40100000);
wia.set(0, valueLow ^ flip);
return wda.get(0) - 5.0;
}
public static int floatToIntBits(final float value) {
wfa.set(0, value);
return wia.get(0);
}
public static int floatToRawIntBits(final float value) {
wfa.set(0, value);
return wia.get(0);
}
public static float intBitsToFloat(final int bits) {
wia.set(0, bits);
return wfa.get(0);
}
public static byte getSelectedByte(final double value, final int whichByte)
{
wda.set(0, value);
return wba.get(whichByte & 7);
}
public static double setSelectedByte(final double value, final int whichByte, final byte newValue)
{
wda.set(0, value);
wba.set(whichByte & 7, newValue);
return wda.get(0);
}
public static byte getSelectedByte(final float value, final int whichByte)
{
wfa.set(0, value);
return wba.get(whichByte & 3);
}
public static float setSelectedByte(final float value, final int whichByte, final byte newValue)
{
wfa.set(0, value);
wba.set(whichByte & 3, newValue);
return wfa.get(0);
}
public static double randomDouble(int seed)
{
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(0, ((seed *= 277803737) >>> 22) ^ seed);
seed += 0x9E3779B9;
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(1, ((((seed *= 277803737) >>> 22) ^ seed) & 0xfffff) | 0x3ff00000);
return wda.get(0) - 1.0;
}
public static float randomFloat(int seed)
{
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(0, (((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000);
return (wfa.get(0) - 1f);
}
/**
* Generates a pseudo-random float between -1.0f (exclusive) and 1.0f (exclusive) using the given int seed, passing
* it once through the (very high-quality and rather fast) {@link PintRNG} algorithm, derived from PCG-Random. This
* produces a random int, which this produces a float from using {@link #intBitsToFloat(int)} (long)} or something
* functionally equivalent on GWT. The sign bit of the result is determined by data that is not used by the float
* otherwise, and keeps the results almost linear in distribution between -1.0 and 1.0, exclusive for both (0 shows
* up twice as often as any single other result, but this shouldn't affect the odds very strongly; it's about a 1 in
* 8 million chance of exactly 0 occurring vs. a 1 in 16 million of any other specific float this can produce).
* <br>
* Consider calling this with {@code NumberTools.randomSignedFloat(seed += 0x9E3779B9)} for an optimal period of 2
* to the 32 when repeatedly called, but {@code NumberTools.randomSignedFloat(++seed)} will also work just fine.
* @param seed any int to be used as a seed
* @return a pseudo-random float from -1.0f (exclusive) to 1.0f (exclusive)
*/
public static float randomSignedFloat(int seed)
{
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(0, (((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000);
return (wfa.get(0) - 1f) * (seed >> 31 | 1);
}
public static double randomDoubleCurved(int seed)
{
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(0, ((((seed *= 277803737) >>> 22) ^ seed) >>> 9) | 0x3f800000);
seed += 0x9E3779B9;
seed ^= seed >>> (4 + (seed >>> 28));
wia.set(1, ((((seed *= 277803737) >>> 22) ^ seed) >>> 9) | 0x3f800000);
return (wfa.get(0) - 1.0) * (wfa.get(1) - 1.0) * (seed >> 31 | 1);
}
static int hashWisp(final float[] data) {
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
double t;
for (int i = 0; i < len; i++) {
result += (a ^= 0x85157AF5 * ((int) (-0xD0E8.9D2D311E289Fp-25f * (t = data[i]) + t * -0x1.39b4dce80194cp9f)));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
static int hashWisp(final double[] data)
{
if (data == null)
return 0;
int result = 0x9E3779B9, a = 0x632BE5AB;
final int len = data.length;
for (int i = 0; i < len; i++) {
wda.set(0, data[i]);
result += (a ^= 0x85157AF5 * wia.get(0)) + (a ^= 0x85157AF5 * wia.get(1));
}
return result * (a | 1) ^ (result >>> 11 | result << 21);
}
}