package DDMT.Nodes;
import java.util.Random;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

public class OwnMatrix
{  
    double[][] A;
    int rows;
    int cols;
    public OwnMatrix(int m, int n)
    {
        this.rows = m;
        this.cols = n;
        this.A = new double[this.rows][this.cols];
    }

    public OwnMatrix(double[][] A, int m, int n)
    {
        this.rows = m;
        this.cols = n;
        this.A = new double[this.rows][this.cols];
        for (int i=0;i<this.rows;i++)
            for (int j=0;j<this.cols;j++)
                this.A[i][j] = A[i][j];
    }
    
    public double[] getCol(int col)
    {
        double[] temp = new double[this.rows];
        for (int i=0;i<temp.length;i++)
            temp[i] = this.A[i][col];
        return temp;
    }
    
    public double[] getRow(int row)
    {
        double[] temp = new double[this.cols];
        for (int i=0;i<temp.length;i++)
            temp[i] = this.A[row][i];
        return temp;
    }
    
    public double get(int i, int j)
    {
        return this.A[i][j];
    }
    
    //gets the mean of a vector
    public double getMean(double[] vector)
    {
        double sum = 0.0;
        for (int i=0;i<vector.length;i++)
            sum+=vector[i];
        return (sum/vector.length);
    }
    
    //gets the variance of a vector
    public double getVariance(double[] vector)
    {
        double sum = 0.0;
        double mean = this.getMean(vector);
        for (int i=0;i<vector.length;i++)
            sum+=(vector[i]-mean)*(vector[i]-mean);
        return sum;
    }
    
    //gets the inner product between two vectors
    public double getInnerProdElement(int i,int j)
    {
        double[] a = this.getCol(i);
        double[] b = this.getCol(j);
        
        double sum = 0.0;
        for (int k=0;k<a.length;k++)
            sum+=a[k]*b[k];
        return sum;
    }
    
    //gets the full inner prod Matrix
    public OwnMatrix getInnerProd()
    {
        double[][] temp = new double[this.cols][this.cols];
        for (int i=0;i<this.cols;i++)
        {
            for (int j=0;j<this.cols;j++)
            {
                temp[i][j] = this.getInnerProdElement(i,j);
            }
        }
        OwnMatrix iprod = new OwnMatrix(temp,this.cols,this.cols);
        return iprod;
    }
    
    //gets the correlation between two vectors
    public double getCorElement(int i,int j)
    {
        double[] a = this.getCol(i);
        double[] b = this.getCol(j);
        
        double meana = this.getMean(a);
        double meanb = this.getMean(b);
        
        double vara = this.getVariance(a);
        double varb = this.getVariance(b);
        
        double sum = 0.0;
        for (int k=0;k<a.length;k++)
        {
            sum+=(a[k]-meana)*(b[k]-meanb);
        }
        return sum/(Math.sqrt(vara)*Math.sqrt(varb));
    }
    
    //gets the full correlation matrix
    public OwnMatrix getCor()
    {
        
        double[][] temp = new double[this.cols][this.cols];
        for (int i=0;i<this.cols;i++)
        {
            for (int j=0;j<this.cols;j++)
            {
                temp[i][j] = this.getCorElement(i,j);
            }
        }
        OwnMatrix cov = new OwnMatrix(temp,this.cols,this.cols);
        return cov;
    }

    //gets the covariance between two vectors
    public double getCovElement(int i,int j)
    {
        double[] a = this.getCol(i);
        double[] b = this.getCol(j);
        
        double meana = this.getMean(a);
        double meanb = this.getMean(b);
        
        double sum = 0.0;
        for (int k=0;k<a.length;k++)
        {
            sum+=(a[k]-meana)*(b[k]-meanb);
        }
        return sum/a.length;
    }
    
    //gets the full covariance matrix
    public OwnMatrix getCov()
    {
        
        double[][] temp = new double[this.cols][this.cols];
        for (int i=0;i<this.cols;i++)
        {
            for (int j=0;j<this.cols;j++)
            {
                temp[i][j] = this.getCovElement(i,j);
            }
        }
        OwnMatrix cov = new OwnMatrix(temp,this.cols,this.cols);
        return cov;
    }

