gmic icon indicating copy to clipboard operation
gmic copied to clipboard

Nested expression processing with vectors

Open Reptorian1125 opened this issue 2 years ago • 3 comments

It would seem that there's a bug with processing nested expression involving vectors.

Here's a sample code (I don't think I can come up with a smaller form at the moment). However, you'd have to look below and ignore the code that you're not supposed to look at when testing, and that small part is what I want you to focus on to see the bug.

rep_lavander_binary_map:
skip ${1=8}
check "$1>0&&($1==int($1))"

repeat $1 {
  k={$>+1}
  +rep_ncr_combinations $1,$k
  {w},1,1,1,$k
  rv[-2,-1]
  a[-2,-1] c
}

local[-$1--1] {
  total_length={sum(expr('w#x',$!))}
  max_spectrum_count=${-max_s}
  
  $total_length,1,1,$max_spectrum_count
  
  p=0
  
  repeat $1 {
    j[-1] [$>],$p
    p+={w#$>}
  }
  
  keep[-1]
}

local[-1] {
  square_size={w#-1}
  
  $square_size,$square_size,1,1,:"begin(
     const max_index=$1;
     
     p(pos)=I(#-1,pos,0,0);
     empty_set=vector(#s#-1,0);
     
     cp=[166,64];
     
     and_set(v0,v1)=(
       new_set=empty_set;
       ins_to_new_set=1;
       
       if(x==cp[0]&&y==cp[1],print(v0);print(v1););
       
       repeat(v0[0],v0_ip,
         v0_p=v0_ip+1;
         repeat(v1[0],v1_ip,
           v1_p=v1_ip+1;
           if(v0[v0_p]==v1[v1_p],          
             new_set[ins_to_new_set]=v0[v0_p];
             ++new_set[0];
             ++ins_to_new_set;
           );
         );
       );
       
       if(x==cp[0]&&y==cp[1],print(new_set););
       
       new_set;
       
     );
     
     or_set(v0,v1)=(
       new_set=empty_set-1;
       new_set[0]=0;       
       ins_to_new_set=1;
       
       if(x==cp[0]&&y==cp[1],print(v0);print(v1););
       
       repeat(v0[0],v0_ip,
         v0_p=v0_ip+1;
         v=v0[v0_p];
         search_result=find(new_set,v,1,1);
         if(search_result==-1,
           new_set[ins_to_new_set]=v;
           ++new_set[0];
           ++ins_to_new_set;
         );
       );

       repeat(v1[0],v1_ip,
         v1_p=v1_ip+1;
         v=v1[v1_p];
         search_result=find(new_set,v,1,1);
         if(search_result==-1,
           new_set[ins_to_new_set]=v;
           ++new_set[0];
           ++ins_to_new_set;
         );
       );
       
       if(x==cp[0]&&y==cp[1],print(new_set));
       
       new_set;
       
     );
     
     diff_set(v0,v1)=(
       new_set=empty_set;
       ins_to_new_set=1;
       
       if(x==cp[0]&&y==cp[1],print(v0);print(v1););
       
       repeat(v0[0],v0_ip,
         v0_p=v0_ip+1;
         v=v0[v0_p];
         found=0;
         
         repeat(v1[0],v1_ip,
           v1_p=v1[v1_ip+1];
           if(v==v1_p,
             found=1;
             break();
           );
         );
         
         if(!found,
           new_set[ins_to_new_set]=v;
           ++new_set[0];
           ++ins_to_new_set;
         );
         
       );
       
       if(x==cp[0]&&y==cp[1],print(new_set));
       
       new_set;
       
     );
     
     xor_set(v0,v1)=(
       new_set=empty_set;
       ins_to_new_set=1;
       
       if(x==cp[0]&&y==cp[1],print(v0);print(v1););
       
       repeat(v1[0],v1_ip,
         v1_p=v1_ip+1;
         v=v1[v1_p];
         found=0;
         
         repeat(v0[0],v0_ip,
           v0_p=v0[v0_ip+1];
           if(v==v0_p,
             found=1;
             break();
           );
         );
         
         if(!found,
           new_set[ins_to_new_set]=v;
           ++new_set[0];
           ++ins_to_new_set;
         );
         
       );
       
       repeat(v0[0],v0_ip,
         v0_p=v0_ip+1;
         v=v0[v0_p];
         found=0;
         
         repeat(v1[0],v1_ip,
           v1_p=v1[v1_ip+1];
           if(v==v1_p,
             found=1;
             break();
           );
         );
         
         if(!found,
           new_set[ins_to_new_set]=v;
           ++new_set[0];
           ++ins_to_new_set;
         );
         
       );
       
       if(x==cp[0]&&y==cp[1],print(new_set));
       
       new_set;
     );
     
   );
   out=xor_set(p(x),p(y));  
   out=and_set(p(y),out);
   
   out[0];"
}

rm..

Now, this gives the expected result after running $ rep_lavander_binary_map.

Replace this part

out=xor_set(p(x),p(y));  
out=diff_set(p(y),out);

With this:

out=diff_set( p(y), xor_set(p(x),p(y)) );

As you can see, it's virtually the same, but what happens after you run it?

The output turns out to be different. It is the first one that is correct.

There seem to be a issue with processing here.

Reptorian1125 avatar Aug 03 '22 02:08 Reptorian1125

out=diff_set( p(y), xor_set(p(x),p(y)) );

As you can see, it's virtually the same, but what happens after you run it?

No it's not. Do not forget that your diff_set() is a macro, not a function. This makes a huge difference. In your case, this means that your argument v1 in the macro diff_set() will be replaced each time it is encountered in the macro code by the expression xor_set(p(x),p(y)), which is probably (definitely) not what you want. In particular, xor_set(p(x),p(y)) will be "called" multiple times here (each time you use v1 in your macro code).

If diff_set() was a function, then xor_set() would be called only once, when the function is called.

dtschump avatar Aug 03 '22 06:08 dtschump

What would be the workaround to this issue? I'm thinking of making a command which basically would generate codes to get around this issue, and I know that's tough to do.

Reptorian1125 avatar Aug 03 '22 15:08 Reptorian1125

One idea I can think of is a new feature using character '@', this character should be put next to macros to have the JIT compiler assume it's a function or at least force it to call inner function only once. But, I don't know if this is feasible.

However, when writing after the macro function, the '@' can be omitted.

Reptorian1125 avatar Aug 03 '22 18:08 Reptorian1125

It's not planed to add functions (rather than macros) in math parser. This would mean: having to manage a stack for arguments push/pop, as well as a stack for keeping the current scope during the evaluation of the expression. It would complexify the parser for an usage where workarounds can be often found. Please don't use the math evaluator if you really need function calls. Use a "regular" G'MIC pipeline instead.

dtschump avatar Oct 25 '22 12:10 dtschump