/* 
 *     XaoS, a fast portable realtime fractal zoomer 
 *                  Copyright (C) 1996,1997 by
 *
 *      Jan Hubicka          (hubicka@paru.cas.cz)
 *      Thomas Marsh         (tmarsh@austin.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*#define DRAW */
#include <config.h>
#include <fconfig.h>
#ifdef _plan9_
#include <u.h>
#include <libc.h>
#include <stdio.h>
#else
#include <stdlib.h>
#include <stdio.h>
#ifndef _MAC
#ifndef NO_MALLOC_H
#include <malloc.h>
#endif
#endif
#ifdef __EMX__
#include <float.h>
#include <sys/cdefs.h>
#endif
#include <aconfig.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <math.h>
#include <string.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#ifndef _plan9_
/*#undef NDEBUG */
#include <assert.h>
#endif
#endif
#define SLARGEITER
#include <filter.h>
#include <zoom.h>
#include <archaccel.h>
#include <complex.h>		/*for myabs */
#include <plane.h>
#include <btrace.h>
#include <xthread.h>
#include <xerror.h>
#include "calculate.h"		/*an inlined calulate function */

#ifdef HAVE_GETTEXT
#include <libintl.h>
#else
#define gettext(STRING) STRING
#endif

#define ASIZE 16
#define ALIGN(x) (((x)+ASIZE-1)&(~(ASIZE-1)))
static int nsymetrized;
unsigned char *tmpdata, *tmpdata1;
struct realloc_s {
    number_t possition;
    number_t price;
    unsigned int plus;
    int recalculate;
    int symto;
    int symref;
    int dirty;
}
#ifdef __GNUC__
__attribute__ ((aligned(32)))
#endif
    ;
typedef struct realloc_s realloc_t;


typedef struct zoom_context {
    number_t *xpos, *ypos;
    int newcalc;
    int forversion;
    int forpversion;
    realloc_t *reallocx, *reallocy;
    int uncomplette;
    int changed;
} zoom_context;

struct filltable {
    int from;
    int to;
    int length;
    int end;
};
#define getzcontext(f) ((zoom_context *)((f)->data))
#define getfcontext(f) ((f)->fractalc)

#define callwait() if(cfilter.wait_function!=NULL) cfilter.wait_function(&cfilter);
#define tcallwait() if(!xth_nthread(task)&&cfilter.wait_function!=NULL) cfilter.wait_function(&cfilter);
#define setuncomplette(i) (getzcontext(&cfilter)->uncomplette=i)
#define incuncomplette() (getzcontext(&cfilter)->uncomplette++)
#define setchanged(i) (getzcontext(&cfilter)->changed=i)


zoom_context czoomc;
struct filter cfilter;
#ifdef STATISTICS
static int tocalculate = 0, avoided = 0;
static int nadded = 0, nsymetry = 0, nskipped = 0;
int nperi = 0;
#endif

#ifdef _NEVER_
#define rdtsc() ({unsigned long long time; asm __volatile__ ("rdtsc":"=A"(time)); time; })
#define startagi() ({asm __volatile__ ("rdmsr ; andw 0xfe00,%%ax ; orw 0x1f, %%ax ; wrmsr; "::"c"(22):"ax","dx"); })
#define countagi() ({unsigned long long count; asm __volatile__ ("rdmsr":"=A"(count):"c"(12)); count;})
#define cli() ({asm __volatile__ ("cli");})
#define sti() ({asm __volatile__ ("sti");})
#else
#define rdtsc() 0
#define cli() 0
#define sti() 0
#define startagi() 0
#define countagi() 0
#endif

#ifndef USE_i386ASM
static void
moveoldpoints(void *data1, struct taskinfo *task, int r1, int r2)
REGISTERS(0);
static void fillline_8(int line) REGISTERS(0);
static void fillline_16(int line) REGISTERS(0);
static void fillline_24(int line) REGISTERS(0);
static void fillline_32(int line) REGISTERS(0);
#endif

/*first of all inline driver section */
/*If you think this way is ugly, I must agree. Please let me know
 *about better one that allows to generate custom code for 8,16,24,32
 *bpp modes and use of static variables
 */
#include <c256.h>
#define fillline fillline_8
#define dosymetry2 dosymetry2_8
#define calcline calcline_8
#define calccolumn calccolumn_8
#include "zoomd.c"

#include <truecolor.h>
#define fillline fillline_32
#define dosymetry2 dosymetry2_32
#define calcline calcline_32
#define calccolumn calccolumn_32
#include "zoomd.c"

#include <true24.h>
#define fillline fillline_24
#define dosymetry2 dosymetry2_24
#define calcline calcline_24
#define calccolumn calccolumn_24
#include "zoomd.c"

#include <hicolor.h>
#define fillline fillline_16
#define dosymetry2 dosymetry2_16
#define calcline calcline_16
#define calccolumn calccolumn_16
#include "zoomd.c"

#define calcline(a) drivercall(cimage,calcline_8(a),calcline_16(a),calcline_24(a),calcline_32(a));
#define calccolumn(a) drivercall(cimage,calccolumn_8(a),calccolumn_16(a),calccolumn_24(a),calccolumn_32(a));


struct dyn_data {
    int price;
    struct dyn_data *previous;
};

#define FPMUL 64		/*Let multable fit into pentium cache */
#define RANGES 2		/*shift equal to x*RANGE */
#define RANGE 4

#define DSIZEHMASK (0x7)	/*mask equal to x%(DSIZE) */
#define DSIZE (2*RANGE)
#define DSIZES (RANGES+1)	/*shift equal to x*DSIZE */


#define adddata(n,i) (dyndata+(((n)<<DSIZES)+(((i)&(DSIZEHMASK)))))
#define getbest(i) (dyndata+((size)<<DSIZES)+(i))
#define nosetadd ((size*2)<<DSIZES)
#ifndef DEBUG
#define CHECKPOS(pos)
#else
#define CHECKPOS(pos) (assert((pos)>=dyndata),assert((pos)<dyndata+(size)+((size)<<DSIZES)))
#endif

#ifdef __POWERPC__
#  undef USE_MULTABLE
#else
#  define USE_MULTABLE 1
#endif

#ifdef USE_MULTABLE
#define PRICE(i,i1) mulmid[(i)-(i1)]
#else
#define PRICE(i,i1) (((i)-(i1)) * ((i)-(i1)))
#endif
#define NEWPRICE (FPMUL*FPMUL*(RANGE)*(RANGE))

#define NOSETMASK ((unsigned int)0x80000000)
#define END NULL
#define MAXPRICE INT_MAX
/*static int dynsize = (int)sizeof (struct dyn_data);*/
#ifndef INT_MIN
#define INT_MIN (- INT_MAX - 1)
#endif
#define IRANGE FPMUL*RANGE

#ifdef USE_MULTABLE
static int multable[RANGE * FPMUL * 2];
static int *mulmid;
#endif

