import java.io.*;
import java.text.*;
import java.util.*;


public final class ArrayUtil {

    // A bunch of FFT related stuff here based on Numerical Recipes FFT routines.
    //double[][] getChunks(double[] data, long chunkSize) {}

    static void print(double[] data) { print(data, System.out, new DecimalFormat("0.000000E0")); }

    static void print(double[] data, DecimalFormat df) { print(data, System.out, df); }

    static void print(double[] data, PrintStream out, DecimalFormat df) {
	for(int i=0; i<data.length; i++) out.println(df.format(data[i]));
    }
  
    // Interpret integral normalized spectrum as amplitudes
    static double[] amplitude(Complex[] data) {
	double[] amp = new double[data.length];
	for(int i=0; i<data.length; i++) amp[i] = data[i].length();
	amp[0] /= 2.0;
	amp[amp.length - 1] /= 2.0;
	return amp;
    }

    // Interpret integral normalized spectrum as power
    static double[] norm(Complex[] data) {
	double[] norm = new double[data.length];
	for(int i=0; i<data.length; i++) norm[i] = data[i].norm();
	norm[0] /= 4.0;
	norm[norm.length - 1] /= 4.0;	
	return norm;
    }

    static double[] real(Complex[] data) {
	double[] real = new double[data.length];
	for(int i=0; i<data.length; i++) real[i] = data[i].x;
	return real;
    }

    static double[] re(Complex[] data) { return real(data); }

    static double[] imaginary(Complex[] data) {
	double[] imaginary = new double[data.length];
	for(int i=0; i<data.length; i++) imaginary[i] = data[i].y;
	return imaginary;
    }
    
    
    static double[] im(Complex[] data) { return imaginary(data); }

    static int getPaddedFFTSize(int size) {
	int n=1;	
	while(n < size) n<<=1;
	return n;
    }

    static int getTruncatedFFTSize(int size) {
	int n=1;	
	while(n <= size) n<<=1;
	return n>>1;
    }

    static double[] truncateForFFT(double[] data) {
	return padWithZeros(data, getTruncatedFFTSize(data.length));
    }

    static Vector2D[] truncateForFFT(Complex[] data) {
	int n = getTruncatedFFTSize(data.length);	
	return data.length == n ? data : padWithZeros(data, n);
    }
    
    static double[] padForFFT(double[] data) {
	int n = getPaddedFFTSize(data.length);
	return data.length == n ? data : padWithZeros(data, n);
    }

    static Complex[] padForFFT(Complex[] data) {
	return padWithZeros(data, getPaddedFFTSize(data.length));
    }

    static double[] padWithZeros(double[] data, int n) {
	double[] padded = new double[n];
	
	int N = Math.min(data.length, n);

	System.arraycopy(data, 0, padded, 0, N);
	
	return padded;
    }

    static Complex[] padWithZeros(Complex[] data, int n) {
	Complex[] padded = new Complex[n];
	
	int N = Math.min(data.length, n);

	System.arraycopy(data, 0, padded, 0, N);
	//for(int i=0; i<N; i++) padded[i] = (Complex) data[i].clone();
	
	return padded;
    }

    static double[] convolveViaFFT(double[] data, double[] beam) { 
	Complex[] A = FFT(data);
	int N = A.length-1;
     
	Complex[] B = FFT(beam, 2*N);

	for(int i=0; i<N+1; i++) {
	    Complex AA = A[i], BB = B[i];
	    double re = AA.x * BB.x - AA.y * BB.y;
	    AA.y = AA.y * BB.x + AA.x * BB.y;
	    AA.x = re;
	}

	return backFFT(A);
    }
	
    static double[] autoCorrelateViaFFT(double[] data) { return convolveViaFFT(data, data); }

    static double[] averagePower(double[] data, int windowSize) {
	if(data.length < windowSize) return averagePower(padWithZeros(data, windowSize), windowSize);

	// figure out the FFT size
	int n=1;	
	while(n < windowSize) n<<=1;
	double[] block = new double[n];

	// Create the cumulated spectrum array
	double[] spectrum = null;
	
	int start = 0, N = 0;
	while(start + windowSize <= data.length) {	    

	    for(int i=0,j=start; i<windowSize; i++,j++) block[i] = data[j];	    

	    Complex[] blockSpectrum = FFT(block);
	    
	    if(spectrum == null) spectrum = new double[blockSpectrum.length];
	    
	    for(int i=0; i<spectrum.length; i++) spectrum[i] += blockSpectrum[i].norm();

	    start += (windowSize+1)/2;

	    N++;
	}
	
	for(int i=0; i<spectrum.length; i++) spectrum[i] /= N;
	
	return spectrum;	
    }

