// instead of adding another permutation array, just use hash table defined above
 public static float newPerlin(float x, float y, float z) {
   int A, AA, AB, B, BA, BB;
   float floorX = (float) Math.floor(x),
       floorY = (float) Math.floor(y),
       floorZ = (float) Math.floor(z);
   int intX = (int) floorX & 0xFF, intY = (int) floorY & 0xFF, intZ = (int) floorZ & 0xFF;
   x -= floorX;
   y -= floorY;
   z -= floorZ;
   // computing fading curves
   floorX = NoiseMath.npfade(x);
   floorY = NoiseMath.npfade(y);
   floorZ = NoiseMath.npfade(z);
   A = hash[intX] + intY;
   AA = hash[A] + intZ;
   AB = hash[A + 1] + intZ;
   B = hash[intX + 1] + intY;
   BA = hash[B] + intZ;
   BB = hash[B + 1] + intZ;
   return NoiseMath.lerp(
       floorZ,
       NoiseMath.lerp(
           floorY,
           NoiseMath.lerp(
               floorX, NoiseMath.grad(hash[AA], x, y, z), NoiseMath.grad(hash[BA], x - 1, y, z)),
           NoiseMath.lerp(
               floorX,
               NoiseMath.grad(hash[AB], x, y - 1, z),
               NoiseMath.grad(hash[BB], x - 1, y - 1, z))),
       NoiseMath.lerp(
           floorY,
           NoiseMath.lerp(
               floorX,
               NoiseMath.grad(hash[AA + 1], x, y, z - 1),
               NoiseMath.grad(hash[BA + 1], x - 1, y, z - 1)),
           NoiseMath.lerp(
               floorX,
               NoiseMath.grad(hash[AB + 1], x, y - 1, z - 1),
               NoiseMath.grad(hash[BB + 1], x - 1, y - 1, z - 1))));
 }
    public static float noise3Perlin(float x, float y, float z) {
      float t = x + 10000.0f;
      int bx0 = (int) t & 0xFF;
      int bx1 = bx0 + 1 & 0xFF;
      float rx0 = t - (int) t;
      float rx1 = rx0 - 1.0f;

      t = y + 10000.0f;
      int by0 = (int) t & 0xFF;
      int by1 = by0 + 1 & 0xFF;
      float ry0 = t - (int) t;
      float ry1 = ry0 - 1.0f;

      t = z + 10000.0f;
      int bz0 = (int) t & 0xFF;
      int bz1 = bz0 + 1 & 0xFF;
      float rz0 = t - (int) t;
      float rz1 = rz0 - 1.0f;

      int i = p[bx0];
      int j = p[bx1];

      int b00 = p[i + by0];
      int b10 = p[j + by0];
      int b01 = p[i + by1];
      int b11 = p[j + by1];

      float sx = NoiseMath.surve(rx0);
      float sy = NoiseMath.surve(ry0);
      float sz = NoiseMath.surve(rz0);

      float[] q = g[b00 + bz0];
      float u = NoiseMath.at(rx0, ry0, rz0, q);
      q = g[b10 + bz0];
      float v = NoiseMath.at(rx1, ry0, rz0, q);
      float a = NoiseMath.lerp(sx, u, v);

      q = g[b01 + bz0];
      u = NoiseMath.at(rx0, ry1, rz0, q);
      q = g[b11 + bz0];
      v = NoiseMath.at(rx1, ry1, rz0, q);
      float b = NoiseMath.lerp(sx, u, v);

      float c = NoiseMath.lerp(sy, a, b);

      q = g[b00 + bz1];
      u = NoiseMath.at(rx0, ry0, rz1, q);
      q = g[b10 + bz1];
      v = NoiseMath.at(rx1, ry0, rz1, q);
      a = NoiseMath.lerp(sx, u, v);

      q = g[b01 + bz1];
      u = NoiseMath.at(rx0, ry1, rz1, q);
      q = g[b11 + bz1];
      v = NoiseMath.at(rx1, ry1, rz1, q);
      b = NoiseMath.lerp(sx, u, v);

      float d = NoiseMath.lerp(sy, a, b);
      return 1.5f * NoiseMath.lerp(sz, c, d);
    }
    /**
     * Not 'pure' Worley, but the results are virtually the same. Returns distances in da and point
     * coords in pa
     */
    public static void voronoi(
        float x,
        float y,
        float z,
        float[] da,
        float[] pa,
        float distanceExponent,
        int distanceType) {
      float xd, yd, zd, d, p[] = new float[3];

      DistanceFunction distanceFunc = distanceFunctions.get(Integer.valueOf(distanceType));
      if (distanceFunc == null) {
        distanceFunc = distanceFunctions.get(Integer.valueOf(0));
      }

      int xi = (int) FastMath.floor(x);
      int yi = (int) FastMath.floor(y);
      int zi = (int) FastMath.floor(z);
      da[0] = da[1] = da[2] = da[3] = Float.MAX_VALUE; // 1e10f;
      for (int i = xi - 1; i <= xi + 1; ++i) {
        for (int j = yi - 1; j <= yi + 1; ++j) {
          for (int k = zi - 1; k <= zi + 1; ++k) {
            NoiseMath.hash(i, j, k, p);
            xd = x - (p[0] + i);
            yd = y - (p[1] + j);
            zd = z - (p[2] + k);
            d = distanceFunc.execute(xd, yd, zd, distanceExponent);
            if (d < da[0]) {
              da[3] = da[2];
              da[2] = da[1];
              da[1] = da[0];
              da[0] = d;
              pa[9] = pa[6];
              pa[10] = pa[7];
              pa[11] = pa[8];
              pa[6] = pa[3];
              pa[7] = pa[4];
              pa[8] = pa[5];
              pa[3] = pa[0];
              pa[4] = pa[1];
              pa[5] = pa[2];
              pa[0] = p[0] + i;
              pa[1] = p[1] + j;
              pa[2] = p[2] + k;
            } else if (d < da[1]) {
              da[3] = da[2];
              da[2] = da[1];
              da[1] = d;
              pa[9] = pa[6];
              pa[10] = pa[7];
              pa[11] = pa[8];
              pa[6] = pa[3];
              pa[7] = pa[4];
              pa[8] = pa[5];
              pa[3] = p[0] + i;
              pa[4] = p[1] + j;
              pa[5] = p[2] + k;
            } else if (d < da[2]) {
              da[3] = da[2];
              da[2] = d;
              pa[9] = pa[6];
              pa[10] = pa[7];
              pa[11] = pa[8];
              pa[6] = p[0] + i;
              pa[7] = p[1] + j;
              pa[8] = p[2] + k;
            } else if (d < da[3]) {
              da[3] = d;
              pa[9] = p[0] + i;
              pa[10] = p[1] + j;
              pa[11] = p[2] + k;
            }
          }
        }
      }
    }