#include "wavefunc.h"

void meyer(const int N, const double lb, const double ub, double* phi, double* psi, double* tgrid)
{
	int i;
	double theta, x, x2, x3, x4, v, cs;

	const int M = divideby(N, 2);

	if (M == 0)
	{
		printf("Size of Wavelet must be a power of 2");
		exit(1);
	}
	if (lb >= ub)
	{
		printf("upper bound must be greater than lower bound");
		exit(1);
	}

	const fft_object obj = fft_init(N, -1);
	double* w            = (double*)malloc(sizeof(double) * N);
	fft_data* phiw       = (fft_data*)malloc(sizeof(fft_data) * N);
	fft_data* psiw       = (fft_data*)malloc(sizeof(fft_data) * N);
	fft_data* oup        = (fft_data*)malloc(sizeof(fft_data) * N);

	const double delta = 2 * (ub - lb) / PI2;

	double j = (double)N;
	j *= -1.0;

	for (i = 0; i < N; ++i)
	{
		w[i] = j / delta;
		j += 2.0;
		psiw[i].re = psiw[i].im = 0.0;
		phiw[i].re = phiw[i].im = 0.0;
	}


	for (i = 0; i < N; ++i)
	{
		const double wf = fabs(w[i]);
		if (wf <= PI2 / 3.0) { phiw[i].re = 1.0; }
		if (wf > PI2 / 3.0 && wf <= 2 * PI2 / 3.0)
		{
			x               = (3 * wf / PI2) - 1.0;
			x2              = x * x;
			x3              = x2 * x;
			x4              = x3 * x;
			v               = x4 * (35 - 84 * x + 70 * x2 - 20 * x3);
			theta           = v * PI2 / 4.0;
			cs              = cos(theta);
			const double sn = sin(theta);

			phiw[i].re = cs;
			psiw[i].re = cos(w[i] / 2.0) * sn;
			psiw[i].im = sin(w[i] / 2.0) * sn;
		}
		if (wf > 2.0 * PI2 / 3.0 && wf <= 4 * PI2 / 3.0)
		{
			x     = (1.5 * wf / PI2) - 1.0;
			x2    = x * x;
			x3    = x2 * x;
			x4    = x3 * x;
			v     = x4 * (35 - 84 * x + 70 * x2 - 20 * x3);
			theta = v * PI2 / 4.0;
			cs    = cos(theta);

			psiw[i].re = cos(w[i] / 2.0) * cs;
			psiw[i].im = sin(w[i] / 2.0) * cs;
		}
	}


	nsfft_exec(obj, phiw, oup, lb, ub, tgrid);


	for (i = 0; i < N; ++i) { phi[i] = oup[i].re / N; }

	nsfft_exec(obj, psiw, oup, lb, ub, tgrid);


	for (i = 0; i < N; ++i) { psi[i] = oup[i].re / N; }


	free(oup);
	free(phiw);
	free(psiw);
	free(w);
}

void gauss(int N, int p, double lb, double ub, double* psi, double* t)
{
	double num, t2, t4;
	int i;

	if (lb >= ub)
	{
		printf("upper bound must be greater than lower bound");
		exit(1);
	}

	t[0]               = lb;
	t[N - 1]           = ub;
	const double delta = (ub - lb) / (N - 1);
	for (i = 1; i < N - 1; ++i) { t[i] = lb + delta * i; }

	const double den = sqrt(cwt_gamma(p + 0.5));

	if ((p + 1) % 2 == 0) { num = 1.0; }
	else { num = -1.0; }

	num /= den;
	
	//printf("\n%g\n",num);

	if (p == 1) { for (i = 0; i < N; ++i) { psi[i] = -t[i] * exp(- t[i] * t[i] / 2.0) * num; } }
	else if (p == 2)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = (-1.0 + t2) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 3)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = t[i] * (3.0 - t2) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 4)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = (t2 * t2 - 6.0 * t2 + 3.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 5)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = t[i] * (-t2 * t2 + 10.0 * t2 - 15.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 6)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = (t2 * t2 * t2 - 15.0 * t2 * t2 + 45.0 * t2 - 15.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 7)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			psi[i] = t[i] * (-t2 * t2 * t2 + 21.0 * t2 * t2 - 105.0 * t2 + 105.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 8)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			t4     = t2 * t2;
			psi[i] = (t4 * t4 - 28.0 * t4 * t2 + 210.0 * t4 - 420.0 * t2 + 105.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 9)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			t4     = t2 * t2;
			psi[i] = t[i] * (- t4 * t4 + 36.0 * t4 * t2 - 378.0 * t4 + 1260.0 * t2 - 945.0) * exp(- t2 / 2.0) * num;
		}
	}
	else if (p == 10)
	{
		for (i = 0; i < N; ++i)
		{
			t2     = t[i] * t[i];
			t4     = t2 * t2;
			psi[i] = (t4 * t4 * t2 - 45.0 * t4 * t4 + 630.0 * t4 * t2 - 3150.0 * t4 + 4725.0 * t2 - 945.0) * exp(- t2 / 2.0) * num;
		}
	}
	else
	{
		printf("\n The Gaussian Derivative Wavelet is only available for Derivatives 1 to 10");
		exit(1);
	}
}

void mexhat(int N, double lb, double ub, double* psi, double* t) { gauss(N, 2, lb, ub, psi, t); }

void morlet(int N, double lb, double ub, double* psi, double* t)
{
	int i;

	if (lb >= ub)
	{
		printf("upper bound must be greater than lower bound");
		exit(1);
	}

	t[0]               = lb;
	t[N - 1]           = ub;
	const double delta = (ub - lb) / (N - 1);
	for (i = 1; i < N - 1; ++i) { t[i] = lb + delta * i; }

	for (i = 0; i < N; ++i) { psi[i] = exp(- t[i] * t[i] / 2.0) * cos(5 * t[i]); }
}
