#include <../source/TAL/objbase.h>

/*
 * Copyright:
 *	Copyright(C) 1996-1999 Takuya OOURA
 * 	email: ooura@mmm.t.u-tokyo.ac.jp
 * 	download: http://momonga.t.u-tokyo.ac.jp/~ooura/fft.html
 * 	You may use, copy, modify this code for any purpose and
 *	without fee. You may distribute this ORIGINAL package.
 */

/*
Fast Fourier/Cosine/Sine Transform
	dimension   :one
	data length :power of 2
	decimation  :frequency
	radix       :4, 2
	data        :inplace
	table       :use
functions
	cdft: Complex Discrete Fourier Transform
	rdft: Real Discrete Fourier Transform
	ddct: Discrete Cosine Transform
	ddst: Discrete Sine Transform
	dfct: Cosine Transform of RDFT (Real Symmetric DFT)
	dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)
function prototypes
	void cdft(int, int, vec_t *, int *, vec_t *);
	void rdft(int, int, vec_t *, int *, vec_t *);
	void ddct(int, int, vec_t *, int *, vec_t *);
	void ddst(int, int, vec_t *, int *, vec_t *);
	void dfct(int, vec_t *, vec_t *, int *, vec_t *);
	void dfst(int, vec_t *, vec_t *, int *, vec_t *);


-------- Complex DFT (Discrete Fourier Transform) --------
	[definition]
		<case1>
			X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n
		<case2>
			X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n
		(notes: sum_j=0^n-1 is a summation from j=0 to n-1)
	[usage]
		<case1>
			ip[0] = 0; // first time only
			cdft(2*n, 1, a, ip, w);
		<case2>
			ip[0] = 0; // first time only
			cdft(2*n, -1, a, ip, w);
	[parameters]
		2*n            :data length (int)
						n >= 1, n = power of 2
		a[0...2*n-1]   :input/output data (vec_t *)
						input data
							a[2*j] = Re(x[j]), 
							a[2*j+1] = Im(x[j]), 0<=j<n
						output data
							a[2*k] = Re(X[k]), 
							a[2*k+1] = Im(X[k]), 0<=k<n
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n/2-1]   :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			cdft(2*n, -1, a, ip, w);
		is 
			cdft(2*n, 1, a, ip, w);
			for (j = 0; j <= 2 * n - 1; j++) {
				a[j] *= 1.0 / n;
			}
		.

-------- Real DFT / Inverse of Real DFT --------
	[definition]
		<case1> RDFT
			R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2
			I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2
		<case2> IRDFT (excluding scale)
			a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + 
				   sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + 
				   sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n
	[usage]
		<case1>
			ip[0] = 0; // first time only
			rdft(n, 1, a, ip, w);
		<case2>
			ip[0] = 0; // first time only
			rdft(n, -1, a, ip, w);
	[parameters]
		n              :data length (int)
						n >= 2, n = power of 2
		a[0...n-1]     :input/output data (vec_t *)
						<case1>
							output data
								a[2*k] = R[k], 0<=k<n/2
								a[2*k+1] = I[k], 0<k<n/2
								a[1] = R[n/2]
						<case2>
							input data
								a[2*j] = R[j], 0<=j<n/2
								a[2*j+1] = I[j], 0<j<n/2
								a[1] = R[n/2]
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n/2)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n/2+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n/2-1]   :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			rdft(n, 1, a, ip, w);
		is 
			rdft(n, -1, a, ip, w);
			for (j = 0; j <= n - 1; j++) {
				a[j] *= 2.0 / n;
			}
		.

-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
	[definition]
		<case1> IDCT (excluding scale)
			C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n
		<case2> DCT
			C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n
	[usage]
		<case1>
			ip[0] = 0; // first time only
			ddct(n, 1, a, ip, w);
		<case2>
			ip[0] = 0; // first time only
			ddct(n, -1, a, ip, w);
	[parameters]
		n              :data length (int)
						n >= 2, n = power of 2
		a[0...n-1]     :input/output data (vec_t *)
						output data
							a[k] = C[k], 0<=k<n
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n/2)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n/2+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n*5/4-1] :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			ddct(n, -1, a, ip, w);
		is 
			a[0] *= 0.5;
			ddct(n, 1, a, ip, w);
			for (j = 0; j <= n - 1; j++) {
				a[j] *= 2.0 / n;
			}
		.


-------- DST (Discrete Sine Transform) / Inverse of DST --------
	[definition]
		<case1> IDST (excluding scale)
			S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n
		<case2> DST
			S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n
	[usage]
		<case1>
			ip[0] = 0; // first time only
			ddst(n, 1, a, ip, w);
		<case2>
			ip[0] = 0; // first time only
			ddst(n, -1, a, ip, w);
	[parameters]
		n              :data length (int)
						n >= 2, n = power of 2
		a[0...n-1]     :input/output data (vec_t *)
						<case1>
							input data
								a[j] = A[j], 0<j<n
								a[0] = A[n]
							output data
								a[k] = S[k], 0<=k<n
						<case2>
							output data
								a[k] = S[k], 0<k<n
								a[0] = S[n]
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n/2)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n/2+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n*5/4-1] :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			ddst(n, -1, a, ip, w);
		is 
			a[0] *= 0.5;
			ddst(n, 1, a, ip, w);
			for (j = 0; j <= n - 1; j++) {
				a[j] *= 2.0 / n;
			}
		.


-------- Cosine Transform of RDFT (Real Symmetric DFT) --------
	[definition]
		C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n
	[usage]
		ip[0] = 0; // first time only
		dfct(n, a, t, ip, w);
	[parameters]
		n              :data length - 1 (int)
						n >= 2, n = power of 2
		a[0...n]       :input/output data (vec_t *)
						output data
							a[k] = C[k], 0<=k<=n
		t[0...n/2]     :work area (vec_t *)
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n/4)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n/4+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n*5/8-1] :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			a[0] *= 0.5;
			a[n] *= 0.5;
			dfct(n, a, t, ip, w);
		is 
			a[0] *= 0.5;
			a[n] *= 0.5;
			dfct(n, a, t, ip, w);
			for (j = 0; j <= n; j++) {
				a[j] *= 2.0 / n;
			}
		.


-------- Sine Transform of RDFT (Real Anti-symmetric DFT) --------
	[definition]
		S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n
	[usage]
		ip[0] = 0; // first time only
		dfst(n, a, t, ip, w);
	[parameters]
		n              :data length + 1 (int)
						n >= 2, n = power of 2
		a[0...n-1]     :input/output data (vec_t *)
						output data
							a[k] = S[k], 0<k<n
						(a[0] is used for work area)
		t[0...n/2-1]   :work area (vec_t *)
		ip[0...*]      :work area for bit reversal (int *)
						length of ip >= 2+sqrt(n/4)
						strictly, 
						length of ip >= 
							2+(1<<(int)(log(n/4+0.5)/log(2))/2).
						ip[0],ip[1] are pointers of the cos/sin table.
		w[0...n*5/8-1] :cos/sin table (vec_t *)
						w[],ip[] are initialized if ip[0] == 0.
	[remark]
		Inverse of 
			dfst(n, a, t, ip, w);
		is 
			dfst(n, a, t, ip, w);
			for (j = 1; j <= n - 1; j++) {
				a[j] *= 2.0 / n;
			}
		.


Appendix :
	The cos/sin table is recalculated when the larger table required.
	w[] and ip[] are compatible with all routines.
*/

