I made an attempt, written mostly by me:
if (!std::strncmp(ss, "hash(", 5)) { // hash(value_or_vector [,seed1 [,seed2]])
_cimg_mp_op("Function 'hash()'");
s0 = ss + 5;
s1 = s0;
// First argument: the value or vector to hash.
s1 = s0; while (s1 < se1 && (*s1 != ',' || level[s1 - expr._data] != clevel1)) ++s1;
arg2 = compile(s0, s1, depth1, 0, block_flags);
// Optional trailing seed1, seed2 (both scalars, both default to 0 if omitted).
arg3 = ~0U; arg4 = ~0U;
if (s1 < se1) {
s0 = ++s1; while (s0 < se1 && (*s0 != ',' || level[s0 - expr._data] != clevel1)) ++s0;
arg3 = compile(s1, s0, depth1, 0, block_flags);
_cimg_mp_check_type(arg3, 2, 1, 0);
if (s0 < se1) {
s1 = ++s0; while (s1 < se1 && (*s1 != ',' || level[s1 - expr._data] != clevel1)) ++s1;
arg4 = compile(s0, s1, depth1, 0, block_flags);
_cimg_mp_check_type(arg4, 3, 1, 0);
}
}
const unsigned int seed1 = arg3 == ~0U ? const_scalar(0) : arg3;
const unsigned int seed2 = arg4 == ~0U ? const_scalar(0) : arg4;
op = mp_hash;
bool arg2_const = is_const_scalar(arg2);
if (is_vector(arg2)) {
arg2_const = true;
for (unsigned int i = 1; i <= size(arg2) && arg2_const; ++i) arg2_const &= is_const_scalar(arg2 + i);
}
is_sth = is_const_scalar(seed1) && is_const_scalar(seed2) && arg2_const;
CImg<ulongT>::vector((ulongT)op, 0, 0).move_to(l_opcode);
CImg<ulongT>::vector(seed1, 1).move_to(l_opcode);
CImg<ulongT>::vector(seed2, 1).move_to(l_opcode);
if (is_vector(arg2)) CImg<ulongT>::vector(arg2 + 1, size(arg2)).move_to(l_opcode);
else CImg<ulongT>::vector(arg2, 1).move_to(l_opcode);
(l_opcode > 'y').move_to(opcode);
opcode[2] = opcode._height;
if (is_sth) _cimg_mp_const_scalar(mp_hash(*this));
pos = opcode[1] = scalar();
opcode.move_to(code);
return_comp = true;
_cimg_mp_return(pos);
}
static double mp_hash(_cimg_math_parser& mp) {
float f;
unsigned int seed1_bits, seed2_bits, h, mult, bits;
f = (float)_mp_arg(3); std::memcpy(&seed1_bits, &f, sizeof(seed1_bits)); // seed1 reg = opcode[3]
f = (float)_mp_arg(5); std::memcpy(&seed2_bits, &f, sizeof(seed2_bits)); // seed2 reg = opcode[5]
mult = seed2_bits | 1U;
h = 2166136261U ^ seed1_bits;
const unsigned int data_reg = (unsigned int)mp.opcode[7]; // data reg = opcode[7]
const unsigned int len = (unsigned int)mp.opcode[8]; // data len = opcode[8]
const double* ptr = &mp.mem[data_reg];
for (unsigned int k = 0; k < len; ++k) {
f = (float)*(ptr++);
std::memcpy(&bits, &f, sizeof(bits));
h = h * mult + bits;
}
// Avalanche finalizer (Murmur3 fmix32) — without this, low-order bits of a plain
// h*mult+v accumulator mix weakly and nearby inputs produce visibly patterned output.
h ^= h >> 16; h *= 0x85ebca6bU;
h ^= h >> 13; h *= 0xc2b2ae35U;
h ^= h >> 16;
return (double)(h & 0xFFFFFFU);
}
Should work ok. Has seed behavior too.
$ gmic.exe 4096,4096,1,1,hash([eps*x,eps*y]
$ gmic.exe 4096,4096,1,1,hash([92135151,x,y])
Small vs large value test:
D:\Documents\G'MIC\G'MIC Internal Development\gmic\src>gmic.exe 4096,4096,1,1,hash([eps*x,eps*y])
[gmic]./ Start G'MIC interpreter (v.4.0.5).
[gmic]./ Input image at position 0, with values 'hash([eps*x,eps*y])' (1 image 4096x4096x1x1).
[gmic]./ Display image [0] = '[hash([eps*x,eps*y])]'.
[0] = '[hash([eps*x,eps*y])]':
size = (4096,4096,1,1) [64 Mio of float32].
data = (4.09499e+06,1.60432e+07,1.51824e+07,2.95465e+06,1.16393e+07,5.00252e+06,8.49319e+06,1.06195e+07,2.52217e+06,2.61584e+06,1.0438e+07,5.75237e+06,1.1929e+06,1.54534e+07,1.35455e+07,3.25785e+06,3.91982e+06,6.54143e+06,1.47922e+07,1.46609e+07,4.02184e+06,8.41322e+06,6.6496e+06,8.02718e+06,1.36874e+06,2.30894e+06,7.38198e+06,7.72405e+06,1.09481e+07,1.94225e+06,1.54826e+07,1.37757e+07,1.32467e+07,1.49449e+07,1.07946e+07,1.00947e+07,7.55972e+06,9.48159e+06,1.02216e+07,4.00174e+06,6.31496e+06,2.95909e+06,1.2798e+07,1.78003e+06,1.38749e+07,1.53777e+07,1.34052e+07,1.27781e+07,1.64428e+07,1.23552e+07,6.1143e+06,431417,1.00968e+07,1.47747e+07,1.31147e+07,1.2339e+07,4.78667e+06,8.95814e+06,762617,1.6602e+07,1.08086e+06,1.22179e+07,783338,5.61549e+06, ... ,5.94056e+06,1.62223e+07,1.11407e+07,1.13604e+07,6.46636e+06,7.53074e+06,3.43968e+06,8.6559e+06,165957,3.07302e+06,489649,2.68745e+06,2.25226e+06,4.13293e+06,4.24438e+06,1.10405e+07,9.41238e+06,7.54227e+06,7.05337e+06,1.11182e+07,1.28977e+07,1.38468e+07,1.35066e+07,1.87717e+06,1.58093e+07,907429,3.21542e+06,1.3864e+07,8.96652e+06,1.60246e+07,1.65485e+06,1.41532e+07,6.11074e+06,1.11074e+07,5.18544e+06,7.63697e+06,4.42407e+06,1.25859e+07,1.14735e+06,4.76586e+06,8.91158e+06,1.09285e+07,9.42094e+06,3.11041e+06,9.74044e+06,1.55776e+07,1.95126e+06,3.26605e+06,1.09278e+07,1.3382e+07,5.70695e+06,6.0107e+06,1.64931e+07,5.73114e+06,1.35473e+07,9.62856e+06,1.23843e+07,6.49081e+06,1.05573e+07,2.50295e+06,7.19162e+06,3.02588e+06,1.50553e+07,1.52121e+07).
min = 738, max = 1.6777e+07, mean = 8.41447e+06, std = 4.82849e+06, norm = 3.9737e+10, coords_min = (4092,1009,0,0), coords_max = (3534,2,0,0).
[gmic]./ End G'MIC interpreter.
D:\Documents\G'MIC\G'MIC Internal Development\gmic\src>gmic.exe 4096,4096,1,1,hash([92135151,x,y])
[gmic]./ Start G'MIC interpreter (v.4.0.5).
[gmic]./ Input image at position 0, with values 'hash([92135151,x,y])' (1 image 4096x4096x1x1).
[gmic]./ Display image [0] = '[hash([92135151,x,y])]'.
[0] = '[hash([92135151,x,y])]':
size = (4096,4096,1,1) [64 Mio of float32].
data = (5.44532e+06,1.44572e+07,1.51749e+07,7.56764e+06,7.98647e+06,1.44838e+06,1.38434e+07,1.07417e+06,5.94309e+06,1.49892e+07,1.18383e+07,1.0348e+07,1.53419e+07,1.10116e+07,1.61617e+07,1.37998e+07,1.1394e+06,1.4757e+07,4.65215e+06,2.86406e+06,485010,5.5922e+06,6.77214e+06,1.02599e+07,7.93379e+06,1.5722e+07,7.46993e+06,7.53287e+06,1.00425e+07,1.21181e+07,2.40427e+06,1.61299e+07,1.28991e+07,1.45883e+07,5.68304e+06,1.50878e+07,1.64169e+07,1.25357e+07,4.66865e+06,3.35736e+06,1.93915e+06,1.24016e+07,1.57055e+07,1.31652e+07,8.57468e+06,2.7797e+06,8.95504e+06,1.32761e+07,6.82782e+06,9.56612e+06,532564,7.36517e+06,5.59048e+06,8.4063e+06,2.83077e+06,4.85318e+06,6.89528e+06,5.30729e+06,290470,3.56891e+06,1.03719e+07,1.0475e+07,1.65226e+06,6.62358e+06, ... ,4.53586e+06,2.96522e+06,2.18587e+06,1.10944e+07,1.5077e+06,5.8309e+06,9.64685e+06,1.04513e+07,8.99307e+06,146139,9.96701e+06,7.29188e+06,1.67383e+07,1.36589e+07,9.41794e+06,1.01919e+07,1.14e+07,6.03066e+06,1.00884e+07,1.18583e+07,1.15981e+07,2.91644e+06,4.70278e+06,2.69347e+06,483166,4.73586e+06,5.98207e+06,1.12482e+07,1.03772e+07,9.97316e+06,1.36502e+07,1.34914e+06,3.50423e+06,5.72276e+06,9.04926e+06,1.47513e+07,1.36552e+07,6.87844e+06,7.90739e+06,4.94088e+06,3.5542e+06,870177,5.75908e+06,5.6115e+06,8.71309e+06,1.04368e+07,2.92896e+06,1.13838e+07,5.70354e+06,3.9704e+06,4.61427e+06,1.0789e+07,5.0489e+06,7.26916e+06,1.5427e+07,3.09303e+06,8.12245e+06,7.1799e+06,1.04703e+06,527396,5.05638e+06,2.64051e+06,4.6396e+06,3.57632e+06).
min = 71, max = 1.6777e+07, mean = 8.4255e+06, std = 4.84841e+06, norm = 3.98169e+10, coords_min = (4033,8,0,0), coords_max = (4079,113,0,0).
[gmic]./ End G'MIC interpreter.
Stats differ a bit, but still uniform hashing.