
/* Hello reader!

 * Are you sure you want read this? Its very cryptic and strange code. YOU
 * HAVE BEEN WARNED! Its purpose is to generate as fast as possible
 * calculation loops for various formulas/algorithms. It uses lots of
 * coprocesor magic. It is included from formulas.c
 */

#ifndef VARIABLES /*supply defaultd values */
#define VARIABLES
#endif
#ifndef PRETEST
#define PRETEST 0
#endif
#ifndef INIT
#define INIT
#endif
#ifndef POSTCALC
#define POSTCALC
#endif
#ifndef PRESMOOTH
#define PRESMOOTH zre = rp + ip
#endif
#ifndef UFORMULA
#define UFORMULA FORMULA
#endif
#ifndef UEND
#define UEND
#endif
#ifndef SAVE
#define SAVE
#endif
#ifndef SAVEVARIABLES
#define SAVEVARIABLES
#endif
#ifndef RESTORE
#define RESTORE
#endif
#ifndef RANGE
#define RANGE 2
#endif

/* Prepare main loop */
#ifndef NSFORMULALOOP
#define NSFORMULALOOP(iter)                                                    \
    do { /*try first 8 iterations */                                           \
        FORMULA;                                                               \
        iter--;                                                                \
    } while (BTEST && iter)
#endif
#ifndef SFORMULALOOP
#define SFORMULALOOP(iter)                                                     \
    do { /*try first 8 iterations */                                           \
        SAVEZMAG;                                                              \
        FORMULA;                                                               \
        iter--;                                                                \
    } while (BTEST && iter)
#endif
#ifndef FORMULALOOP
#ifdef SMOOTHMODE
#define FORMULALOOP SFORMULALOOP
#else
#define FORMULALOOP NSFORMULALOOP
#endif
#endif

#ifdef SMOOTHMODE
#ifdef CUSTOMSAVEZMAG
#define SAVEZMAG CUSTOMSAVEZMAG;
#else
#define SAVEZMAG szmag = rp + ip;
#endif
#else
#define SAVEZMAG
#endif

#ifdef UNCOMPRESS
/*uncompressed version of loop */
#ifdef SMOOTHMODE
static unsigned int SCALC(number_t zre, number_t zim, number_t pre,
                          number_t pim);

static unsigned int SCALC(number_t zre, number_t zim, number_t pre,
                          number_t pim)
#else
static unsigned int CALC(number_t zre, number_t zim, number_t pre,
                         number_t pim);

static unsigned CALC(number_t zre, number_t zim, number_t pre, number_t pim)
#endif
{
    unsigned int iter = cfractalc.maxiter;
    number_t szre = 0, szim = 0;
#ifdef RPIP
    number_t rp = 0, ip;
#endif
#ifdef SMOOTHMODE
    number_t szmag = 0;
#endif
    SAVEVARIABLES VARIABLES;
    INIT;
    if (PRETEST)
        iter = 0;
    else {
#ifdef RPIP
        rp = zre * zre;
        ip = zim * zim;
#endif
        if (iter < 16) {

            /*try first 8 iterations */
            if (BTEST && iter) {
                FORMULALOOP(iter);
            }
            /*
               while (BTEST && iter)
               {
               SAVEZMAG;
               FORMULA;
               iter--;
               } */
        } else {
            iter = 8 + (cfractalc.maxiter & 7);

            /*try first 8 iterations */
            if (BTEST && iter) {
                FORMULALOOP(iter);
            }
            /*
               while (BTEST && iter)
               {
               SAVEZMAG;
               FORMULA;
               iter--;
               } */
            if (BTEST) {
                iter = (cfractalc.maxiter - 8) & (~7);
                iter >>= 3;
                /*do next 8 iteration w/o out of bounds checking */
                do {
                    /*hmm..we are probably in some deep area. */
                    szre = zre; /*save current position */
                    szim = zim;
                    SAVE;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UFORMULA;
                    UEND;
                    iter--;
                } while (BTEST && iter);
                if (!(BTEST)) { /*we got out of bounds */
                    iter <<= 3;
                    iter += 8; /*restore saved position */
                    RESTORE;
                    zre = szre;
                    zim = szim;
#ifdef RPIP
                    rp = zre * zre;
                    ip = zim * zim;
#endif
                    FORMULALOOP(iter);
                    /*
                       do
                       {
                       SAVEZMAG
                       FORMULA;
                       iter--;
                       }
                       while (BTEST && iter); */
                }
            } else
                iter += cfractalc.maxiter - 8 - (cfractalc.maxiter & 7);
        }
    }
#ifdef SMOOTHMODE
    if (iter)
        SMOOTHOUTPUT();
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    INOUTPUT();
#else
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    OUTPUT();
#endif
}
#else
#ifdef SMOOTHMODE
static unsigned int SCALC(number_t zre, number_t zim, number_t pre,
                          number_t pim);

