Unsigned arithmetric functions? Direct hash function?

For the past weeks, I have been working on things dealing with hashing colors, and I saw a few issue with them. The following issue I experienced:

  1. If values are too large, then the value of hash would repeat.
  2. If values are too small, then the value of hash would repeat.

So, I had to modify my macro to account for the second case until today.

But, what if we can have these?

c2int(f32_value)* casts float32 values as a int representation of its binary.
*Double are treated as f32 here, casted to unsigned int 
unsigned_mul(a,b,...) performs unsigned int multiplication and returns value.

Or just a hash function that does combines both. I think a direct hash function for math evaluator would be slightly faster. Also, I can see myself using direct hash function even outside colormap-like tools. I think they would need seed for it to work. _rep_colors_inform_r use two different hash values (primary, and merge hash).

For this:

#@cli rep_encode_str: string
#@cli : Encode string.
#@cli : Note: Every commas are eliminated.
rep_encode_str:
('{'$*'}') discard. 44 u {t} rm.

I wouldn’t even need to do this trick. And let the hash() does the encode.

There’s also the srand();v(large_value) trick too, but only works for scalar.

What I understand from your post is you have a desire to handle large integers in the math parser, eventually with some coding tricks.
But it’s not possible : every value or variable managed by the math parser is a double, so it will have the limitations described in the page : IEEE 754 - Wikipedia
whatever trick we are trying to apply.

Suppose you are able to store a large int64 into a double memory slot. OK, but then ?
There will be no way you can do something with it, even basic arithmetic operations like additions or multiplications.

The only way would be to be able to have both double and int64 types managed by the math parser, but this would be another story: having typed variables would basically require a major rewritting of the math parser code.

I’m afraid we have to deal with the limitations inherent to the use of double values.

More on to the point, a way to hash vectors without dealing with double limitations. I was thinking of modifying CImg.h and try my hand at doing that, and simply calling hash(vector,seed) or hash(vector,seed_a,seed_b) if there’s two consts within hash(). Right now, if I multiply by large value, only even buckets get filled, otherwise, small values like multiple of epsilons, might not even have any other buckets other than first one filled.

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.