/*Functions looks trought rows/columns marked for calculation and tries to use
 *some symetrical one instead
 */

/*FIXME should be threaded...but thread overhead should take more work than
 *do it in one, since it is quite simple and executes just in case fractal
 *on the screen is symetrical and it is quite rare case...who knows
 */
static void
				    /*INLINE */ preparesymetries(register realloc_t * realloc, CONST int size,
								 register
								 int symi,
								 number_t
								 sym,
								 number_t
								 step)
{
    register int i;
    register int istart = 0;
    number_t fy, ftmp;
    realloc_t *r = realloc, *reallocs;

    sym *= 2;
    i = 2 * symi - size;
    if (i < 0)
	i = 0;
    realloc += i;

    for (; i <= symi; i++, realloc++) {	/*makes symetries */
	int j, min = 0;
	number_t dist = NUMBER_BIG, tmp1;

	if (realloc->symto != -1)
	    continue;

	fy = realloc->possition;
	realloc->symto = 2 * symi - i;

	if (realloc->symto >= size - RANGE)
	    realloc->symto = size - RANGE - 1;

	dist = RANGE * step;
	min = RANGE;
#ifndef NDEBUG
	if (realloc->symto < 0 || realloc->symto >= size) {
	    x_fatalerror("Internal error #22-1 %i", realloc->symto);
	    assert(0);
	}
#endif
	reallocs = &r[realloc->symto];
	j = (realloc->symto - istart >
	     RANGE) ? -RANGE : (-realloc->symto + istart);

	if (realloc->recalculate) {
	    for (; j < RANGE && realloc->symto + j < size - 1; j++) {
		ftmp = sym - (reallocs + j)->possition;
		if ((tmp1 = myabs(ftmp - fy)) < dist) {
		    if ((realloc == r || ftmp > (realloc - 1)->possition)
			&& (ftmp < (realloc + 1)->possition)) {
			dist = tmp1;
			min = j;
		    }
		} else if (ftmp < fy)
		    break;
	    }

	} else {
	    for (; j < RANGE && realloc->symto + j < size - 1; j++) {
		if (!realloc->recalculate)
		    continue;
		ftmp = sym - (reallocs + j)->possition;
		if ((tmp1 = myabs(ftmp - fy)) < dist) {
		    if ((realloc == r || ftmp > (realloc - 1)->possition)
			&& (ftmp < (realloc + 1)->possition)) {
			dist = tmp1;
			min = j;
		    }
		} else if (ftmp < fy)
		    break;
	    }
	}
	realloc->symto += min;

	if (min == RANGE || realloc->symto <= symi ||
	    (reallocs = reallocs + min)->symto != -1
	    || reallocs->symref != -1) {
	    realloc->symto = -1;
	    continue;
	}

	if (!realloc->recalculate) {
	    realloc->symto = -1;
	    if (reallocs->symto != -1 || !reallocs->recalculate)
		continue;
	    reallocs->plus = realloc->plus;
	    reallocs->symto = i;
	    nsymetrized++;
	    istart = realloc->symto - 1;
	    reallocs->dirty = 1;
	    realloc->symref = (int) (reallocs - r);
	    STAT(nadded -= reallocs->recalculate);
	    reallocs->recalculate = 0;
	    reallocs->possition = sym - realloc->possition;
	} else {
	    if (reallocs->symto != -1) {
		realloc->symto = -1;
		continue;
	    }
	    istart = realloc->symto - 1;
	    STAT(nadded -= realloc->recalculate);
	    nsymetrized++;
	    realloc->dirty = 1;
	    realloc->plus = reallocs->plus;
	    realloc->recalculate = 0;
	    reallocs->symref = i;
	    realloc->possition = sym - reallocs->possition;
	}
	STAT(nsymetry++);

#ifndef NDEBUG
	if (realloc->symto < -1 || realloc->symto >= size) {
	    x_fatalerror("Internal error #22 %i", realloc->symto);
	    assert(0);
	}
	if (reallocs->symto < -1 || reallocs->symto >= size) {
	    x_fatalerror("Internal error #22-2 %i", reallocs->symto);
	    assert(0);
	}
#endif
    }

}

static /*INLINE */ void
newpossitions(realloc_t * realloc, unsigned int size, number_t begin1,
	      number_t end1, CONST number_t * fpos, int yend)
{
    realloc_t *rs, *re, *rend;
    number_t step = size / (end1 - begin1);
    number_t start;
    number_t end;
    rend = realloc + size;
    rs = realloc - 1;
    re = realloc;
    while (rs < rend - 1) {
	re = rs + 1;
	if (re->recalculate) {
	    while (re < rend && re->recalculate)
		re++;

	    if (re == rend)
		end = end1;
	    else
		end = re->possition;

	    if (rs == realloc - 1) {
		start = begin1;
		if (start > end)
		    start = end;
	    } else
		start = rs->possition;

	    if (re == rend && start > end)
		end = start;

	    if (re - rs == 2)
		end = (end - start) * 0.5;
	    else
		end = ((number_t) (end - start)) / (re - rs);


	    switch (yend) {
	    case 1:
		for (rs++; rs < re; rs++) {
		    start += end, rs->possition = start;
		    rs->price =
			1 / (1 + myabs(fpos[rs - realloc] - start) * step);
		}
		break;
	    case 2:
		for (rs++; rs < re; rs++) {
		    start += end, rs->possition = start;
		    rs->price = (myabs(fpos[rs - realloc] - start) * step);
		    if (rs == realloc || rs == rend - 1)
			rs->price *= 500;
		}
		break;
	    default:
		for (rs++; rs < re; rs++) {
		    start += end, rs->possition = start;
		    rs->price = (number_t) 1;
		}
		break;
	    }
	}
	rs = re;
    }
}

/* This is the main reallocation algorithm described in xaos.info
 * It is quite complex since many loops are unrooled and uses custom
 * fixedpoint
 *
 * Takes approx 30% of time so looking for way to do it threaded.
 * Let me know :)
 */
static /*INLINE */ void
mkrealloc_table(CONST number_t * RESTRICT fpos,
		realloc_t * RESTRICT realloc,
		CONST unsigned int size, CONST number_t begin,
		CONST number_t end, number_t sym, unsigned char *tmpdata)
{
    unsigned int i;
    int counter;
    unsigned int ps, ps1 = 0, pe;
    register unsigned int p;
    int bestprice = MAXPRICE;
    realloc_t *r = realloc;
    struct dyn_data *RESTRICT dyndata;
    int yend, y;
    register struct dyn_data **RESTRICT best;
    struct dyn_data **RESTRICT best1, **tmp;
    register int *RESTRICT pos;
    number_t step, tofix;
    int symi = -1;
    unsigned int lastplus = 0;
    struct dyn_data *RESTRICT data;
    register struct dyn_data *previous = NULL, *bestdata = NULL;
    register int myprice;
#ifdef STATISTICS
    nadded = 0, nsymetry = 0, nskipped = 0;
#endif

    pos = (int *) tmpdata;
    best =
	(struct dyn_data **) (tmpdata + ALIGN((size + 2) * sizeof(int)));
    best1 =
	(struct dyn_data **) (tmpdata + ALIGN((size + 2) * sizeof(int)) +
			      ALIGN(size * sizeof(struct dyn_data **)));
    dyndata =
	(struct dyn_data *) (tmpdata + ALIGN((size + 2) * sizeof(int)) +
			     2 * ALIGN(size * sizeof(struct dyn_data **)));

    tofix = size * FPMUL / (end - begin);
    pos[0] = INT_MIN;
    pos++;
    for (counter = (int) size - 1; counter >= 0; counter--) {
	pos[counter] = (int) ((fpos[counter] - begin) * tofix);	/*first convert everything into fixedpoint */
	if (counter < (int) size - 1 && pos[counter] > pos[counter + 1])
	    /*Avoid processing of missordered rows.
	       They should happend because of limited
	       precisity of FP numbers */
	    pos[counter] = pos[counter + 1];
    }
    pos[size] = INT_MAX;
    step = (end - begin) / (number_t) size;
    if (begin > sym || sym > end)	/*calculate symetry point */
	symi = -2;
    else {
	symi = (int) ((sym - begin) / step);

    }

    ps = 0;
    pe = 0;
    y = 0;

    /* This is first pass that fills table dyndata, that holds information
     * about all ways algorithm thinks about. Correct way is discovered at
     * end by looking backward and determining witch way algorithm used to
     * calculate minimal value*/

    for (i = 0; i < size; i++, y += FPMUL) {
	bestprice = MAXPRICE;
	p = ps;			/*just inicialize parameters */

	tmp = best1;
	best1 = best;
	best = tmp;

	yend = y - IRANGE;
	if (yend < -FPMUL)	/*do no allow lines outside screen */
	    yend = -FPMUL;

	while (pos[p] <= yend)	/*skip lines out of range */
	    p++;
#ifdef _UNDEFINED_
	if (pos[p - 1] > yend)	/*warning...maybe this is the bug :) */
	    p--, assert(0);
#endif
	ps1 = p;
	yend = y + IRANGE;

	/*First try case that current line will be newly calculated */

	/*Look for best way how to connect previous lines */
	if (ps != pe && p > ps) {	/*previous point had lines */
	    assert(p >= ps);
	    if (p < pe) {
		previous = best[p - 1];
		CHECKPOS(previous);
	    } else
		previous = best[pe - 1];
	    CHECKPOS(previous);
	    myprice = previous->price;	/*find best one */
	} else {
	    if (i > 0) {	/*previous line had no lines */
		previous = getbest(i - 1);
		myprice = previous->price;
	    } else
		previous = END, myprice = 0;
	}

	data = getbest(i);	/*find store possition */
	myprice += NEWPRICE;
	bestdata = data;
	data->previous = previous;
	bestprice = myprice;	/*calculate best available price */
	data->price = myprice;	/*store data */
	assert(bestprice >= 0);	/*FIXME:tenhle assert muze FAILIT! */
#ifdef _UNDEFINED_
	if (yend > end + FPMUL)	/*check bounds */
	    yend = end + FPMUL;
#endif
	data = adddata(p, i);	/*calcualte all lines good for this y */

	/* Now try all acceptable connection and calculate best possibility
	 * with this connection
	 */
	if (ps != pe) {		/*in case that previous had also possitions */
	    int price1 = INT_MAX;
	    /*At first line of previous interval we have only one possibility
	     *don't connect previous line at all.
	     */
	    if (p == ps) {	/*here we must skip previous point */
		if (pos[p] != pos[p + 1]) {
		    previous = getbest(i - 1);
		    myprice = previous->price;
		    myprice += PRICE(pos[p], y);	/*store data */
		    if (myprice < bestprice) {	/*calcualte best */
			bestprice = myprice, bestdata = data;
			data->price = myprice;
			data->previous = previous;
		    }
		}
		assert(bestprice >= 0);
		assert(myprice >= 0);
		best1[p] = bestdata;
		data += DSIZE;
		p++;
	    }

	    previous = NULL;
	    price1 = myprice;
	    while (p < pe) {	/*this is area where intervals of current point and previous one are crossed */
		if (pos[p] != pos[p + 1]) {
		    if (previous != best[p - 1]) {

			previous = best[p - 1];
			CHECKPOS(previous);
			price1 = myprice = previous->price;

			/*In case we found revolutional point, we should think
			 *about changing our gusesses in last point too - don't
			 *connect it at all, but use this way instead*/
			if (myprice + NEWPRICE < bestprice) {	/*true in approx 2/3 of cases */
			    bestprice = myprice + NEWPRICE, bestdata =
				data - DSIZE;
			    (bestdata)->price = bestprice;
			    (bestdata)->previous = previous + nosetadd;
			    best1[p - 1] = bestdata;
			}
		    } else
			myprice = price1;

		    myprice += PRICE(pos[p], y);	/*calculate price of new connection */

		    if (myprice < bestprice) {	/*2/3 of cases *//*if it is better than previous, store it */
			bestprice = myprice, bestdata = data;
			data->price = myprice;
			data->previous = previous;
		    } else if (pos[p] > y) {
			best1[p] = bestdata;
			data += DSIZE;
			p++;
			break;
		    }

		}

		assert(myprice >= 0);
		assert(bestprice >= 0);	/*FIXME:tenhle assert FAILI! */

		best1[p] = bestdata;
		data += DSIZE;
		p++;
	    }
	    while (p < pe) {	/*this is area where intervals of current point and previous one are crossed */
#ifdef DEBUG
		if (pos[p] != pos[p + 1]) {
		    if (previous != best[p - 1]) {
			x_fatalerror("Missoptimization found!");
		    }
		}
#endif
#ifdef _UNDEFINED_
		/* Experimental results show, that probability for better approximation
		 * in this area is extremly low. Maybe it never happends. 
		 * I will have to think about it a bit more... It seems to have
		 * to do something with meaning of universe and god... no idea
		 * why it is true.
		 *
		 * Anyway it don't seems to worth include the expensive tests
		 * here.
		 */
		if (pos[p] != pos[p + 1]) {
		    if (previous != best[p - 1]) {

			previous = best[p - 1];
			CHECKPOS(previous);
			myprice = previous->price;

			/*In case we found revolutional point, we should think
			 *about changing our gusesses in last point too - don't
			 *connect it at all, but use this way instead*/
			if (myprice + NEWPRICE < bestprice) {	/*true in approx 2/3 of cases */
			    bestprice = myprice + NEWPRICE, bestdata =
				data - DSIZE;
			    (bestdata)->price = bestprice;
			    (bestdata)->previous = previous + nosetadd;
			    best1[p - 1] = bestdata;
			}
			myprice += PRICE(pos[p], y);	/*calculate price of new connection */
			if (myprice < bestprice) {	/*if it is better than previous, store it */
			    bestprice = myprice, bestdata = data;
			    data->price = myprice;
			    data->previous = previous;
			}
		    }
		}
#endif
		assert(myprice >= 0);
		assert(bestprice >= 0);	/*FIXME:tenhle assert FAILI! */

		best1[p] = bestdata;
		data += DSIZE;
		p++;
	    }

	    /* OK...we passed crossed area. All next areas have same previous
	     * situation so our job is easier
	     * So find the best solution once for all od them
	     */
	    if (p > ps) {
		previous = best[p - 1];	/*find best one in previous */
		CHECKPOS(previous);
		price1 = previous->price;
	    } else {
		previous = getbest(i - 1);
		price1 = previous->price;
	    }

	    /* Since guesses for "revolutional point" was allways one
	     * step back, we need to do last one*/
	    if (price1 + NEWPRICE < bestprice && p > ps1) {
		myprice = price1 + NEWPRICE;
		bestprice = myprice, bestdata = data - DSIZE;
		(bestdata)->price = myprice;
		(bestdata)->previous = previous + nosetadd;
		best1[p - 1] = bestdata;
		myprice -= NEWPRICE;
	    }

	    while (pos[p] < yend) {
		if (pos[p] != pos[p + 1]) {
		    myprice = price1;
		    myprice += PRICE(pos[p], y);	/*store data */
		    if (myprice < bestprice) {	/*calcualte best */
			bestprice = myprice, bestdata = data;
			data->price = myprice;
			data->previous = previous;
		    } else if (pos[p] > y)
			break;
		}

		assert(bestprice >= 0);
		assert(myprice >= 0);

		best1[p] = bestdata;
		data += DSIZE;
		p++;
	    }
	    while (pos[p] < yend) {
		best1[p] = bestdata;
		p++;
	    }
	} else {
	    /* This is second case - previous y was not mapped at all.
	     * Situation is simplier now, since we know that behind us is
	     * large hole and our decisions don't affect best solution for
	     * previous problem. Se we have just one answer
	     * Situation is similiar to latest loop in previous case
	     */
	    int myprice1;	/*simplified loop for case that previous
				   y had no lines */
	    if (pos[p] < yend) {
		if (i > 0) {
		    previous = getbest(i - 1);
		    myprice1 = previous->price;
		} else
		    previous = END, myprice1 = 0;
		while (pos[p] < yend) {
		    if (pos[p] != pos[p + 1]) {
			myprice = myprice1 + PRICE(pos[p], y);
			if (myprice < bestprice) {
			    data->price = myprice;
			    data->previous = previous;
			    bestprice = myprice, bestdata = data;
			} else if (pos[p] > y)
			    break;
		    }
		    assert(bestprice >= 0);
		    assert(myprice >= 0);
		    best1[p] = bestdata;
		    p++;
		    data += DSIZE;
		}
		while (pos[p] < yend) {
		    best1[p] = bestdata;
		    p++;
		}
	    }
	}
	/*previous = ps; *//*store possitions for next loop */
	ps = ps1;
	ps1 = pe;
	pe = p;
    }


    assert(bestprice >= 0);

    realloc = realloc + size;
    yend = (int) ((begin > fpos[0]) && (end < fpos[size - 1]));

    if (pos[0] > 0 && pos[size - 1] < (int) size * FPMUL)
	yend = 2;



    /*This part should be made threaded quite easily...but does it worth
     *since it is quite simple loop 0...xmax
     */
    for (i = size; i > 0;) {	/*and finally traces the path */
	struct dyn_data *bestdata1;
	realloc--;
	i--;
	realloc->symto = -1;
	realloc->symref = -1;
	bestdata1 = bestdata->previous;

	if (bestdata1 >= dyndata + nosetadd
	    || bestdata >= dyndata + ((size) << DSIZES)) {
	    if (bestdata1 >= dyndata + nosetadd)
		bestdata1 -= nosetadd;

	    realloc->recalculate = 1;
	    STAT(nadded++);
	    realloc->dirty = 1;
	    lastplus++;

	    if (lastplus >= size)
		lastplus = 0;

	    realloc->plus = lastplus;

	} else {
	    p = ((unsigned int) (bestdata - dyndata)) >> DSIZES;
	    assert(p >= 0 && p < size);
	    realloc->possition = fpos[p];
	    realloc->plus = p;
	    realloc->dirty = 0;
	    realloc->recalculate = 0;
	    lastplus = p;
	}
	bestdata = bestdata1;
    }



    newpossitions(realloc, size, begin, end, fpos, yend);
    realloc = r;
    if (symi <= (int) size && symi >= 0) {
	preparesymetries(r, (int) size, symi, sym, step);
    }


    STAT(printf
	 ("%i added %i skipped %i mirrored\n", nadded, nskipped,
	  nsymetry));
    STAT(nadded2 += nadded;
	 nskipped2 += nskipped;
	 nsymetry2 += nsymetry);
}

struct movedata {
    unsigned int size;
    unsigned int start;
    unsigned int plus;
};
int avgsize;
/* 
 * this function prepares fast moving table for moveoldpoints
 * see xaos.info for details. It is not threaded since it is quite
 * fast.
 */
static /*INLINE */ void preparemoveoldpoints(void)
{
    struct movedata *data, *sizend;
    realloc_t *rx, *rx1, *rend1;
    int sum = 0, num = 0;
    int plus1 = 0;

    data = (struct movedata *) tmpdata;
    for (rx = czoomc.reallocx, rend1 = rx + cimage.width; rx < rend1; rx++)
	if ((rx->dirty) && plus1 < cimage.width + 1)
	    plus1++;
	else
	    break;
    data->start = czoomc.reallocx->plus;
    data->size = 0;
    data->plus = plus1;
    rend1--;
    while (rend1->dirty) {
	if (rend1 == czoomc.reallocx)
	    return;
	rend1--;
    }
    rend1++;
    for (; rx < rend1; rx++) {
	if ((rx->dirty || rx->plus == data->start + data->size))
	    data->size++;
	else {
	    if (data->size) {
		plus1 = 0;
		rx1 = rx - 1;
		while (rx1 > czoomc.reallocx && rx1->dirty)
		    plus1++, data->size--, rx1--;
		if (!
		    (data->start + data->size <
		     (unsigned int) cimage.width)
&& !rx->dirty) {
		    int i;
		    if (rx == rend1)
			break;
		    for (i = 0; rx->dirty && rx < rend1; rx++)
			i++;
		    data++;
		    data->plus = plus1;
		    data->size = (unsigned int) i;
		    data->start = rx->plus - i;
		} else {
		    sum += data->size;
		    num++;
		    data++;
		    data->plus = plus1;
		    data->start = rx->plus;
		}
	    } else
		data->start = rx->plus;
	    assert(rx->plus >= 0
		   && rx->plus < (unsigned int) cimage.width);
	    data->size = 1;
	}

    }
    if (data->size) {
	sizend = data + 1;
	sum += data->size;
	rx1 = rx - 1;
	while (rx1 > czoomc.reallocx && rx1->dirty)
	    data->size--, rx1--;
	num++;
    } else
	sizend = data;
    sizend->size = 0;
    if (cimage.bytesperpixel != 1) {
	sum *= cimage.bytesperpixel;
	for (data = (struct movedata *) tmpdata; data < sizend; data++) {
	    data->plus *= cimage.bytesperpixel;
	    data->size *= cimage.bytesperpixel;
	    data->start *= cimage.bytesperpixel;
	}
    }
    if (num)
	avgsize = sum / num;
}

#ifndef USE_i386ASM
static /*INLINE */ void
moveoldpoints(void /*@unused@ */ *data1,
	      struct taskinfo /*@unused@ */ *task,
	      int r1, int r2)
{
    struct movedata *data;
    register unsigned char *vline, *vbuff;
    realloc_t *ry, *rend;
    int i = r1;

    for (ry = czoomc.reallocy + r1, rend = czoomc.reallocy + r2; ry < rend;
	 ry++, i++) {
	if (!ry->dirty) {
	    assert(ry->plus >= 0
		   && ry->plus < (unsigned int) cimage.height);
	    vbuff = cimage.currlines[i];
	    vline = cimage.oldlines[ry->plus];
	    for (data = (struct movedata *) tmpdata; data->size; data++) {
		vbuff += data->plus;
		memcpy(vbuff, vline + data->start, (size_t) data->size),
		    vbuff += data->size;
	    }
	}
    }
}
#endif
/* This function prepares fast filling tables for fillline */
static /*INLINE */ int mkfilltable(void)
{
    int vsrc;
    int pos;
    realloc_t *rx, *r1, *r2, *rend, *rend2;
    int n = 0;
    int num = 0;
    struct filltable *tbl = (struct filltable *) tmpdata;

    pos = 0;
    vsrc = 0;

    rx = czoomc.reallocx;
    while (rx > czoomc.reallocx && rx->dirty)
	rx--;
    for (rend = czoomc.reallocx + cimage.width, rend2 =
	 czoomc.reallocx + cimage.width; rx < rend; rx++) {
	if (rx->dirty) {
	    r1 = rx - 1;
	    for (r2 = rx + 1; r2 < rend2 && r2->dirty; r2++);
	    while (rx < rend2 && rx->dirty) {
		n = (int) (r2 - rx);
		assert(n > 0);
		if (r2 < rend2
		    && (r1 < czoomc.reallocx
			|| rx->possition - r1->possition >
			r2->possition - rx->possition))
		    vsrc = (int) (r2 - czoomc.reallocx), r1 = r2;
		else {
		    vsrc = (int) (r1 - czoomc.reallocx);
		    if (vsrc < 0)
			goto end;
		}
		pos = (int) (rx - czoomc.reallocx);
		assert(pos >= 0 && pos < cimage.width);
		assert(vsrc >= 0 && vsrc < cimage.width);

		tbl[num].length = n;
		tbl[num].to = pos * cimage.bytesperpixel;
		tbl[num].from = vsrc * cimage.bytesperpixel;
		tbl[num].end =
		    tbl[num].length * cimage.bytesperpixel + tbl[num].to;
		/*printf("%i %i %i %i\n",num,tbl[num].length, tbl[num].to, tbl[num].from); */
		while (n) {
		    rx->possition = czoomc.reallocx[vsrc].possition;
		    rx->dirty = 0;
		    rx++;
		    n--;
		}
		num++;
	    }			/*while rx->dirty */
	}			/*if rx->dirty */
    }				/*for czoomc */
  end:
    tbl[num].length = 0;
    tbl[num].to = pos;
    tbl[num].from = vsrc;
    return num;
}

static /*INLINE */ void filly(void	/*@unused@ */
			      /*@null@ */ *data,
			      struct taskinfo /*@unused@ */ *task, int rr1,
			      int rr2)
{
    register unsigned char **vbuff = cimage.currlines;
    realloc_t *ry, *r1, *r2, *rend, *rend2, *rs = NULL;
    int linesize = cimage.width * cimage.bytesperpixel;

    ry = czoomc.reallocy + rr1;

    ry = czoomc.reallocy + rr1;
    while (ry > czoomc.reallocy && ry->dirty > 0)
	ry--;
    for (rend = czoomc.reallocy + rr2, rend2 =
	 czoomc.reallocy + cimage.height; ry < rend; ry++) {
	if (ry->dirty > 0) {
	    incuncomplette();
	    r1 = ry - 1;
	    for (r2 = ry + 1; r2 < rend2 && r2->dirty > 0; r2++);
#ifdef _UNDEFINED_
	    if (r2 >= rend && (rr2 != cimage.height || ry == 0))
#else
	    if (r2 >= rend2 && (rr2 != cimage.height || ry == 0))
#endif
		return;
	    while (ry < rend2 && ry->dirty > 0) {
		if (r1 < czoomc.reallocy) {
		    rs = r2;
		    if (r2 >= rend2)
			return;
		} else if (r2 >= rend2)
		    rs = r1;
		else if (ry->possition - r1->possition <
			 r2->possition - ry->possition)
		    rs = r1;
		else
		    rs = r2;
		if (!rs->dirty) {
		    drivercall(cimage,
			       fillline_8(rs - czoomc.reallocy),
			       fillline_16(rs - czoomc.reallocy),
			       fillline_24(rs - czoomc.reallocy),
			       fillline_32(rs - czoomc.reallocy));
		    ry->dirty = -1;
		}
		memcpy(vbuff[ry - czoomc.reallocy],
		       vbuff[rs - czoomc.reallocy], (size_t) linesize);
		ry->possition = rs->possition;
		ry->dirty = -1;
		ry++;
	    }
	}
	if (ry < rend && !ry->dirty) {
	    drivercall(cimage,
		       fillline_8(ry - czoomc.reallocy),
		       fillline_16(ry - czoomc.reallocy),
		       fillline_24(ry - czoomc.reallocy),
		       fillline_32(ry - czoomc.reallocy));
	    ry->dirty = -1;
	}
    }
}

static void fill(void)
{
    if (cfilter.interrupt) {
	cfilter.pass = "reducing resolution";
	mkfilltable();
	xth_function(filly, NULL, cimage.height);
    }
    xth_sync();
}

static /*INLINE */ void
calculatenew(void /*@unused@ */ *data, struct taskinfo /*@unused@ */ *task,
	     int /*@unused@ */ r1, int /*@unused@ */ r2)
{
    int s;
    int i, y;
    realloc_t *rx, *ry, *rend;
    int range = cfractalc.range * 2;
    int positions[16];
    int calcpositions[16];
    /*int s3; */
    if (range < 1)
	range = 1;
    if (range > 16)
	range = 16;
    memset(positions, 0, sizeof(positions));
    calcpositions[0] = 0;
    positions[0] = 1;
    for (s = 1; s < range;) {
	for (i = 0; i < range; i++) {
	    if (!positions[i]) {
		for (y = i; y < range && !positions[y]; y++);
		positions[(y + i) / 2] = 1;
		calcpositions[s++] = (y + i) / 2;
	    }
	}
    }

    if (!xth_nthread(task)) {
	STAT(tocalculate = 0);
	STAT(avoided = 0);
	cfilter.pass = gettext("Solid guessing 1");
	cfilter.max = 0;
	cfilter.pos = 0;
    }

    /* We don't need to wory about race conditions here, since only
     * problem that should happend is incorrectly counted number
     * of lines to do...
     *
     * I will fix that problem later, but I think that this information
     * should be quite useless at multithreaded systems so it should
     * be a bit inaccurate. Just need to take care in percentage
     * displayers that thinks like -100% or 150% should happend
     */
    if (!xth_nthread(task)) {
	for (ry = czoomc.reallocy, rend = ry + cimage.height; ry < rend;
	     ry++) {
	    if (ry->recalculate)
		cfilter.max++;
	}
	for (rx = czoomc.reallocx, rend = rx + cimage.width; rx < rend;
	     rx++) {
	    if (rx->recalculate) {
		cfilter.max++;
	    }
	}
    }
    tcallwait();
    for (s = 0; s < range; s++) {
	for (ry = czoomc.reallocy + calcpositions[s], rend =
	     czoomc.reallocy + cimage.height; ry < rend; ry += range) {
	    xth_lock(0);
	    if (ry->recalculate == 1) {
		ry->recalculate = 2;
		xth_unlock(0);
		setchanged(1);
		ry->dirty = 0;
		calcline(ry);
		cfilter.pos++;
#ifndef DRAW
		tcallwait();
#endif
		if (cfilter.interrupt) {
		    break;
		}
	    } else {
		xth_unlock(0);
	    }
	}			/*for ry */
	for (rx = czoomc.reallocx + calcpositions[s], rend =
	     czoomc.reallocx + cimage.width; rx < rend; rx += range) {
	    xth_lock(1);
	    if (rx->recalculate == 1) {
		rx->recalculate = 2;
		xth_unlock(1);
		setchanged(1);
		rx->dirty = 0;
		calccolumn(rx);
		cfilter.pos++;
#ifndef DRAW
		tcallwait();
#endif
		if (cfilter.interrupt) {
		    return;
		}
	    } else {
		xth_unlock(1);
	    }
	}
    }
    STAT(printf
	 ("Avoided caluclating of %i points from %i and %2.2f%% %2.2f%%\n",
	  avoided, tocalculate, 100.0 * (avoided) / tocalculate,
	  100.0 * (tocalculate - avoided) / cimage.width / cimage.height));
    STAT(avoided2 += avoided;
	 tocalculate2 += tocalculate;
	 frames2 += 1);
}

static void addprices(realloc_t * r, realloc_t * r2) REGISTERS(3);
REGISTERS(3)
static void addprices(realloc_t * r, realloc_t * r2)
{
    realloc_t *r3;
    while (r < r2) {
	r3 = r + (((unsigned int) (r2 - r)) >> 1);
	r3->price = (r2->possition - r3->possition) * (r3->price);
	if (r3->symref != -1)
	    r3->price = r3->price / 2;
	addprices(r, r3);
	r = r3 + 1;
    }
}

/* We can't do both symetryies (x and y) in one loop at multithreaded
 * systems,since we need to take care to points at the cross of symetrized
 * point/column
 */
static /*INLINE */ void
dosymetry(void /*@unused@ */ *data, struct taskinfo /*@unused@ */ *task,
	  int r1, int r2)
{
    unsigned char **vbuff = cimage.currlines + r1;
    realloc_t *ry, *rend;
    int linesize = cimage.width * cimage.bytesperpixel;

    for (ry = czoomc.reallocy + r1, rend = czoomc.reallocy + r2; ry < rend;
	 ry++) {
	assert(ry->symto >= 0 || ry->symto == -1);
	if (ry->symto >= 0) {
	    assert(ry->symto < cimage.height);
	    if (!czoomc.reallocy[ry->symto].dirty) {
		memcpy(*vbuff, cimage.currlines[ry->symto],
		       (size_t) linesize);
		ry->dirty = 0;
	    }
	}
	vbuff++;
    }
}

/*Well, clasical simple quicksort. Should be faster than library one
 *because of reduced number of function calls :)
 */
static INLINE void myqsort(realloc_t ** start, realloc_t ** end)
{
    number_t med;
    realloc_t **left = start, **right = end - 1;
    while (1) {

	/*Quite strange caluclation of median, but should be
	 *as good as Sedgewick middle of three method and is faster*/
	med = ((*start)->price + (*(end - 1))->price) * 0.5;

	/*Avoid one comparsion */
	if (med > (*start)->price) {
	    realloc_t *tmp;
	    tmp = *left;
	    *left = *right;
	    *right = tmp;
	}
	right--;
	left++;

	while (1) {
	    realloc_t *tmp;

	    while (left < right && (*left)->price > med)
		left++;
	    while (left < right && med > (*right)->price)
		right--;

	    if (left < right) {
		tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	    } else
		break;
	}
	if (left - start > 1)
	    myqsort(start, left);
	if (end - right <= 2)
	    return;
	left = start = right;
	right = end - 1;
    }
}

static int tocalcx, tocalcy;
static void processqueue(void *data, struct taskinfo /*@unused@ */ *task,
			 int /*@unused@ */ r1, int /*@unused@ */ r2)
{
    realloc_t **tptr = (realloc_t **) data, **tptr1 =
	(realloc_t **) tmpdata;
    realloc_t *r, *end;
    end = czoomc.reallocx + cimage.width;

    while (tptr1 < tptr
	   && (!cfilter.interrupt || tocalcx == cimage.width
	       || tocalcy == cimage.height)) {
	xth_lock(0);
	r = *tptr1;
	if (r != NULL) {
	    *tptr1 = NULL;
	    xth_unlock(0);
	    cfilter.pos++;
	    if (tocalcx < cimage.width - 2 && tocalcy < cimage.height - 2)
		cfilter.readyforinterrupt = 1;
	    tcallwait();
	    if (r >= czoomc.reallocx && r < end) {
		r->dirty = 0;
		tocalcx--;
		calccolumn(r);
	    } else {
		r->dirty = 0;
		tocalcy--;
		calcline(r);
	    }
	} else {
	    xth_unlock(0);
	}
	tptr1++;
    }
}

/*
 * Another long unthreaded code. It seems to be really long and
 * ugly, but believe or not it takes just about 4% of calculation time,
 * so why to worry about? :)
 *
 * This code looks for columns/lines to calculate, adds them into queue,
 * sorts it in order of significancy and then calls parrel processqueue,
 * that does the job.
 */