static unsigned int SCALC(number_t zre, number_t zim, number_t pre,
                          number_t pim)
#else
static unsigned int CALC(number_t zre, number_t zim, number_t pre,
                         number_t pim);

static unsigned int CALC(number_t zre, number_t zim, number_t pre, number_t pim)
#endif
{
    unsigned int iter = cfractalc.maxiter /*& (~(int) 3) */;
#ifdef RPIP
    number_t rp, ip;
#endif
#ifdef SMOOTHMODE
    number_t szmag = 0;
#endif
    VARIABLES;
    INIT;
    if (PRETEST)
        iter = 0;
    else {
#ifdef RPIP
        rp = zre * zre;
        ip = zim * zim;
#endif
        if (BTEST && iter) {
            FORMULALOOP(iter);
        }
        /*
           while (BTEST && iter)
           {
           SAVEZMAG
           FORMULA;
           iter--;

           } */
    }
#ifdef SMOOTHMODE
    if (iter)
        SMOOTHOUTPUT();
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    INOUTPUT();
#else
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    OUTPUT();
#endif
}
#endif

/*F. : Periodicity checking routines.          (16-02-97)
   All comments preceded by F. are mine (Fabrice Premel premelfa@etu.utc.fr).
   Tried to make code as efficient as possible.
   Next to do is convert lim in a variable that would be updated sometimes
   I'll try to make here a short explanation on periodicity checking :
   first, we'll define 2 variables : whentosave and whenincsave, which are,
   respectively, a measure of when we have to update saved values to be checked,
   and when to increase interval between 2 updates, as if they're too close,
   we'll miss large periods. We save Z at the beginning, and then we compare
   each new iteration with this Z, and if naerly equal, we declare the suite to
   be periodic. When ( iter mod whentosave ) == 0, we store a new value, and we
   repeat.

   UNCOMPRESSed form is just an extension, with careful that if we only check
   whentosave all 8 iterations, number of iterations must be well set at the
   beginning.This is done by adding a (iter&7) in the while statement preceding
   then uncompressed calculation. */

/*F. : This is from then lim factor that depends all periodicity check spped :
   the bigger it is, the faster we can detect periodicity, but the bigger it is,
   the more we can introduce errors. I suggest a value of
   (maxx-minx)/(double)getmaxx for a classic Mandelbrot Set, and maybe a lesser
   value for an extra power Mandelbrot. But this should be calculated outer
   from here (ie each frame, for example), to avoid new calculus */
#ifdef PERI
#define PCHECK                                                                 \
    (abs_less_than(r1 - zre, cfractalc.periodicity_limit) &&                   \
     abs_less_than(s1 - zim, cfractalc.periodicity_limit))

#ifndef UNCOMPRESS

#ifdef SMOOTHMODE
static unsigned int SPERI(number_t zre, number_t zim, number_t pre,
                          number_t pim);

static unsigned int SPERI(number_t zre, number_t zim, number_t pre,
                          number_t pim)
#else
static unsigned int PERI(number_t zre, number_t zim, number_t pre,
                         number_t pim);

