001package squidpony.squidmath;
002
003/**
004 * Performance-oriented white noise generator for 1D, 2D, 3D, 4D, and 6D. Produces noise values from -1.0 inclusive
005 * to 1.0 exclusive. Should produce a completely different double even for extremely-nearby points, so this is not a
006 * kind of continuous noise like {@link SeededNoise}. It is not actually random, and the value is always determined by
007 * the exact double positions (and possibly a long seed) given to it. Even a slight change should drastically alter the
008 * returned value, though below some very small epsilon the results might not be different between two very close points
009 * in space. The output should look like "TV static" if rendered as grayscale pixels.
010 * <br>
011 * Meant for cases where you want to use a different number for every pixel, tile, or other unit of noise generated,
012 * often coupled with another kind of noise but with the result of the WhiteNoise made less significant to add a "fuzzy"
013 * effect to the appearance.
014 * <br>
015 * Implementation is based on {@link CrossHash.Wisp#hash64(double[])}, treating doubles as ints
016 * by using {@link NumberTools#doubleToMixedIntBits(double)}, which XORs the bottom and top halves of the long bits of
017 * each double. Finishes by passing the top 52 bits as a significand to {@link NumberTools#longBitsToDouble(long)},
018 * using an exponent that allows this to produce numbers between -1.0 and 1.0. Has special treatment for the seed when
019 * present (since it is a long).
020 * <br>
021 * This can also be used as a sort of hashing function that produces a double, if you find a need for such a thing, with
022 * {@link #hash(double...)}.
023 */
024public class WhiteNoise implements Noise.Noise1D, Noise.Noise2D, Noise.Noise3D, Noise.Noise4D, Noise.Noise6D {
025    public static final WhiteNoise instance = new WhiteNoise();
026
027    public WhiteNoise() {
028    }
029
030    @Override
031    public double getNoise(double x) {
032        long a = 0x632BE59BD9B4E019L,
033                result = 0x9E3779B97F4A7C94L +
034                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3));
035        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
036    }
037
038    @Override
039    public double getNoiseWithSeed(double x, long seed) {
040        long a = 0x632BE59BD9B4E019L,
041                result = 0x9E3779B97F4A7C94L +
042                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
043                        + (a ^= 0x8329C6EB9E6AD3E3L * seed);
044        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
045    }
046
047    @Override
048    public double getNoise(double x, double y) {
049
050        long a = 0x632BE59BD9B4E019L,
051                result = 0x9E3779B97F4A7C94L +
052                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
053                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.8329C6DFp3));
054        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L) - 3.0;
055    }
056    @Override
057    public double getNoiseWithSeed(double x, double y, long seed) {
058        long a = 0x632BE59BD9B4E019L,
059                result = 0x9E3779B97F4A7C94L +
060                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
061                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.8329C6DFp3))
062                        + (a ^= 0x8329C6EB9E6AD3E3L * seed);
063        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
064    }
065
066    @Override
067    public double getNoise(double x, double y, double z) {
068        long a = 0x632BE59BD9B4E019L,
069                result = 0x9E3779B97F4A7C94L +
070                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
071                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.8329C6DFp3))
072                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x11.85157AF5p3));
073        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
074    }
075
076    @Override
077    public double getNoiseWithSeed(double x, double y, double z, long seed) {
078        long a = 0x632BE59BD9B4E019L,
079                result = 0x9E3779B97F4A7C94L +
080                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
081                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.8329C6DFp3))
082                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x11.85157AF5p3))
083                        + (a ^= 0x8329C6EB9E6AD3E3L * seed);
084        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
085    }
086    @Override
087    public double getNoise(double x, double y, double z, double w) {
088        long a = 0x632BE59BD9B4E019L,
089                result = 0x9E3779B97F4A7C94L +
090                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
091                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.953976F9p3))
092                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x11.85157AF5p3))
093                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(w*0x11.8329C6DFp3));
094        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
095    }
096
097    @Override
098    public double getNoiseWithSeed(double x, double y, double z, double w, long seed) {
099        long a = 0x632BE59BD9B4E019L,
100                result = 0x9E3779B97F4A7C94L +
101                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
102                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x11.953976F9p3))
103                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x11.85157AF5p3))
104                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(w*0x11.8329C6DFp3))
105                        + (a ^= 0x8329C6EB9E6AD3E3L * seed);
106        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
107    }
108
109    @Override
110    public double getNoise(double x, double y, double z, double w, double u, double v) {
111        long a = 0x632BE59BD9B4E019L,
112                result = 0x9E3779B97F4A7C94L +
113                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
114                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x17.08329C6DFp2))
115                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x29.085157AF5p1))
116                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(w*0x11.85157AF5p3))
117                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(u*0x17.09E3779B9p2))
118                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(v*0x29.08329C6DFp1));
119        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
120    }
121
122    @Override
123    public double getNoiseWithSeed(double x, double y, double z, double w, double u, double v, long seed) {
124        long a = 0x632BE59BD9B4E019L,
125                result = 0x9E3779B97F4A7C94L +
126                        (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(x*0x11.9E3779B9p3))
127                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(y*0x17.08329C6DFp2))
128                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(z*0x29.085157AF5p1))
129                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(w*0x11.85157AF5p3))
130                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(u*0x17.09E3779B9p2))
131                        + (a ^= 0x8329C6EB9E6AD3E3L * NumberTools.doubleToMixedIntBits(v*0x29.08329C6DFp1))
132                        + (a ^= 0x8329C6EB9E6AD3E3L * seed);
133        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
134    }
135    
136    /**
137     * Hashes the input double array or vararg and produces a double with unpredictable value, between -1.0 inclusive
138     * and 1.0 exclusive.
139     * @param data an array or vararg of double items; should not include NaN or infinite values but has no other limits
140     * @return an unpredictable double between -1.0 inclusive and 1.0 exclusive
141     */
142    public static double hash(double... data) {
143        if(data == null) return 0.0;
144        long a = 0x632BE59BD9B4E019L, result = 0x9E3779B97F4A7C94L;
145        double t = 0x13.9E3779B9p-5;
146        int len = data.length;
147        for (int i = 0; i < len; i++) {
148            result += (a ^= 0x8329C6EB9E6AD3E3L * (NumberTools.doubleToMixedIntBits((t *= t - data[i]))));
149        }
150        return NumberTools.longBitsToDouble(((result * (a | 1L) ^ (result >>> 27 | result << 37)) >>> 12) | 0x4000000000000000L)  - 3.0;
151    }
152
153}