/* Automatically generated from Squeak on #(21 October 2001 9:18:56 pm) */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Default EXPORT macro that does nothing (see comment in sq.h): */
#define EXPORT(returnType) returnType

/* Do not include the entire sq.h file but just those parts needed. */
/*  The virtual machine proxy definition */
#include "sqVirtualMachine.h"
/* Configuration options */
#include "sqConfig.h"
/* Platform specific definitions */
#include "sqPlatformSpecific.h"

#define true 1
#define false 0
#define null 0  /* using 'null' because nil is predefined in Think C */

/* memory access macros */
#define byteAt(i) (*((unsigned char *) (i)))
#define byteAtput(i, val) (*((unsigned char *) (i)) = val)
#define longAt(i) (*((int *) (i)))
#define longAtput(i, val) (*((int *) (i)) = val)

/*** Variables ***/
static int fftSize;
static float* imagData;
static int imagDataSize;
static struct VirtualMachine* interpreterProxy;
static const char *moduleName = "FFTPlugin 21 October 2001 (i)";
static int nu;
static unsigned int* permTable;
static int permTableSize;
static float* realData;
static int realDataSize;
static float* sinTable;
static int sinTableSize;

/*** Function Prototypes ***/
static float * checkedFloatPtrOf(int oop);
static unsigned int * checkedWordPtrOf(int oop);
#pragma export on
EXPORT(const char*) FFTPlugin_getModuleName(void);
#pragma export off
static int loadFFTFrom(int fftOop);
static int permuteData(void);
#pragma export on
EXPORT(int) FFTPlugin_primitiveFFTPermuteData(void);
EXPORT(int) FFTPlugin_primitiveFFTScaleData(void);
EXPORT(int) FFTPlugin_primitiveFFTTransformData(void);
#pragma export off
static int scaleData(void);
#pragma export on
EXPORT(int) FFTPlugin_setInterpreter(struct VirtualMachine* anInterpreter);
#pragma export off
static int transformData(int forward);
static int transformForward(int forward);


/*	Return the first indexable word of oop which is assumed to be variableWordSubclass */

static float * checkedFloatPtrOf(int oop) {
	interpreterProxy->success(interpreterProxy->isWords(oop));
	if (interpreterProxy->failed()) {
		return 0;
	}
	return ((float *) (interpreterProxy->firstIndexableField(oop)));
}


/*	Return the first indexable word of oop which is assumed to be variableWordSubclass */

static unsigned int * checkedWordPtrOf(int oop) {
	interpreterProxy->success(interpreterProxy->isWords(oop));
	return ((unsigned int *) (interpreterProxy->firstIndexableField(oop)));
}


/*	Note: This is hardcoded so it can be run from Squeak.
	The module name is used for validating a module *after*
	it is loaded to check if it does really contain the module
	we're thinking it contains. This is important! */

EXPORT(const char*) FFTPlugin_getModuleName(void) {
	return moduleName;
}

static int loadFFTFrom(int fftOop) {
    int oop;

	interpreterProxy->success((interpreterProxy->slotSizeOf(fftOop)) >= 6);
	if (interpreterProxy->failed()) {
		return 0;
	}
	nu = interpreterProxy->fetchIntegerofObject(0, fftOop);
	fftSize = interpreterProxy->fetchIntegerofObject(1, fftOop);
	oop = interpreterProxy->fetchPointerofObject(2, fftOop);
	sinTableSize = interpreterProxy->stSizeOf(oop);
	/* begin checkedFloatPtrOf: */
	interpreterProxy->success(interpreterProxy->isWords(oop));
	if (interpreterProxy->failed()) {
		sinTable = 0;
		goto l1;
	}
	sinTable = ((float *) (interpreterProxy->firstIndexableField(oop)));
l1:	/* end checkedFloatPtrOf: */;
	oop = interpreterProxy->fetchPointerofObject(3, fftOop);
	permTableSize = interpreterProxy->stSizeOf(oop);
	/* begin checkedWordPtrOf: */
	interpreterProxy->success(interpreterProxy->isWords(oop));
	permTable = ((unsigned int *) (interpreterProxy->firstIndexableField(oop)));
	oop = interpreterProxy->fetchPointerofObject(4, fftOop);
	realDataSize = interpreterProxy->stSizeOf(oop);
	/* begin checkedFloatPtrOf: */
	interpreterProxy->success(interpreterProxy->isWords(oop));
	if (interpreterProxy->failed()) {
		realData = 0;
		goto l2;
	}
	realData = ((float *) (interpreterProxy->firstIndexableField(oop)));
l2:	/* end checkedFloatPtrOf: */;
	oop = interpreterProxy->fetchPointerofObject(5, fftOop);
	imagDataSize = interpreterProxy->stSizeOf(oop);
	/* begin checkedFloatPtrOf: */
	interpreterProxy->success(interpreterProxy->isWords(oop));
	if (interpreterProxy->failed()) {
		imagData = 0;
		goto l3;
	}
	imagData = ((float *) (interpreterProxy->firstIndexableField(oop)));
l3:	/* end checkedFloatPtrOf: */;
	interpreterProxy->success((((((1 << nu) == fftSize) && (((((int) fftSize >> 2)) + 1) == sinTableSize)) && (fftSize == realDataSize)) && (fftSize == imagDataSize)) && (realDataSize == imagDataSize));
	return (interpreterProxy->failed()) == 0;
}

