
/**
 *  Cohen-Daubechies-Feauveau adapted to the interval with N=2 and Ntilde=4
 *   Free software - use at your risks (commercial usage allowed)
 *  
 * For support: Wavelet Forum http://www.ondelette.com/indexfr.html (French) or 
 * http://www.ondelette.com/indexen.html (English)
 *
 * Reference:
 * 
 * Gilles Deslauriers, Serge Dubuc and Daniel Lemire, Une famille
 * d'ondelettes biorthogonales sur l'intervalle obtenue par un
 * schéma d'interpolation itérative, Ann. Sci. Math. Québec 23 (1999), no. 1, 37-48. 
 * English title: "A family of biorthogonal wavelets on the interval derived
 * from an interpolatory subdivision scheme"
 *
 * URL (article) : 
 *  http://www.ondelette.com/lemire/abstracts/ANNQC1999.html
 *
 * This code was also used in the production of the following paper:
 * 
 * Daniel Lemire, Chantal Pharand, Jean-Claude Rajaonah, Bruno Dubé, A.-Robert LeBlanc,
 * Wavelet time entropy, T wave morphology and myocardial ischemia, IEEE Transactions in
 *  Biomedical Engineering, vol. 47, no. 7, July 2000.
 * 
 * http://www.ondelette.com/lemire/abstracts/IEEE2000.html
 *
 * @author    Daniel Lemire
 */
public class FastCDF2_4 {

	/*
	* usage sample
	*/
	public static void main(String[] arg) {
		// this is a test, just for fun!
		float[] data = new float[33];// choose your length to be a power of 2 + 1
		// initialize!
		for(int k = 0; k < data.length; ++k) {
			data[k] = 1.0f;
		}
		FastCDF2_4 cdf = new FastCDF2_4();
		cdf.transform(data);
		// print out transformed data (one data element per row!)
		System.out.println("--Transformed data--");
		for(int k = 0; k < data.length; ++k) {
			System.out.println(" data[ "+k+" ]= "+data[k]);
		}
		cdf.invTransform(data);
		System.out.println("--Recovered data--"); 		
		for(int k = 0; k < data.length; ++k) {
			System.out.println(" data[ "+k+" ]= "+data[k]);
		}		
	}


	// linear splines!
	public static final int FilterType = 1;

	final static float[] scale = {0.03314563036811941f, -0.06629126073623882f,
			-0.17677669529663687f, 0.41984465132951254f, 0.9943689110435824f,
			0.41984465132951254f, -0.17677669529663687f, -0.06629126073623882f,
			0.03314563036811941f};
	final static float[] wavelet = {-0.5f, 1.0f, -0.5f};
	final static float[][] scaleLeft = {{1.0275145414117017f, 0.7733980419227863f
			, -0.22097086912079608f, -0.3314563036811941f, 0.16572815184059705f},
			{-0.22189158107546605f, 0.4437831621509321f,
			0.902297715576584f, 0.5800485314420897f, -0.25687863535292543f,
			-0.06629126073623882f, 0.03314563036811941f},
			{0.07549838028293866f, -0.15099676056587732f,
			-0.0957540432856783f, 0.34250484713723395f, 1.0330388131397217f,
			0.41984465132951254f, -0.17677669529663687f, -0.06629126073623882f,
			0.03314563036811941f},
			{-0.013810679320049755f, 0.02762135864009951f,
			0.011048543456039804f, -0.04971844555217912f, -0.18506310288866673f,
			0.41984465132951254f, 0.9943689110435824f, 0.41984465132951254f,
			-0.17677669529663687f, -0.06629126073623882f, 0.03314563036811941f}};
	final static float[][] scaleRight = {{0.03314563036811941f,
			-0.06629126073623882f, -0.17677669529663687f, 0.41984465132951254f,
			0.9943689110435824f, 0.41984465132951254f, -0.18506310288866673f,
			-0.04971844555217912f, 0.011048543456039804f, 0.02762135864009951f,
			-0.013810679320049755f},
			{0.03314563036811941f, -0.06629126073623882f,
			-0.17677669529663687f, 0.41984465132951254f, 1.0330388131397217f,
			0.34250484713723395f, -0.0957540432856783f, -0.15099676056587732f,
			0.07549838028293866f},
			{0.03314563036811941f, -0.06629126073623882f,
			-0.25687863535292543f, 0.5800485314420897f, 0.902297715576584f,
			0.4437831621509321f, -0.22189158107546605f},
			{0.16572815184059705f, -0.3314563036811941f,
			-0.22097086912079608f, 0.7733980419227863f, 1.0275145414117017f}};
	final static float[] scalePrimary = {0.35355339059327373f,
			0.7071067811865475f, 0.35355339059327373f};
	final static float[] waveletPrimary = {0.0234375f, 0.046875f, -0.125f,
			-0.296875f, 0.703125f, -0.296875f, -0.125f, 0.046875f, 0.0234375f};
	final static float[][] scalePrimaryLeft = {{0.7071067811865475f,
			0.35355339059327373f}};
	final static float[][] scalePrimaryRight = {{0.35355339059327373f,
			0.7071067811865475f}};
	final static float[][] waveletPrimaryLeft = {{-0.546875f, 0.5696614583333334f
			, -0.3138020833333333f, -0.103515625f, 0.10677083333333333f,
			0.043619791666666664f, -0.01953125f, -0.009765625f},
			{0.234375f, -0.087890625f, -0.41015625f, 0.673828125f,
			-0.2421875f, -0.103515625f, 0.03515625f, 0.017578125f}};
	final static float[][] waveletPrimaryRight = {{0.017578125f, 0.03515625f,
			-0.103515625f, -0.2421875f, 0.673828125f, -0.41015625f, -0.087890625f,
			0.234375f},
			{-0.009765625f, -0.01953125f, 0.043619791666666664f,
			0.10677083333333333f, -0.103515625f, -0.3138020833333333f,
			0.5696614583333334f, -0.546875f}};


