const N_GRADS_3D_EXPONENT = 8
const N_GRADS_3D = 1 << N_GRADS_3D_EXPONENT

const NORMALIZER_3D = 0.2781926117527186

struct Grad3
    x::Float32
    y::Float32
    z::Float32
end

const grad3 = Grad3[
    Grad3( 2.22474487139,       2.22474487139,      -1.0),
    Grad3( 2.22474487139,       2.22474487139,       1.0),
    Grad3( 3.0862664687972017,  1.1721513422464978,  0.0),
    Grad3( 1.1721513422464978,  3.0862664687972017,  0.0),
    Grad3(-2.22474487139,       2.22474487139,      -1.0),
    Grad3(-2.22474487139,       2.22474487139,       1.0),
    Grad3(-1.1721513422464978,  3.0862664687972017,  0.0),
    Grad3(-3.0862664687972017,  1.1721513422464978,  0.0),
    Grad3(-1.0,                -2.22474487139,      -2.22474487139),
    Grad3( 1.0,                -2.22474487139,      -2.22474487139),
    Grad3( 0.0,                -3.0862664687972017, -1.1721513422464978),
    Grad3( 0.0,                -1.1721513422464978, -3.0862664687972017),
    Grad3(-1.0,                -2.22474487139,       2.22474487139),
    Grad3( 1.0,                -2.22474487139,       2.22474487139),
    Grad3( 0.0,                -1.1721513422464978,  3.0862664687972017),
    Grad3( 0.0,                -3.0862664687972017,  1.1721513422464978),
    #-------------------------------------------------------------------#
    Grad3(-2.22474487139,      -2.22474487139,      -1.0),
    Grad3(-2.22474487139,      -2.22474487139,       1.0),
    Grad3(-3.0862664687972017, -1.1721513422464978,  0.0),
    Grad3(-1.1721513422464978, -3.0862664687972017,  0.0),
    Grad3(-2.22474487139,      -1.0,                -2.22474487139),
    Grad3(-2.22474487139,       1.0,                -2.22474487139),
    Grad3(-1.1721513422464978,  0.0,                -3.0862664687972017),
    Grad3(-3.0862664687972017,  0.0,                -1.1721513422464978),
    Grad3(-2.22474487139,      -1.0,                 2.22474487139),
    Grad3(-2.22474487139,       1.0,                 2.22474487139),
    Grad3(-3.0862664687972017,  0.0,                 1.1721513422464978),
    Grad3(-1.1721513422464978,  0.0,                 3.0862664687972017),
    Grad3(-1.0,                 2.22474487139,      -2.22474487139),
    Grad3( 1.0,                 2.22474487139,      -2.22474487139),
    Grad3( 0.0,                 1.1721513422464978, -3.0862664687972017),
    Grad3( 0.0,                 3.0862664687972017, -1.1721513422464978),
    Grad3(-1.0,                 2.22474487139,       2.22474487139),
    Grad3( 1.0,                 2.22474487139,       2.22474487139),
    Grad3( 0.0,                 3.0862664687972017,  1.1721513422464978),
    Grad3( 0.0,                 1.1721513422464978,  3.0862664687972017),
    Grad3( 2.22474487139,      -2.22474487139,      -1.0),
    Grad3( 2.22474487139,      -2.22474487139,       1.0),
    Grad3( 1.1721513422464978, -3.0862664687972017,  0.0),
    Grad3( 3.0862664687972017, -1.1721513422464978,  0.0),
    Grad3( 2.22474487139,      -1.0,                -2.22474487139),
    Grad3( 2.22474487139,       1.0,                -2.22474487139),
    Grad3( 3.0862664687972017,  0.0,                -1.1721513422464978),
    Grad3( 1.1721513422464978,  0.0,                -3.0862664687972017),
    Grad3( 2.22474487139,      -1.0,                 2.22474487139),
    Grad3( 2.22474487139,       1.0,                 2.22474487139),
    Grad3( 1.1721513422464978,  0.0,                 3.0862664687972017),
    Grad3( 3.0862664687972017,  0.0,                 1.1721513422464978),
]

const GRADIENTS_3D = Vector{Grad3}(undef, N_GRADS_3D)

function grad(a, seed, xrvp, yrvp, zrvp, dx, dy, dz)
    hash = xor(seed, xrvp, yrvp, zrvp) * HASH_MULTIPLIER
    g = GRADIENTS_3D[(xor(hash, hash >> (64 - N_GRADS_3D_EXPONENT)) & (N_GRADS_3D - 1)) + 1]
    Float32(p4(a) * (g.x * dx + g.y * dy + g.z * dz) / NORMALIZER_3D)
end

function fill_3d()
    j = 0
    len = length(grad3)
    for i = 1:N_GRADS_3D
        GRADIENTS_3D[i] = grad3[j += 1]
        j == len && (j = 0)
    end
end

fill_3d()