static int permuteData(void) {
    float tmp;
    int end;
    int i;
    int a;
    int b;

	i = 0;
	end = permTableSize;
	while (i < end) {
		a = (permTable[i]) - 1;
		b = (permTable[i + 1]) - 1;
		if (!((a < realDataSize) && (b < realDataSize))) {
			return interpreterProxy->success(0);
		}
		tmp = realData[a];
		realData[a] = (realData[b]);
		realData[b] = tmp;
		tmp = imagData[a];
		imagData[a] = (imagData[b]);
		imagData[b] = tmp;
		i += 2;
	}
}

EXPORT(int) FFTPlugin_primitiveFFTPermuteData(void) {
    int rcvr;

	rcvr = interpreterProxy->stackObjectValue(0);
	if (!(loadFFTFrom(rcvr))) {
		return null;
	}
	permuteData();
	if (interpreterProxy->failed()) {
		permuteData();
	}
}

EXPORT(int) FFTPlugin_primitiveFFTScaleData(void) {
    int rcvr;

	rcvr = interpreterProxy->stackObjectValue(0);
	if (!(loadFFTFrom(rcvr))) {
		return null;
	}
	scaleData();
}

EXPORT(int) FFTPlugin_primitiveFFTTransformData(void) {
    int rcvr;
    int forward;

	forward = interpreterProxy->booleanValueOf(interpreterProxy->stackValue(0));
	rcvr = interpreterProxy->stackObjectValue(1);
	if (!(loadFFTFrom(rcvr))) {
		return null;
	}
	/* begin transformData: */
	permuteData();
	if (interpreterProxy->failed()) {
		permuteData();
		goto l1;
	}
	transformForward(forward);
	if (!(forward)) {
		scaleData();
	}
l1:	/* end transformData: */;
	if (!(interpreterProxy->failed())) {
		interpreterProxy->pop(1);
	}
}


/*	Scale all elements by 1/n when doing inverse */

static int scaleData(void) {
    int i;
    float realN;

	if (fftSize <= 1) {
		return null;
	}
	realN = ((float) (1.0 / (((double) fftSize))));
	for (i = 0; i <= (fftSize - 1); i += 1) {
		realData[i] = ((realData[i]) * realN);
		imagData[i] = ((imagData[i]) * realN);
	}
}


/*	Note: This is coded so that is can be run from Squeak. */

EXPORT(int) FFTPlugin_setInterpreter(struct VirtualMachine* anInterpreter) {
    int ok;

	interpreterProxy = anInterpreter;
	ok = interpreterProxy->majorVersion() == VM_PROXY_MAJOR;
	if (ok == 0) {
		return 0;
	}
	ok = interpreterProxy->minorVersion() >= VM_PROXY_MINOR;
	return ok;
}

static int transformData(int forward) {
	permuteData();
	if (interpreterProxy->failed()) {
		permuteData();
		return null;
	}
	transformForward(forward);
	if (!(forward)) {
		scaleData();
	}
}

static int transformForward(int forward) {
    int lev1;
    int theta;
    int fftScale;
    float imagT;
    int fftSize2;
    float imagU;
    float realT;
    int i;
    int fftSize4;
    float realU;
    int j;
    int level;
    int ii;
    int lev;
    int ip;

	fftSize2 = ((int) fftSize >> 1);
	fftSize4 = ((int) fftSize >> 2);
	for (level = 1; level <= nu; level += 1) {
		lev = ((level < 0) ? ((unsigned) 1 >> -level) : ((unsigned) 1 << level));
		lev1 = ((int) lev >> 1);
		fftScale = fftSize / lev;
		for (j = 1; j <= lev1; j += 1) {

			/* pi * (j-1) / lev1 mapped onto 0..n/2 */

			theta = (j - 1) * fftScale;
			if (theta < fftSize4) {
				realU = sinTable[(sinTableSize - theta) - 1];
				imagU = sinTable[theta];
			} else {
				realU = 0.0 - (sinTable[theta - fftSize4]);
				imagU = sinTable[fftSize2 - theta];
			}
			if (!(forward)) {
				imagU = 0.0 - imagU;
			}
			i = j;
			while (i <= fftSize) {
				ip = (i + lev1) - 1;
				ii = i - 1;
				realT = ((realData[ip]) * realU) - ((imagData[ip]) * imagU);
				imagT = ((realData[ip]) * imagU) + ((imagData[ip]) * realU);
				realData[ip] = ((realData[ii]) - realT);
				imagData[ip] = ((imagData[ii]) - imagT);
				realData[ii] = ((realData[ii]) + realT);
				imagData[ii] = ((imagData[ii]) + imagT);
				i += lev;
			}
		}
	}
}