static unsigned int PERI(number_t zre, number_t zim, number_t pre, number_t pim)
#endif
{
    unsigned int iter = cfractalc.maxiter /*& (~(int) 3) */, iter1 = 8;
    number_t r1, s1;
    int whensavenew, whenincsave;
#ifdef RPIP
    number_t rp, ip;
#endif
#ifdef SMOOTHMODE
    number_t szmag = 0;
#endif
    VARIABLES;
    INIT;
    if (PRETEST)
        iter = 0;
    else {
#ifdef RPIP
        rp = zre * zre;
        ip = zim * zim;
#endif
        if (iter < iter1)
            iter1 = iter, iter = 8;

        /*H. : do first few iterations w/o checking */
        if (BTEST && iter1) {
            FORMULALOOP(iter1);
        }
        /*
           while (BTEST && iter1)
           {
           SAVEZMAG;
           FORMULA;
           iter1--;
           } */
        if (iter1) {
            if (iter >= 8)
                iter -= 8 - iter1;
            goto end;
        }
        if (iter <= 8) {
            iter = iter1;
        } else {
            iter -= 8;
            r1 = zre;
            s1 = zim;
            whensavenew = 3; /*You should adapt these values */
            /*F. : We should always define whensavenew as 2^N-1, so we could use
             * a AND instead of % */

            whenincsave = 10;
            /*F. : problem is that after deep zooming, peiodicity is never
               detected early, cause is is quite slow before going in a periodic
               loop. So, we should start checking periodicity only after some
               times */
            while (BTEST && iter) {
                SAVEZMAG;
                FORMULA;
                if ((iter & whensavenew) == 0) { /*F. : changed % to & */
                    r1 = zre;
                    s1 = zim;
                    whenincsave--;
                    if (!whenincsave) {
                        whensavenew =
                            ((whensavenew + 1) << 1) -
                            1; /*F. : Changed to define a new AND mask */
                        whenincsave = 10;
                    }
                } else {
                    if (PCHECK) {
                        PERIINOUTPUT();
                    }
                }
                iter--;
            }
        }
    }
end:
#ifdef SMOOTHMODE
    if (iter)
        SMOOTHOUTPUT();
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    INOUTPUT();
#else
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    OUTPUT();
#endif
}

#else

/*F. : UNCOMPRESSed version. Note that whensavenew+1 should be a multiple of 8,
   else periodicity won't be able to detect anything. */
/*F. : this macros definitions are really strange, but after a while, it's good
 */

#ifdef SMOOTHMODE
static unsigned int SPERI(number_t zre, number_t zim, number_t pre,
                          number_t pim);

static unsigned int SPERI(number_t zre, number_t zim, number_t pre,
                          number_t pim)
#else
static unsigned int PERI(number_t zre, number_t zim, number_t pre,
                         number_t pim);

