#include "../header/wauxlib.h"
#include "waux.h"

int compare_double(const void* a, const void* b)
{
    double arg1 = *(const double*)a;
    double arg2 = *(const double*)b;

    if (arg1 < arg2) return -1;
    if (arg1 > arg2) return 1;
    return 0;

}

double mean(const double* vec, int N) {
	int i;
	double m;
	m = 0.0;

	for (i = 0; i < N; ++i) {
		m+= vec[i];
	}
	m = m / N;
	return m;
}

double var(const double* vec, int N) {
	double v,temp,m;
	int i;
	v = 0.0;
	m = mean(vec,N);

	for (i = 0; i < N; ++i) {
		temp = vec[i] - m;
		v+= temp*temp;
	}

	v = v / N;

	return v;

}

double median(double *x, int N) {
	double sigma;

    qsort(x, N, sizeof(double), compare_double);

    if ((N % 2) == 0) {
		sigma = (x[N/2 - 1] + x[N/2] ) / 2.0;
	} else {
		sigma = x[N/2];
	}

	return sigma;
}

double mad(double *x, int N) {
	double sigma;
	int i;

	sigma = median(x,N);

	for(i = 0; i < N;++i) {
		x[i] = (x[i] - sigma) > 0 ? (x[i] - sigma) : -(x[i] - sigma);
	}

	sigma = median(x,N);

	return sigma;
}

int minindex(const double *arr, int N) {
	double min;
	int index,i;

	min = DBL_MAX;
	index = 0;
	for(i = 0; i < N;++i) {
		if (arr[i] < min) {
			min = arr[i];
			index = i;
		}
	}

	return index;

}

void getDWTAppx(wt_object wt, double *appx,int N) {
	/*
	Wavelet decomposition is stored as
	[A(J) D(J) D(J-1) ..... D(1)] in wt->output vector

	Length of A(J) , N = wt->length[0]
	*/
	int i;

	for (i = 0; i < N; ++i) {
		appx[i] = wt->output[i];
	}
}

void getDWTDetail(wt_object wt, double *detail, int N, int level) {
	/*
	returns Detail coefficents at the jth level where j = J,J-1,...,1
	and Wavelet decomposition is stored as
	[A(J) D(J) D(J-1) ..... D(1)] in wt->output vector
	Use getDWTAppx() to get A(J)
	Level 1 : Length of D(J), ie N, is stored in wt->length[1]
	Level 2 :Length of D(J-1), ie N, is stored in wt->length[2]
	....
	Level J : Length of D(1), ie N, is stored in wt->length[J]
	*/
	int i, iter, J;
	J = wt->J;

	if (level > J || level < 1) {
		printf("The decomposition only has 1,..,%d levels", J);
		exit(-1);
	}

	iter = wt->length[0];

	for (i = 1; i < J-level; ++i) {
		iter += wt->length[i];
	}

	for (i = 0; i < N; ++i) {
		detail[i] = wt->output[i + iter];
	}
}

