I'm trying out a new continuous histogram coloring method for Mandelbrot rendering in R, and since it requires three loops (three passes), each of them doubly nested, there should be a way to vectorize the code to speed it up.
However, I'm not yet familiar enough with vectorization in R, especially when conditionals are involved, and I would appreciate the help.
I know there's a which function which gives an array of indices of the elements which obey a condition, but I'm not sure how to make it work in this case.
Basically, I would like to make some zoom animations which is why I want the code to run faster than it does now.
If any additional improvements are possible, I would very much appreciate the advice as well.
While it's not very relevant for the question, the coloring method is as follows: instead of escape count, as usual, we are counting a kind of "cumulative convergence/divergence speed", which is defined as the sum of min(1/|z_n|^2,|z_n|^2) over the whole iterations run. It is continuous, and allows for interior coloring. The disadvantage is that we have to use floating point numbers, so very deep zooms are hard, if not impossible to render.
Here is the R code I've created, with comments:
library("jcolors")
It <- 400; #Max iterations
Es <- 10^3; #Escape radius squared (much larger than 4, intentionally)
Xm <- 1600; #x pixels
Ym <- 900; #y pixels
Mxy <- matrix(rep(1,Xm*Ym),nrow=Xm,ncol=Ym); #initiate Mandelbrot array
Hxy <- Mxy; #Histogram index array
Nb <- 40; #Number of bins
hb <- rep(0,Nb); #Histogram heights vector
Mb <- rep(0,Nb); #Bin boundaries, for later
mx <- 1:Xm;
my <- 1:Ym;
Cx <- -0.5+(-1+mx/Xm*2)*1.6; #Re(C) vector
Cy <- (-1+my/Ym*2)*0.9; #Im(C) vector
ny <- 1; #First pass loop, computing Mxy
while(ny <= Ym){nx <- 1;
while(nx <= Xm){
j <- 1;
x0 <- 0;
y0 <- 0;
s <- 1;
while(j <= It){r <- x0^2+y0^2; #|z|^2
if(r<Es){x1 <- x0^2-y0^2+Cx[nx];
y1 <- 2*x0*y0+Cy[ny]}
else{x1 <- x0; y1 <- y0; break};
x0 <- x1;
y0 <- y1;
if(r>0){s <- s+min(r,1/r)}; #this is what we measure instead of escape count
j <- j+1}
Mxy[nx,ny] <- s;
nx <- nx+1}
ny <- ny+1}
Mmax <- max(Mxy);
Mmin <- min(Mxy);
DM <- Mmax-Mmin;
dM <- DM/Nb; #Bin size
Mxy <- Mxy-Mmin; #Shifting the array values so they start with 0
ny <- 1; #Second pass loop, computing the histogram
while(ny <= Ym){nx <- 1;
while(nx <= Xm){
nb <- 1;
while(nb <= Nb){if(Mxy[nx,ny] < (nb+0.01)*dM && Mxy[nx,ny] > (nb-1.01)*dM) #Bins overlap a little to avoid missed pixels
{hb[nb] <- hb[nb]+1; #Bin height increases
Hxy[nx,ny] <- nb}; #Each pixel is assigned its bin index
nb <- nb+1}
nx <- nx+1}
ny <- ny+1}
hb <- hb/max(hb); #Normalizing the histogram
nb <- 1; #computing the new bin boundaries
while(nb < Nb){Mb[nb+1] <- Mb[nb]+dM*hb[nb];
nb <- nb+1}
ny <- 1; #Third pass loop, scaling the array according to the histogram (multiply each interval by the bin height)
while(ny <= Ym){nx <- 1;
while(nx <= Xm){
nb <- 1;
while(nb <= Nb){
if(Hxy[nx,ny]==nb){Mxy[nx,ny] <- Mb[nb]+(Mxy[nx,ny]-(nb-1)*dM)*hb[nb]; break}
nb <- nb+1}
nx <- nx+1}
ny <- ny+1};
image(Mxy, col = jcolors_contin(palette = "rainbow", reverse=FALSE)(200), axes=FALSE, xlab="", ylab="", useRaster=TRUE)
#done
And here's the resulting image:

And an example of a zoom *1.0625^(-225) at the point x=0.3602404434376143632361252444, y=-0.641313061064803174860375015.

Another zoom at the same point, *1.0625^(-300) and 1700 iterations.

Edit:
I accepted the answer, so here's an additional comment on the coloring method itself, for future reference.
I've read a dozen posts on Mandelbrot coloring, and usually histogram coloring is used with discrete escape count measure, which is then interpolated to form a continuous image.
Using histogram coloring with bins for continuous measure is not ideal, and leads to the loss of important details (like minibrots) and highlights less important details (the "rings" and "webs" of color you can see in the images above).
I found that the best way so far is to mix the arrays without histogram scaling (a "single bin" histogram) and with histogram scaling. Then the details look good, and the colors are rich even at very high interation counts.
Here's an example of low zoom with high iteration count It=3000 and Nb=100 bins, where I use the arithmetic mean of Mxy without histogram scaling and with it.
And a 8.2E-9 zoom with the same parameters:

