/*
 * engine.c
 */

#include <wms.h>
#include "base.h"
#include "engine.h"

/**********************************************************************
 * Constants
 **********************************************************************/

#define  LOOP_UNROLL  0


/**********************************************************************
 * Forward Declarations
 **********************************************************************/
static int calcRow(UInt8 *out, int w, Real cr1, Real pixelStep, Real cI,
		   int limit);


/**********************************************************************
 * Functions
 **********************************************************************/
int  engine(Engine *e, int y, UInt8 *dataBuffer) {
  Real  wRadius, hRadius, pixelStep;

  if (e->w > e->h) {
    hRadius = e->radius;
    pixelStep = 2.0 * hRadius / (Real)(e->h - 1);
    wRadius = hRadius * (Real)e->w / (Real)e->h;
  } else {
    wRadius = e->radius;
    pixelStep = 2.0 * wRadius / (Real)(e->w - 1);
    hRadius = wRadius * (Real)e->w / (Real)e->h;
  }
  return(calcRow(dataBuffer, e->w,
		 e->centerR - wRadius, pixelStep,
		 e->centerI - hRadius + (pixelStep * (Real)y),
		 e->limit));
}


#if !LOOP_UNROLL

static int calcRow(UInt8 *out, int w, Real cr1, Real pixelStep, Real cI,
		   int limit) {
  int  i, reps;
  int  latecomers = 0;
  Real  cR, xR, xI;
  Real  xR2, xI2;

  for (i = 0;  i < w;  ++i) {
    cR = cr1 + (Real)i * pixelStep;
    xR = 0.0;
    xI = 0.0;
    xR2 = 0.0;
    xI2 = 0.0;
    for (reps = 0;  reps < limit;  ++reps) {
      xI = 2.0 * xR * xI + cI;
      xR = xR2 - xI2 + cR;
      xI2 = xI * xI;
      xR2 = xR * xR;
      if (xI2 + xR2 > 4.0) {
	if (reps > limit / 2) {
	  ++latecomers;
	}
	break;
      }
    }
    if (reps == limit) {
      *(out++) = base_limit;
    } else {
      *(out++) = reps & (base_limit - 1);
    }
  }
  return(latecomers);
}

#else  /* LOOP_UNROLL */

/*
 * I thought that by unrolling the loop by one, I'd be able to get better
 *   performance because there'd be fewer dependencies in the computations.
 *   It turns out that unrolling the loop slows me down by 50% on my
 *   Pentium-II system with gcc 2.7.2.1! So I never finished this code. It
 *   won't finish the row, will leave the last few pixels empty.
 * When I get a new CPU I may give this another try.
 */
static int calcRow(UInt8 *out, int w, Real cr1, Real crN, Real cI, int limit,
		   int numColors) {
  int  col, repsA, repsB;
  int  latecomers = 0;
  Real  cRA, xRA, newXRA, xIA;
  Real  cRB, xRB, newXRB, xIB;
  UInt8  *storeA, *storeB;

  col = 0;

  storeA = out++;
  repsA = 0;
  cRA = cr1 + ((crN - cr1) * (Real)(col++) / (Real)(w - 1));
  xRA = 0.0;
  xIA = 0.0;
  
  storeB = out++;
  repsB = 0;
  cRB = cr1 + ((crN - cr1) * (Real)(col++) / (Real)(w - 1));
  xRB = 0.0;
  xIB = 0.0;
  
  for (;;) {
    newXRA = xRA * xRA - xIA * xIA + cRA;
    newXRB = xRB * xRB - xIB * xIB + cRB;
    xIA = 2.0 * xRA * xIA + cI;
    xIB = 2.0 * xRB * xIB + cI;
    xRA = newXRA;
    xRB = newXRB;
    
    if ((xIA * xIA + xRA * xRA > 4.0) || (++repsA > limit)) {
      if (repsA > limit) {
	*storeA = numColors;
      } else {
	if (repsA > limit / 2) {
	  ++latecomers;
	}
	*(out++) = (UInt8)repsA & (numColors - 1);
      }
      if (col > w) {
	return(latecomers);
      } else {
	storeA = out++;
	repsA = 0;
	cRA = cr1 + ((crN - cr1) * (Real)(col++) / (Real)(w - 1));
	xRA = 0.0;
	xIA = 0.0;
      }  
    }

    if ((xIB * xIB + xRB * xRB > 4.0) || (++repsB > limit)) {
      if (repsB > limit) {
	*storeB = numColors;
      } else {
	if (repsB > limit / 2) {
	  ++latecomers;
	}
	*(out++) = (UInt8)repsB & (numColors - 1);
      }
      if (col > w) {
	return(latecomers);
      } else {
	storeB = out++;
	repsB = 0;
	cRB = cr1 + ((crN - cr1) * (Real)(col++) / (Real)(w - 1));
	xRB = 0.0;
	xIB = 0.0;
      }  
    }
  }
}

#endif /* LOOP_UNROLL */
