/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

/*
*	This file contains a wrapper that turns the N-point 32-bit complex
*	FFT into a 2N point real-valued FFT. The complex FFT works with
*	N points in both the time and the frequency domains, this function
*	works with 2N real valued points in the time domain and N complex
*	valued points in the frequency domain.
*
*	The details of the math behind it can be found in any texbook on
*	linear transformations and function analysis, so the explanation is terse
*	here. In brief terms, there are three features of the Fourier transform
*	that allow us to process 2N real valued data points using an N-point
*	complex FFT with only a little additional cost:
*
*	-	The Fourier transform is linear, therefore the transform of the
*		sum of two datasets is the sum of the transforms of the individual
*		sets
*
*	-	The Fourier transform of purely real data ends up with a complex
*		result where the coefficients form complex conjugate pairs, while
*		processing purely imaginary data results in pairs that are negative
*		complex conjugates of each other.
*
*	-	You can build an m*n point transform by doing m n-point transforms,
*		then you multiply the result with a rotation vector, then you send
*		the result to n m-point transforms. In the special case of n and m
*		being relative primes, the rotational vector becomes unity, that's
*		the basis of the Winograd Fourier transform. In our case n is 2^k
*		and m is 2, so we have to do rotations, which cost us as many
*		(real) multiplications as many real data points we have to process.
*
*	Thus, we can cut our real-valued data into two halves, load them into
*	the real and the complex portions of the half-sized complex array, do
*	a complex FFT (with half the size), run through the result and separate
*	the resulting coefficients corresponding to the two halves of the input,
*	do the rotation and do the 2-point FFTs. Since the 2-point FFT is actually
*	an addition and a subtraction, that's a trivial excercise. The inverse
*	transform works the same way just in the opposite direction. The rotational
*	vector comes in a table. The first half of that table happens to be the
*	same what the underlying complex FFT needs, so we can get away with a
*	single unified table.
*/

#include <arm_func.h>

/*
*	Register definitions Do *not* change them!
*/

#define	R1		r0					// Data point 1 real part
#define	I1		r1					// Data point 1 imaginary part
#define	R2		r2					// Data point 2 real part
#define	I2		r3					// Data point 2 imaginary part
#define	RC		r4					// Coefficient real part
#define	IC		r5					// Coefficient imaginary part
#define	ML		r6					// Multiplication result low
#define	MH		r7					// Multiplication result high
#define	LC		r8					// Loop counter
#define	DP		r9					// Data pointer
#define	CP		r10					// Coefficient table pointer
#define	N2		r11					// Transform size
#define	TP		r12					// Temporary pointer