    static Complex[] hanningWindowedFFT(double[] data, int windowSize) {
	if(data.length < windowSize) return hanningWindowedFFT(padWithZeros(data, windowSize), windowSize);

	// figure out the FFT size
	int n=1;	
	while(n < windowSize) n<<=1;
	double[] block = new double[n];

	// Create the cumulated spectrum array
	Complex[] spectrum = null;
	
	int start = 0, N=0;
	while(start + windowSize <= data.length) {	    

	    for(int i=0,j=start; i<windowSize; i++,j++) block[i] = data[j];	    

	    Complex[] blockSpectrum = FFT(block);
	    
	    if(spectrum == null) spectrum = blockSpectrum;
	    else for(int i=0; i<spectrum.length; i++) spectrum[i].add(blockSpectrum[i]);

	    start += (windowSize+1)/2;
	    
	    N++;
	}

	double norm = 1.0 / N;
	
	for(int i=0; i<spectrum.length; i++) spectrum[i].scale(norm);
	
	return spectrum;	
    }

    static Complex[] FFT(double[] data) { return FFT(data, getPaddedFFTSize(data.length)); }

    static double[][] backFFT(Complex[][] spectrum) {
	return null;
    }

    static Complex[] FFT(double[] data, int n) {
	double[] tdata = padWithZeros(data, n);
	int N = n/2;

	Complex[] fdata = new Complex[N+1];
	for(int i=0; i<N+1; i++) fdata[i] = new Complex();

	unsecureFFT(tdata, fdata);

	return fdata;
    }

    // This performs the fft on tdata, and puts the
    // apmlitude normalized frequency data into the supplied Complex array.
    static void unsecureFFT(double[] data, Complex[] fdata) {
	final int n = data.length;
	final int N = n/2;

	realft(data, n, true);

	fdata[0].x = data[0] / N;
	fdata[N].x = data[1] / N;
	
	for(int i=1,ii=2; i<N; i++) {
	    fdata[i].x = data[ii++] / N;
	    fdata[i].y = data[ii++] / N;
	}
    }

    static void unsecureBackFFT(double[] data, Complex[] fdata) {
	final int n = data.length;
	final int N = n/2;

	for(int i=1,ii=2; i<N; i++) {
	    data[ii++] = fdata[i].x;
	    data[ii++] = fdata[i].y;
	}

	realft(data, n, false);
    }
    


    static double[] backFFT(Complex[] data) {
	return backFFT(data, getPaddedFFTSize(data.length-1));
    }

    static double[] backFFT(Complex[] data, int n) {
	double[] fdata = new double[2 * n];

	fdata[0] = data[0].x;
	fdata[1] = data[n].x;

	for(int i=1,ii=2; i<n; i++) {
	    fdata[ii++] = data[i].x;
	    fdata[ii++] = data[i].y;
	}
	
	realft(fdata, 2*n, false);
	
	return fdata;
    }

    public static void unsecureFFT(Complex[] data, boolean isForward, double[] temp) {
	if(temp == null) temp = new double[2*data.length];
	else if(temp.length != 2*data.length) temp = new double[2*data.length];
	
	for(int i=0, ii=0; i<data.length; i++) {
	    temp[ii++] = data[i].x;
	    temp[ii++] = data[i].y;
	}

	four1(temp, temp.length>>1, isForward);
	
	double norm = isForward ? 1.0 / data.length : 1.0;

	for(int i=0, ii=0; i<data.length; i++) {
	    data[i].x = norm * temp[ii++];
	    data[i].y = norm * temp[ii++];
	}	    
    }

    public static void unsecureFFT(Complex[][] data, boolean isForward) {
	Complex[] ctemp = null;
	double[] dtemp = null;
	unsecureFFT(data, isForward, ctemp, dtemp);
    }

    public static void unsecureFFT(Complex[][] data, boolean isForward, Complex[] temp, double[] dtemp) {
	int nx = data.length;
	int ny = data[0].length;

	if(temp == null) temp = new Complex[nx];
	else if(temp.length != nx) temp = new Complex[nx];
	
	for(int i=0; i<nx; i++) {
	    if(temp[i] == null) temp[i] = new Complex();
	    unsecureFFT(data[i], isForward, dtemp);
	}

	for(int j=0; j<ny; j++) {
	    for(int i=0; i<nx; i++) temp[i] = data[i][j];
	    unsecureFFT(temp, isForward, dtemp);
	    for(int i=0; i<nx; i++) data[i][j] = temp[i];
	}
    }