static void calculatenewinterruptible(void)
{
    realloc_t *r, *r2, *end, *end1;
    realloc_t **table, **tptr;

    /*tptr = table = (realloc_t **) malloc (sizeof (*table) * (cimage.width + cimage.height)); */
    tptr = table = (realloc_t **) tmpdata;
    end = czoomc.reallocx + cimage.width;
    tocalcx = 0, tocalcy = 0;

    STAT(tocalculate = 0);
    STAT(avoided = 0);

    cfilter.pass = gettext("Solid guessing");

    for (r = czoomc.reallocx; r < end; r++)
	if (r->dirty)
	    tocalcx++, setchanged(1);

    for (r = czoomc.reallocx; r < end; r++) {
	if (r->recalculate) {
	    for (r2 = r; r2 < end && r2->recalculate; r2++)
		*(tptr++) = r2;
	    if (r2 == end)
		/*(r2 - 1)->price = 0, */
		r2--;
	    addprices(r, r2);
	    r = r2;
	}
    }

    end1 = czoomc.reallocy + cimage.height;

    for (r = czoomc.reallocy; r < end1; r++)
	if (r->dirty)
	    tocalcy++, setchanged(1);

    for (r = czoomc.reallocy; r < end1; r++) {
	if (r->recalculate) {
	    for (r2 = r; r2 < end1 && r2->recalculate; r2++)
		*(tptr++) = r2;
	    if (r2 == end1)
		/*(r2 - 1)->price = 0, */
		r2--;
	    addprices(r, r2);
	    r = r2;
	}
    }
    if (table != tptr) {

	if (tptr - table > 1)
	    myqsort(table, tptr);

	cfilter.pos = 0;
	cfilter.max = (int) (tptr - table);
	cfilter.incalculation = 1;
	callwait();

	xth_function(processqueue, tptr, 1);

	callwait();
    }

    cfilter.pos = 0;
    cfilter.max = 0;
    cfilter.pass = "Procesing symetries";
    cfilter.incalculation = 0;
    callwait();

    xth_sync();
    if (nsymetrized) {
	xth_function(dosymetry, NULL, cimage.height);
	xth_sync();
	drivercall(cimage,
		   xth_function(dosymetry2_8, NULL, cimage.width),
		   xth_function(dosymetry2_16, NULL, cimage.width),
		   xth_function(dosymetry2_24, NULL, cimage.width),
		   xth_function(dosymetry2_32, NULL, cimage.width));
	xth_sync();
    }
    if (cfilter.interrupt) {
	cfilter.pass = "reducing resolution";
	mkfilltable();
	xth_function(filly, NULL, cimage.height);
    }
    xth_sync();

    STAT(printf
	 ("Avoided caluclating of %i points from %i and %2.2f%% %2.2f%%\n",
	  avoided, tocalculate, 100.0 * (avoided) / tocalculate,
	  100.0 * (tocalculate - avoided) / cimage.width / cimage.height));
    STAT(avoided2 += avoided;
	 tocalculate2 += tocalculate;
	 frames2 += 1);
}

static void init_tables(struct filter *f)
{
    int i;
    zoom_context *c = getzcontext(f);

    /*c->dirty = 2; */
    for (i = 0; i < f->image->width + 1; i++)
	c->xpos[i] =
	    (-f->fractalc->rs.nc + f->fractalc->rs.mc) +
	    f->fractalc->rs.mc;
    for (i = 0; i < f->image->height + 1; i++)
	c->ypos[i] =
	    (-f->fractalc->rs.ni + f->fractalc->rs.mi) +
	    f->fractalc->rs.mi;
}


static int alloc_tables(struct filter *f)
{
    zoom_context *c = getzcontext(f);
    c->xpos =
	(number_t *) malloc((f->image->width + 8) * sizeof(*c->xpos));
    if (c->xpos == NULL)
	return 0;
    c->ypos =
	(number_t *) malloc((f->image->height + 8) * sizeof(*c->ypos));
    if (c->ypos == NULL) {
	free((void *) c->xpos);
	return 0;
    }
    c->reallocx =
	(realloc_t *) malloc(sizeof(realloc_t) * (f->image->width + 8));
    if (c->reallocx == NULL) {
	free((void *) c->xpos);
	free((void *) c->ypos);
	return 0;
    }
    c->reallocy =
	(realloc_t *) malloc(sizeof(realloc_t) * (f->image->height + 8));
    if (c->reallocy == NULL) {
	free((void *) c->xpos);
	free((void *) c->ypos);
	free((void *) c->reallocx);
	return 0;
    }
    return 1;
}

static void free_tables(struct filter *f)
{
    zoom_context *c = getzcontext(f);
    if (c->xpos != NULL)
	free((void *) c->xpos), c->xpos = NULL;
    if (c->ypos != NULL)
	free((void *) c->ypos), c->ypos = NULL;
    if (c->reallocx != NULL)
	free((void *) c->reallocx), c->reallocx = NULL;
    if (c->reallocy != NULL)
	free((void *) c->reallocy), c->reallocy = NULL;
}

static void free_context(struct filter *f)
{
    zoom_context *c;
    c = getzcontext(f);
    free_tables(f);
    free((void *) c);
    f->data = NULL;
}

static zoom_context *make_context(void)
{
    zoom_context *new_ctxt;

    new_ctxt = (zoom_context *) calloc(1, sizeof(zoom_context));
    if (new_ctxt == NULL)
	return NULL;
    new_ctxt->forversion = -1;
    new_ctxt->newcalc = 1;
    new_ctxt->reallocx = NULL;
    new_ctxt->reallocy = NULL;
    new_ctxt->xpos = NULL;
    new_ctxt->ypos = NULL;
    new_ctxt->uncomplette = 0;
    return (new_ctxt);
}

static void startbgmkrealloc(void /*@unused@ */ *data,
			     struct taskinfo /*@unused@ */ *task,
			     int /*@unused@ */ r1,
			     int /*@unused@ */ r2)
{
    mkrealloc_table(czoomc.ypos, czoomc.reallocy,
		    (unsigned int) cimage.height, cfractalc.rs.ni,
		    cfractalc.rs.mi, cursymetry.ysym, tmpdata1);
}

