class LinearPredictiveCoding {

    public double[][] applyLinearPredictiveCoding(double[] window) {
        
        if(windowSize != window.length) {
            throw new IllegalArgumentException("Given window length was not equal to the one provided in constructor : [" 
                    + window.length +"] != [" + windowSize + "]");
        }
        
        Arrays.fill(k,  0.0d);
        Arrays.fill(output, 0.0d);
        Arrays.fill(error, 0.0d);
        for(double[] d : matrix) {
            Arrays.fill(d, 0.0d);
        }

        DiscreteAutocorrelationAtLagJ dalj = new DiscreteAutocorrelationAtLagJ();
        double[] autocorrelations = new double[poles];
        for(int i = 0; i < poles; i++) {
            autocorrelations[i] = dalj.autocorrelate(window, i);
        }

        error[0] = autocorrelations[0];

        for (int m = 1; m < poles; m++) {
            double tmp = autocorrelations[m];
            for (int i = 1; i < m; i++) {
                tmp -= matrix[m - 1][i] * autocorrelations[m - i];
            }
            k[m] = tmp / error[m - 1];

            for (int i = 0; i < m; i++) {
                matrix[m][i] = matrix[m - 1][i] - k[m] * matrix[m - 1][m - i];
            }
            matrix[m][m] = k[m];
            error[m] = (1 - (k[m] * k[m])) * error[m - 1];
        }

        for (int i = 0; i < poles; i++) {
            if (Double.isNaN(matrix[poles - 1][i])) {
                output[i] = 0.0;
            } else {
                output[i] = matrix[poles - 1][i];
            }
        }
        
        return new double[][] { output, error };
    }

    public  LinearPredictiveCoding(int windowSize, int poles);

}