    //get a chi-square value
    public double getChiSq()
    {
        //get row total
        double[] rowTotal = new double[this.rows];
        for (int i=0;i<this.rows;i++)
        {
            rowTotal[i] = 0.0;
            for (int j=0;j<this.cols;j++)
                rowTotal[i] += this.A[i][j];
        }
        //get column total
        double[] colTotal = new double[this.cols];
        for (int i=0;i<this.cols;i++)
        {
            colTotal[i] = 0.0;
            for (int j=0;j<this.rows;j++)
                colTotal[i] += this.A[j][i];
        }
        
        //get total of all marginals
        double sum = 0.0;
        for (int i=0;i<colTotal.length;i++)
            sum += colTotal[i];
        //calculate the expected matrix
        double[][] Expected = new double[rows][cols];
        for (int i=0;i<this.rows;i++)
            for (int j=0;j<this.cols;j++)
                Expected[i][j] = (double)(rowTotal[i]*colTotal[j])/sum;
            
        double chisquare = 0.0;
        for (int i=0;i<this.rows;i++)
            for (int j=0;j<this.cols;j++)
                chisquare += (this.A[i][j]-Expected[i][j])*(this.A[i][j]-Expected[i][j])/Expected[i][j];
        return chisquare;
    }
    
    public double[][] getMatrix()
    {
        return this.A;
    }
    
    public static void main(String [] args)
    {
        try
        {
            int numrows = 250000;
            int numcols = 100;
            int numSamples = 59;

            double[][] A = new double[numrows][numcols];
            double[] samples = new double[numSamples];

            Random r = new Random();
            String toWrite = "";
            FileWriter fw = new FileWriter("c:\\SynData.txt");
            BufferedWriter bw = new BufferedWriter(fw);
            for(int i=0;i<numrows;i++)
            {
                for(int j=0;j<numcols;j++)
                {
                    System.out.println(i+" "+j);
                    if (j%2==0)
                        A[i][j] = r.nextInt((j+1)*1000);
                    else
                        A[i][j] = (2*j)*r.nextDouble();
                    bw.write(A[i][j]+"\t");
//                    toWrite = toWrite.concat(A[i][j]+"\t");
                }
                bw.write("\n");
//                toWrite = toWrite.concat("\n");
            }   
            bw.close();fw.close();

//            WriteFile("c:\\SynData.txt", toWrite);
    //        Matrix m = new Matrix(A,numrows,numcols);        
    //        Matrix t = m.getInnerProd();
    //        toWrite = "";
    //        for(int i=0;i<numcols;i++)
    //            for(int j=0;j<numcols;j++)
    //                toWrite = toWrite.concat(t.A[i][j]+"\n");
    //        
    //        WriteFile("c:\\InnerProd.txt", toWrite);
    //        
    //        int distinctInnerProdEntries = (numcols*numcols-numcols)/2;
    //        double[] distinct = new double[distinctInnerProdEntries];
    //        
    //        toWrite = "";
    //        int k=0;
    //        for(int i=0;i<numcols;i++)
    //            for(int j=0;j<numcols;j++)
    //            {
    //                if (i<j)
    //                {
    //                    distinct[k++] = t.A[i][j];
    //                    toWrite = toWrite.concat(t.A[i][j]+"\n");
    //                }
    //            }
    //        
    //        WriteFile("c:\\DistinctInnerProd.txt", toWrite);
    //        
    //        for (int i=0;i<samples.length;i++)
    //            samples[i] = distinct[r.nextInt(distinctInnerProdEntries)];
    //        
    //        toWrite = "";
    //        for(int i=0;i<samples.length;i++)
    //        {
    //            toWrite = toWrite.concat(samples[i]+"\n");
    //        }
    //
    //        WriteFile("c:\\OrdianlSamplesInnerProd.txt", toWrite);
        }
        catch(IOException e)
        {
            
        }
    }
    
    public static void WriteFile(String filename,String fileData) 
    {
        try 
        {
            BufferedWriter out = new BufferedWriter(new FileWriter(filename,false));
            out.write(fileData);
            out.close();
        }
        catch (IOException e) 
        {
            System.out.println("Writing exception");
        }
    }
}