static unsigned int PERI(number_t zre, number_t zim, number_t pre, number_t pim)
#endif
{
    unsigned int iter = cfractalc.maxiter /*& (~(int) 3) */;
    number_t r1 = zre, s1 = zim;
    number_t szre = 0,
             szim =
                 0; /*F. : Didn't declared register, cause they are few used */
    unsigned int whensavenew, whenincsave;
#ifdef RPIP
    number_t rp = 0, ip;
#endif
#ifdef SMOOTHMODE
    number_t szmag = 0;
#endif
    SAVEVARIABLES VARIABLES;
    INIT;
    if (PRETEST)
        iter = 0;
    else {
        if (cfractalc.maxiter <= 16) {
#ifdef RPIP
            rp = zre * zre;
            ip = zim * zim;
#endif
            /*F. : Added iter&7 to be sure we'll be on a 8 multiple */
            if (BTEST && iter) {
                FORMULALOOP(iter);
            }
            /*
               while (BTEST && iter)
               {
               SAVEZMAG
               FORMULA;
               iter--;
               } */
        } else {
            whensavenew = 7; /*You should adapt these values */
            /*F. : We should always define whensavenew as 2^N-1, so we could use
             * a AND instead of % */

            whenincsave = 10;
#ifdef RPIP
            rp = zre * zre;
            ip = zim * zim;
#endif
            /*F. : problem is that after deep zooming, peiodicity is never
               detected early, cause is is quite slow before going in a periodic
               loop. So, we should start checking periodicity only after some
               times */
            iter = 8 + (cfractalc.maxiter & 7);
            while (BTEST && iter) { /*F. : Added iter&7 to be sure we'll be on a
                                       8 multiple */
                SAVEZMAG FORMULA;
                iter--;
            }
            if (BTEST) { /*F. : BTEST is calculed two times here, isn't it ? */
                /*H. : No gcc is clever and adds test to the end :) */
                iter = (cfractalc.maxiter - 8) & (~7);
                do {
                    szre = zre, szim = zim;
                    SAVE;
                    SAVEZMAG
                    FORMULA; /*F. : Calculate one time */
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    FORMULA;
                    if (PCHECK)
                        goto periodicity;
                    iter -= 8;
                    /*F. : We only test this now, as it can't be true before */
                    if ((iter & whensavenew) == 0) { /*F. : changed % to & */
                        r1 = zre, s1 = zim;          /*F. : Save new values */
                        whenincsave--;
                        if (!whenincsave) {
                            whensavenew =
                                ((whensavenew + 1) << 1) -
                                1; /*F. : Changed to define a new AND mask */
                            whenincsave = 10; /*F. : Start back */
                        }
                    }
                } while (BTEST && iter);
                if (!BTEST) {  /*we got out of bounds */
                    iter += 8; /*restore saved position */
                    RESTORE;
                    zre = szre;
                    zim = szim;
#ifdef RPIP
                    rp = zre * zre;
                    ip = zim * zim;
#endif
                    FORMULALOOP(iter);
                    /*
                       do
                       {
                       SAVEZMAG
                       FORMULA;
                       iter--;
                       }
                       while (BTEST && iter); */
                }
            } else
                iter += cfractalc.maxiter - 8 - (cfractalc.maxiter & 7);
        }
    }
#ifdef SMOOTHMODE
    if (iter)
        SMOOTHOUTPUT();
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    INOUTPUT();
#else
    POSTCALC;
    iter = cfractalc.maxiter - iter;
    OUTPUT();
#endif
periodicity:
    PERIINOUTPUT();
}

/*else uncompress */
#endif

/*endif PERI */
#undef PCHECK
#endif