    public static Complex[][] loadFFT(double[][] data) {
	Complex[][] cdata = null;
	return loadFFT(data, cdata);
    }
    
    public static Complex[][] loadFFT(double[][] data, Complex[][] cdata) {
	int nx = data.length;
	int ny = data[0].length;
	
	int nu = getPaddedFFTSize(nx);
	int nv = getPaddedFFTSize(ny);
	
	if(cdata == null) cdata = new Complex[nu][nv];
	else if(cdata.length != nu || cdata[0].length != nv) cdata = new Complex[nu][nv];
	
	for(int i=0; i<nu; i++) for(int j=0; j<nv; j++) {
	    if(cdata[i][j] == null) cdata[i][j] = new Complex();
	    cdata[i][j].x = (i<nx && j<ny) ? data[i][j] : 0.0;
	    cdata[i][j].y = 0.0;
	} 

	return cdata;
    }

    public static void unloadFFT(Complex[][] cdata, double[][] data) {
	int nx = data.length;
	int ny = data[0].length;
	
	for(int i=0; i<nx; i++) for(int j=0; j<ny; j++) data[i][j] = cdata[i][j].x;
    }

    public static double[][] amplitude(Complex[][] data) {
	int nx = data.length;
	int ny = data[0].length;
	
	double[][] a = new double[nx][ny];
	
	for(int i=0; i<nx; i++) for(int j=0; j<ny; j++) a[i][j] = data[i][j].length();
	
	// Change from integral to amplitide normalization
	for(int i=0; i<nx; i++) {
	    a[i][0] /= 2.0;
	    a[i][ny-1] /= 2.0;
	}

	for(int j=0; j<ny; j++) {
	    a[0][j] /= 2.0;
	    a[nx-1][j] /= 2.0;
	}
	
	return a;
    }

    public static double[][] norm(Complex[][] data) {
	int nx = data.length;
	int ny = data[0].length;

	double[][] a = new double[nx][ny];

	for(int i=0; i<nx; i++) for(int j=0; j<ny; j++) a[i][j] = data[i][j].norm();

	return a;
    }
    

    
    public static void realft(double data[], int n, boolean forward) {
	double c1=0.5, c2, theta;
	
	theta=Math.PI/(double) (n>>1);
	
	if(forward) {
	    c2 = -0.5;
	    four1(data,n>>1, true);
	} 
	else {
	    c2=0.5;
	    theta = -theta;
	}

	double wtemp=Math.sin(0.5*theta);
	double wpr = -2.0*wtemp*wtemp;
	double wpi=Math.sin(theta);
	double wr=1.0+wpr;
	double wi=wpi;
	int np1=n+1;

	double h1r, h1i;

	// ii = i-1
	final int imax = n>>2;
	for(int i=1;i<imax;i++) {
	    int i1 = i + i;
	    int i2 = i1 + 1;

	    int i3 = np1 - i2;
	    int i4 = i3 + 1;

	    h1r=c1*(data[i1]+data[i3]);
	    h1i=c1*(data[i2]-data[i4]);
	    double h2r = -c2*(data[i2]+data[i4]);
	    double h2i=c2*(data[i1]-data[i3]);
	    data[i1]=h1r+wr*h2r-wi*h2i;
	    data[i2]=h1i+wr*h2i+wi*h2r;
	    data[i3]=h1r-wr*h2r+wi*h2i;
	    data[i4] = -h1i+wr*h2i+wi*h2r;
	    wr=(wtemp=wr)*wpr-wi*wpi+wr;
	    wi=wi*wpr+wtemp*wpi+wi;
	}
	if(forward) {
	    data[0] = (h1r=data[0])+data[1];
	    data[1] = h1r-data[1];
	} 
	else {
	    data[0]=c1*((h1r=data[0])+data[1]);
	    data[1]=c1*(h1r-data[1]);
	    four1(data,n>>1, false);
	}
    }
  

