package squidpony.squidmath; /** * 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 { public static long doubleToLongBits(final double value) { return Double.doubleToLongBits(value); } public static long doubleToRawLongBits(final double value) { return Double.doubleToRawLongBits(value); } public static double longBitsToDouble(final long bits) { return Double.longBitsToDouble(bits); } public static int doubleToLowIntBits(final double value) { return (int)(Double.doubleToLongBits(value) & 0xffffffffL); } public static int doubleToHighIntBits(final double value) { return (int)(Double.doubleToLongBits(value) >>> 32); } public static int doubleToMixedIntBits(final double value) { final long l = Double.doubleToLongBits(value); return (int)l ^ (int)(l >>> 32); } /** * Makes a modified version of value that uses the specified bits (up to 12) for its exponent and sign. * Meant for some specific cases, like adjusting the exponent on an unknown double to the 1.0 to 2.0 range (which * would pass 0x3ff for exponentBits). If you have a double from 1.0 to 2.0, you can subtract 1.0 from it to get the * often-desirable 0.0-1.0 range. Other common cases are 0x400, which adjusts to between 2.0 and 4.0 (subtracting * 3.0 from this gives the -1.0 to 1.0 range, useful for noise), and 0xBFF, which adjusts to between -2.0 and -1.0. * For the last case, you might think that -0x3ff would work, but sadly it doesn't. You can use * {@code exponentBits |= 0x800} to set the sign bit to negative, or {@code exponentBits &= 0x7ff} for positive. * @param value a double that will have its sign and exponent set to the specified bits * @param exponentBits the bits to use for the sign and exponent section of the returned modification of value * @return the double produced by keeping the significand of value but changing its exponent and sign as given */ public static double setExponent(final double value, final int exponentBits) { return Double.longBitsToDouble((Double.doubleToLongBits(value) & 0xfffffffffffffL) | ((long) exponentBits << 52)); } /** * Gets an 8-bit section of the given double {@code value}, using {@code whichByte} to select whether this should * return byte 0 (least significant), 1, 2, and so on up to 7 (most significant). * @param value a float * @param whichByte an int that will be used to select the byte to take from value (any int is allowed, only the bottom 3 bits are used to select) * @return the selected byte from the given float */ public static byte getSelectedByte(final double value, final int whichByte) { return (byte)(Double.doubleToLongBits(value) >>> ((whichByte & 7) << 3)); } /** * Like {@link #getSelectedByte(double, int)}, this sets the byte at a selected position in the int representation of * a double, then returns the double produced by the bit change. Uses {@code whichByte} to select whether this should * set byte 0 (least significant), 1, 2, and so on up to 7 (most significant). {@code newValue} is a byte. * @param value a double * @param whichByte an int that will be used to select the byte to take from value (any int is allowed, only the bottom 3 bits are used to select) * @param newValue a byte that will be placed into the returned double's bits at the selected position * @return a double that results from changing the bits at the selected position to match newValue */ public static double setSelectedByte(final double value, final int whichByte, final byte newValue) { return Double.longBitsToDouble((Double.doubleToLongBits(value) & ~(255 << ((whichByte & 7) << 3))) | ((newValue & 255) << ((whichByte & 7) << 3))); } /** * Very limited-use. Takes any double and produces a double in the -1.0 to 1.0 range, with similar inputs producing * close to a consistent rate of up and down through the range. This is meant for noise, where it may be useful to * limit the amount of change between nearby points' noise values and prevent sudden "jumps" in noise value. * @param value any double * @return a double from -1.0 (inclusive) to 1.0 (exclusive) */ public static double bounce(final double value) { long s = Double.doubleToLongBits(value) & 0xfffffffffffffL; return Double.longBitsToDouble(((s ^ -((s & 0x8000000000000L)>>51)) & 0xfffffffffffffL) | 0x4010000000000000L) - 5.0; } /** * Very limited-use. Takes any double and produces a double in the -1.0 to 1.0 range, with similar inputs producing * close to a consistent rate of up and down through the range. This is meant for noise, where it may be useful to * limit the amount of change between nearby points' noise values and prevent sudden "jumps" in noise value. * @param value any double * @return a double from -1.0 (inclusive) to 1.0 (exclusive) */ public static float bounce(final float value) { int s = Float.floatToIntBits(value) & 0x007fffff; return Float.intBitsToFloat(((s ^ -((s & 0x00400000)>>22)) & 0x007fffff) | 0x40800000) - 5f; } /** * Very limited-use. Takes the significand bits of a double, represented as a long of which this uses 52 bits, and * produces a double in the -1.0 to 1.0 range, with similar inputs producing close to a consistent rate of up and * down through the range. This is meant for noise, where it may be useful to limit the amount of change between * nearby points' noise values and prevent sudden "jumps" in noise value. * @param value any long; only the lower 52 bits will be used * @return a double from -1.0 (inclusive) to 1.0 (exclusive) */ public static double bounce(final long value) { long s = value & 0xfffffffffffffL; return Double.longBitsToDouble(((s ^ -((s & 0x8000000000000L)>>51)) & 0xfffffffffffffL) | 0x4010000000000000L) - 5.0; } /** * Very limited-use. Takes the significand bits of a double, represented as a pair of ints {@code valueLow} and * {@code valueHigh}, using all bits in valueLow and the least-significant 20 bits of valueHigh, and * produces a double in the -1.0 to 1.0 range, with similar inputs producing close to a consistent rate of up and * down through the range. This is meant for noise, where it may be useful to limit the amount of change between * nearby points' noise values and prevent sudden "jumps" in noise value. * @param valueLow any int; all bits will be used as the less-significant bits of the significand * @param valueHigh any int; only the bottom 20 bits will be used as the more-significant bits of the significand * @return a double from -1.0 (inclusive) to 1.0 (exclusive) */ public static double bounce(final int valueLow, final int valueHigh) { long s = (((long) valueHigh) << 32 | valueLow) & 0xfffffffffffffL; return Double.longBitsToDouble(((s ^ -((s & 0x8000000000000L)>>51)) & 0xfffffffffffffL) | 0x4010000000000000L) - 5.0; } public static int floatToIntBits(final float value) { return Float.floatToIntBits(value); } public static int floatToRawIntBits(final float value) { return Float.floatToRawIntBits(value); } public static float intBitsToFloat(final int bits) { return Float.intBitsToFloat(bits); } /** * Gets an 8-bit section of the given float {@code value}, using {@code whichByte} to select whether this should * return byte 0 (least significant), 1, 2, or 3 (most significant). * @param value a float * @param whichByte an int that will be used to select the byte to take from value (any int is allowed, only the bottom 2 bits are used to select) * @return the selected byte from the given float */ public static byte getSelectedByte(final float value, final int whichByte) { return (byte)(Float.floatToIntBits(value) >>> ((whichByte & 3) << 3)); } /** * Like {@link #getSelectedByte(float, int)}, this sets the byte at a selected position in the int representation of * a float, then returns the float produced by the bit change. Uses {@code whichByte} to select whether this should * set byte 0 (least significant), 1, 2, or 3 (most significant). {@code newValue} is a byte. * @param value a float * @param whichByte an int that will be used to select the byte to take from value (any int is allowed, only the bottom 2 bits are used to select) * @param newValue a byte that will be placed into the returned float's bits at the selected position * @return a float that results from changing the bits at the selected position to match newValue */ public static float setSelectedByte(final float value, final int whichByte, final byte newValue) { return Float.intBitsToFloat((Float.floatToIntBits(value) & ~(255 << ((whichByte & 3) << 3))) | ((newValue & 255) << ((whichByte & 3) << 3))); } /** * Generates a pseudo-random double between 0.0 (inclusive) and 1.0 (exclusive) using the given int seed, passing it * twice through the (very high-quality and rather fast) {@link PintRNG} algorithm, derived from PCG-Random. This * produces a pair of random ints, which this produces a double from using the equivalent of * {@link #longBitsToDouble(long)} or something functionally equivalent on GWT. * <br> * Consider calling this with {@code NumberTools.randomDouble(seed += 0x3C6EF372)} for an optimal period of 2 to the * 31 when repeatedly called, but {@code NumberTools.randomDouble(++seed)} will also work just fine. * @param seed any int to be used as a seed * @return a pseudo-random double from 0.0 (inclusive) to 1.0 (exclusive) */ public static double randomDouble(int seed) { seed ^= seed >>> (4 + (seed >>> 28)); long bits = ((seed *= 0x108EF2D9) >>> 22 ^ seed) & 0xffffffffL; seed += 0x9E3779B9; seed ^= seed >>> (4 + (seed >>> 28)); bits |= ((((seed *= 0x108EF2D9) >>> 22 ^ seed) & 0xfffffL) << 32 | 0x3ff0000000000000L); return Double.longBitsToDouble(bits) - 1.0; } /** * Generates a pseudo-random float between 0.0f (inclusive) 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. * <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 randomFloat(int seed) { seed ^= seed >>> (4 + (seed >>> 28)); return (Float.intBitsToFloat((((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000) - 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)); return (Float.intBitsToFloat((((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000) - 1f) * (seed >> 31 | 1); } /** * Generates a pseudo-random double between -1.0 (exclusive) and 1.0 (exclusive) with a distribution that has a * strong central bias (around 0.0). Uses the given int seed, passing it twice through the (very high-quality and * rather fast) {@link PintRNG} algorithm, derived from PCG-Random. This produces a pair of random ints, which this * uses to generate a pair of floats between 0.0 (inclusive)and 1.0 (exclusive) using the equivalent of * {@link #intBitsToFloat(int)} or something functionally equivalent on GWT, multiplies the floats, and sets the * sign pseudo-randomly based on an unused bit from earlier. * <br> * Consider calling this with {@code NumberTools.randomDoubleCurved(seed += 0x3C6EF372)} for an optimal period of 2 * to the 31 when repeatedly called, but {@code NumberTools.randomDoubleCurved(++seed)} will also work just fine. * @param seed any int to be used as a seed * @return a pseudo-random double from -1.0 (exclusive) to 1.0 (exclusive), distributed on a curve centered on 0.0 */ public static double randomDoubleCurved(int seed) { seed ^= seed >>> (4 + (seed >>> 28)); float a = Float.intBitsToFloat((((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000); seed += 0x9E3779B9; seed ^= seed >>> (4 + (seed >>> 28)); return (a - 1.0) * (Float.intBitsToFloat((((seed *= 0x108EF2D9) >>> 22 ^ seed) >>> 9) | 0x3f800000) - 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; for (int i = 0; i < len; i++) { result += (a ^= 0x85157AF5 * Float.floatToIntBits(data[i])); } return result * (a | 1) ^ (result >>> 11 | result << 21); } static int hashWisp(final double[] data) { if (data == null) return 0; long result = 0x9E3779B97F4A7C94L, a = 0x632BE59BD9B4E019L; final int len = data.length; double t; for (int i = 0; i < len; i++) { result += (a ^= 0x8329C6EB9E6AD3E3L * ((long) (-0xD0E8.9D2D311E289Fp-25 * (t = data[i]) + t * -0x1.39b4dce80194cp9))); } return (int)((result = (result * (a | 1L) ^ (result >>> 27 | result << 37))) ^ (result >>> 32)); } }