package dotnet54.classifiers.tschief.splitters.rise;

import dotnet54.tscore.exceptions.MultivariateDataNotSupportedException;
import dotnet54.tscore.data.MTimeSeries;
import dotnet54.tscore.data.TimeSeries;
import dotnet54.tscore.data.MTSDataset;

public class PACF {

	public int maxLag;

	public void setMaxLag(int a) {
		maxLag = a;
	}

	public MTSDataset transform(MTSDataset input) throws Exception {
		int size = input.size();
		int length = input.length();

		if (input.isMultivariate()){
			throw new MultivariateDataNotSupportedException();
		}

		// TODO can do this better?
		maxLag = length / 4;
		if (maxLag > 100) {
			maxLag = 100;
		} else if (maxLag < 10) {
			maxLag = length;
		}

		MTSDataset output = new MTSDataset(size);

		for (int i = 0; i < size; i++) {
			// 2. Fit Autocorrelations
			//TODO using only dimension 1
			double[] autos = ACF.transformArray(input.getSeries(i).data()[0], maxLag);
			// 3. Form Partials
			double[][] partials = transformACFArray(autos);
			// 5. Find parameters
			double[] pi = new double[maxLag];
			for (int k = 0; k < maxLag; k++) { // Set NANs to zero
				if (Double.isNaN(partials[k][k]) || Double.isInfinite(partials[k][k])) {
					pi[k] = 0;
				} else
					pi[k] = partials[k][k];
			}

			TimeSeries ts = new MTimeSeries(pi, input.getClass(i));
			output.add(ts);
		}

		return output;
	}

	public double[] transformArray(double[] data) {
		// 2. Fit Autocorrelations
		double[] acf = ACF.transformArray(data, maxLag);
		// 3. Form Partials
		double[][] pacf = transformACFArray(acf);
		// 5. Find parameters
		double[] pi = new double[maxLag];
		for (int k = 0; k < maxLag; k++) { // Set NANs to zero
			if (Double.isNaN(pacf[k][k]) || Double.isInfinite(pacf[k][k])) {
				pi[k] = 0;
			} else
				pi[k] = pacf[k][k];
		}

		return pi;
	}	
	
	public static double[] transformArray(double[] acf, int maxLag) {
		// 3. Form Partials
		double[][] pacf = transformACFArray(acf);
		// 5. Find parameters
		double[] pi = new double[maxLag];
		for (int k = 0; k < maxLag; k++) { // Set NANs to zero
			if (Double.isNaN(pacf[k][k]) || Double.isInfinite(pacf[k][k])) {
				pi[k] = 0;
			} else
				pi[k] = pacf[k][k];
		}

		return pi;
	}		
	
	public static double[][] transformACFArray(double[] r) {
		// Using the Durban-Leverson
		int p = r.length;
		double[][] phi = new double[p][p];
		double numerator, denominator;
		phi[0][0] = r[0];

		for (int k = 1; k < p; k++) {
			// Find diagonal k,k
			// Naive implementation, should be able to do with running sums
			numerator = r[k];
			for (int i = 0; i < k; i++)
				numerator -= phi[i][k - 1] * r[k - 1 - i];
			denominator = 1;
			for (int i = 0; i < k; i++)
				denominator -= phi[k - 1 - i][k - 1] * r[k - 1 - i];
			phi[k][k] = numerator / denominator;
			// Find terms 1,k to k-1,k
			for (int i = 0; i < k; i++)
				phi[i][k] = phi[i][k - 1] - phi[k][k] * phi[k - 1 - i][k - 1];
		}
		return phi;
	}
}
