The GIMP API used by G’MIC-QT-GIMP 3.2.0_pre :o)
I’m quite puzzled by a strange bug. I think it could be to do with the “dynamic” fill, perhaps threading not being disabled. This command used to work normally (shuffles vectors), but now does not:
gcd_shuffle : f "<begin(P=whd-2^-14);swap(I[int(u(P--))],I)"
Any clues?
Version 3.2.0 (pre-release #221123) (jammy)
Yes, in 3.2.0, most functions of the math parser that are not intended to return an obvious value, jut returns nan. This is the case, e.g., for draw(), copy(), … and swap().
So,
f "<begin(P=whd-2^-14);swap(I[int(u(P--))],I);I"
should fix your problem.
The reason for that is twofold:
- It was not always simple to remember what the corresponding returned value was. For instance, in
swap(), which value is returned, the first or the second? I must admit that it disturbed me a lot for a while
Now that is definitely simpler. - This was sometimes leading to issues when you write longer pieces of code including conditions, with
if(cond,x,y)or ternary expressionscond?x:y. In these cases, the compiler requires that types ofxandymust be the same, and whenxandywere long expressions, it was easy to get tricked. for instance, ifxorywere ending with a function like the one discussed above, the returned types were incompatible and it was super hard to debug.
Now, the rules are simpler: math functions that are not intended to return a meaningful value just return the scalar nan.
It still seems broken for me sadly, this should swap all vectors but many are not swapped:

The stuff about return values makes sense though 
I think that is because when you use the predefined variable I in an expression, it it not a reference (e.g. (I) = pi is not equivalent to I() = pi).
In your case, I suppose you should write:
f ">begin(P=whd-2^-14); swap(I[int(u(P--))],I()); I"
because I() is a reference to the current pixel (basically it’s the same as I(x,y,z,c)), and swap() needs a reference here.
With the previous expression, I was actually read in a temporary memory slot, then swapped with one existing pixel of the image, so it was in fact equivalent to write:
f ">begin(P=whd-2^-14); I[int(u(P--))] = I"
EDIT: I cannot be a reference to the current pixel, because that would mean writing I = some_value would not be equivalent to assigning a new variable, named I with some_value, but rather set the current vector-valued pixel of the image to some_value
Ah, OK thanks that all makes sense. It works in older g’mic versions, so I suppose it used to be a bug or just change of design.
I’m very surprised it worked before by the way.
Any predefined variable, as I ,i0, R, etc… have never been intended to return reference to values, but values in temporary (unamed) variables instead).
Me too actually. It seemed like a handy shortcut, but seems I was abusing a flaw! I just confirmed, the “wrong” code definitely works in 3.1.2.
Funny, it must be a bug that has been fixed since then 
I was intrigued with the ramp-like quality of the failure in Post 208. I like to think of swap(A,B) as an atomic, dual assignment, to wit: temp=A;A=B;B=temp, but predefined variable I is not wholly vector-like in mutable settings, for swap(A,I) only gives rise to one, not two changes. This may be observed with a (small) ramp where the values of the pixels are also their positions in the image:
gosgood@bertha ~ $ gmic 8,1,1,1,"x" nm. ramp f[ramp] "<begin(P=whd-2^-14);k=int(u(P--));swap(I[k],I);print(k);print(I[k]);print(I);print(I());I"
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input image at position 0, with values 'x' (1 image 8x1x1x1).
[gmic]-1./ Set name of image [0] to 'ramp'.
[gmic]-1./ Fill image [0] with expression '<begin(P=whd-2^-14);k=int(u(P--));swap(...)(k);print(I[k]);print(I);print(I());I'.
[gmic_math_parser] k = (uninitialized) (compiled as 'scalar', memslot = 38)
[gmic_math_parser] I[k] = (uninitialized) (compiled as 'vector1', memslot = 43)
[gmic_math_parser] I = (uninitialized) (compiled as 'vector1', memslot = 45)
[gmic_math_parser] I() = (uninitialized) (compiled as 'vector1', memslot = 47)
[gmic_math_parser] k = 3
[gmic_math_parser] I[k] = [ 7 ] (size: 1)
[gmic_math_parser] I = [ 7 ] (size: 1)
[gmic_math_parser] I() = [ 7 ] (size: 1)
…
First swap and we’re already off the rails. Since, initially, a pixel’s value is equal to its position in the ramp, the second, the post-swap() printings of the math expression constant I and and pixel assessor functional form I() should reflect values 3 for k=3, but they do not because only the first argument to swap(), I[k] — a reference — alters, not the second, I, which is a “predefined variable.” Only one half of the swap takes place, from second argument to first. Since this is one pass, from bottom to top, it is the bottom pixel values that can propagate to other pixels (second argument to first), but the propagation of the first argument to the second silently fails. This asymmetry favors preservation of bottom pixels: they can spread, but they cannot be spread to. If I understand @David_Tschumperle correctly, I[k] is a reference; I is not. For symmetry’s sake, ensure that swap(A,B) arguments are both references: swap(I[k],I()), not swap(I[k],I);
gosgood@bertha ~ $ gmic 8,1,1,1,"x" nm. ramp f[ramp] "<begin(P=whd-2^-14);k=int(u(P--));swap(I[k],I());print(k);print(I[k]);print(I);print(I());I"
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input image at position 0, with values 'x' (1 image 8x1x1x1).
[gmic]-1./ Set name of image [0] to 'ramp'.
[gmic]-1./ Fill image [0] with expression '<begin(P=whd-2^-14);k=int(u(P--));swap(...)(k);print(I[k]);print(I);print(I());I'.
[gmic_math_parser] k = (uninitialized) (compiled as 'scalar', memslot = 38)
[gmic_math_parser] I[k] = (uninitialized) (compiled as 'vector1', memslot = 43)
[gmic_math_parser] I = (uninitialized) (compiled as 'vector1', memslot = 45)
[gmic_math_parser] I() = (uninitialized) (compiled as 'vector1', memslot = 47)
[gmic_math_parser] k = 3
[gmic_math_parser] I[k] = [ 7 ] (size: 1)
[gmic_math_parser] I = [ 3 ] (size: 1)
[gmic_math_parser] I() = [ 3 ] (size: 1)
…
In that form, full swap takes place.
Somewhere in Tutorial Land, I need to deal with this nuance.
That is correct.
More precisely, when you write something as I[k] or I(x,y,z,c) in the math parser, the value is read in a new memory slot which is marked as linked to a reference, i.e. any modification of this memory slot will be also reflected in a change in the corresponding image value.
And, in that case, the math parser actually updates two memory slots.
Not only image values can be referenced, but also vector-valued variable slots
(so that ++vec[k] works as expected for instance).
Maybe that could be nice if print() or debug() also show this special ‘reference’ states of the memory slots. I’ll check if this is possible to add.
Is there a explanation as to why is this added as part of the code? int(u(n)) is equilavent of selecting a random pixel in expr('x',n);.
u(n) values never reaches n. It can go to 0, but never n.
If that’s true, the reference could do with a tweak of the interval notation
Correct, @Reptorian. The language at Specific Functions, third bullet point defines the range given to u(min,max) to be “between [0,max] or [min,max].” which, to my ear, is language that specifies an open set. That is, an open set may include points arbitrarily close to its specified limits, min and max, but cannot include those limits. Otherwise, it would be a closed set. Thus:
$ gmic 1024,1024,1,1,'u(4)' nm nofourhere +f[nofourhere] 'i==4?1:0' nm flag if 'im#$flag<iM#$flag' echo[] 'Bingo!' fi
Nowhere in nofourhere is there a pixel exactly equal to 4; pixels in image flag are everywhere zero, and Bingo never prints.
But I think @garagecoder, from whom I borrowed that snippet, was in some kind of glass-half-empty frame of mind and wanted to be very sure that any returned random number would never,ever be exactly equal to P itself, but a tiny increment (2^-14 ≈ 6.103515625e-05) less. Justifiable paranoia.
to my (french) ear, range [min,max] is a closed set, so min and max included.
At least this is the common way to write it in French math.
An open set would be denoted as ]min,max[.
I confirm that in G’MIC math parser, u takes values in [0,1], as you can verify by yourself:
$ gmic eval "while (u!=1,0)"
which always returned for me, after a while.
By the way, I feel that a version of u(min,max) that does not include min, max in the possible returned values will be super useful.
Maybe u_() ?
What do you think?
Yes. Then we can pick the conditions as we see fit in our particular playpens and not find ourselves subtracting tiny increments when we are not sure.
And I could also add u_ and v_ as predefined variables, respectively meaning u_(-1,1) and u_(0,1). The underscore looks like a bit like a minus sign, so I think this could be easy to remember, like a slight variation of u() minus the extremity values of the specified range.
Well maybe this is better:
Define an extended version of the current function u(min,max) that would be u(min,max,include_min,include_max).
This way, u(0,1,1,0) would mean: a random number in set [0,1[.
Even more flexible and avoid defining new functions.
That matches how I understand it, plus what wikipedia says on interval notation
I think that’s a bit more readable too; underscore with particular meaning is perhaps a bit arbitrary.