/*
*	Real-valued FFT, using the complex FFT as its core
*	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/

	afunc	fft32r
	sub		r2,r2,#1				// Halve the number of points
	tst		r3,r3					// Inverse transform ?
	bne		1f						// If yes, go there

	// Forward transform processing

	stmdb	sp!,{DP-N2,lr}			// Save some registers
	mov		DP,r0					// Copy the input parameters
	mov		CP,r1					// to the registers where they
	mov		N2,r2					// need to be later
	bl		_blm_fft32c				// Call the complex FFT
	stmdb	sp!,{RC-LC}				// Save all the other registers
	mov		LC,#1					// Loop counter starts at 1
	mov		N2,LC,lsl N2			// Number of elements

0:
	// We have to fetch a data point at f and at -f

	add		TP,DP,LC,lsl #3			// Address of data
	ldmia	TP,{R1,I1}				// Fetch one complex
	sub		TP,N2,LC
	add		TP,DP,TP,lsl #3
	ldmia	TP,{R2,I2}				// Fetch the other complex

	// Now we want to calculate (we can use TP for temporary):
	// R1' = ( R2 + R1 ) / 2
	// I1' = ( I1 - I2 ) / 2
	// R2' = ( I1 + I2 ) / 2
	// I2' = ( R2 - R1 ) / 2

	sub		TP,R2,R1				// TP = 2 * I2'
	add		R1,R2,R1				// R1 = 2 * R1'
	add		R2,I1,I2				// R2 = 2 * R2'
	sub		I1,I1,I2				// I1 = 2 * I1'
	movs	R1,R1,asr #1			// R1 = R1'
	adcpl	R1,R1,#0
	movs	R2,R2,asr #1			// R2 = R2'
	adcpl	R2,R2,#0
	movs	I1,I1,asr #1			// I1 = I1'
	adcpl	I1,I1,#0
	movs	I2,TP,asr #1			// I2 = I2'
	adcpl	I2,I2,#0

	// Now, we have to work out the following values (PI is not a register
	// but the mathematical constant 3.1415...):
	//
	// RC = cos( LC * 2 * PI / N2 )
	// IC = sin( LC * 2 * PI / N2 )
	//
	// The first thing we notice that LC goes from 0 to N2/4, which means
	// that the argument of the functions goes from 0 to PI/2. Now we have
	// a table containing the sine values for that range. The only thing
	// is, that the table is split in two. The first half contains the
	// the elements corresponding to the even values of LC, then an element
	// should be skipped and then there are the values for the odd LCs.
	// Furthermore, the table contains only the sine values, the cosine
	// must be derived by using the identity cos(x) = sin(PI/2-x).

	movs	ML,LC,lsr #1			// Get the primary index, C is 1 if was odd
	rsb		MH,ML,N2,lsr #2			// Get the secondary index
	adccs	ML,ML,N2,lsr #2			// Correct primary index for odd LC
	addcs	MH,MH,N2,lsr #2			// Correct secondary index for odd LC
	ldr		IC,[CP,ML,lsl #2]		// Fetch sin(x)
	ldr		RC,[CP,MH,lsl #2]		// Fetch cos(x)

	// Now we need to do a complex multiplication. We want
	// RC' = RC * R2 - IC * I2
	// IC' = RC * I2 + IC * R2
	// Plus we want the multiplication to be properly rounded.
	// We calculate IC first and then RC. When calculating RC, instead of
	// subtracting we change the signum of I2 (that we don't need any more)
	// and do an addition.

	smull	ML,MH,RC,I2
	smlal	ML,MH,IC,R2
	mov		MH,MH,lsl #2
	orrs	TP,MH,ML,lsr #30
	adcpl	TP,TP,#0
	rsb		I2,I2,#0
	smull	ML,MH,RC,R2
	smlal	ML,MH,IC,I2
	mov		MH,MH,lsl #2
	orrs	RC,MH,ML,lsr #30
	adcpl	RC,RC,#0
	mov		IC,TP

	// Now we have to calculate
	// R1' = R1 + RC
	// I1' = IC + I1
	// R2' = R1 - RC
	// I2' = IC - I1
	// then store it back to where R1,I1,R2,I2 originally came from

	sub		R2,R1,RC
	add		R1,R1,RC
	sub		I2,IC,I1
	add		I1,IC,I1
	add		TP,DP,LC,lsl #3
	stmia	TP,{R1,I1}
	sub		TP,N2,LC
	add		TP,DP,TP,lsl #3
	stmia	TP,{R2,I2}

	// Increment the loop counter and check if we can leave the loop

	add		LC,LC,#1
	cmp		LC,N2,lsr #1
	blt		0b

	// The final step processes the 0th element.

	ldmia	DP,{R1,R2}
	sub		I1,R1,R2
	add		R1,R1,R2
	movs	I1,I1,asr #1
	adcpl	I1,I1,#0
	movs	R1,R1,asr #1
	adcpl	R1,R1,#0
	stmia	DP,{R1,I1}

	// Restore the registers and return

	ldmia	sp!,{RC-N2,lr}
	bx		lr

	// Inverse transform processing
	// Save all registers and move the parameters to their place

1:
	stmdb	sp!,{RC-N2,lr}
	mov		DP,r0
	mov		CP,r1
	mov		lr,r2					// We will need pow2 later
	mov		LC,#1					// Looping starts from 1
	mov		N2,LC,lsl lr
0:
	// Fetch two data points, same as with forward transform

	add		TP,DP,LC,lsl #3
	ldmia	TP,{R1,I1}
	sub		TP,N2,LC
	add		TP,DP,TP,lsl #3
	ldmia	TP,{R2,I2}

	// Now calculate:
	// R1' = R1 + R2
	// I1' = I1 - I2
	// R2' = -( I1 + I2 )
	// I2' = R1 - R2

	add		TP,I1,I2
	sub		I1,I1,I2
	sub		I2,R1,R2
	add		R1,R1,R2
	rsb		R2,TP,#0

	// Fetch the multiplication coefficients and do the complex multiply.
	// Exactly the same as with the forward transform, except that we have
	// to rotate in the opposite direction. Thus, our argument for sin() and
	// cos() is the negative of the corresponding argument of the forward
	// transform. Since cos() is even and sin() is odd, we just fetch the data
	// as with the forward one (i.e. using positive arguments) and negate sin()
	// when we multiply with it.

	movs	ML,LC,lsr #1
	rsb		MH,ML,N2,lsr #2
	adccs	ML,ML,N2,lsr #2
	addcs	MH,MH,N2,lsr #2
	ldr		IC,[CP,ML,lsl #2]
	ldr		RC,[CP,MH,lsl #2]
	smull	ML,MH,RC,R2
	smlal	ML,MH,IC,I2
	mov		MH,MH,lsl #2
	orrs	TP,MH,ML,lsr #30
	adcpl	TP,TP,#0
	rsb		IC,IC,#0
	smull	ML,MH,RC,I2
	smlal	ML,MH,IC,R2
	mov		MH,MH,lsl #2
	orrs	IC,MH,ML,lsr #30
	adcpl	IC,IC,#0
	mov		RC,TP

	// Now calculate:
	// R1' = ( R1 + RC ) / 2
	// I1' = ( IC + I1 ) / 2
	// R2' = ( R1 - RC ) / 2
	// I2' = ( IC - I1 ) / 2
	// then store the result

	sub		I2,IC,I1
	sub		R2,R1,RC
	add		R1,R1,RC
	add		I1,IC,I1
	movs	R1,R1,asr #1
	adcpl	R1,R1,#0
	movs	R2,R2,asr #1
	adcpl	R2,R2,#0
	movs	I1,I1,asr #1
	adcpl	I1,I1,#0
	movs	I2,I2,asr #1
	adcpl	I2,I2,#0
	add		TP,DP,LC,lsl #3
	stmia	TP,{R1,I1}
	sub		TP,N2,LC
	add		TP,DP,TP,lsl #3
	stmia	TP,{R2,I2}

	// Increment the loop counter and check if we can leave the loop

	add		LC,LC,#1
	cmp		LC,N2,lsr #1
	blt		0b

	// Process the DC component element

	ldmia	DP,{R1,I1}
	add		TP,R1,I1
	sub		I1,R1,I1
	mov		R1,TP
	stmia	DP,{R1,I1}

	// Now we can restore almost all registers, then prepare for
	// and execute the call of the inverse complex FFT.

	ldmia	sp!,{RC-LC}
	mov		r0,DP
	mov		r1,CP
	mov		r2,lr
	mov		r3,#-1
	bl		_blm_fft32c

	// Restore the remaining registers and return

	ldmia	sp!,{DP-N2,lr}
	bx		lr
	.endfunc