void getDWTRecCoeff(double *coeff,int *length,const char *ctype,const char *ext, int level, int J,double *lpr,
		double *hpr,int lf,int siglength,double *reccoeff) {

	int i,j,k,det_len,N,l,m,n,v,t,l2;
	double *out,*X_lp,*filt;
	out = (double*)malloc(sizeof(double)* (siglength + 1));
	l2 = lf / 2;
	m = -2;
	n = -1;
	if (!strcmp(ext, "per")) {
		if (!strcmp((ctype), "appx")) {
			det_len = length[0];
		} else {
			det_len = length[J - level + 1];
		}

		N = 2 * length[J];

		X_lp = (double*)malloc(sizeof(double)* (N + 2 * lf - 1));

		for (i = 0; i < det_len; ++i) {
			out[i] = coeff[i];
		}

		for (j = level; j > 0; --j) {

			//idwt1(wt, temp, cA_up, out, det_len, wt->output + iter, det_len, X_lp, X_hp, out);

			if (!strcmp((ctype), "det") && j == level) {
				filt = hpr;
			} else {
				filt = lpr;
			}

			//idwt_per(wt,out, det_len, wt->output + iter, det_len, X_lp);
			m = -2;
			n = -1;

			for (i = 0; i < det_len + l2 - 1; ++i) {
				m += 2;
				n += 2;
				X_lp[m] = 0.0;
				X_lp[n] = 0.0;
				for (l = 0; l < l2; ++l) {
					t = 2 * l;
					if ((i - l) >= 0 && (i - l) < det_len) {
						X_lp[m] += filt[t] * out[i - l];
						X_lp[n] += filt[t + 1] * out[i - l];
					}
					else if ((i - l) >= det_len && (i-l) < det_len + lf - 1) {
						X_lp[m] += filt[t] * out[i - l - det_len];
						X_lp[n] += filt[t + 1] * out[i - l - det_len];
					}
					else if ((i - l) < 0 && (i-l) > -l2) {
						X_lp[m] += filt[t] * out[det_len + i - l] ;
						X_lp[n] += filt[t + 1] * out[det_len + i - l];
					}
				}
			}

			for (k = lf/2 - 1; k < 2 * det_len + lf/2 - 1; ++k) {
				out[k - lf/2 + 1] = X_lp[k];
			}

			if (j != 1) {
				det_len = length[J - j + 2];
			}
		}

		free(X_lp);

	}
	else if (!strcmp(ext, "sym")) {
		if (!strcmp((ctype), "appx")) {
			det_len = length[0];
		} else {
			det_len = length[J - level + 1];
		}

		N = 2 * length[J] - 1;

		X_lp = (double*)malloc(sizeof(double)* (N + 2 * lf - 1));

		for (i = 0; i < det_len; ++i) {
			out[i] = coeff[i];
		}

		for (j = level; j > 0; --j) {

			//idwt1(wt, temp, cA_up, out, det_len, wt->output + iter, det_len, X_lp, X_hp, out);

			if (!strcmp((ctype), "det") && j == level) {
				filt = hpr;
			} else {
				filt = lpr;
			}

			//idwt_sym(wt, out, det_len, wt->output + iter, det_len, X_lp);

			m = -2;
			n = -1;

			for (v = 0; v < det_len; ++v) {
				i = v;
				m += 2;
				n += 2;
				X_lp[m] = 0.0;
				X_lp[n] = 0.0;
				for (l = 0; l < lf / 2; ++l) {
					t = 2 * l;
					if ((i - l) >= 0 && (i - l) < det_len) {
						X_lp[m] += filt[t] * out[i - l];
						X_lp[n] += filt[t + 1] * out[i - l];
					}
				}
			}

			for (k = lf-2; k < 2 * det_len; ++k) {
				out[k - lf + 2] = X_lp[k];
			}


			if (j != 1) {
				det_len = length[J - j + 2];
			}
		}

		free(X_lp);

	}
	else {
			printf("Signal extension can be either per or sym");
			exit(-1);
	}

	for (i = 0; i < siglength; ++i) {
		reccoeff[i] = out[i];
	}

	free(out);

}


void autocovar(const double* vec,int N, double* acov,int M) {
	double m,temp1,temp2;
	int i,t;
	m = mean(vec,N);

	if ( M > N) {
		M = N-1;
		printf("\n Lag is greater than the length N of the input vector. It is automatically set to length N - 1.\n");
		printf("\n The Output Vector only contains N calculated values.");
	} else if ( M < 0) {
		M = 0;
	}

	for(i = 0; i < M;i++) {
		acov[i] = 0.0;
		for (t = 0; t < N-i;t++) {
			temp1 = vec[t] - m;
			temp2 = vec[t+i] - m;
			acov[i]+= temp1*temp2;
		}
		acov[i] = acov[i] / N;

	}


}

void autocorr(const double* vec,int N,double* acorr, int M) {
	double var;
	int i;
	if (M > N) {
		M = N - 1;
		printf("\n Lag is greater than the length N of the input vector. It is automatically set to length N - 1.\n");
		printf("\n The Output Vector only contains N calculated values.");
	}
	else if (M < 0) {
		M = 0;
	}
	autocovar(vec,N,acorr,M);
	var = acorr[0];
	acorr[0] = 1.0;

	for(i = 1; i < M; i++) {
		acorr[i] = acorr[i]/var;
	}

}