	public FastCDF2_4() { }


	public void transform(float[] v) {
		int last;
		for (last = v.length; last >= 15; last = (last + FilterType) / 2) {
			transform(v, last);
		}
	}


	public void invTransform(float[] v) {
		int last;
		for (last = v.length; last >= 15; last = last / 2 + FilterType) {
			;
		}
		for (; 2 * last - FilterType <= v.length; last = 2 * last - FilterType) {
			invTransform(v, last);
		}
	}


	public static void transform(float[] v, int last) {
		float[] ans = new float[last];
		int half = (last + FilterType) / 2;
		if (2 * half - FilterType != last) {
			throw new IllegalArgumentException("Illegal subband : " + last + " within array of length " + v.length);
		}
		//lowpass
		for (int k = 0; k < scaleLeft.length; k++) {
			for (int l = 0; l < scaleLeft[k].length; l++) {
				ans[k] += scaleLeft[k][l] * v[l];
			}
		}
		for (int k = scaleLeft.length; k < half - scaleRight.length; k++) {
			for (int l = 0; l < scale.length; l++) {
				ans[k] += scale[l] * v[2 * k + l - scaleLeft.length];
			}
		}
		for (int k = 0; k < scaleRight.length; k++) {
			for (int l = 0; l < scaleRight[k].length; l++) {
				ans[k + half - scaleRight.length] += scaleRight[k][l] * v[last -
						scaleRight[k].length + l];
			}
		}
		//highpass
		for (int k = 0; k < half - FilterType; k++) {
			for (int l = 0; l < wavelet.length; l++) {
				ans[k + half] += wavelet[l] * v[2 * k + l];
			}
		}
		System.arraycopy(ans, 0, v, 0, last);
	}


	public static void invTransform(float[] v, int last) {
		float[] ans = new float[2 * last - FilterType];
		//scale coefficients
		for (int k = 0; k < scalePrimaryLeft.length; k++) {
			for (int l = 0; l < scalePrimaryLeft[k].length; l++) {
				ans[l] += scalePrimaryLeft[k][l] * v[k];
			}
		}
		for (int k = scalePrimaryLeft.length; k < last - scalePrimaryRight.length; k++) {
			for (int l = 0; l < scalePrimary.length; l++) {
				ans[2 * k - FilterType + l] += scalePrimary[l] * v[k];
			}
		}
		for (int k = 0; k < scalePrimaryRight.length; k++) {
			for (int l = 0; l < scalePrimaryRight[k].length; l++) {
				ans[l - scalePrimaryRight[k].length + ans.length] +=
						scalePrimaryRight[k][l] * v[k + last - scalePrimaryRight.length];
			}
		}
		//wavelet coefficients
		for (int k = 0; k < waveletPrimaryLeft.length; k++) {
			for (int l = 0; l < waveletPrimaryLeft[k].length; l++) {
				ans[l] += waveletPrimaryLeft[k][l] * v[k + last];
			}
		}
		for (int k = waveletPrimaryLeft.length; k < last - FilterType
				 - waveletPrimaryRight.length; k++) {
			for (int l = 0; l < waveletPrimary.length; l++) {
				ans[2 * (k - FilterType) - 1 + l] += waveletPrimary[l] * v[k + last];
			}
		}
		for (int k = 0; k < waveletPrimaryRight.length; k++) {
			for (int l = 0; l < waveletPrimaryRight[k].length; l++) {
				ans[l - waveletPrimaryRight[k].length + ans.length] +=
						waveletPrimaryRight[k][l] * v[k + 2 * last - FilterType -
						waveletPrimaryRight.length];
			}
		}
		System.arraycopy(ans, 0, v, 0, ans.length);
	}



}



