Reptorian G'MIC Filters

Any interest in this effect?

Not really. Would have been neat if it was a more interesting pattern, like a decaying one. What is it trying to achieve? Apples should be banned? LOL

It’s basically making holes within a image, and the border has a pattern similar to the cut-out part of the image.

For example, you can make this with it:

image

1 Like

My next filter is going to be on internal of G’MIC now. These are the math parser things I will be working on:

  1. Concatenate - This one will concatenate any numbers of numbers within the math parser. It’ll be in the form of concat().
  2. Add options to Reverse(A). After the vector argument, there should be a way to specify which one should be reversed.

Edit: Started and made a pull request on #1. Not functioning, however it’s getting there. Ok, now with editing makefile, I think I am closer to figuring this out.


Made my first gmic feature:

The above gmic feature ended up as a custom command to be used for math evaluator. Not quite ideal, but it’s acceptable for coding purpose. It’s pushed to gmic-community.

I’ll try #2 then.

Released Serendiptous Circle. Not ideal, but it works with the limits I have.

I’m still going to do mostly cleanup of gmic-community files for now, and there will be a thread about that.

That being said, I think this code is quite good for what it does.

rep_output_no_duplicates:
if narg($*)>1
 ($*) (0)

 eval "
  da_push(#-1,i(#-2,0));
  for(n=1,n<w#-2,++n,
   new_val=i(#-2,n);
   found=0;
   for(p=0,p<da_size(#-1),++p,
    if(new_val==i[#-1,p],
     found=1;
     break();
    );
   );
   if(!found,da_push(#-1,new_val));
  );
  resize(#-1,1,da_size(#-1),1,1,0);"
 
 u {crop(#-1)} rm[-2,-1]
elif narg($*)==1
 u $1
else
 error inv_num_args
fi

Output:

C:\Windows\System32>gmic rep_output_no_duplicates 5,4,3,1,11,5,5,1 echo ${}
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ 5,4,3,1,11
[gmic]-0./ End G'MIC interpreter.

Edit: I forgot colormap 0, but this would not work if I wanted to keep the order. I should try regardless.

I think I will stop cleanup from here. I am now finishing up on the new version of Popcorn Fractal [Transformative]. The GUI version is what I am working on now. Probably will be released just in time for 3.1.

Also, can’t help, but share a test result of the new Popcorn Fractal [Transformative]:

3 Likes

Arachnid feels. @patdavid apparently.

New Version of Popcorn Fractal [Transformative] is released.

Also, broke 20000 lines.

2 Likes

Hmm, I decided to rewrite rep_find_factors a bit. Found a faster algorithm.

Here is what I have:

#@cli rep_find_factors_of: num_0...
rep_find_factors_of:
1,1,1,2 :: primeFactors
(0)     :: factors

eval "
 v=$1;
 square=int(sqrt(v));
 
 for(n=2,n<=square&&n<=n,++n,
 
  isPrime=1;
  
  for(p=0,p<da_size(#$primeFactors),++p,
  
   prime=(I[#$primeFactors,p])[0];
   
   sqr(prime)>n?break();
   
   !(n%prime)?(
    isPrime=0;
    break();
   );
   
  );
  
  isPrime?(
   count=0;
   
   while(!(v%n),
    ++count;
    v/=n;
   );
   
   da_push(#$primeFactors,[n,count]);
   
   count?square=int(sqrt(v));
   
  );
  
 );
 
 v!=1?(da_push(#$primeFactors,[v,1]));
 
"

The above is a partial translation of a C++ code that I don’t understand yet:

#include <chrono>
#include <iostream>
#include <utility>
#include <vector>

using PrimeFactors = std::vector<std::pair<uint64_t, uint64_t>>;

std::vector<std::pair<uint64_t, uint64_t>> FindFactors(uint64_t n)
{
    PrimeFactors primeFactors;

    uint64_t square = static_cast<uint64_t>(std::sqrt(n));
    for (uint64_t i = 2; i <= square && i <= n; ++i)
    {
        bool isPrime = true;
        for (auto [prime, exponent] : primeFactors)
        {
            if (prime * prime > i)
            {
                break;
            }
            if (i % prime == 0u)
            {
                isPrime = false;
                break;
            }
        }

        if (isPrime)
        {
            uint64_t count = 0;
            while (n % i == 0)
            {
                ++count;
                n /= i;
            }
            primeFactors.emplace_back(i, count);
            if (count != 0)
            {
                square = static_cast<uint64_t>(std::sqrt(n));
            }
        }
    }
    if (n != 1)
    {
        primeFactors.emplace_back(n, 1);
    }
    return primeFactors;
}

void PrintFactors(uint64_t factor, PrimeFactors::const_iterator pos, PrimeFactors::const_iterator const end)
{
    while (pos != end)
    {
        while (pos != end && pos->second == 0)
        {
            ++pos;
        }
        auto newFactor = factor;
        for (auto count = pos->second; count != 0; --count)
        {
            newFactor *= pos->first;
            std::cout << newFactor << '\n';
            PrintFactors(newFactor, pos + 1, end);
        }
        ++pos;
    }
}

int main()
{
    using Clock = std::chrono::steady_clock;

    uint64_t const input = 10'000'000'000'000'000ull;
    //uint64_t const input = 2ull * 3ull * 5ull * 7ull *11ull * 13ull *17ull * 19ull * 23ull * 29ull *31ull*37ull * 41ull*43ull;

    auto start = Clock::now();
    auto factors = FindFactors(input);

    // print
    std::cout << 1 << '\n';
    PrintFactors(1, factors.begin(), factors.end());
    auto end = Clock::now();
    std::cout << "took " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << " ms\n";
}

The part I don’t understand is this section → void PrintFactors(uint64_t factor, PrimeFactors::const_iterator pos, PrimeFactors::const_iterator const end).

Based on the naming alone, seems like it prints a list of factors starting and ending at certain positions of the list.

That part I do get, but the part that confuse me is how it works altogether, and how I can translate it to G’MIC code. I think only David can help with that since he knows C++ much more in depth than I do, and maybe @garagecoder as well.

Okay, I"m starting to understand it as a recursive code. Maybe, with that knowledge, I can finally translate it to G’MIC. I hate recursions with a passion, but it is what it is.


EDIT:

This leads to infinite recursion?

 printfactors(factor,pos,endval)=(
 
  new_factor=factor;
  new_pos=pos;
  
  while(new_pos!=endval,
  
   while(new_pos!=endval&&!(I[#$primeFactors,new_pos])[1],
    ++new_pos;
   );
   
   for(count=(I[#$primeFactors,new_pos])[1],!count,--count,
    new_factor*=(I[#$primeFactors,new_pos])[0];
    da_push(#-2,new_factor);
    printfactors(new_factor,new_pos+1,endval);
   );
   
   ++new_pos;
  );
  
 );
 
Whole Code
#@cli rep_find_factors_of: num_0...
rep_find_factors_of:
1,1,1,2 :: primeFactors
(0)     :: factors

eval "

 printfactors(factor,pos,endval)=(
 
  new_factor=factor;
  new_pos=pos;
  
  while(new_pos!=endval,
  
   while(new_pos!=endval&&!(I[#$primeFactors,new_pos])[1],
    ++new_pos;
   );
   
   for(count=(I[#$primeFactors,new_pos])[1],!count,--count,
    new_factor*=(I[#$primeFactors,new_pos])[0];
    da_push(#-2,new_factor);
    printfactors(new_factor,new_pos+1,endval);
   );
   
   ++new_pos;
  );
  
 );
 
 v=$1;
 square=int(sqrt(v));
 
 for(n=2,n<=square&&n<=n,++n,
 
  isPrime=1;
  
  for(p=0,p<da_size(#$primeFactors),++p,
  
   prime=(I[#$primeFactors,p])[0];
   
   sqr(prime)>n?break();
   
   !(n%prime)?(
    isPrime=0;
    break();
   );
   
  );
  
  isPrime?(
   count=0;
   
   while(!(v%n),
    ++count;
    v/=n;
   );
   
   da_push(#$primeFactors,[n,count]);
   
   count?square=int(sqrt(v));
   
  );
  
 );
 
 v!=1?(da_push(#$primeFactors,[v,1]));
 
 end_val=da_size(#$factors)-1;
 printfactors(1,0,end_val);
"

Not sure what is going on here, but a general note about the math evaluator :
It is not possible to define functions, only macros, which is a bit different because macros are just the repetition of some code in the expression, while functions is “external” code that could be called from an expression.
The two mechanisms are really different in the sense that macros do not require a stack to push/pop macro arguments (a function would need it).
As a direct consequence, it is just not possible in the general case to do recursion with macros.

The only case where this is possible is when you deal only with constant values as arguments, so that the math compiler can eventually deduce a single expression from the macro.
I don’t have an example to show right now, but I’ll think about it :slight_smile:

PS : Here is a simple example of recursive macro (won’t probably work with large numbers!!) :slight_smile:

foo :
  eval "
    factorial(x) = ( x>0?x*factorial(x - 1):1 );
    print(factorial(5));
  "
1 Like

Two additionnal notes :

  • The example of the recursive factorial() macro works only because it is called with a constant argument, so the math evaluator can deduce the value by “unrolling” the recursive calls. Do not call factorial() with a non-constant value, it won’t work.

  • While somehow convenient and sometimes nice to write, recursive functions are in general not that useful. Most of the time, it is possible to transform a recursive function into a regular function using your own “stack” to store the different arguments for the different call. Using da_push() for that will certainly solve your problem.
    Most of the time, “unrolling” a recursive function is possible, and even often more efficient
    (see e.g. python - "Unrolling" a recursive function? - Stack Overflow)

2 Likes

Basically what David said - the first thing I would do is rewrite it without recursion, probably with a stack. I wish I had time to look at that today but I don’t, I expect you will figure it out soon anyway!

Finally, made the unrolled version though it leads to infinite loop, I think I"m missing something, but what it could be?

The problem code part:

 while(1,
  stop_loop=1;
  inc_pos=1;
  
  while(pos!=endval,
  
   while(pos!=endval&&!(I[#$primeFactors,pos])[1],++pos;);
   
   for(count=(I[#$primeFactors,pos])[1],!count,--count,
    factor*=(I[#$primeFactors,pos])[0];
    da_push(#-1,factor);
    stop_loop=0;++pos;break();
   );
   
  );
 
  if(stop_loop,break(),if(inc_pos,++pos;););
 );"
Fixed version (Still Bugged)
#@cli rep_find_factors_of: num_0...
rep_find_factors_of:
1,1,1,2 :: primeFactors
(0)     :: factors

eval "

 v=$1;
 square=int(sqrt(v));
 
 for(n=2,n<=square&&n<=n,++n,
 
  isPrime=1;
  
  for(p=0,p<da_size(#$primeFactors),++p,
  
   prime=(I[#$primeFactors,p])[0];
   
   sqr(prime)>n?break();
   
   !(n%prime)?(
    isPrime=0;
    break();
   );
   
  );
  
  isPrime?(
   count=0;
   
   while(!(v%n),
    ++count;
    v/=n;
   );
   
   da_push(#$primeFactors,[n,count]);
   
   count?square=int(sqrt(v));
   
  );
  
 );
 
 v!=1?(da_push(#$primeFactors,[v,1]));
 
 stop_loop=0;
 inc_pos=1;
 
 factor=1;
 pos=0;
 endval=da_size(#$primeFactors)-1;
 
 while(1,
  stop_loop=1;
  inc_pos=1;
  
  while(pos!=endval,
  
   while(pos!=endval&&!(I[#$primeFactors,pos])[1],++pos;);
   
   for(count=(I[#$primeFactors,pos])[1],!count,--count,
    factor*=(I[#$primeFactors,pos])[0];
    da_push(#-1,factor);
    stop_loop=0;++pos;break();
   );
   
  );
 
  if(stop_loop,break(),if(inc_pos,++pos;););
 );"

From printing factor, it stays stuck here:

C:\Windows\System32>gmic rep_find_factors_of 110880
[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] factor = (uninitialized) (compiled as 'scalar', memslot = 65

Being super obvious here: to see what is wrong, you need to start testing the inner most loop first and then move outward. I know you move at a faster pace than most of us: take it slow. :wink:

1 Like

It looks like I didn’t understand the code after all. → in the c++ seem to be doing something, but I don’t know what it is.

EDIT: I checked with Visual Studio, there’s no patterns, so I don’t think unrolling it is possible. There’s no clear pattern yet. It is confirmed that FindFactors() in the C++ has been successful replicated. It’s printFactors() that is the problem.

EDIT: I think I have 1:1 conversion, but for some reason, it doesn’t work.

Reasonings on why I think it works. It uses pos==endval which means it will stop the loop once pos meets endval. The same condition as the c++ version. endval is not modified. Variables are updated inside count for loop, and this mean it should go right back at the beginning.

 current_factor=1;
 pos=0;
 endval=da_size(#$primeFactors)-1;
 
 while(1,
 
  if(pos==endval,break(););

  use_inc=1;
  
  while(pos!=endval&&!(I[#$primeFactors,pos])[1],
   ++pos;
  );
  
  for( count=(I[#$primeFactors,pos])[1],!count,--count,
   current_factor*=(I[#$primeFactors,pos])[0];
   da_push(#$factors,current_factor);
   ++pos;
   use_inc=0; 
   break();
  );
  
  if(use_inc,++pos);
  
 );

I think the problem occurs inside the for() within while(1,....);. Strangely, print(variable) inside it returns nothing. Zilch. Not even a 0. That’s why da_push() does nothing.

Full Code
#@cli rep_find_factors_of: num_0...
rep_find_factors_of:
1,1,1,2 :: primeFactors
1,1,1,1 :: factors

eval "

 v=$1;
 square=int(sqrt(v));
 
 for(n=2,n<=square&&n<=n,++n,
 
  isPrime=1;
  
  for(p=0,p<da_size(#$primeFactors),++p,
  
   prime=(I[#$primeFactors,p])[0];
   
   sqr(prime)>n?break();
   
   !(n%prime)?(
    isPrime=0;
    break();
   );
   
  );
  
  isPrime?(
   count=0;
   
   while(!(v%n),
    ++count;
    v/=n;
   );
   
   da_push(#$primeFactors,[n,count]);
   
   count?square=int(sqrt(v));
   
  );
  
 );
 
 v!=1?(da_push(#$primeFactors,[v,1]));
 
 current_factor=1;
 pos=0;
 endval=da_size(#$primeFactors)-1;
 
 while(1,
 
  if(pos==endval,break(););

  use_inc=1;
  
  while(pos!=endval&&!(I[#$primeFactors,pos])[1],
   ++pos;
  );
  
  for( count=(I[#$primeFactors,pos])[1],!count,--count,
   current_factor*=(I[#$primeFactors,pos])[0];
   da_push(#$factors,current_factor);
   ++pos;
   use_inc=0; 
   break();
  );
  
  if(use_inc,++pos);
  
 );"

EDIT: Ah, my mistake is translating the auto pos->second inside for loop in the C++ code. What it seems is that auto access the index pos, and then retrieve the pair.second, and within the for loop, it modifies the pair.second. That seem hard to translate.

And I give up, there’s no translating printFactors()…

EDIT: Maybe, I’m thinking of this in the wrong way.

From the description of how the code works : “The best approach here is probably to calculate the prime factorization of the number and then printing out the products of all possible combinations of those prime factors.”

So, to find a iteration version of said code, I need to do the above.

48 → 2,2,2,2,3

The patterns goes like:

2
6
4
12
8
24
16
48
3

Here’s my observation of what is happening:

2
2*3=6
2*2=4
2*2*3=12
2*2*2=8
2*2*2*3=24
2*2*2*2=16
2*2*2*2*3=48
3

As for 60:

<<2,2>><<3,1>><<5,1>>
1
2  = 2
6  = 2*3
30 = 2*3*5
10 = 2*5
4  = 2*2
12 = 2*2*3
60 = 2*2*3*5
20 = 2*2*5
3  = 3
15 = 5*3
5  = 5

So, all I have to do is write a code that use da_push, and do the following behavior. Maybe one without needing to sort after can be made. Not sure how to write it though. The order does not matter at all.

More information, it seems that I make the above as some sort of graph.

2 | 3 | 5
---------
2 | 1 | 1
---------
1 | 0 | 0 = 2 * 1 = 2
1 | 1 | 0 = 2 * 3 = 6
1 | 1 | 1 = 2 * 3 * 5 = 30
1 | 0 | 1 ....
2 | 0 | 0
2 | 1 | 0
2 | 1 | 1
2 | 0 | 1
0 | 1 | 0
0 | 1 | 1
0 | 0 | 1

So, I’m closer to making a iterative function to doing this.

This means my current code (Added graph image for use to try to do the above)
#@cli rep_find_factors_of: num_0...
rep_find_factors_of:
1,1,1,2 :: primeFactors
1,1,1,1 :: graph
1,1,1,1 :: factors

eval "

 pos_first(v)=(I[#$primeFactors,v])[0];
 pos_second(v)=(I[#$primeFactors,v])[1];
 
 v=$1;
 square=int(sqrt(v));
 
 for(n=2,n<=square&&n<=n,++n,
 
  isPrime=1;
  
  for(p=0,p<da_size(#$primeFactors),++p,
  
   prime=(I[#$primeFactors,p])[0];
   
   sqr(prime)>n?break();
   
   !(n%prime)?(
    isPrime=0;
    break();
   );
   
  );
  
  isPrime?(
   count=0;
   
   while(!(v%n),
    ++count;
    v/=n;
   );
   
   da_push(#$primeFactors,[n,count]);
   
   count?square=int(sqrt(v));
   
  );
  
 );
 
 v!=1?(da_push(#$primeFactors,[v,1]));
 
 #The for loop below is used to remove all multi-channel index with a zero at the second channel.
 
 for(p=da_size(#$primeFactors)-1,p>-1,--p,
  if(!(I[#$primeFactors,p])[1],
   da_remove(#$primeFactors,p);
  );
 );
 
 resize(#$graph,da_size(#$primeFactors),1,1,1,0);
 "

One more graph to see how it works:

3 | 5 | 7 | 11
--------------
3 | 1 | 3 | 2
--------------
1 | 0 | 0 | 0
1 | 1 | 0 | 0
1 | 1 | 1 | 1
1 | 1 | 1 | 2
1 | 1 | 2 | 0
1 | 1 | 2 | 1
1 | 1 | 2 | 2
1 | 1 | 3 | 0
1 | 1 | 3 | 1
1 | 1 | 3 | 2
1 | 1 | 0 | 1
1 | 1 | 0 | 2
1 | 0 | 1 | 0
1 | 0 | 1 | 1
1 | 0 | 1 | 2
1 | 0 | 2 | 0
1 | 0 | 2 | 1
1 | 0 | 2 | 2
....

With a help from someone, there’s this non-recursive c++ code:

Code to non-recursive version of printFactors()
void PrintFactors2(uint64_t factor, PrimeFactors &fs) {
  auto pos = fs.begin();
  auto end = fs.end();

  std::vector<std::tuple<uint64_t, uint64_t, uint64_t, PrimeFactors::const_iterator> > stack;

  stack.push_back(std::make_tuple(factor, pos->first, pos->second, pos));
  while (stack.size() > 0) {
    auto top = stack.back();
    stack.pop_back();

    uint64_t tval = 0;
    if (stack.size() > 0) {
      auto prev = stack.back();
      tval = std::get<0>(prev);
    }
    auto fac = std::get<0>(top) * std::get<1>(top);
    if (std::get<2>(top) > 0) {
      std::cout << fac << std::endl;
      stack.push_back(std::make_tuple(fac,
                                      std::get<1>(top),
                                      std::get<2>(top)-1,
                                      std::get<3>(top)));
      auto p = std::get<3>(top);
      ++p;
      if (p != end) {
        stack.push_back(std::make_tuple(fac,
                                        p->first,
                                        p->second,
                                        p));
      }
    } else if (tval > 0) {
      auto p = std::get<3>(top);
      ++p;
      if (p != end) {
        stack.push_back(std::make_tuple(tval,
                                        p->first,
                                        p->second,
                                        p));
      }
    } else {
      auto p = std::get<3>(top);
      ++p;
      if (p != end) {
        stack.push_back(std::make_tuple(factor,
                                        p->first,
                                        p->second,
                                        p));
      }
    }
  }
}

I will leave that replication of printFactors() to others. However, this post is for something else

I upgraded Premade Palette for G’MIC 3.1.

For those who have 3.1 and uses Windows: Look at filename before export because Windows doesn’t support certain characters. Regardless, most of them will export successfully. I should probably find a way to address that though. And I should find a way to remove periods too.

Issue fixed.