/*
 * conv.c
 *
 *  Created on: May 1, 2013
 *      Author: Rafat Hussain
 */

#include "conv.h"

int factorf(int M)
{
	int N = M;
	while (N % 7 == 0) { N = N / 7; }
	while (N % 3 == 0) { N = N / 3; }
	while (N % 5 == 0) { N = N / 5; }
	while (N % 2 == 0) { N = N / 2; }

	return N;
}


int findnext(int M)
{
	int N = M;

	while (factorf(N) != 1) { ++N; }

	return N;
}

int findnexte(int M)
{
	int N = M;

	while (factorf(N) != 1 || N % 2 != 0) { ++N; }

	return N;
}


conv_object conv_init(int N, int L)
{
	const int conv_len    = N + L - 1;
	const conv_object obj = (conv_object)malloc(sizeof(struct conv_set));
	 
	//obj->clen = npow2(conv_len);
	//obj->clen = conv_len;
	obj->clen  = findnexte(conv_len);
	obj->ilen1 = N;
	obj->ilen2 = L;

	obj->fobj = fft_real_init(obj->clen, 1);
	obj->iobj = fft_real_init(obj->clen, -1);

	return obj;
}

void conv_directx(fft_type* inp1, int N, fft_type* inp2, int L,fft_type* oup)
{
	const int M = N + L - 1;

	for (int k = 0; k < M; ++k)
	{
		oup[k] = 0.0;
		for (int n = 0; n < N; ++n) { if ((k - n) >= 0 && (k - n) < L) { oup[k] += inp1[n] * inp2[k - n]; } }
	}
}

void conv_direct(fft_type* inp1, int N, fft_type* inp2, int L,fft_type* oup)
{
	int k, m;
	fft_type t1, tmin;

	const int M = N + L - 1;
	int i       = 0;

	if (N >= L)
	{
		for (k = 0; k < L; ++k)
		{
			oup[k] = 0.0;
			for (m = 0; m <= k; ++m) { oup[k] += inp1[m] * inp2[k - m]; }
		}

		for (k = L; k < M; ++k)
		{
			oup[k] = 0.0;
			i++;
			t1   = L + i;
			tmin = MIN(t1, N);
			for (m = i; m < tmin; ++m) { oup[k] += inp1[m] * inp2[k - m]; }
		}
	}
	else
	{
		for (k = 0; k < N; ++k)
		{
			oup[k] = 0.0;
			for (m = 0; m <= k; ++m) { oup[k] += inp2[m] * inp1[k - m]; }
		}

		for (k = N; k < M; ++k)
		{
			oup[k] = 0.0;
			i++;
			t1   = N + i;
			tmin = MIN(t1, L);
			for (m = i; m < tmin; ++m) { oup[k] += inp2[m] * inp1[k - m]; }
		}
	}
}


void conv_fft(const conv_object obj,fft_type* inp1,fft_type* inp2,fft_type* oup)
{
	int i;

	const int N  = obj->clen;
	const int L1 = obj->ilen1;
	const int L2 = obj->ilen2;
	const int ls = L1 + L2 - 1;

	fft_type* a  = (fft_type*)malloc(sizeof(fft_data) * N);
	fft_type* b  = (fft_type*)malloc(sizeof(fft_data) * N);
	fft_data* c  = (fft_data*)malloc(sizeof(fft_data) * N);
	fft_data* ao = (fft_data*)malloc(sizeof(fft_data) * N);
	fft_data* bo = (fft_data*)malloc(sizeof(fft_data) * N);
	fft_type* co = (fft_type*)malloc(sizeof(fft_data) * N);

	for (i = 0; i < N; ++i)
	{
		if (i < L1) { a[i] = inp1[i]; }
		else { a[i] = 0.0; }

		if (i < L2) { b[i] = inp2[i]; }
		else { b[i] = 0.0; }
	}

	fft_r2c_exec(obj->fobj, a, ao);
	fft_r2c_exec(obj->fobj, b, bo);

	for (i = 0; i < N; ++i)
	{
		c[i].re = ao[i].re * bo[i].re - ao[i].im * bo[i].im;
		c[i].im = ao[i].im * bo[i].re + ao[i].re * bo[i].im;
	}

	fft_c2r_exec(obj->iobj, c, co);

	for (i = 0; i < ls; ++i) { oup[i] = co[i] / N; }

	free(a);
	free(b);
	free(c);
	free(ao);
	free(bo);
	free(co);
}


void free_conv(conv_object object)
{
	free_real_fft(object->fobj);
	free_real_fft(object->iobj);
	free(object);
}