#ifndef SMOOTHMODE
#ifdef JULIA
static void JULIA(struct image *image, number_t pre, number_t pim)
{
    int i, i1, i2, j, x, y;
    unsigned char iter, itmp2, itmp;
    number_t rp = 0, ip = 0;
    number_t zre, zim, im, xdelta, ydelta, range, rangep;
    number_t xstep, ystep;
    unsigned char *queue[QMAX];
    unsigned char **qptr;
    unsigned char *addr, **addr1 = image->currlines;
#ifdef STATISTICS
    int guessed = 0, unguessed = 0, iters = 0;
#endif
    VARIABLES;
    range = (number_t)RANGE;
    rangep = range * range;

    xdelta = image->width / (RMAX - RMIN);
    ydelta = image->height / (IMAX - IMIN);
    xstep = (RMAX - RMIN) / image->width;
    ystep = (IMAX - IMIN) / image->height;
    init_julia(image, rangep, range, xdelta, ystep);
    for (i2 = 0; i2 < 2; i2++)
        for (i1 = 0; i1 < image->height; i1++) {
            if (i1 % 2)
                i = image->height / 2 - i1 / 2;
            else
                i = image->height / 2 + i1 / 2 + 1;
            if (i >= image->height)
                continue;
            im = IMIN + (i + 0.5) * ystep;
            for (j = (i + i2) & 1; j < image->width; j += 2) {
                STAT(total2++);
                addr = addr1[i] + j;
                if (*addr != NOT_CALCULATED)
                    continue;
                x = j;
                y = i;
                if (y > 0 && y < image->height - 1 && *(addr + 1) && x > 0 &&
                    x < image->width - 1) {
                    if ((iter = *(addr + 1)) != NOT_CALCULATED &&
                        iter == *(addr - 1) && iter == addr1[y - 1][x] &&
                        iter == addr1[y + 1][x]) {
                        *addr = *(addr + 1);
                        continue;
                    }
                }
                zim = im;
                zre = RMIN + (j + 0.5) * xstep;
                iter = (unsigned char)0;
                qptr = queue;
                ip = (zim * zim);
                rp = (zre * zre);
                INIT;
                while (1) {
                    if (*addr != NOT_CALCULATED
#ifdef SAG
                        && (*addr == INPROCESS ||
                            (*addr != (unsigned char)1 &&
                             (itmp2 = *(addr + 1)) != NOT_CALCULATED &&
                             ((itmp2 != (itmp = *(addr - 1)) &&
                               itmp != NOT_CALCULATED) ||
                              (itmp2 != (itmp = *((addr1[y + 1]) + x)) &&
                               itmp != NOT_CALCULATED) ||
                              (itmp2 != (itmp = *((addr1[y - 1]) + x)) &&
                               itmp != NOT_CALCULATED))))
#endif
                    ) {
                        if (*addr == INPROCESS || *addr == INSET) {
                            *qptr = addr;
                            qptr++;
                            STAT(guessed++);
                            goto inset;
                        }
                        STAT(guessed++);
                        iter = *addr;
                        goto outset;
                    }
#ifdef STATISTICS
                    if (*addr != NOT_CALCULATED)
                        unguessed++;
#endif
                    if (*addr != INPROCESS) {
                        *qptr = addr;
                        qptr++;
                        *addr = INPROCESS;
                        if (qptr >= queue + QMAX)
                            goto inset;
                    }
                    STAT(iters++);
                    FORMULA;
                    ip = (zim * zim);
                    rp = (zre * zre);
                    if (greater_than(rp + ip, RANGE) || !(BTEST))
                        goto outset;
                    x = (int)((zre - RMIN) * xdelta);
                    y = (int)((zim - IMIN) * ydelta);
                    addr = addr1[y] + x;
                    if ((itmp = *(addr + 1)) != NOT_CALCULATED &&
                        itmp == *(addr - 1) && itmp == addr1[y - 1][x] &&
                        itmp == addr1[y + 1][x]) {
                        *addr = *(addr + 1);
                    }
                }
            inset:
                while (qptr > queue) {
                    qptr--;
                    **qptr = INSET;
                }
                continue;
            outset:
                y = image->palette->size;
                while (qptr > queue) {
                    qptr--;
                    iter++;
                    if ((int)iter >= y)
                        iter = (unsigned char)1;
                    **qptr = iter;
                }
            }
        }
#ifdef STATISTICS
    printf("guessed %i, unguessed %i, iterations %i\n", guessed, unguessed,
           iters);
    guessed2 += guessed;
    unguessed2 += unguessed;
    iters2 += iters;
#endif
}
#endif
#endif

#undef FORMULALOOP
#undef PCHECK
#undef SAVEZMAG
#ifndef SMOOTHMODE
#ifdef SMOOTH
#define SMOOTHMODE
#include "docalc.h"
#endif
#endif

/*cleanup for next formula */
#undef NSFORMULALOOP
#undef SFORMULALOOP
#undef PRESMOOTH
#undef SMOOTH
#undef SMOOTHMODE
#undef RANGE
#undef JULIA
#undef PERI
#undef SPERI
#undef INIT
#undef VARIABLES
#undef PRETEST
#undef BTEST
#undef FORMULA
#undef CALC
#undef SCALC
#undef RPIP
#undef POSTCALC
#undef UNCOMPRESS
#undef SAVE
#undef SAVEVARIABLES
#undef RESTORE
#undef UFORMULA
#undef UEND