void makewt(int nw, int *ip, vec_t *w);
void makect(int nc, int *ip, vec_t *c);
void bitrv2(int n, int *ip, vec_t *a);
void bitrv2conj(int n, int *ip, vec_t *a);
void cftfsub(int n, vec_t *a, vec_t *w);
void cftbsub(int n, vec_t *a, vec_t *w);
void rftfsub(int n, vec_t *a, int nc, vec_t *c);
void rftbsub(int n, vec_t *a, int nc, vec_t *c);
void cft1st(int n, vec_t *a, vec_t *w);
void cftmdl(int n, int l, vec_t *a, vec_t *w);

V_LOCAL void
oo_cdft(int n, int isgn, vec_t *a, int *ip, vec_t *w)
{
	if (n > (ip[0] << 2)) {
		makewt(n >> 2, ip, w);
	}
	if (n > 4) {
		if (isgn >= 0) {
			bitrv2(n, ip + 2, a);
			cftfsub(n, a, w);
		} else {
			bitrv2conj(n, ip + 2, a);
			cftbsub(n, a, w);
		}
	} else if (n == 4) {
		cftfsub(n, a, w);
	}
	if(isgn >= 0)
	{
		int j;
		vec_t scale = (vec_t)1.0 / n;
		for (j = 0; j < n; j++)
			a[j] *= scale;
	}
}

