1 module open_simplex_2.open_simplex_2_f;
2 
3 import open_simplex_2.open_simplex_2;
4 import std.math : abs;
5 
6 
7 /**
8  * K.jpg's OpenSimplex 2, faster variant
9  *
10  * - 2D is standard simplex implemented using a lookup table.
11  * - 3D is "Re-oriented 4-point BCC noise" which constructs a
12  *   congruent BCC lattice in a much different way than usual.
13  * - 4D constructs the lattice as a union of five copies of its
14  *   reciprocal. It successively finds the closest point on each.
15  *
16  * Multiple versions of each function are provided. See the
17  * documentation above each, for more info.
18  */
19 public final class OpenSimplex2F : OpenSimplex2 {
20 
21     private enum int PSIZE = 2048;
22     private enum int PMASK = 2047;
23 
24     private short[] perm;
25     private Grad2[] permGrad2;
26     private Grad3[] permGrad3;
27     private Grad4[] permGrad4;
28 
29     public this(long seed)
30     {
31         perm = new short[PSIZE];
32         permGrad2 = new Grad2[PSIZE];
33         permGrad3 = new Grad3[PSIZE];
34         permGrad4 = new Grad4[PSIZE];
35         short[] source = new short[PSIZE];
36         for (short i = 0; i < PSIZE; i++)
37             source[i] = i;
38         for (int i = PSIZE - 1; i >= 0; i--) {
39             seed = seed * 6_364_136_223_846_793_005L + 1_442_695_040_888_963_407L;
40             int r = cast(int)((seed + 31) % (i + 1));
41             if (r < 0)
42                 r += (i + 1);
43             perm[i] = source[r];
44             permGrad2[i] = GRADIENTS_2D[perm[i]];
45             permGrad3[i] = GRADIENTS_3D[perm[i]];
46             permGrad4[i] = GRADIENTS_4D[perm[i]];
47             source[r] = source[i];
48         }
49     }
50 
51     /*
52      * Noise Evaluators
53      */
54 
55     /**
56      * 2D Simplex noise, standard lattice orientation.
57      */
58     public double noise2(double x, double y) {
59 
60         // Get points for A2* lattice
61         double s = 0.366025403784439 * (x + y);
62         double xs = x + s, ys = y + s;
63 
64         return noise2_Base(xs, ys);
65     }
66 
67     /**
68      * 2D Simplex noise, with Y pointing down the main diagonal.
69      * Might be better for a 2D sandbox style game, where Y is vertical.
70      * Probably slightly less optimal for heightmaps or continent maps.
71      */
72     public double noise2_XBeforeY(double x, double y) {
73 
74         // Skew transform and rotation baked into one.
75         double xx = x * 0.7071067811865476;
76         double yy = y * 1.224744871380249;
77 
78         return noise2_Base(yy + xx, yy - xx);
79     }
80 
81     /**
82      * 2D Simplex noise base.
83      * Lookup table implementation inspired by DigitalShadow.
84      */
85     private double noise2_Base(double xs, double ys) {
86         double value = 0;
87 
88         // Get base points and offsets
89         int xsb = fastFloor(xs), ysb = fastFloor(ys);
90         double xsi = xs - xsb, ysi = ys - ysb;
91 
92         // Index to point list
93         int index = cast(int)((ysi - xsi) / 2 + 1);
94 
95         double ssi = (xsi + ysi) * -0.211324865405187;
96         double xi = xsi + ssi, yi = ysi + ssi;
97 
98         // Point contributions
99         for (int i = 0; i < 3; i++) {
100             LatticePoint2D c = LOOKUP_2D[index + i];
101 
102             double dx = xi + c.dx, dy = yi + c.dy;
103             double attn = 0.5 - dx * dx - dy * dy;
104             if (attn <= 0) continue;
105 
106             int pxm = (xsb + c.xsv) & PMASK, pym = (ysb + c.ysv) & PMASK;
107             Grad2 grad = permGrad2[perm[pxm] ^ pym];
108             double extrapolation = grad.dx * dx + grad.dy * dy;
109 
110             attn *= attn;
111             value += attn * attn * extrapolation;
112         }
113 
114         return value;
115     }
116 
117     /**
118      * 3D Re-oriented 4-point BCC noise, classic orientation.
119      * Proper substitute for 3D Simplex in light of Forbidden Formulae.
120      * Use noise3_XYBeforeZ or noise3_XZBeforeY instead, wherever appropriate.
121      */
122     public double noise3_Classic(double x, double y, double z) {
123 
124         // Re-orient the cubic lattices via rotation, to produce the expected look on cardinal planar slices.
125         // If texturing objects that don't tend to have cardinal plane faces, you could even remove this.
126         // Orthonormal rotation. Not a skew transform.
127         double r = (2.0 / 3.0) * (x + y + z);
128         double xr = r - x, yr = r - y, zr = r - z;
129 
130         // Evaluate both lattices to form a BCC lattice.
131         return noise3_BCC(xr, yr, zr);
132     }
133 
134     /**
135      * 3D Re-oriented 4-point BCC noise, with better visual isotropy in (X, Y).
136      * Recommended for 3D terrain and time-varied animations.
137      * The Z coordinate should always be the "different" coordinate in your use case.
138      * If Y is vertical in world coordinates, call noise3_XYBeforeZ(x, z, Y) or use noise3_XZBeforeY.
139      * If Z is vertical in world coordinates, call noise3_XYBeforeZ(x, y, Z).
140      * For a time varied animation, call noise3_XYBeforeZ(x, y, T).
141      */
142     public double noise3_XYBeforeZ(double x, double y, double z) {
143 
144         // Re-orient the cubic lattices without skewing, to make X and Y triangular like 2D.
145         // Orthonormal rotation. Not a skew transform.
146         double xy = x + y;
147         double s2 = xy * -0.211324865405187;
148         double zz = z * 0.577350269189626;
149         double xr = x + s2 - zz, yr = y + s2 - zz;
150         double zr = xy * 0.577350269189626 + zz;
151 
152         // Evaluate both lattices to form a BCC lattice.
153         return noise3_BCC(xr, yr, zr);
154     }
155 
156     /**
157      * 3D Re-oriented 4-point BCC noise, with better visual isotropy in (X, Z).
158      * Recommended for 3D terrain and time-varied animations.
159      * The Y coordinate should always be the "different" coordinate in your use case.
160      * If Y is vertical in world coordinates, call noise3_XZBeforeY(x, Y, z).
161      * If Z is vertical in world coordinates, call noise3_XZBeforeY(x, Z, y) or use noise3_XYBeforeZ.
162      * For a time varied animation, call noise3_XZBeforeY(x, T, y) or use noise3_XYBeforeZ.
163      */
164     public double noise3_XZBeforeY(double x, double y, double z) {
165 
166         // Re-orient the cubic lattices without skewing, to make X and Z triangular like 2D.
167         // Orthonormal rotation. Not a skew transform.
168         double xz = x + z;
169         double s2 = xz * -0.211324865405187;
170         double yy = y * 0.577350269189626;
171         double xr = x + s2 - yy; double zr = z + s2 - yy;
172         double yr = xz * 0.577350269189626 + yy;
173 
174         // Evaluate both lattices to form a BCC lattice.
175         return noise3_BCC(xr, yr, zr);
176     }
177 
178     /**
179      * Generate overlapping cubic lattices for 3D Re-oriented BCC noise.
180      * Lookup table implementation inspired by DigitalShadow.
181      * It was actually faster to narrow down the points in the loop itself,
182      * than to build up the index with enough info to isolate 4 points.
183      */
184     private double noise3_BCC(double xr, double yr, double zr) {
185 
186         // Get base and offsets inside cube of first lattice.
187         int xrb = fastFloor(xr), yrb = fastFloor(yr), zrb = fastFloor(zr);
188         double xri = xr - xrb, yri = yr - yrb, zri = zr - zrb;
189 
190         // Identify which octant of the cube we're in. This determines which cell
191         // in the other cubic lattice we're in, and also narrows down one point on each.
192         int xht = cast(int)(xri + 0.5), yht = cast(int)(yri + 0.5), zht = cast(int)(zri + 0.5);
193         int index = (xht << 0) | (yht << 1) | (zht << 2);
194 
195         // Point contributions
196         double value = 0;
197         LatticePoint3D c = LOOKUP_3D[index];
198         while (c !is null) {
199             double dxr = xri + c.dxr, dyr = yri + c.dyr, dzr = zri + c.dzr;
200             double attn = 0.5 - dxr * dxr - dyr * dyr - dzr * dzr;
201             if (attn < 0) {
202                 c = c.nextOnFailure;
203             } else {
204                 int pxm = (xrb + c.xrv) & PMASK, pym = (yrb + c.yrv) & PMASK, pzm = (zrb + c.zrv) & PMASK;
205                 Grad3 grad = permGrad3[perm[perm[pxm] ^ pym] ^ pzm];
206                 double extrapolation = grad.dx * dxr + grad.dy * dyr + grad.dz * dzr;
207 
208                 attn *= attn;
209                 value += attn * attn * extrapolation;
210                 c = c.nextOnSuccess;
211             }
212         }
213         return value;
214     }
215 
216     /**
217      * 4D OpenSimplex2F noise, classic lattice orientation.
218      */
219     public double noise4_Classic(double x, double y, double z, double w) {
220 
221         // Get points for A4 lattice
222         double s = -0.138196601125011 * (x + y + z + w);
223         double xs = x + s, ys = y + s, zs = z + s, ws = w + s;
224 
225         return noise4_Base(xs, ys, zs, ws);
226     }
227 
228     /**
229      * 4D OpenSimplex2F noise, with XY and ZW forming orthogonal triangular-based planes.
230      * Recommended for 3D terrain, where X and Y (or Z and W) are horizontal.
231      * Recommended for noise(x, y, sin(time), cos(time)) trick.
232      */
233     public double noise4_XYBeforeZW(double x, double y, double z, double w) {
234 
235         double s2 = (x + y) * -0.178275657951399372 + (z + w) * 0.215623393288842828;
236         double t2 = (z + w) * -0.403949762580207112 + (x + y) * -0.375199083010075342;
237         double xs = x + s2, ys = y + s2, zs = z + t2, ws = w + t2;
238 
239         return noise4_Base(xs, ys, zs, ws);
240     }
241 
242     /**
243      * 4D OpenSimplex2F noise, with XZ and YW forming orthogonal triangular-based planes.
244      * Recommended for 3D terrain, where X and Z (or Y and W) are horizontal.
245      */
246     public double noise4_XZBeforeYW(double x, double y, double z, double w) {
247 
248         double s2 = (x + z) * -0.178275657951399372 + (y + w) * 0.215623393288842828;
249         double t2 = (y + w) * -0.403949762580207112 + (x + z) * -0.375199083010075342;
250         double xs = x + s2, ys = y + t2, zs = z + s2, ws = w + t2;
251 
252         return noise4_Base(xs, ys, zs, ws);
253     }
254 
255     /**
256      * 4D OpenSimplex2F noise, with XYZ oriented like noise3_Classic,
257      * and W for an extra degree of freedom. W repeats eventually.
258      * Recommended for time-varied animations which texture a 3D object (W=time)
259      */
260     public double noise4_XYZBeforeW(double x, double y, double z, double w) {
261 
262         double xyz = x + y + z;
263         double ww = w * 0.2236067977499788;
264         double s2 = xyz * -0.16666666666666666 + ww;
265         double xs = x + s2, ys = y + s2, zs = z + s2, ws = -0.5 * xyz + ww;
266 
267         return noise4_Base(xs, ys, zs, ws);
268     }
269 
270     /**
271      * 4D OpenSimplex2F noise base.
272      * Current implementation not fully optimized by lookup tables.
273      * But still comes out slightly ahead of Gustavson's Simplex in tests.
274      */
275     private double noise4_Base(double xs, double ys, double zs, double ws) {
276         double value = 0;
277 
278         // Get base points and offsets
279         int xsb = fastFloor(xs), ysb = fastFloor(ys), zsb = fastFloor(zs), wsb = fastFloor(ws);
280         double xsi = xs - xsb, ysi = ys - ysb, zsi = zs - zsb, wsi = ws - wsb;
281 
282         // If we're in the lower half, flip so we can repeat the code for the upper half. We'll flip back later.
283         double siSum = xsi + ysi + zsi + wsi;
284         double ssi = siSum * 0.309016994374947; // Prep for vertex contributions.
285         bool inLowerHalf = (siSum < 2);
286         if (inLowerHalf) {
287             xsi = 1 - xsi; ysi = 1 - ysi; zsi = 1 - zsi; wsi = 1 - wsi;
288             siSum = 4 - siSum;
289         }
290 
291         // Consider opposing vertex pairs of the octahedron formed by the central cross-section of the stretched tesseract
292         double aabb = xsi + ysi - zsi - wsi, abab = xsi - ysi + zsi - wsi, abba = xsi - ysi - zsi + wsi;
293         double aabbScore = abs(aabb), ababScore = abs(abab), abbaScore = abs(abba);
294 
295         // Find the closest point on the stretched tesseract as if it were the upper half
296         int vertexIndex, via, vib;
297         double asi, bsi;
298         if (aabbScore > ababScore && aabbScore > abbaScore) {
299             if (aabb > 0) {
300                 asi = zsi; bsi = wsi; vertexIndex = 0b0011; via = 0b0111; vib = 0b1011;
301             } else {
302                 asi = xsi; bsi = ysi; vertexIndex = 0b1100; via = 0b1101; vib = 0b1110;
303             }
304         } else if (ababScore > abbaScore) {
305             if (abab > 0) {
306                 asi = ysi; bsi = wsi; vertexIndex = 0b0101; via = 0b0111; vib = 0b1101;
307             } else {
308                 asi = xsi; bsi = zsi; vertexIndex = 0b1010; via = 0b1011; vib = 0b1110;
309             }
310         } else {
311             if (abba > 0) {
312                 asi = ysi; bsi = zsi; vertexIndex = 0b1001; via = 0b1011; vib = 0b1101;
313             } else {
314                 asi = xsi; bsi = wsi; vertexIndex = 0b0110; via = 0b0111; vib = 0b1110;
315             }
316         }
317         if (bsi > asi) {
318             via = vib;
319             double temp = bsi;
320             bsi = asi;
321             asi = temp;
322         }
323         if (siSum + asi > 3) {
324             vertexIndex = via;
325             if (siSum + bsi > 4) {
326                 vertexIndex = 0b1111;
327             }
328         }
329 
330         // Now flip back if we're actually in the lower half.
331         if (inLowerHalf) {
332             xsi = 1 - xsi; ysi = 1 - ysi; zsi = 1 - zsi; wsi = 1 - wsi;
333             vertexIndex ^= 0b1111;
334         }
335 
336         // Five points to add, total, from five copies of the A4 lattice.
337         for (int i = 0; i < 5; i++) {
338 
339             // Update xsb/etc. and add the lattice point's contribution.
340             LatticePoint4D c = VERTICES_4D[vertexIndex];
341             xsb += c.xsv; ysb += c.ysv; zsb += c.zsv; wsb += c.wsv;
342             double xi = xsi + ssi, yi = ysi + ssi, zi = zsi + ssi, wi = wsi + ssi;
343             double dx = xi + c.dx, dy = yi + c.dy, dz = zi + c.dz, dw = wi + c.dw;
344             double attn = 0.5 - dx * dx - dy * dy - dz * dz - dw * dw;
345             if (attn > 0) {
346                 int pxm = xsb & PMASK, pym = ysb & PMASK, pzm = zsb & PMASK, pwm = wsb & PMASK;
347                 Grad4 grad = permGrad4[perm[perm[perm[pxm] ^ pym] ^ pzm] ^ pwm];
348                 double ramped = grad.dx * dx + grad.dy * dy + grad.dz * dz + grad.dw * dw;
349 
350                 attn *= attn;
351                 value += attn * attn * ramped;
352             }
353 
354             // Maybe this helps the compiler/JVM/LLVM/etc. know we can end the loop here. Maybe not.
355             if (i == 4) break;
356 
357             // Update the relative skewed coordinates to reference the vertex we just added.
358             // Rather, reference its counterpart on the lattice copy that is shifted down by
359             // the vector <-0.2, -0.2, -0.2, -0.2>
360             xsi += c.xsi; ysi += c.ysi; zsi += c.zsi; wsi += c.wsi;
361             ssi += c.ssiDelta;
362 
363             // Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
364             double score0 = 1.0 + ssi * (-1.0 / 0.309016994374947); // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
365             vertexIndex = 0b0000;
366             if (xsi >= ysi && xsi >= zsi && xsi >= wsi && xsi >= score0) {
367                 vertexIndex = 0b0001;
368             }
369             else if (ysi > xsi && ysi >= zsi && ysi >= wsi && ysi >= score0) {
370                 vertexIndex = 0b0010;
371             }
372             else if (zsi > xsi && zsi > ysi && zsi >= wsi && zsi >= score0) {
373                 vertexIndex = 0b0100;
374             }
375             else if (wsi > xsi && wsi > ysi && wsi > zsi && wsi >= score0) {
376                 vertexIndex = 0b1000;
377             }
378         }
379 
380         return value;
381     }
382 
383     /*
384      * Utility
385      */
386 
387     private static int fastFloor(double x) {
388         int xi = cast(int)x;
389         return x < xi ? xi - 1 : xi;
390     }
391 
392     /*
393      * Definitions
394      */
395 
396     private static LatticePoint2D[] LOOKUP_2D;
397     private static LatticePoint3D[] LOOKUP_3D;
398     private static LatticePoint4D[] VERTICES_4D;
399 
400     static this()
401     {
402         LOOKUP_2D = new LatticePoint2D[4];
403         LOOKUP_3D = new LatticePoint3D[8];
404         VERTICES_4D = new LatticePoint4D[16];
405 
406         LOOKUP_2D[0] = new LatticePoint2D(1, 0);
407         LOOKUP_2D[1] = new LatticePoint2D(0, 0);
408         LOOKUP_2D[2] = new LatticePoint2D(1, 1);
409         LOOKUP_2D[3] = new LatticePoint2D(0, 1);
410 
411         for (int i = 0; i < 8; i++) {
412             int i1, j1, k1, i2, j2, k2;
413             i1 = (i >> 0) & 1; j1 = (i >> 1) & 1; k1 = (i >> 2) & 1;
414             i2 = i1 ^ 1; j2 = j1 ^ 1; k2 = k1 ^ 1;
415 
416             // The two points within this octant, one from each of the two cubic half-lattices.
417             LatticePoint3D c0 = new LatticePoint3D(i1, j1, k1, 0);
418             LatticePoint3D c1 = new LatticePoint3D(i1 + i2, j1 + j2, k1 + k2, 1);
419 
420             // Each single step away on the first half-lattice.
421             LatticePoint3D c2 = new LatticePoint3D(i1 ^ 1, j1, k1, 0);
422             LatticePoint3D c3 = new LatticePoint3D(i1, j1 ^ 1, k1, 0);
423             LatticePoint3D c4 = new LatticePoint3D(i1, j1, k1 ^ 1, 0);
424 
425             // Each single step away on the second half-lattice.
426             LatticePoint3D c5 = new LatticePoint3D(i1 + (i2 ^ 1), j1 + j2, k1 + k2, 1);
427             LatticePoint3D c6 = new LatticePoint3D(i1 + i2, j1 + (j2 ^ 1), k1 + k2, 1);
428             LatticePoint3D c7 = new LatticePoint3D(i1 + i2, j1 + j2, k1 + (k2 ^ 1), 1);
429 
430             // First two are guaranteed.
431             c0.nextOnFailure = c0.nextOnSuccess = c1;
432             c1.nextOnFailure = c1.nextOnSuccess = c2;
433 
434             // Once we find one on the first half-lattice, the rest are out.
435             // In addition, knowing c2 rules out c5.
436             c2.nextOnFailure = c3; c2.nextOnSuccess = c6;
437             c3.nextOnFailure = c4; c3.nextOnSuccess = c5;
438             c4.nextOnFailure = c4.nextOnSuccess = c5;
439 
440             // Once we find one on the second half-lattice, the rest are out.
441             c5.nextOnFailure = c6; c5.nextOnSuccess = null;
442             c6.nextOnFailure = c7; c6.nextOnSuccess = null;
443             c7.nextOnFailure = c7.nextOnSuccess = null;
444 
445             LOOKUP_3D[i] = c0;
446         }
447 
448         for (int i = 0; i < 16; i++) {
449             VERTICES_4D[i] = new LatticePoint4D((i >> 0) & 1, (i >> 1) & 1, (i >> 2) & 1, (i >> 3) & 1);
450         }
451     }
452 
453     private static class LatticePoint2D {
454         int xsv, ysv;
455         double dx, dy;
456         public this(int xsv, int ysv) {
457             this.xsv = xsv; this.ysv = ysv;
458             double ssv = (xsv + ysv) * -0.211324865405187;
459             this.dx = -xsv - ssv;
460             this.dy = -ysv - ssv;
461         }
462     }
463 
464     private static class LatticePoint3D {
465         public double dxr, dyr, dzr;
466         public int xrv, yrv, zrv;
467         LatticePoint3D nextOnFailure, nextOnSuccess;
468         public this(int xrv, int yrv, int zrv, int lattice) {
469             this.dxr = -xrv + lattice * 0.5; this.dyr = -yrv + lattice * 0.5; this.dzr = -zrv + lattice * 0.5;
470             this.xrv = xrv + lattice * 1024; this.yrv = yrv + lattice * 1024; this.zrv = zrv + lattice * 1024;
471         }
472     }
473 
474     private static class LatticePoint4D {
475         int xsv, ysv, zsv, wsv;
476         double dx, dy, dz, dw;
477         double xsi, ysi, zsi, wsi;
478         double ssiDelta;
479         public this(int xsv, int ysv, int zsv, int wsv) {
480             this.xsv = xsv + 409; this.ysv = ysv + 409; this.zsv = zsv + 409; this.wsv = wsv + 409;
481             double ssv = (xsv + ysv + zsv + wsv) * 0.309016994374947;
482             this.dx = -xsv - ssv;
483             this.dy = -ysv - ssv;
484             this.dz = -zsv - ssv;
485             this.dw = -wsv - ssv;
486             this.xsi = xsi = 0.2 - xsv;
487             this.ysi = ysi = 0.2 - ysv;
488             this.zsi = zsi = 0.2 - zsv;
489             this.wsi = wsi = 0.2 - wsv;
490             this.ssiDelta = (0.8 - xsv - ysv - zsv - wsv) * 0.309016994374947;
491         }
492     }
493 
494     /*
495      * Gradients
496      */
497 
498     private static class Grad2 {
499         double dx, dy;
500         public this(double dx, double dy) {
501             this.dx = dx; this.dy = dy;
502         }
503     }
504 
505     private static class Grad3 {
506         double dx, dy, dz;
507         public this(double dx, double dy, double dz) {
508             this.dx = dx; this.dy = dy; this.dz = dz;
509         }
510     }
511 
512     private static class Grad4 {
513         double dx, dy, dz, dw;
514         public this(double dx, double dy, double dz,  double dw) {
515             this.dx = dx; this.dy = dy; this.dz = dz; this.dw = dw;
516         }
517     }
518 
519     private enum double N2 = 0.01001634121365712;
520     private enum double N3 = 0.030485933181293584;
521     private enum double N4 = 0.009202377986303158;
522     private static Grad2[] GRADIENTS_2D;
523     private static Grad3[] GRADIENTS_3D;
524     private static Grad4[] GRADIENTS_4D;
525 
526     static this()
527     {
528 
529         GRADIENTS_2D = new Grad2[PSIZE];
530         Grad2[] grad2 = [
531             new Grad2( 0.130526192220052,  0.99144486137381),
532             new Grad2( 0.38268343236509,   0.923879532511287),
533             new Grad2( 0.608761429008721,  0.793353340291235),
534             new Grad2( 0.793353340291235,  0.608761429008721),
535             new Grad2( 0.923879532511287,  0.38268343236509),
536             new Grad2( 0.99144486137381,   0.130526192220051),
537             new Grad2( 0.99144486137381,  -0.130526192220051),
538             new Grad2( 0.923879532511287, -0.38268343236509),
539             new Grad2( 0.793353340291235, -0.60876142900872),
540             new Grad2( 0.608761429008721, -0.793353340291235),
541             new Grad2( 0.38268343236509,  -0.923879532511287),
542             new Grad2( 0.130526192220052, -0.99144486137381),
543             new Grad2(-0.130526192220052, -0.99144486137381),
544             new Grad2(-0.38268343236509,  -0.923879532511287),
545             new Grad2(-0.608761429008721, -0.793353340291235),
546             new Grad2(-0.793353340291235, -0.608761429008721),
547             new Grad2(-0.923879532511287, -0.38268343236509),
548             new Grad2(-0.99144486137381,  -0.130526192220052),
549             new Grad2(-0.99144486137381,   0.130526192220051),
550             new Grad2(-0.923879532511287,  0.38268343236509),
551             new Grad2(-0.793353340291235,  0.608761429008721),
552             new Grad2(-0.608761429008721,  0.793353340291235),
553             new Grad2(-0.38268343236509,   0.923879532511287),
554             new Grad2(-0.130526192220052,  0.99144486137381)
555         ];
556         for (int i = 0; i < grad2.length; i++) {
557             grad2[i].dx /= N2; grad2[i].dy /= N2;
558         }
559         for (int i = 0; i < PSIZE; i++) {
560             GRADIENTS_2D[i] = grad2[i % grad2.length];
561         }
562 
563         GRADIENTS_3D = new Grad3[PSIZE];
564         Grad3[] grad3 = [
565             new Grad3(-2.22474487139,      -2.22474487139,      -1.0),
566             new Grad3(-2.22474487139,      -2.22474487139,       1.0),
567             new Grad3(-3.0862664687972017, -1.1721513422464978,  0.0),
568             new Grad3(-1.1721513422464978, -3.0862664687972017,  0.0),
569             new Grad3(-2.22474487139,      -1.0,                -2.22474487139),
570             new Grad3(-2.22474487139,       1.0,                -2.22474487139),
571             new Grad3(-1.1721513422464978,  0.0,                -3.0862664687972017),
572             new Grad3(-3.0862664687972017,  0.0,                -1.1721513422464978),
573             new Grad3(-2.22474487139,      -1.0,                 2.22474487139),
574             new Grad3(-2.22474487139,       1.0,                 2.22474487139),
575             new Grad3(-3.0862664687972017,  0.0,                 1.1721513422464978),
576             new Grad3(-1.1721513422464978,  0.0,                 3.0862664687972017),
577             new Grad3(-2.22474487139,       2.22474487139,      -1.0),
578             new Grad3(-2.22474487139,       2.22474487139,       1.0),
579             new Grad3(-1.1721513422464978,  3.0862664687972017,  0.0),
580             new Grad3(-3.0862664687972017,  1.1721513422464978,  0.0),
581             new Grad3(-1.0,                -2.22474487139,      -2.22474487139),
582             new Grad3( 1.0,                -2.22474487139,      -2.22474487139),
583             new Grad3( 0.0,                -3.0862664687972017, -1.1721513422464978),
584             new Grad3( 0.0,                -1.1721513422464978, -3.0862664687972017),
585             new Grad3(-1.0,                -2.22474487139,       2.22474487139),
586             new Grad3( 1.0,                -2.22474487139,       2.22474487139),
587             new Grad3( 0.0,                -1.1721513422464978,  3.0862664687972017),
588             new Grad3( 0.0,                -3.0862664687972017,  1.1721513422464978),
589             new Grad3(-1.0,                 2.22474487139,      -2.22474487139),
590             new Grad3( 1.0,                 2.22474487139,      -2.22474487139),
591             new Grad3( 0.0,                 1.1721513422464978, -3.0862664687972017),
592             new Grad3( 0.0,                 3.0862664687972017, -1.1721513422464978),
593             new Grad3(-1.0,                 2.22474487139,       2.22474487139),
594             new Grad3( 1.0,                 2.22474487139,       2.22474487139),
595             new Grad3( 0.0,                 3.0862664687972017,  1.1721513422464978),
596             new Grad3( 0.0,                 1.1721513422464978,  3.0862664687972017),
597             new Grad3( 2.22474487139,      -2.22474487139,      -1.0),
598             new Grad3( 2.22474487139,      -2.22474487139,       1.0),
599             new Grad3( 1.1721513422464978, -3.0862664687972017,  0.0),
600             new Grad3( 3.0862664687972017, -1.1721513422464978,  0.0),
601             new Grad3( 2.22474487139,      -1.0,                -2.22474487139),
602             new Grad3( 2.22474487139,       1.0,                -2.22474487139),
603             new Grad3( 3.0862664687972017,  0.0,                -1.1721513422464978),
604             new Grad3( 1.1721513422464978,  0.0,                -3.0862664687972017),
605             new Grad3( 2.22474487139,      -1.0,                 2.22474487139),
606             new Grad3( 2.22474487139,       1.0,                 2.22474487139),
607             new Grad3( 1.1721513422464978,  0.0,                 3.0862664687972017),
608             new Grad3( 3.0862664687972017,  0.0,                 1.1721513422464978),
609             new Grad3( 2.22474487139,       2.22474487139,      -1.0),
610             new Grad3( 2.22474487139,       2.22474487139,       1.0),
611             new Grad3( 3.0862664687972017,  1.1721513422464978,  0.0),
612             new Grad3( 1.1721513422464978,  3.0862664687972017,  0.0)
613         ];
614         for (int i = 0; i < grad3.length; i++) {
615             grad3[i].dx /= N3; grad3[i].dy /= N3; grad3[i].dz /= N3;
616         }
617         for (int i = 0; i < PSIZE; i++) {
618             GRADIENTS_3D[i] = grad3[i % grad3.length];
619         }
620 
621         GRADIENTS_4D = new Grad4[PSIZE];
622         Grad4[] grad4 = [
623             new Grad4(-0.753341017856078,    -0.37968289875261624,  -0.37968289875261624,  -0.37968289875261624),
624             new Grad4(-0.7821684431180708,   -0.4321472685365301,   -0.4321472685365301,    0.12128480194602098),
625             new Grad4(-0.7821684431180708,   -0.4321472685365301,    0.12128480194602098,  -0.4321472685365301),
626             new Grad4(-0.7821684431180708,    0.12128480194602098,  -0.4321472685365301,   -0.4321472685365301),
627             new Grad4(-0.8586508742123365,   -0.508629699630796,     0.044802370851755174,  0.044802370851755174),
628             new Grad4(-0.8586508742123365,    0.044802370851755174, -0.508629699630796,     0.044802370851755174),
629             new Grad4(-0.8586508742123365,    0.044802370851755174,  0.044802370851755174, -0.508629699630796),
630             new Grad4(-0.9982828964265062,   -0.03381941603233842,  -0.03381941603233842,  -0.03381941603233842),
631             new Grad4(-0.37968289875261624,  -0.753341017856078,    -0.37968289875261624,  -0.37968289875261624),
632             new Grad4(-0.4321472685365301,   -0.7821684431180708,   -0.4321472685365301,    0.12128480194602098),
633             new Grad4(-0.4321472685365301,   -0.7821684431180708,    0.12128480194602098,  -0.4321472685365301),
634             new Grad4( 0.12128480194602098,  -0.7821684431180708,   -0.4321472685365301,   -0.4321472685365301),
635             new Grad4(-0.508629699630796,    -0.8586508742123365,    0.044802370851755174,  0.044802370851755174),
636             new Grad4( 0.044802370851755174, -0.8586508742123365,   -0.508629699630796,     0.044802370851755174),
637             new Grad4( 0.044802370851755174, -0.8586508742123365,    0.044802370851755174, -0.508629699630796),
638             new Grad4(-0.03381941603233842,  -0.9982828964265062,   -0.03381941603233842,  -0.03381941603233842),
639             new Grad4(-0.37968289875261624,  -0.37968289875261624,  -0.753341017856078,    -0.37968289875261624),
640             new Grad4(-0.4321472685365301,   -0.4321472685365301,   -0.7821684431180708,    0.12128480194602098),
641             new Grad4(-0.4321472685365301,    0.12128480194602098,  -0.7821684431180708,   -0.4321472685365301),
642             new Grad4( 0.12128480194602098,  -0.4321472685365301,   -0.7821684431180708,   -0.4321472685365301),
643             new Grad4(-0.508629699630796,     0.044802370851755174, -0.8586508742123365,    0.044802370851755174),
644             new Grad4( 0.044802370851755174, -0.508629699630796,    -0.8586508742123365,    0.044802370851755174),
645             new Grad4( 0.044802370851755174,  0.044802370851755174, -0.8586508742123365,   -0.508629699630796),
646             new Grad4(-0.03381941603233842,  -0.03381941603233842,  -0.9982828964265062,   -0.03381941603233842),
647             new Grad4(-0.37968289875261624,  -0.37968289875261624,  -0.37968289875261624,  -0.753341017856078),
648             new Grad4(-0.4321472685365301,   -0.4321472685365301,    0.12128480194602098,  -0.7821684431180708),
649             new Grad4(-0.4321472685365301,    0.12128480194602098,  -0.4321472685365301,   -0.7821684431180708),
650             new Grad4( 0.12128480194602098,  -0.4321472685365301,   -0.4321472685365301,   -0.7821684431180708),
651             new Grad4(-0.508629699630796,     0.044802370851755174,  0.044802370851755174, -0.8586508742123365),
652             new Grad4( 0.044802370851755174, -0.508629699630796,     0.044802370851755174, -0.8586508742123365),
653             new Grad4( 0.044802370851755174,  0.044802370851755174, -0.508629699630796,    -0.8586508742123365),
654             new Grad4(-0.03381941603233842,  -0.03381941603233842,  -0.03381941603233842,  -0.9982828964265062),
655             new Grad4(-0.6740059517812944,   -0.3239847771997537,   -0.3239847771997537,    0.5794684678643381),
656             new Grad4(-0.7504883828755602,   -0.4004672082940195,    0.15296486218853164,   0.5029860367700724),
657             new Grad4(-0.7504883828755602,    0.15296486218853164,  -0.4004672082940195,    0.5029860367700724),
658             new Grad4(-0.8828161875373585,    0.08164729285680945,   0.08164729285680945,   0.4553054119602712),
659             new Grad4(-0.4553054119602712,   -0.08164729285680945,  -0.08164729285680945,   0.8828161875373585),
660             new Grad4(-0.5029860367700724,   -0.15296486218853164,   0.4004672082940195,    0.7504883828755602),
661             new Grad4(-0.5029860367700724,    0.4004672082940195,   -0.15296486218853164,   0.7504883828755602),
662             new Grad4(-0.5794684678643381,    0.3239847771997537,    0.3239847771997537,    0.6740059517812944),
663             new Grad4(-0.3239847771997537,   -0.6740059517812944,   -0.3239847771997537,    0.5794684678643381),
664             new Grad4(-0.4004672082940195,   -0.7504883828755602,    0.15296486218853164,   0.5029860367700724),
665             new Grad4( 0.15296486218853164,  -0.7504883828755602,   -0.4004672082940195,    0.5029860367700724),
666             new Grad4( 0.08164729285680945,  -0.8828161875373585,    0.08164729285680945,   0.4553054119602712),
667             new Grad4(-0.08164729285680945,  -0.4553054119602712,   -0.08164729285680945,   0.8828161875373585),
668             new Grad4(-0.15296486218853164,  -0.5029860367700724,    0.4004672082940195,    0.7504883828755602),
669             new Grad4( 0.4004672082940195,   -0.5029860367700724,   -0.15296486218853164,   0.7504883828755602),
670             new Grad4( 0.3239847771997537,   -0.5794684678643381,    0.3239847771997537,    0.6740059517812944),
671             new Grad4(-0.3239847771997537,   -0.3239847771997537,   -0.6740059517812944,    0.5794684678643381),
672             new Grad4(-0.4004672082940195,    0.15296486218853164,  -0.7504883828755602,    0.5029860367700724),
673             new Grad4( 0.15296486218853164,  -0.4004672082940195,   -0.7504883828755602,    0.5029860367700724),
674             new Grad4( 0.08164729285680945,   0.08164729285680945,  -0.8828161875373585,    0.4553054119602712),
675             new Grad4(-0.08164729285680945,  -0.08164729285680945,  -0.4553054119602712,    0.8828161875373585),
676             new Grad4(-0.15296486218853164,   0.4004672082940195,   -0.5029860367700724,    0.7504883828755602),
677             new Grad4( 0.4004672082940195,   -0.15296486218853164,  -0.5029860367700724,    0.7504883828755602),
678             new Grad4( 0.3239847771997537,    0.3239847771997537,   -0.5794684678643381,    0.6740059517812944),
679             new Grad4(-0.6740059517812944,   -0.3239847771997537,    0.5794684678643381,   -0.3239847771997537),
680             new Grad4(-0.7504883828755602,   -0.4004672082940195,    0.5029860367700724,    0.15296486218853164),
681             new Grad4(-0.7504883828755602,    0.15296486218853164,   0.5029860367700724,   -0.4004672082940195),
682             new Grad4(-0.8828161875373585,    0.08164729285680945,   0.4553054119602712,    0.08164729285680945),
683             new Grad4(-0.4553054119602712,   -0.08164729285680945,   0.8828161875373585,   -0.08164729285680945),
684             new Grad4(-0.5029860367700724,   -0.15296486218853164,   0.7504883828755602,    0.4004672082940195),
685             new Grad4(-0.5029860367700724,    0.4004672082940195,    0.7504883828755602,   -0.15296486218853164),
686             new Grad4(-0.5794684678643381,    0.3239847771997537,    0.6740059517812944,    0.3239847771997537),
687             new Grad4(-0.3239847771997537,   -0.6740059517812944,    0.5794684678643381,   -0.3239847771997537),
688             new Grad4(-0.4004672082940195,   -0.7504883828755602,    0.5029860367700724,    0.15296486218853164),
689             new Grad4( 0.15296486218853164,  -0.7504883828755602,    0.5029860367700724,   -0.4004672082940195),
690             new Grad4( 0.08164729285680945,  -0.8828161875373585,    0.4553054119602712,    0.08164729285680945),
691             new Grad4(-0.08164729285680945,  -0.4553054119602712,    0.8828161875373585,   -0.08164729285680945),
692             new Grad4(-0.15296486218853164,  -0.5029860367700724,    0.7504883828755602,    0.4004672082940195),
693             new Grad4( 0.4004672082940195,   -0.5029860367700724,    0.7504883828755602,   -0.15296486218853164),
694             new Grad4( 0.3239847771997537,   -0.5794684678643381,    0.6740059517812944,    0.3239847771997537),
695             new Grad4(-0.3239847771997537,   -0.3239847771997537,    0.5794684678643381,   -0.6740059517812944),
696             new Grad4(-0.4004672082940195,    0.15296486218853164,   0.5029860367700724,   -0.7504883828755602),
697             new Grad4( 0.15296486218853164,  -0.4004672082940195,    0.5029860367700724,   -0.7504883828755602),
698             new Grad4( 0.08164729285680945,   0.08164729285680945,   0.4553054119602712,   -0.8828161875373585),
699             new Grad4(-0.08164729285680945,  -0.08164729285680945,   0.8828161875373585,   -0.4553054119602712),
700             new Grad4(-0.15296486218853164,   0.4004672082940195,    0.7504883828755602,   -0.5029860367700724),
701             new Grad4( 0.4004672082940195,   -0.15296486218853164,   0.7504883828755602,   -0.5029860367700724),
702             new Grad4( 0.3239847771997537,    0.3239847771997537,    0.6740059517812944,   -0.5794684678643381),
703             new Grad4(-0.6740059517812944,    0.5794684678643381,   -0.3239847771997537,   -0.3239847771997537),
704             new Grad4(-0.7504883828755602,    0.5029860367700724,   -0.4004672082940195,    0.15296486218853164),
705             new Grad4(-0.7504883828755602,    0.5029860367700724,    0.15296486218853164,  -0.4004672082940195),
706             new Grad4(-0.8828161875373585,    0.4553054119602712,    0.08164729285680945,   0.08164729285680945),
707             new Grad4(-0.4553054119602712,    0.8828161875373585,   -0.08164729285680945,  -0.08164729285680945),
708             new Grad4(-0.5029860367700724,    0.7504883828755602,   -0.15296486218853164,   0.4004672082940195),
709             new Grad4(-0.5029860367700724,    0.7504883828755602,    0.4004672082940195,   -0.15296486218853164),
710             new Grad4(-0.5794684678643381,    0.6740059517812944,    0.3239847771997537,    0.3239847771997537),
711             new Grad4(-0.3239847771997537,    0.5794684678643381,   -0.6740059517812944,   -0.3239847771997537),
712             new Grad4(-0.4004672082940195,    0.5029860367700724,   -0.7504883828755602,    0.15296486218853164),
713             new Grad4( 0.15296486218853164,   0.5029860367700724,   -0.7504883828755602,   -0.4004672082940195),
714             new Grad4( 0.08164729285680945,   0.4553054119602712,   -0.8828161875373585,    0.08164729285680945),
715             new Grad4(-0.08164729285680945,   0.8828161875373585,   -0.4553054119602712,   -0.08164729285680945),
716             new Grad4(-0.15296486218853164,   0.7504883828755602,   -0.5029860367700724,    0.4004672082940195),
717             new Grad4( 0.4004672082940195,    0.7504883828755602,   -0.5029860367700724,   -0.15296486218853164),
718             new Grad4( 0.3239847771997537,    0.6740059517812944,   -0.5794684678643381,    0.3239847771997537),
719             new Grad4(-0.3239847771997537,    0.5794684678643381,   -0.3239847771997537,   -0.6740059517812944),
720             new Grad4(-0.4004672082940195,    0.5029860367700724,    0.15296486218853164,  -0.7504883828755602),
721             new Grad4( 0.15296486218853164,   0.5029860367700724,   -0.4004672082940195,   -0.7504883828755602),
722             new Grad4( 0.08164729285680945,   0.4553054119602712,    0.08164729285680945,  -0.8828161875373585),
723             new Grad4(-0.08164729285680945,   0.8828161875373585,   -0.08164729285680945,  -0.4553054119602712),
724             new Grad4(-0.15296486218853164,   0.7504883828755602,    0.4004672082940195,   -0.5029860367700724),
725             new Grad4( 0.4004672082940195,    0.7504883828755602,   -0.15296486218853164,  -0.5029860367700724),
726             new Grad4( 0.3239847771997537,    0.6740059517812944,    0.3239847771997537,   -0.5794684678643381),
727             new Grad4( 0.5794684678643381,   -0.6740059517812944,   -0.3239847771997537,   -0.3239847771997537),
728             new Grad4( 0.5029860367700724,   -0.7504883828755602,   -0.4004672082940195,    0.15296486218853164),
729             new Grad4( 0.5029860367700724,   -0.7504883828755602,    0.15296486218853164,  -0.4004672082940195),
730             new Grad4( 0.4553054119602712,   -0.8828161875373585,    0.08164729285680945,   0.08164729285680945),
731             new Grad4( 0.8828161875373585,   -0.4553054119602712,   -0.08164729285680945,  -0.08164729285680945),
732             new Grad4( 0.7504883828755602,   -0.5029860367700724,   -0.15296486218853164,   0.4004672082940195),
733             new Grad4( 0.7504883828755602,   -0.5029860367700724,    0.4004672082940195,   -0.15296486218853164),
734             new Grad4( 0.6740059517812944,   -0.5794684678643381,    0.3239847771997537,    0.3239847771997537),
735             new Grad4( 0.5794684678643381,   -0.3239847771997537,   -0.6740059517812944,   -0.3239847771997537),
736             new Grad4( 0.5029860367700724,   -0.4004672082940195,   -0.7504883828755602,    0.15296486218853164),
737             new Grad4( 0.5029860367700724,    0.15296486218853164,  -0.7504883828755602,   -0.4004672082940195),
738             new Grad4( 0.4553054119602712,    0.08164729285680945,  -0.8828161875373585,    0.08164729285680945),
739             new Grad4( 0.8828161875373585,   -0.08164729285680945,  -0.4553054119602712,   -0.08164729285680945),
740             new Grad4( 0.7504883828755602,   -0.15296486218853164,  -0.5029860367700724,    0.4004672082940195),
741             new Grad4( 0.7504883828755602,    0.4004672082940195,   -0.5029860367700724,   -0.15296486218853164),
742             new Grad4( 0.6740059517812944,    0.3239847771997537,   -0.5794684678643381,    0.3239847771997537),
743             new Grad4( 0.5794684678643381,   -0.3239847771997537,   -0.3239847771997537,   -0.6740059517812944),
744             new Grad4( 0.5029860367700724,   -0.4004672082940195,    0.15296486218853164,  -0.7504883828755602),
745             new Grad4( 0.5029860367700724,    0.15296486218853164,  -0.4004672082940195,   -0.7504883828755602),
746             new Grad4( 0.4553054119602712,    0.08164729285680945,   0.08164729285680945,  -0.8828161875373585),
747             new Grad4( 0.8828161875373585,   -0.08164729285680945,  -0.08164729285680945,  -0.4553054119602712),
748             new Grad4( 0.7504883828755602,   -0.15296486218853164,   0.4004672082940195,   -0.5029860367700724),
749             new Grad4( 0.7504883828755602,    0.4004672082940195,   -0.15296486218853164,  -0.5029860367700724),
750             new Grad4( 0.6740059517812944,    0.3239847771997537,    0.3239847771997537,   -0.5794684678643381),
751             new Grad4( 0.03381941603233842,   0.03381941603233842,   0.03381941603233842,   0.9982828964265062),
752             new Grad4(-0.044802370851755174, -0.044802370851755174,  0.508629699630796,     0.8586508742123365),
753             new Grad4(-0.044802370851755174,  0.508629699630796,    -0.044802370851755174,  0.8586508742123365),
754             new Grad4(-0.12128480194602098,   0.4321472685365301,    0.4321472685365301,    0.7821684431180708),
755             new Grad4( 0.508629699630796,    -0.044802370851755174, -0.044802370851755174,  0.8586508742123365),
756             new Grad4( 0.4321472685365301,   -0.12128480194602098,   0.4321472685365301,    0.7821684431180708),
757             new Grad4( 0.4321472685365301,    0.4321472685365301,   -0.12128480194602098,   0.7821684431180708),
758             new Grad4( 0.37968289875261624,   0.37968289875261624,   0.37968289875261624,   0.753341017856078),
759             new Grad4( 0.03381941603233842,   0.03381941603233842,   0.9982828964265062,    0.03381941603233842),
760             new Grad4(-0.044802370851755174,  0.044802370851755174,  0.8586508742123365,    0.508629699630796),
761             new Grad4(-0.044802370851755174,  0.508629699630796,     0.8586508742123365,   -0.044802370851755174),
762             new Grad4(-0.12128480194602098,   0.4321472685365301,    0.7821684431180708,    0.4321472685365301),
763             new Grad4( 0.508629699630796,    -0.044802370851755174,  0.8586508742123365,   -0.044802370851755174),
764             new Grad4( 0.4321472685365301,   -0.12128480194602098,   0.7821684431180708,    0.4321472685365301),
765             new Grad4( 0.4321472685365301,    0.4321472685365301,    0.7821684431180708,   -0.12128480194602098),
766             new Grad4( 0.37968289875261624,   0.37968289875261624,   0.753341017856078,     0.37968289875261624),
767             new Grad4( 0.03381941603233842,   0.9982828964265062,    0.03381941603233842,   0.03381941603233842),
768             new Grad4(-0.044802370851755174,  0.8586508742123365,   -0.044802370851755174,  0.508629699630796),
769             new Grad4(-0.044802370851755174,  0.8586508742123365,    0.508629699630796,    -0.044802370851755174),
770             new Grad4(-0.12128480194602098,   0.7821684431180708,    0.4321472685365301,    0.4321472685365301),
771             new Grad4( 0.508629699630796,     0.8586508742123365,   -0.044802370851755174, -0.044802370851755174),
772             new Grad4( 0.4321472685365301,    0.7821684431180708,   -0.12128480194602098,   0.4321472685365301),
773             new Grad4( 0.4321472685365301,    0.7821684431180708,    0.4321472685365301,   -0.12128480194602098),
774             new Grad4( 0.37968289875261624,   0.753341017856078,     0.37968289875261624,   0.37968289875261624),
775             new Grad4( 0.9982828964265062,    0.03381941603233842,   0.03381941603233842,   0.03381941603233842),
776             new Grad4( 0.8586508742123365,   -0.044802370851755174, -0.044802370851755174,  0.508629699630796),
777             new Grad4( 0.8586508742123365,   -0.044802370851755174,  0.508629699630796,    -0.044802370851755174),
778             new Grad4( 0.7821684431180708,   -0.12128480194602098,   0.4321472685365301,    0.4321472685365301),
779             new Grad4( 0.8586508742123365,    0.508629699630796,    -0.044802370851755174, -0.044802370851755174),
780             new Grad4( 0.7821684431180708,    0.4321472685365301,   -0.12128480194602098,   0.4321472685365301),
781             new Grad4( 0.7821684431180708,    0.4321472685365301,    0.4321472685365301,   -0.12128480194602098),
782             new Grad4( 0.753341017856078,     0.37968289875261624,   0.37968289875261624,   0.37968289875261624)
783         ];
784         for (int i = 0; i < grad4.length; i++) {
785             grad4[i].dx /= N4; grad4[i].dy /= N4; grad4[i].dz /= N4; grad4[i].dw /= N4;
786         }
787         for (int i = 0; i < PSIZE; i++) {
788             GRADIENTS_4D[i] = grad4[i % grad4.length];
789         }
790     }
791 }
792 
793 @("OpenSimplex2F 2D")
794 unittest
795 {
796     import imageformats : IFImage, ColFmt, write_png;
797     import std.conv : to;
798 
799     OpenSimplex2F openSimplex2f = new OpenSimplex2F(3_874_6527_389_465L);
800 
801     enum WIDTH = 512;
802     enum HEIGHT = 512;
803     enum SCALE = 64;
804 
805     ubyte[WIDTH * HEIGHT] pixels;
806 
807     foreach (size_t i, ref ubyte pixel; pixels)
808     {
809         immutable size_t x = i % WIDTH;
810         immutable size_t y = i / HEIGHT;
811         pixel = ((openSimplex2f.noise2(x.to!double / SCALE, y.to!double / SCALE) + 1) * 127).to!ubyte;
812     }
813 
814 
815     IFImage img = IFImage(WIDTH, HEIGHT, ColFmt.Y, pixels);
816     write_png("./test-results/OpenSimplex2F 2D.png", WIDTH, HEIGHT, img.pixels);
817 }
818 
819 @("OpenSimplex2F 3D slice")
820 unittest
821 {
822     import imageformats : IFImage, ColFmt, write_png;
823     import std.conv : to;
824 
825     OpenSimplex2F openSimplex2f = new OpenSimplex2F(3_874_6527_389_465L);
826 
827     enum WIDTH = 512;
828     enum HEIGHT = 512;
829     enum SCALE = 64;
830 
831     ubyte[WIDTH * HEIGHT] pixels;
832 
833     foreach (size_t i, ref ubyte pixel; pixels)
834     {
835         immutable size_t x = i % WIDTH;
836         immutable size_t y = i / HEIGHT;
837         pixel = ((openSimplex2f.noise3_XYBeforeZ(x.to!double / SCALE, y.to!double / SCALE, 0) + 1) * 127).to!ubyte;
838     }
839 
840 
841     IFImage img = IFImage(WIDTH, HEIGHT, ColFmt.Y, pixels);
842     write_png("./test-results/OpenSimplex2F 3D XYbeforeZ.png", WIDTH, HEIGHT, img.pixels);
843 }
844 
845 @("OpenSimplex2F 4D slice")
846 unittest
847 {
848     import imageformats : IFImage, ColFmt, write_png;
849     import std.conv : to;
850 
851     OpenSimplex2F openSimplex2f = new OpenSimplex2F(3_874_6527_389_465L);
852 
853     enum WIDTH = 512;
854     enum HEIGHT = 512;
855     enum SCALE = 64;
856 
857     ubyte[WIDTH * HEIGHT] pixels;
858 
859     foreach (size_t i, ref ubyte pixel; pixels)
860     {
861         immutable size_t x = i % WIDTH;
862         immutable size_t y = i / HEIGHT;
863         pixel = ((openSimplex2f.noise4_Classic(x.to!double / SCALE, y.to!double / SCALE, 0, 0) + 1) * 127).to!ubyte;
864     }
865 
866 
867     IFImage img = IFImage(WIDTH, HEIGHT, ColFmt.Y, pixels);
868     write_png("./test-results/OpenSimplex2F 4D.png", WIDTH, HEIGHT, img.pixels);
869 }
870 
871 @("OpenSimplex2F 2D speed test")
872 unittest
873 {
874     // For best results, run unittests compiled with ldc2 and compiler flags "-optimize" and "-inline"
875     import std.conv : to;
876     import std.datetime.stopwatch : StopWatch, Duration;
877     import std.stdio : writeln;
878 
879     OpenSimplex2F openSimplex2f = new OpenSimplex2F(3_874_6527_389_465L);
880 
881 
882     StopWatch stopwatch;
883     stopwatch.start();
884     foreach (i; 0..1_000_000)
885     {
886         openSimplex2f.noise2(i.to!double, (999_999 - i).to!double);
887     }
888     writeln(stopwatch.peek().split!"nsecs".nsecs / 1_000_000.0);
889 }