    public static void four1(double data[], int nn, boolean forward) {
	double temp;
	
	final int isign = forward ? 1: -1;
	
	int n=nn << 1;
	int j=1;
	
	for(int i=1;i<n;i+=2) {
	    if (j > i) {		
		temp = data[i-1]; data[i-1] = data[j-1]; data[j-1] = temp;
		temp = data[i]; data[i] = data[j]; data[j] = temp;
	    }
	    int m=n >> 1;
	    while(m >= 2 && j > m) {
		j -= m;
		m >>= 1;
	    }
	    j += m;
	}

	int mmax=2;

	while (n > mmax) {
	    int istep=mmax << 1;
	    double theta=isign*(2.0*Math.PI/mmax);
	    double wtemp=Math.sin(0.5*theta);
	    double wpr = -2.0*wtemp*wtemp;
	    double wpi=Math.sin(theta);
	    double wr=1.0;
	    double wi=0.0;
	    for (int m=1;m<mmax;m+=2) {
		for (int i=m;i<=n;i+=istep) {
		    j=i+mmax;
		    
		    double tempr=wr*data[j-1]-wi*data[j];
		    double tempi=wr*data[j]+wi*data[j-1];
		    data[j-1]=data[i-1]-tempr;
		    data[j]=data[i]-tempi;
		    data[i-1] += tempr;
		    data[i] += tempi;
		}
		wr=(wtemp=wr)*wpr-wi*wpi+wr;
		wi=wi*wpr+wtemp*wpi+wi;
	    }
	    mmax=istep;
	}
    } 
    

    /*
    public static void realft(double data[], int n, boolean forward) {
	int i,i1,i2,i3,i4,np3;
	double c1=0.5,c2,h1r,h1i,h2r,h2i;
	double wr,wi,wpr,wpi,wtemp,theta;
	
	theta=Math.PI/(double) (n>>1);
	
	if(forward) {
	    c2 = -0.5;
	    four1(data,n>>1, true);
	} 
	else {
	    c2=0.5;
	    theta = -theta;
	}

	wtemp=Math.sin(0.5*theta);
	wpr = -2.0*wtemp*wtemp;
	wpi=Math.sin(theta);
	wr=1.0+wpr;
	wi=wpi;
	np3=n+3;

	for(i=2;i<=(n>>2);i++) {
	    i4=1+(i3=np3-(i2=1+(i1=i+i-1)));
	    // Change Fortran indeces to C style indexes
	    final int ii1=i1-1,ii2=i2-1,ii3=i3-1,ii4=i4-1;
	    h1r=c1*(data[ii1]+data[ii3]);
	    h1i=c1*(data[ii2]-data[ii4]);
	    h2r = -c2*(data[ii2]+data[ii4]);
	    h2i=c2*(data[ii1]-data[ii3]);
	    data[ii1]=h1r+wr*h2r-wi*h2i;
	    data[ii2]=h1i+wr*h2i+wi*h2r;
	    data[ii3]=h1r-wr*h2r+wi*h2i;
	    data[ii4] = -h1i+wr*h2i+wi*h2r;
	    wr=(wtemp=wr)*wpr-wi*wpi+wr;
	    wi=wi*wpr+wtemp*wpi+wi;
	}
	if(forward) {
	    data[0] = (h1r=data[0])+data[1];
	    data[1] = h1r-data[1];
	} 
	else {
	    data[0]=c1*((h1r=data[0])+data[1]);
	    data[1]=c1*(h1r-data[1]);
	    four1(data,n>>1, false);
	}
    }
  
    
    public static void four1(double data[], int nn, boolean forward) {
	int n,mmax,m,j,istep,i;
	double wtemp,wr,wpr,wpi,wi,theta;
	double temp,tempr,tempi;
	
	final int isign = forward ? 1: -1;
	
	n=nn << 1;
	j=1;
	
	for(i=1;i<n;i+=2) {
	    if (j > i) {		
		temp = data[i-1]; data[i-1] = data[j-1]; data[j-1] = temp;
		temp = data[i]; data[i] = data[j]; data[j] = temp;
	    }
	    m=n >> 1;
	    while(m >= 2 && j > m) {
		j -= m;
		m >>= 1;
	    }
	    j += m;
	}

	mmax=2;

	while (n > mmax) {
	    istep=mmax << 1;
	    theta=isign*(2.0*Math.PI/mmax);
	    wtemp=Math.sin(0.5*theta);
	    wpr = -2.0*wtemp*wtemp;
	    wpi=Math.sin(theta);
	    wr=1.0;
	    wi=0.0;
	    for (m=1;m<mmax;m+=2) {
		for (i=m;i<=n;i+=istep) {
		    j=i+mmax;
		    
		    // Change Fortran indeces to C style indexes
		    final int ii = i-1, jj = j-1;		    
		    
		    tempr=wr*data[jj]-wi*data[jj+1];
		    tempi=wr*data[jj+1]+wi*data[jj];
		    data[jj]=data[ii]-tempr;
		    data[jj+1]=data[ii+1]-tempi;
		    data[ii] += tempr;
		    data[ii+1] += tempi;
		}
		wr=(wtemp=wr)*wpr-wi*wpi+wr;
		wi=wi*wpr+wtemp*wpi+wi;
	    }
	    mmax=istep;
	}
    }
    */
}