V_LOCAL void
oo_rdft(int n, int isgn, vec_t *a, int *ip, vec_t *w)
{
	int nw, nc;
	vec_t xi;

	nw = ip[0];
	if (n > (nw << 2)) {
		nw = n >> 2;
		makewt(nw, ip, w);
	}
	nc = ip[1];
	if (n > (nc << 2)) {
		nc = n >> 2;
		makect(nc, ip, w + nw);
	}
	if (isgn >= 0) {
		if (n > 4) {
			bitrv2(n, ip + 2, a);
			cftfsub(n, a, w);
			rftfsub(n, a, nc, w + nw);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
		xi = a[0] - a[1];
		a[0] += a[1];
		a[1] = xi;
	} else {
		a[1] = 0.5 * (a[0] - a[1]);
		a[0] -= a[1];
		if (n > 4) {
			rftbsub(n, a, nc, w + nw);
			bitrv2(n, ip + 2, a);
			cftbsub(n, a, w);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
	}
	if(isgn < 0)
	{
		int j;
		vec_t scale = (vec_t)2.0 / n;
		for (j = 0; j < n; j++)
			a[j] *= scale;
	}
}

#if 0

void ddct(int n, int isgn, vec_t *a, int *ip, vec_t *w)
{
	void makewt(int nw, int *ip, vec_t *w);
	void makect(int nc, int *ip, vec_t *c);
	void bitrv2(int n, int *ip, vec_t *a);
	void cftfsub(int n, vec_t *a, vec_t *w);
	void cftbsub(int n, vec_t *a, vec_t *w);
	void rftfsub(int n, vec_t *a, int nc, vec_t *c);
	void rftbsub(int n, vec_t *a, int nc, vec_t *c);
	void dctsub(int n, vec_t *a, int nc, vec_t *c);
	int j, nw, nc;
	vec_t xr;

	nw = ip[0];
	if (n > (nw << 2)) {
		nw = n >> 2;
		makewt(nw, ip, w);
	}
	nc = ip[1];
	if (n > nc) {
		nc = n;
		makect(nc, ip, w + nw);
	}
	if (isgn < 0) {
		xr = a[n - 1];
		for (j = n - 2; j >= 2; j -= 2) {
			a[j + 1] = a[j] - a[j - 1];
			a[j] += a[j - 1];
		}
		a[1] = a[0] - xr;
		a[0] += xr;
		if (n > 4) {
			rftbsub(n, a, nc, w + nw);
			bitrv2(n, ip + 2, a);
			cftbsub(n, a, w);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
	}
	dctsub(n, a, nc, w + nw);
	if (isgn >= 0) {
		if (n > 4) {
			bitrv2(n, ip + 2, a);
			cftfsub(n, a, w);
			rftfsub(n, a, nc, w + nw);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
		xr = a[0] - a[1];
		a[0] += a[1];
		for (j = 2; j < n; j += 2) {
			a[j - 1] = a[j] - a[j + 1];
			a[j] += a[j + 1];
		}
		a[n - 1] = xr;
	}
}

void ddst(int n, int isgn, vec_t *a, int *ip, vec_t *w)
{
	void makewt(int nw, int *ip, vec_t *w);
	void makect(int nc, int *ip, vec_t *c);
	void bitrv2(int n, int *ip, vec_t *a);
	void cftfsub(int n, vec_t *a, vec_t *w);
	void cftbsub(int n, vec_t *a, vec_t *w);
	void rftfsub(int n, vec_t *a, int nc, vec_t *c);
	void rftbsub(int n, vec_t *a, int nc, vec_t *c);
	void dstsub(int n, vec_t *a, int nc, vec_t *c);
	int j, nw, nc;
	vec_t xr;

	nw = ip[0];
	if (n > (nw << 2)) {
		nw = n >> 2;
		makewt(nw, ip, w);
	}
	nc = ip[1];
	if (n > nc) {
		nc = n;
		makect(nc, ip, w + nw);
	}
	if (isgn < 0) {
		xr = a[n - 1];
		for (j = n - 2; j >= 2; j -= 2) {
			a[j + 1] = -a[j] - a[j - 1];
			a[j] -= a[j - 1];
		}
		a[1] = a[0] + xr;
		a[0] -= xr;
		if (n > 4) {
			rftbsub(n, a, nc, w + nw);
			bitrv2(n, ip + 2, a);
			cftbsub(n, a, w);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
	}
	dstsub(n, a, nc, w + nw);
	if (isgn >= 0) {
		if (n > 4) {
			bitrv2(n, ip + 2, a);
			cftfsub(n, a, w);
			rftfsub(n, a, nc, w + nw);
		} else if (n == 4) {
			cftfsub(n, a, w);
		}
		xr = a[0] - a[1];
		a[0] += a[1];
		for (j = 2; j < n; j += 2) {
			a[j - 1] = -a[j] - a[j + 1];
			a[j] -= a[j + 1];
		}
		a[n - 1] = -xr;
	}
}


void dfct(int n, vec_t *a, vec_t *t, int *ip, vec_t *w)
{
	void makewt(int nw, int *ip, vec_t *w);
	void makect(int nc, int *ip, vec_t *c);
	void bitrv2(int n, int *ip, vec_t *a);
	void cftfsub(int n, vec_t *a, vec_t *w);
	void rftfsub(int n, vec_t *a, int nc, vec_t *c);
	void dctsub(int n, vec_t *a, int nc, vec_t *c);
	int j, k, l, m, mh, nw, nc;
	vec_t xr, xi, yr, yi;

	nw = ip[0];
	if (n > (nw << 3)) {
		nw = n >> 3;
		makewt(nw, ip, w);
	}
	nc = ip[1];
	if (n > (nc << 1)) {
		nc = n >> 1;
		makect(nc, ip, w + nw);
	}
	m = n >> 1;
	yi = a[m];
	xi = a[0] + a[n];
	a[0] -= a[n];
	t[0] = xi - yi;
	t[m] = xi + yi;
	if (n > 2) {
		mh = m >> 1;
		for (j = 1; j < mh; j++) {
			k = m - j;
			xr = a[j] - a[n - j];
			xi = a[j] + a[n - j];
			yr = a[k] - a[n - k];
			yi = a[k] + a[n - k];
			a[j] = xr;
			a[k] = yr;
			t[j] = xi - yi;
			t[k] = xi + yi;
		}
		t[mh] = a[mh] + a[n - mh];
		a[mh] -= a[n - mh];
		dctsub(m, a, nc, w + nw);
		if (m > 4) {
			bitrv2(m, ip + 2, a);
			cftfsub(m, a, w);
			rftfsub(m, a, nc, w + nw);
		} else if (m == 4) {
			cftfsub(m, a, w);
		}
		a[n - 1] = a[0] - a[1];
		a[1] = a[0] + a[1];
		for (j = m - 2; j >= 2; j -= 2) {
			a[2 * j + 1] = a[j] + a[j + 1];
			a[2 * j - 1] = a[j] - a[j + 1];
		}
		l = 2;
		m = mh;
		while (m >= 2) {
			dctsub(m, t, nc, w + nw);
			if (m > 4) {
				bitrv2(m, ip + 2, t);
				cftfsub(m, t, w);
				rftfsub(m, t, nc, w + nw);
			} else if (m == 4) {
				cftfsub(m, t, w);
			}
			a[n - l] = t[0] - t[1];
			a[l] = t[0] + t[1];
			k = 0;
			for (j = 2; j < m; j += 2) {
				k += l << 2;
				a[k - l] = t[j] - t[j + 1];
				a[k + l] = t[j] + t[j + 1];
			}
			l <<= 1;
			mh = m >> 1;
			for (j = 0; j < mh; j++) {
				k = m - j;
				t[j] = t[m + k] - t[m + j];
				t[k] = t[m + k] + t[m + j];
			}
			t[mh] = t[m + mh];
			m = mh;
		}
		a[l] = t[0];
		a[n] = t[2] - t[1];
		a[0] = t[2] + t[1];
	} else {
		a[1] = a[0];
		a[2] = t[0];
		a[0] = t[1];
	}
}

void dfst(int n, vec_t *a, vec_t *t, int *ip, vec_t *w)
{
	void makewt(int nw, int *ip, vec_t *w);
	void makect(int nc, int *ip, vec_t *c);
	void bitrv2(int n, int *ip, vec_t *a);
	void cftfsub(int n, vec_t *a, vec_t *w);
	void rftfsub(int n, vec_t *a, int nc, vec_t *c);
	void dstsub(int n, vec_t *a, int nc, vec_t *c);
	int j, k, l, m, mh, nw, nc;
	vec_t xr, xi, yr, yi;

	nw = ip[0];
	if (n > (nw << 3)) {
		nw = n >> 3;
		makewt(nw, ip, w);
	}
	nc = ip[1];
	if (n > (nc << 1)) {
		nc = n >> 1;
		makect(nc, ip, w + nw);
	}
	if (n > 2) {
		m = n >> 1;
		mh = m >> 1;
		for (j = 1; j < mh; j++) {
			k = m - j;
			xr = a[j] + a[n - j];
			xi = a[j] - a[n - j];
			yr = a[k] + a[n - k];
			yi = a[k] - a[n - k];
			a[j] = xr;
			a[k] = yr;
			t[j] = xi + yi;
			t[k] = xi - yi;
		}
		t[0] = a[mh] - a[n - mh];
		a[mh] += a[n - mh];
		a[0] = a[m];
		dstsub(m, a, nc, w + nw);
		if (m > 4) {
			bitrv2(m, ip + 2, a);
			cftfsub(m, a, w);
			rftfsub(m, a, nc, w + nw);
		} else if (m == 4) {
			cftfsub(m, a, w);
		}
		a[n - 1] = a[1] - a[0];
		a[1] = a[0] + a[1];
		for (j = m - 2; j >= 2; j -= 2) {
			a[2 * j + 1] = a[j] - a[j + 1];
			a[2 * j - 1] = -a[j] - a[j + 1];
		}
		l = 2;
		m = mh;
		while (m >= 2) {
			dstsub(m, t, nc, w + nw);
			if (m > 4) {
				bitrv2(m, ip + 2, t);
				cftfsub(m, t, w);
				rftfsub(m, t, nc, w + nw);
			} else if (m == 4) {
				cftfsub(m, t, w);
			}
			a[n - l] = t[1] - t[0];
			a[l] = t[0] + t[1];
			k = 0;
			for (j = 2; j < m; j += 2) {
				k += l << 2;
				a[k - l] = -t[j] - t[j + 1];
				a[k + l] = t[j] - t[j + 1];
			}
			l <<= 1;
			mh = m >> 1;
			for (j = 1; j < mh; j++) {
				k = m - j;
				t[j] = t[m + k] + t[m + j];
				t[k] = t[m + k] - t[m + j];
			}
			t[0] = t[m + mh];
			m = mh;
		}
		a[l] = t[0];
	}
	a[0] = 0;
}

#endif

/* -------- initializing routines -------- */


#include <math.h>

void makewt(int nw, int *ip, vec_t *w)
{
	void bitrv2(int n, int *ip, vec_t *a);
	int j, nwh;
	vec_t delta, x, y;

	ip[0] = nw;
	ip[1] = 1;
	if (nw > 2) {
		nwh = nw >> 1;
		delta = atan(1.0) / nwh;
		w[0] = 1;
		w[1] = 0;
		w[nwh] = cos(delta * nwh);
		w[nwh + 1] = w[nwh];
		if (nwh > 2) {
			for (j = 2; j < nwh; j += 2) {
				x = cos(delta * j);
				y = sin(delta * j);
				w[j] = x;
				w[j + 1] = y;
				w[nw - j] = y;
				w[nw - j + 1] = x;
			}
			bitrv2(nw, ip + 2, w);
		}
	}
}

void makect(int nc, int *ip, vec_t *c)
{
	int j, nch;
	vec_t delta;

	ip[1] = nc;
	if (nc > 1) {
		nch = nc >> 1;
		delta = (vec_t)atan(1.0) / nch;
		c[0] = (vec_t)cos(delta * nch);
		c[nch] = (vec_t)0.5 * c[0];
		for (j = 1; j < nch; j++) {
			c[j] = (vec_t)(0.5 * cos(delta * j));
			c[nc - j] = (vec_t)(0.5 * sin(delta * j));
		}
	}
}

/* -------- child routines -------- */

void bitrv2(int n, int *ip, vec_t *a)
{
	int j, j1, k, k1, l, m, m2;
	vec_t xr, xi, yr, yi;

	ip[0] = 0;
	l = n;
	m = 1;
	while ((m << 3) < l) {
		l >>= 1;
		for (j = 0; j < m; j++) {
			ip[m + j] = ip[j] + l;
		}
		m <<= 1;
	}
	m2 = 2 * m;
	if ((m << 3) == l) {
		for (k = 0; k < m; k++) {
			for (j = 0; j < k; j++) {
				j1 = 2 * j + ip[k];
				k1 = 2 * k + ip[j];
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += 2 * m2;
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 -= m2;
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += 2 * m2;
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
			}
			j1 = 2 * k + m2 + ip[k];
			k1 = j1 + m2;
			xr = a[j1];
			xi = a[j1 + 1];
			yr = a[k1];
			yi = a[k1 + 1];
			a[j1] = yr;
			a[j1 + 1] = yi;
			a[k1] = xr;
			a[k1 + 1] = xi;
		}
	} else {
		for (k = 1; k < m; k++) {
			for (j = 0; j < k; j++) {
				j1 = 2 * j + ip[k];
				k1 = 2 * k + ip[j];
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += m2;
				xr = a[j1];
				xi = a[j1 + 1];
				yr = a[k1];
				yi = a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
			}
		}
	}
}

void bitrv2conj(int n, int *ip, vec_t *a)
{
	int j, j1, k, k1, l, m, m2;
	vec_t xr, xi, yr, yi;

	ip[0] = 0;
	l = n;
	m = 1;
	while ((m << 3) < l) {
		l >>= 1;
		for (j = 0; j < m; j++) {
			ip[m + j] = ip[j] + l;
		}
		m <<= 1;
	}
	m2 = 2 * m;
	if ((m << 3) == l) {
		for (k = 0; k < m; k++) {
			for (j = 0; j < k; j++) {
				j1 = 2 * j + ip[k];
				k1 = 2 * k + ip[j];
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += 2 * m2;
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 -= m2;
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += 2 * m2;
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
			}
			k1 = 2 * k + ip[k];
			a[k1 + 1] = -a[k1 + 1];
			j1 = k1 + m2;
			k1 = j1 + m2;
			xr = a[j1];
			xi = -a[j1 + 1];
			yr = a[k1];
			yi = -a[k1 + 1];
			a[j1] = yr;
			a[j1 + 1] = yi;
			a[k1] = xr;
			a[k1 + 1] = xi;
			k1 += m2;
			a[k1 + 1] = -a[k1 + 1];
		}
	} else {
		a[1] = -a[1];
		a[m2 + 1] = -a[m2 + 1];
		for (k = 1; k < m; k++) {
			for (j = 0; j < k; j++) {
				j1 = 2 * j + ip[k];
				k1 = 2 * k + ip[j];
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
				j1 += m2;
				k1 += m2;
				xr = a[j1];
				xi = -a[j1 + 1];
				yr = a[k1];
				yi = -a[k1 + 1];
				a[j1] = yr;
				a[j1 + 1] = yi;
				a[k1] = xr;
				a[k1 + 1] = xi;
			}
			k1 = 2 * k + ip[k];
			a[k1 + 1] = -a[k1 + 1];
			a[k1 + m2 + 1] = -a[k1 + m2 + 1];
		}
	}
}

void cftfsub(int n, vec_t *a, vec_t *w)
{
	int j, j1, j2, j3, l;
	vec_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

	l = 2;
	if (n > 8) {
		cft1st(n, a, w);
		l = 8;
		while ((l << 2) < n) {
			cftmdl(n, l, a, w);
			l <<= 2;
		}
	}
	if ((l << 2) == n) {
		for (j = 0; j < l; j += 2) {
			j1 = j + l;
			j2 = j1 + l;
			j3 = j2 + l;
			x0r = a[j] + a[j1];
			x0i = a[j + 1] + a[j1 + 1];
			x1r = a[j] - a[j1];
			x1i = a[j + 1] - a[j1 + 1];
			x2r = a[j2] + a[j3];
			x2i = a[j2 + 1] + a[j3 + 1];
			x3r = a[j2] - a[j3];
			x3i = a[j2 + 1] - a[j3 + 1];
			a[j] = x0r + x2r;
			a[j + 1] = x0i + x2i;
			a[j2] = x0r - x2r;
			a[j2 + 1] = x0i - x2i;
			a[j1] = x1r - x3i;
			a[j1 + 1] = x1i + x3r;
			a[j3] = x1r + x3i;
			a[j3 + 1] = x1i - x3r;
		}
	} else {
		for (j = 0; j < l; j += 2) {
			j1 = j + l;
			x0r = a[j] - a[j1];
			x0i = a[j + 1] - a[j1 + 1];
			a[j] += a[j1];
			a[j + 1] += a[j1 + 1];
			a[j1] = x0r;
			a[j1 + 1] = x0i;
		}
	}
}

void cftbsub(int n, vec_t *a, vec_t *w)
{
	void cft1st(int n, vec_t *a, vec_t *w);
	void cftmdl(int n, int l, vec_t *a, vec_t *w);
	int j, j1, j2, j3, l;
	vec_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

	l = 2;
	if (n > 8) {
		cft1st(n, a, w);
		l = 8;
		while ((l << 2) < n) {
			cftmdl(n, l, a, w);
			l <<= 2;
		}
	}
	if ((l << 2) == n) {
		for (j = 0; j < l; j += 2) {
			j1 = j + l;
			j2 = j1 + l;
			j3 = j2 + l;
			x0r = a[j] + a[j1];
			x0i = -a[j + 1] - a[j1 + 1];
			x1r = a[j] - a[j1];
			x1i = -a[j + 1] + a[j1 + 1];
			x2r = a[j2] + a[j3];
			x2i = a[j2 + 1] + a[j3 + 1];
			x3r = a[j2] - a[j3];
			x3i = a[j2 + 1] - a[j3 + 1];
			a[j] = x0r + x2r;
			a[j + 1] = x0i - x2i;
			a[j2] = x0r - x2r;
			a[j2 + 1] = x0i + x2i;
			a[j1] = x1r - x3i;
			a[j1 + 1] = x1i - x3r;
			a[j3] = x1r + x3i;
			a[j3 + 1] = x1i + x3r;
		}
	} else {
		for (j = 0; j < l; j += 2) {
			j1 = j + l;
			x0r = a[j] - a[j1];
			x0i = -a[j + 1] + a[j1 + 1];
			a[j] += a[j1];
			a[j + 1] = -a[j + 1] - a[j1 + 1];
			a[j1] = x0r;
			a[j1 + 1] = x0i;
		}
	}
}

void cft1st(int n, vec_t *a, vec_t *w)
{
	int j, k1, k2;
	vec_t wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
	vec_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

	x0r = a[0] + a[2];
	x0i = a[1] + a[3];
	x1r = a[0] - a[2];
	x1i = a[1] - a[3];
	x2r = a[4] + a[6];
	x2i = a[5] + a[7];
	x3r = a[4] - a[6];
	x3i = a[5] - a[7];
	a[0] = x0r + x2r;
	a[1] = x0i + x2i;
	a[4] = x0r - x2r;
	a[5] = x0i - x2i;
	a[2] = x1r - x3i;
	a[3] = x1i + x3r;
	a[6] = x1r + x3i;
	a[7] = x1i - x3r;
	wk1r = w[2];
	x0r = a[8] + a[10];
	x0i = a[9] + a[11];
	x1r = a[8] - a[10];
	x1i = a[9] - a[11];
	x2r = a[12] + a[14];
	x2i = a[13] + a[15];
	x3r = a[12] - a[14];
	x3i = a[13] - a[15];
	a[8] = x0r + x2r;
	a[9] = x0i + x2i;
	a[12] = x2i - x0i;
	a[13] = x0r - x2r;
	x0r = x1r - x3i;
	x0i = x1i + x3r;
	a[10] = wk1r * (x0r - x0i);
	a[11] = wk1r * (x0r + x0i);
	x0r = x3i + x1r;
	x0i = x3r - x1i;
	a[14] = wk1r * (x0i - x0r);
	a[15] = wk1r * (x0i + x0r);
	k1 = 0;
	for (j = 16; j < n; j += 16) {
		k1 += 2;
		k2 = 2 * k1;
		wk2r = w[k1];
		wk2i = w[k1 + 1];
		wk1r = w[k2];
		wk1i = w[k2 + 1];
		wk3r = wk1r - 2 * wk2i * wk1i;
		wk3i = 2 * wk2i * wk1r - wk1i;
		x0r = a[j] + a[j + 2];
		x0i = a[j + 1] + a[j + 3];
		x1r = a[j] - a[j + 2];
		x1i = a[j + 1] - a[j + 3];
		x2r = a[j + 4] + a[j + 6];
		x2i = a[j + 5] + a[j + 7];
		x3r = a[j + 4] - a[j + 6];
		x3i = a[j + 5] - a[j + 7];
		a[j] = x0r + x2r;
		a[j + 1] = x0i + x2i;
		x0r -= x2r;
		x0i -= x2i;
		a[j + 4] = wk2r * x0r - wk2i * x0i;
		a[j + 5] = wk2r * x0i + wk2i * x0r;
		x0r = x1r - x3i;
		x0i = x1i + x3r;
		a[j + 2] = wk1r * x0r - wk1i * x0i;
		a[j + 3] = wk1r * x0i + wk1i * x0r;
		x0r = x1r + x3i;
		x0i = x1i - x3r;
		a[j + 6] = wk3r * x0r - wk3i * x0i;
		a[j + 7] = wk3r * x0i + wk3i * x0r;
		wk1r = w[k2 + 2];
		wk1i = w[k2 + 3];
		wk3r = wk1r - 2 * wk2r * wk1i;
		wk3i = 2 * wk2r * wk1r - wk1i;
		x0r = a[j + 8] + a[j + 10];
		x0i = a[j + 9] + a[j + 11];
		x1r = a[j + 8] - a[j + 10];
		x1i = a[j + 9] - a[j + 11];
		x2r = a[j + 12] + a[j + 14];
		x2i = a[j + 13] + a[j + 15];
		x3r = a[j + 12] - a[j + 14];
		x3i = a[j + 13] - a[j + 15];
		a[j + 8] = x0r + x2r;
		a[j + 9] = x0i + x2i;
		x0r -= x2r;
		x0i -= x2i;
		a[j + 12] = -wk2i * x0r - wk2r * x0i;
		a[j + 13] = -wk2i * x0i + wk2r * x0r;
		x0r = x1r - x3i;
		x0i = x1i + x3r;
		a[j + 10] = wk1r * x0r - wk1i * x0i;
		a[j + 11] = wk1r * x0i + wk1i * x0r;
		x0r = x1r + x3i;
		x0i = x1i - x3r;
		a[j + 14] = wk3r * x0r - wk3i * x0i;
		a[j + 15] = wk3r * x0i + wk3i * x0r;
	}
}

void cftmdl(int n, int l, vec_t *a, vec_t *w)
{
	int j, j1, j2, j3, k, k1, k2, m, m2;
	vec_t wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
	vec_t x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;

	m = l << 2;
	for (j = 0; j < l; j += 2) {
		j1 = j + l;
		j2 = j1 + l;
		j3 = j2 + l;
		x0r = a[j] + a[j1];
		x0i = a[j + 1] + a[j1 + 1];
		x1r = a[j] - a[j1];
		x1i = a[j + 1] - a[j1 + 1];
		x2r = a[j2] + a[j3];
		x2i = a[j2 + 1] + a[j3 + 1];
		x3r = a[j2] - a[j3];
		x3i = a[j2 + 1] - a[j3 + 1];
		a[j] = x0r + x2r;
		a[j + 1] = x0i + x2i;
		a[j2] = x0r - x2r;
		a[j2 + 1] = x0i - x2i;
		a[j1] = x1r - x3i;
		a[j1 + 1] = x1i + x3r;
		a[j3] = x1r + x3i;
		a[j3 + 1] = x1i - x3r;
	}
	wk1r = w[2];
	for (j = m; j < l + m; j += 2) {
		j1 = j + l;
		j2 = j1 + l;
		j3 = j2 + l;
		x0r = a[j] + a[j1];
		x0i = a[j + 1] + a[j1 + 1];
		x1r = a[j] - a[j1];
		x1i = a[j + 1] - a[j1 + 1];
		x2r = a[j2] + a[j3];
		x2i = a[j2 + 1] + a[j3 + 1];
		x3r = a[j2] - a[j3];
		x3i = a[j2 + 1] - a[j3 + 1];
		a[j] = x0r + x2r;
		a[j + 1] = x0i + x2i;
		a[j2] = x2i - x0i;
		a[j2 + 1] = x0r - x2r;
		x0r = x1r - x3i;
		x0i = x1i + x3r;
		a[j1] = wk1r * (x0r - x0i);
		a[j1 + 1] = wk1r * (x0r + x0i);
		x0r = x3i + x1r;
		x0i = x3r - x1i;
		a[j3] = wk1r * (x0i - x0r);
		a[j3 + 1] = wk1r * (x0i + x0r);
	}
	k1 = 0;
	m2 = 2 * m;
	for (k = m2; k < n; k += m2) {
		k1 += 2;
		k2 = 2 * k1;
		wk2r = w[k1];
		wk2i = w[k1 + 1];
		wk1r = w[k2];
		wk1i = w[k2 + 1];
		wk3r = wk1r - 2 * wk2i * wk1i;
		wk3i = 2 * wk2i * wk1r - wk1i;
		for (j = k; j < l + k; j += 2) {
			j1 = j + l;
			j2 = j1 + l;
			j3 = j2 + l;
			x0r = a[j] + a[j1];
			x0i = a[j + 1] + a[j1 + 1];
			x1r = a[j] - a[j1];
			x1i = a[j + 1] - a[j1 + 1];
			x2r = a[j2] + a[j3];
			x2i = a[j2 + 1] + a[j3 + 1];
			x3r = a[j2] - a[j3];
			x3i = a[j2 + 1] - a[j3 + 1];
			a[j] = x0r + x2r;
			a[j + 1] = x0i + x2i;
			x0r -= x2r;
			x0i -= x2i;
			a[j2] = wk2r * x0r - wk2i * x0i;
			a[j2 + 1] = wk2r * x0i + wk2i * x0r;
			x0r = x1r - x3i;
			x0i = x1i + x3r;
			a[j1] = wk1r * x0r - wk1i * x0i;
			a[j1 + 1] = wk1r * x0i + wk1i * x0r;
			x0r = x1r + x3i;
			x0i = x1i - x3r;
			a[j3] = wk3r * x0r - wk3i * x0i;
			a[j3 + 1] = wk3r * x0i + wk3i * x0r;
		}
		wk1r = w[k2 + 2];
		wk1i = w[k2 + 3];
		wk3r = wk1r - 2 * wk2r * wk1i;
		wk3i = 2 * wk2r * wk1r - wk1i;
		for (j = k + m; j < l + (k + m); j += 2) {
			j1 = j + l;
			j2 = j1 + l;
			j3 = j2 + l;
			x0r = a[j] + a[j1];
			x0i = a[j + 1] + a[j1 + 1];
			x1r = a[j] - a[j1];
			x1i = a[j + 1] - a[j1 + 1];
			x2r = a[j2] + a[j3];
			x2i = a[j2 + 1] + a[j3 + 1];
			x3r = a[j2] - a[j3];
			x3i = a[j2 + 1] - a[j3 + 1];
			a[j] = x0r + x2r;
			a[j + 1] = x0i + x2i;
			x0r -= x2r;
			x0i -= x2i;
			a[j2] = -wk2i * x0r - wk2r * x0i;
			a[j2 + 1] = -wk2i * x0i + wk2r * x0r;
			x0r = x1r - x3i;
			x0i = x1i + x3r;
			a[j1] = wk1r * x0r - wk1i * x0i;
			a[j1 + 1] = wk1r * x0i + wk1i * x0r;
			x0r = x1r + x3i;
			x0i = x1i - x3r;
			a[j3] = wk3r * x0r - wk3i * x0i;
			a[j3 + 1] = wk3r * x0i + wk3i * x0r;
		}
	}
}

void rftfsub(int n, vec_t *a, int nc, vec_t *c)
{
	int j, k, kk, ks, m;
	vec_t wkr, wki, xr, xi, yr, yi;

	m = n >> 1;
	ks = 2 * nc / m;
	kk = 0;
	for (j = 2; j < m; j += 2) {
		k = n - j;
		kk += ks;
		wkr = 0.5 - c[nc - kk];
		wki = c[kk];
		xr = a[j] - a[k];
		xi = a[j + 1] + a[k + 1];
		yr = wkr * xr - wki * xi;
		yi = wkr * xi + wki * xr;
		a[j] -= yr;
		a[j + 1] -= yi;
		a[k] += yr;
		a[k + 1] -= yi;
	}
}

void rftbsub(int n, vec_t *a, int nc, vec_t *c)
{
	int j, k, kk, ks, m;
	vec_t wkr, wki, xr, xi, yr, yi;

	a[1] = -a[1];
	m = n >> 1;
	ks = 2 * nc / m;
	kk = 0;
	for (j = 2; j < m; j += 2) {
		k = n - j;
		kk += ks;
		wkr = 0.5 - c[nc - kk];
		wki = c[kk];
		xr = a[j] - a[k];
		xi = a[j + 1] + a[k + 1];
		yr = wkr * xr + wki * xi;
		yi = wkr * xi - wki * xr;
		a[j] -= yr;
		a[j + 1] = yi - a[j + 1];
		a[k] += yr;
		a[k + 1] = yi - a[k + 1];
	}
	a[m + 1] = -a[m + 1];
}

void dctsub(int n, vec_t *a, int nc, vec_t *c)
{
	int j, k, kk, ks, m;
	vec_t wkr, wki, xr;

	m = n >> 1;
	ks = nc / n;
	kk = 0;
	for (j = 1; j < m; j++) {
		k = n - j;
		kk += ks;
		wkr = c[kk] - c[nc - kk];
		wki = c[kk] + c[nc - kk];
		xr = wki * a[j] - wkr * a[k];
		a[j] = wkr * a[j] + wki * a[k];
		a[k] = xr;
	}
	a[m] *= c[0];
}

void dstsub(int n, vec_t *a, int nc, vec_t *c)
{
	int j, k, kk, ks, m;
	vec_t wkr, wki, xr;

	m = n >> 1;
	ks = nc / n;
	kk = 0;
	for (j = 1; j < m; j++) {
		k = n - j;
		kk += ks;
		wkr = c[kk] - c[nc - kk];
		wki = c[kk] + c[nc - kk];
		xr = wki * a[k] - wkr * a[j];
		a[k] = wkr * a[k] + wki * a[j];
		a[j] = xr;
	}
	a[m] *= c[0];
}

/*
 * Wrapper object
 */

typedef struct fft_context_s
{
	libv_object_t base;
	void (*transform)(int, int, vec_t *, int *, vec_t *);
	int pwr, len, sig_forward, sig_inverse;
	u32_t data_len;
	int *bitrev;
	vec_t *sintab;
}	fft_context_t, *fft_context_p;

#ifdef	CHECKED_BUILD
V_LOCAL char *fftc_name = "FFT context";
#define	FFTC_MAGIC	0x46465443 /* FFTC */
#endif	/* CHECKED_BUILD */

V_LOCAL u32_t V_CALL
destroy_fft_context(vhandle_t handle)
{
	fft_context_p self = (fft_context_p)handle;
	if(--self->base.ref_count)
		return self->base.ref_count;
	if(self->bitrev)
		memFree(self->bitrev);
	if(self->sintab)
		memFree(self->sintab);
/*
	if(self->work_area)
		memFree(self->work_area);
*/
	memFreeObject(self);
	return 0;
}

vhandle_t	V_GLOBAL
fftCreateContext(u32_t pwr,vboolean fft_real)
{
	fft_context_p self;
	if((pwr < 2) || (pwr > 31))
		return LIBV_INVALID_HANDLE;
	ALLOC_OBJECT(self,fft_context_t,0,FFTC_MAGIC);
	self->base.destroy = destroy_fft_context;
	self->pwr = pwr;
	self->data_len = 1 << pwr;
	self->len = (int)self->data_len;
	self->bitrev = (int*)memAllocZ(self->len * sizeof(int));
	self->sintab = (vec_t*)memAllocZ(self->len * sizeof(vec_t*));
	if(!self->bitrev || !self->sintab)
	{
		destroy_fft_context(self);
		return LIBV_INVALID_HANDLE;
	}
	if(fft_real)
	{
		u32_t nw = self->len >> 2;
		self->transform = oo_rdft;
		self->sig_forward = 1;
		self->sig_inverse = -1;
		makewt(nw,self->bitrev,self->sintab);
		makect(nw,self->bitrev,self->sintab + nw);
	}
	else
	{
		self->len = self->len << 1;
		self->transform = oo_cdft;
		self->sig_forward = -1;
		self->sig_inverse = 1;
		makewt(self->len >> 2,self->bitrev,self->sintab);
	}
	return self;
}

#define	GET_FFTC(handle,caller)	\
	(fft_context_p)(libvIsHandle(handle,caller,fftc_name,FFTC_MAGIC))

void	V_GLOBAL
fftForward(vhandle_t context,vec_t *samples)
{
	fft_context_p self = GET_FFTC(context,"fftForward");
	if(!self)
		return;
	self->transform(self->len,self->sig_forward,samples,
		self->bitrev,self->sintab);
}

void	V_GLOBAL
fftForward2D(vhandle_t context,matrix_p m)
{
	u32_t i;
	fft_context_p self = GET_FFTC(context,"fftForward2D");
	if(!self)
		return;
	if(m->rows != m->cols || m->rows != self->data_len)
		return;
	for(i=0;i<m->rows;i++)
		self->transform(self->len,self->sig_forward,m->cell[i],
			self->bitrev,self->sintab);
	matTransposeIP(m);
	for(i=0;i<m->rows;i++)
		self->transform(self->len,self->sig_forward,m->cell[i],
			self->bitrev,self->sintab);
}

void	V_GLOBAL
fftInverse(vhandle_t context,vec_t *samples)
{
	fft_context_p self = GET_FFTC(context,"fftInverse");
	if(!self)
		return;
	self->transform(self->len,self->sig_inverse,samples,
		self->bitrev,self->sintab);
}

vboolean	V_GLOBAL
fftGetLen(vhandle_t context,u32_t *len_ret)
{
	fft_context_p self = GET_FFTC(context,"fftGetLen");
	if(!self)
		return false;
	*len_ret = self->len;
	return true;
}

void	V_GLOBAL
fftInverse2D(vhandle_t context,matrix_p m)
{
	u32_t i;
	fft_context_p self = GET_FFTC(context,"fftForward2D");
	if(!self)
		return;
	if(m->rows != m->cols || m->rows != self->data_len)
		return;
	for(i=0;i<m->rows;i++)
		self->transform(self->len,self->sig_inverse,m->cell[i],
			self->bitrev,self->sintab);
	matTransposeIP(m);
	for(i=0;i<m->rows;i++)
		self->transform(self->len,self->sig_inverse,m->cell[i],
			self->bitrev,self->sintab);
}

void V_GLOBAL
fftDestroyContext(vhandle_t handle)
{
	libvCloseHandle(handle,"fftDestroyContext",fftc_name,FFTC_MAGIC);
}

#if 0

/*
 * Cut - and - paste from 'Numerical recipies in C'.
 */
#define NRC_FFT_SWAP(a, b) tempr = (a); (a) = (b); (b) = tempr

/*
 * Replaces data by its discrete Fourier transform, if isign is input as 1;
 * or replaces data by nn times its inverse discrete Fourier transform,
 * if isign is input as -1.
 * data is a complex array of length nn, input as a real array data[1..2*nn].
 * nn MUST be an integer power of 2.
*/

V_LOCAL void
_nrc_four1(vec_t data[], int nn, int isign)
{
	int i, n, mmax, m, j, istep;
	vec_t wtemp, wr, wpr, wpi, wi, theta;
	vec_t tempr, tempi;
	n = nn << 1;
	j = 1;
	/* This is the bit-reversal section of the routine. */
	for (i = 1; i < n; i += 2)
	{
    	if (j > i)
		{
			/* Exchange the two complex numbers. */
	  		NRC_FFT_SWAP(data[j  ],data[i  ]);
			NRC_FFT_SWAP(data[j+1],data[i+1]);
		};
		m = n >> 1;
		while (m >= 2 && j > m)
		{
			j  -= m;
			m >>= 1;
		};
		j += m;
	};
	/* Here begins the Danielson-Lanczos section of the routine. */
	mmax = 2;
	while (n > mmax)
	{
		/* Initialize trigonometric recurrence. */
		theta	= (vec_t)6.28318530717959/(isign*mmax);
		wtemp	= (vec_t)sin(0.5*theta);
		wpr	= (vec_t)(-2.0)*wtemp*wtemp;
		wpi	= (vec_t)sin(theta);
		wr	= 1;
		wi	= 0;
		/* Here are the two nested inner loops. */
		istep	= 2*mmax;
		for (m = 1; m < mmax; m += 2)
		{
			for (i = m; i <= n; i += istep)
			{
				/* This is the Danielson-Lanczos formula. */
				j = i + mmax;
				tempr = wr*data[j  ] - wi*data[j+1];
				tempi = wr*data[j+1] + wi*data[j  ];
				data[j  ] = data[i  ] - tempr;
				data[j+1] = data[i+1] - tempi;
				data[i  ] += tempr;
				data[i+1] += tempi;
			}; /* Trigonometric recurrence. */
			wr = (wtemp = wr)*wpr - wi*wpi + wr;
			wi = wi*wpr + wtemp*wpi + wi;
		};
		mmax = istep;
	};
}

/*
 * Again, cut-and-paste
 */

/*
  Calculates the Fourier Transform of a set of 2n real-valued data points
  stored in array data[1..2n] replacing it by the positive frequency half
  of it's complex Fourier Transform if isign is set to +1.
  The real-valued first and last components of the complex transform
  are returned as elements data[1] and data[2] respectively.
  n must be a power of 2.
  Calculates the inverse transform of a complex data array
  which is the transform of real data if isign is set to -1.
  But the inverse transform array should be multiplied by 1/n.
*/

V_LOCAL void
_nrc_realft(vec_t data[],int n,int isign)
{
	int	 i, i1, i2, i3, i4, n2p3;
	vec_t c1 = 0.5, c2, h1r, h1i, h2r, h2i;
	vec_t wr, wi, wpr, wpi, wtemp, theta;
	theta = (vec_t)M_PI/(vec_t)n;
	if(isign == 1)
	{
		c2 = -(vec_t)0.5;
		_nrc_four1(data, n, 1);
	}
	else
	{
		c2 = (vec_t)0.5;
		theta = -theta;
	}
	wtemp = (vec_t)sin(0.5*theta);
	wpr = -(vec_t)2.0 * wtemp * wtemp;
	wpi = (vec_t)sin(theta);
	wr = 1 + wpr;
	wi = wpi;
	n2p3 = 2*n + 3;
	for (i = 2; i <= n/2; i++ )
	{
		i4	 =   1 + (i3 = n2p3 - (i2 = 1 + (i1 = i + i - 1)));
		h1r	 =  c1*(data[i1] + data[i3]);
		h1i	 =  c1*(data[i2] - data[i4]);
		h2r	 = -c2*(data[i2] + data[i4]);
		h2i	 =  c2*(data[i1] - data[i3]);
		data[i1]	 =  h1r + wr*h2r - wi*h2i;
		data[i2]	 =  h1i + wr*h2i + wi*h2r;
		data[i3]	 =  h1r - wr*h2r + wi*h2i;
		data[i4]	 = -h1i + wr*h2i + wi*h2r;
		wr	 = (wtemp = wr) * wpr - wi * wpi + wr;
		wi	 =  wi*wpr + wtemp*wpi + wi;
	}
	if (isign == 1)
    {
		data[1]	 = (h1r = data[1]) + data[2];
		data[2]	 =  h1r - data[2];
	}
	else
    {
		data[1]	 =  c1*((h1r = data[1]) + data[2]);
		data[2]	 =  c1*(h1r - data[2]);
		_nrc_four1(data, n, -1);
    }
}

void
fft_RealNip(vec_t *dest,vec_t *src,unsigned size_power,vboolean forward)
{
	vec_t scale;
	unsigned i, len;
	if(!dest||!src||!size_power)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fftRealNip(): incorrect parameters.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	len = 1 << size_power;
	memCopy(dest,src,len * sizeof(vec_t));
	_nrc_realft(dest-1,len >> 1,forward ? 1 : -1);
	if(!forward)
	{
		scale = (vec_t)2.0 / (vec_t)len;
		for(i=0;i<len;i++)
			dest[i] *= scale;
	}
}	

void fft2d(matrix_t *m,u32_t size_power,vboolean forward)
{
	u32_t i;
	matrix_t *m2;
	V_ASSERT((m->cols == m->rows) && (m->rows == (u32_t)(1 << size_power)));
	m2 = matDuplicate(m);
	for(i=0;i<m->rows;i++)
		fft_RealNip(m->cell[i],m2->cell[i],size_power,forward);
	matTranspose(m2,m);
	for(i=0;i<m->rows;i++)
		fft_RealNip(m->cell[i],m2->cell[i],size_power,forward);
	memFree(m2);
}

matrix_p load_image(char *name)
{
	u32_t w,h,i,j;
	matrix_p m;
	u8_t *ptr,*img = jpgLoad(name,&w,&h);
	if(!img)
		return NULL;
	m = matAlloc(w,h);
	ptr = img;
	for(i=0;i<m->rows;i++)
		for(j=0;j<m->cols;j++)
		{
			m->cell[i][j] = ptr[0]+ptr[1]+ptr[2];
			ptr += 4;
		}
	memFree(img);
	return m;
}

vec_t mat_diff(matrix_p m1,matrix_p m2)
{
	u32_t i,j;
	vec_t t,d = 0;
	V_ASSERT(m1->rows==m2->rows && m1->cols==m2->cols);
	for(i=0;i<m1->rows;i++)
		for(j=0;j<m1->cols;j++)
		{
			t = v_abs(m1->cell[i][j] - m2->cell[i][j]);
			if(t > d)
				d = t;
		}
	return d;
}

#define PWR			9
#define	DIM			(1 << PWR)
#define	MAX_ITER	10

int main()
{
	u32_t i;
	vhandle_t tmr, ctx;
	matrix_p m1, m2, m;
	char *names[]={"img128.jpg","img256.jpg","img512.jpg"};
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	tmr = timerCreate();
	ctx = fftCreateContext(PWR,true);
	m = load_image(names[PWR-7]);
	m1 = matDuplicate(m);
	m2 = matDuplicate(m);
	timerStart(tmr);
	for(i=0;i<MAX_ITER;i++)
	{
		fftForward2D(ctx,m1);
		fftInverse2D(ctx,m1);
	}
	printf("NEW [%u samples]: %g sec (diff %g).\n",DIM,
		timerDiffTime(tmr),mat_diff(m,m1));
	for(i=0;i<MAX_ITER;i++)
	{
		fft2d(m2,PWR,true);
		fft2d(m2,PWR,false);
	}
	printf("OLD [%u samples]: %g sec (diff %g).\n",DIM,
		timerDiffTime(tmr),mat_diff(m,m2));
	timerDestroy(tmr);
	fftDestroyContext(ctx);
	memFree(m1);
	memFree(m2);
	return 0;
}

/*
 * results for Deschutes-450:
 *
 * MSVC 6.0: 2.17 / 3.66 sec; 1.2e-12 / 1.7e-11 (-O2 -Ot -Gs -G6);
 * Watcom 11: 2.7 / 4.9 sec; 1.2e-12 / 3.2e-12 (-oneatxl+ -s)
 * Borland 5.3: 4.0 / 6.9 sec; 1.3e-12 / 3.0e-12 (-O2 -6)
 * GCC 3.03 (Linux): 2.11 / 5.2; 1.25e-12 / 2.33e-12 (-O4 -ffast-math \
 *	-fexpensive-optimizations -fregmove -fomit-frame-pointer \
 *	-funroll-loops -frerun-loop-opt -fschedule-insns2 \
 *	-falign-functions -falign-loops -falign-jumps -fgcse)
 */

#endif /* 0 */