static int do_fractal(struct filter *f, int flags, int /*@unused@ */ time)
{
    number_t *posptr;
    int maxres;
    int size;
    int rflags = 0;
    realloc_t *r, *rend;

    f->image->flip(f->image);
    cfilter = *f;
    set_fractalc(f->fractalc, f->image);

    if (getzcontext(f)->forversion != f->fractalc->version ||
	getzcontext(f)->newcalc ||
	getzcontext(f)->forpversion != f->image->palette->version) {
	clear_image(f->image);
	free_tables(f);
	if (!alloc_tables(f))
	    return 0;
	init_tables(f);
	getzcontext(f)->newcalc = 0;
	getzcontext(f)->forversion = getfcontext(f)->version;
	getzcontext(f)->forpversion = f->image->palette->version;
	czoomc = *getzcontext(f);
	if (BTRACEOK && !(flags & INTERRUPTIBLE)) {
	    boundarytraceall(czoomc.xpos, czoomc.ypos);
	    f->flags &= ~ZOOMMASK;
	    return CHANGED | (cfilter.interrupt ? UNCOMPLETTE : 0);
	}
    } else
	rflags |= INEXACT;

    czoomc = *getzcontext(f);

    setuncomplette(0);
    setchanged(0);

    maxres = cimage.width;
    if (maxres < cimage.height)
	maxres = cimage.height;
    size =
	ALIGN((maxres) * (DSIZE + 1) * (int) sizeof(struct dyn_data)) +
	2 * ALIGN(maxres * (int) sizeof(struct dyn_data **)) +
	ALIGN((maxres + 2) * (int) sizeof(int));
#ifdef HAVE_ALLOCA
    tmpdata = (unsigned char *) alloca(size);
#else
    tmpdata = (unsigned char *) malloc(size);
#endif
    if (tmpdata == NULL) {
	x_error
	    ("XaoS fatal error:Could not allocate memory for temporary data of size %i. "
	     "I am unable to handle this problem so please resize to smaller window.",
	     size);
	return 0;
    }
    if (nthreads != 1) {
#ifdef HAVE_ALLOCA
	tmpdata1 = (unsigned char *) alloca(size);
#else
	tmpdata1 = (unsigned char *) malloc(size);
#endif
	if (tmpdata1 == NULL) {
	    x_error
		("XaoS fatal error:Could not allocate memory for temporary data of size %i. "
		 "I am unable to handle this problem so please resize to smaller window",
		 size);
	    return 0;
	}
    } else
	tmpdata1 = tmpdata;

    cfilter.incalculation = 0;
    cfilter.readyforinterrupt = 0;
    cfilter.interrupt = 0;

    nsymetrized = 0;
    cfilter.max = 0;
    cfilter.pos = 0;
    cfilter.pass = "Making y realloc table";
    xth_bgjob(startbgmkrealloc, NULL);

    cfilter.pass = "Making x realloc table";
    mkrealloc_table(czoomc.xpos, czoomc.reallocx,
		    (unsigned int) cimage.width, cfractalc.rs.nc,
		    cfractalc.rs.mc, cursymetry.xsym, tmpdata);

    callwait();

    cfilter.pass = "Moving old points";
    callwait();
    preparemoveoldpoints();
    xth_sync();
#ifdef _NEVER_
    {
	static long long sum2, sum;
	cli();
	startagi();
	sum -= rdtsc();
	sum2 -= countagi();
	xth_function(moveoldpoints, NULL, cimage.height);
	sum += rdtsc();
	sum2 += countagi();
	sti();
	printf("%i %i\n", (int) sum, (int) sum2);
    }
#else
    xth_function(moveoldpoints, NULL, cimage.height);
#endif

    cfilter.pass = "Starting calculation";
    callwait();
    xth_sync();
    if (flags & INTERRUPTIBLE)
	calculatenewinterruptible();
    else {
	xth_function(calculatenew, NULL, 1);
	if (cfilter.interrupt) {
	    getzcontext(f)->uncomplette = 1;
	}
	cfilter.pos = 0;
	cfilter.max = 0;
	cfilter.pass = "Procesing symetries";
	callwait();
	xth_sync();
	if (nsymetrized) {
	    xth_function(dosymetry, NULL, cimage.height);
	    xth_sync();
	    drivercall(cimage,
		       xth_function(dosymetry2_8, NULL, cimage.width),
		       xth_function(dosymetry2_16, NULL, cimage.width),
		       xth_function(dosymetry2_24, NULL, cimage.width),
		       xth_function(dosymetry2_32, NULL, cimage.width));
	    xth_sync();
	}
	if (getzcontext(f)->uncomplette) {
	    fill();
	}
    }
    for (r = czoomc.reallocx, posptr = czoomc.xpos, rend =
	 czoomc.reallocx + cimage.width; r < rend; r++, posptr++) {
	*posptr = r->possition;
    }
    for (r = czoomc.reallocy, posptr = czoomc.ypos, rend =
	 czoomc.reallocy + cimage.height; r < rend; r++, posptr++) {
	*posptr = r->possition;
    }
#ifdef STATISTICS
    STAT(printf("Statistics: frames %i\n"
		"mkrealloctable: added %i, symetry %i\n"
		"calculate loop: tocalculate %i avoided %i\n"
		"calculate:calculated %i inside %i\n"
		"iters inside:%i iters outside:%i periodicty:%i\n",
		frames2, nadded2, nsymetry2, tocalculate2, avoided2,
		ncalculated2, ninside2, niter2, niter1, nperi));
#endif
    f->flags &= ~ZOOMMASK;
    if (getzcontext(f)->uncomplette)
	rflags |= UNCOMPLETTE, f->flags |= UNCOMPLETTE;
    if (getzcontext(f)->uncomplette > (cimage.width + cimage.height) / 2)
	f->flags |= LOWQUALITY;
    if (getzcontext(f)->changed)
	rflags |= CHANGED;
#ifndef HAVE_ALLOCA
    free(tmpdata);
    if (nthreads != 1)
	free(tmpdata1);
#endif
    return rflags;
}


static struct filter *getinstance(CONST struct filteraction *a)
{
    struct filter *f = createfilter(a);
    f->data = make_context();
    f->name = "Zooming engine";
    return (f);
}

static void destroyinstance(struct filter *f)
{
    free_context(f);
    free(f);
}

static int requirement(struct filter *f, struct requirements *r)
{
    r->nimages = 2;
    r->supportedmask =
	C256 | TRUECOLOR | TRUECOLOR24 | TRUECOLOR16 | LARGEITER |
	SMALLITER | GRAYSCALE;
    r->flags = IMAGEDATA | TOUCHIMAGE;
    return (f->next->action->requirement(f->next, r));
}

static int initialize(struct filter *f, struct initdata *i)
{
#ifdef USE_MULTABLE
    if (!multable[0]) {
	int i;
	mulmid = multable + RANGE * FPMUL;
	for (i = -RANGE * FPMUL; i < RANGE * FPMUL; i++)
	    mulmid[i] = i * i;
    }
#endif
    inhermisc(f, i);
    if (i->image != f->image || datalost(f, i))
	getzcontext(f)->forversion = -1, f->image = i->image;
    f->imageversion = i->image->version;
    return (1);
}

CONST struct filteraction zoom_filter = {
    "XaoS's zooming engine",
    "zoom",
    0,
    getinstance,
    destroyinstance,
    do_fractal,
    requirement,
    initialize,
    convertupgeneric,
    convertdowngeneric,
    NULL,
};
