package curve;

//Class to represent a matrix
public class Matrix {
  private double element[][];
  private int rows,columns;
  public static final int zero=0;
  public static final int identity=1;
  
  
  //Create empty matrix
  public Matrix(int aColumns,int aRows,int type) {
    int rowNumber,columnNumber;
    rows=aRows;
    columns=aColumns;
    element=new double[columns][rows];
    for(rowNumber=0;rowNumber<rows;rowNumber++)
      for(columnNumber=0;columnNumber<columns;columnNumber++)
        if(type==identity && rowNumber==columnNumber)
          element[columnNumber][rowNumber]=1;
        else
          element[columnNumber][rowNumber]=0;
  };
  
  //Create new matrix from given argument
  public Matrix(Matrix aMatrix) {
    int rowNumber,columnNumber;
    rows=aMatrix.getRows();
    columns=aMatrix.getColumns();
    element=new double[columns][rows];    

    for(rowNumber=0;rowNumber<rows;rowNumber++)
      for(columnNumber=0;columnNumber<columns;columnNumber++)
        element[columnNumber][rowNumber]=
          aMatrix.getElement(columnNumber,rowNumber);
  };
  
  //Create new matrix from array of doubles
  public Matrix(int aColumns,int aRows,double array[][]) {
    int rowNumber,columnNumber;
    rows=aRows;
    columns=aColumns;
    element=new double[columns][rows];
    
    for(rowNumber=0;rowNumber<rows;rowNumber++)
      for(columnNumber=0;columnNumber<columns;columnNumber++)
        element[columnNumber][rowNumber]=array[rowNumber][columnNumber];
  };

  //Return number of rows
  public int getRows() {
    return rows;
  };

  //Return number of columns
  public int getColumns() {
    return columns;
  };
  
  //Returns an element of the array
  public double getElement(int aColumn,int aRow) {
    return element[aColumn][aRow];
  };
  
  //Augment a matrix to the right of this
  public boolean augmentAB(Matrix aMatrix) { 
    Matrix oldMatrix;
    int rowNumber,columnNumber;

    oldMatrix=new Matrix(this);
    if(rows==aMatrix.getRows()) {
      columns+=aMatrix.getColumns();
      element=new double[columns][rows];
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          if(columnNumber<oldMatrix.getColumns())
            element[columnNumber][rowNumber]=
              oldMatrix.getElement(columnNumber,rowNumber);
          else
            element[columnNumber][rowNumber]=aMatrix.getElement(
              columnNumber-oldMatrix.getColumns(),rowNumber);
      return true;
    } else return false;
  };
  
  //Augment a matrix to the left of this
  public boolean augmentBA(Matrix aMatrix) { 
    Matrix oldMatrix;
    int rowNumber,columnNumber;

    oldMatrix=new Matrix(this);

    if(rows==aMatrix.getRows()) {
      columns+=aMatrix.getColumns();
      element=new double[columns][rows];
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          if(columnNumber<aMatrix.getColumns())
            element[columnNumber][rowNumber]=
              aMatrix.getElement(columnNumber,rowNumber);
          else
            element[columnNumber][rowNumber]=oldMatrix.getElement(
              columnNumber-aMatrix.getColumns(),rowNumber);
      return true;
    } else return false;
  };

  //Add a matrix to this one
  public boolean add(Matrix aMatrix) {
    int rowNumber,columnNumber;
    if(rows==aMatrix.getRows() && columns==aMatrix.getColumns()) {
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          element[columnNumber][rowNumber]+=
            aMatrix.getElement(columnNumber,rowNumber);
      return true;
    } else return false;
  };
  
  //Subtract a matrix from this one
  public boolean subAB(Matrix aMatrix) {
    int rowNumber,columnNumber;
    if(rows==aMatrix.getRows() && columns==aMatrix.getColumns()) {
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          element[columnNumber][rowNumber]-=
            aMatrix.getElement(columnNumber,rowNumber);
      return true;
    } else return false;
  };

  //Subtract this matrix from another one
  public boolean subBA(Matrix aMatrix) {
    int rowNumber,columnNumber;
    if(rows==aMatrix.getRows() && columns==aMatrix.getColumns()) {
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          element[columnNumber][rowNumber]=
            aMatrix.getElement(columnNumber,rowNumber)-
            element[columnNumber][rowNumber];
      return true;
    } else return false;
  };

  //multiply this matrix on a vector
  public double[] mulAB(double array[],int numRows) {
    int rowNumber,columnNumber;
    double returnArray[];
    int ctr;
    double result;
    
    if(columns==numRows) {
      returnArray=new double[rows];
      for(rowNumber=0;rowNumber<rows;rowNumber++) {
        result=0;
        //Note because of row*col thing, array[col] really is array[row]
        for(columnNumber=0;columnNumber<columns;columnNumber++)
            result+=element[columnNumber][rowNumber]*array[columnNumber];
        returnArray[rowNumber]=result;
      };
      return returnArray;
    } else return null;
  };

  //Multiply this matrix by another
  public boolean mulAB(Matrix aMatrix) {
    int rowNumber,columnNumber;
    Matrix oldMatrix;
    int result,ctr;
    if(rows==aMatrix.getColumns() && columns==aMatrix.getRows()) {
      oldMatrix=new Matrix(this);
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++) {
          result=0;
          for(ctr=0;ctr<columns;ctr++)
            result+=oldMatrix.getElement(ctr,rowNumber)*
              aMatrix.getElement(columnNumber,ctr);
          element[columnNumber][rowNumber]=result;
        };
      return true;
    } else return false;
  };
  
  //Multipy another matrix by this
  public boolean mulBA(Matrix aMatrix) {
    int rowNumber,columnNumber;
    Matrix oldMatrix;
    int result,ctr;
    if(rows==aMatrix.getColumns() && columns==aMatrix.getRows()) {
      oldMatrix=new Matrix(this);
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++) {
          result=0;
          for(ctr=0;ctr<rows;ctr++)
            result+=oldMatrix.getElement(columnNumber,ctr)*
              aMatrix.getElement(ctr,rowNumber);
          element[columnNumber][rowNumber]=result;
        };
      return true;
    } else return false;
  };
  
  //Calculate smaller grid for calculating dets
  private Matrix smallerForDet(Matrix aMatrix,int column,int row) {
    Matrix returnValue;
    int rowNumber,columnNumber;
    int getRow,getColumn;
    double array[][]=new double[aMatrix.getColumns()-1]
      [aMatrix.getRows()-1];
    for(rowNumber=0;rowNumber<aMatrix.getRows()-1;rowNumber++) {
      getRow=rowNumber;
      if(rowNumber>=row) getRow++;
      for(columnNumber=0;columnNumber<aMatrix.getColumns()-1;columnNumber++) {
        getColumn=columnNumber;
        if(columnNumber>=column) getColumn++;
        array[columnNumber][rowNumber]=aMatrix.getElement(getColumn,getRow);
      };
    };
    return new Matrix(aMatrix.getColumns()-1,aMatrix.getRows()-1,array);
  };
  
  //Calculate determinant of aMatrix
  private double det(Matrix aMatrix) {
    int columnNumber,totalColumns,plusMinus;
    double det;
    if((totalColumns=aMatrix.getColumns())==1) {
      return aMatrix.getElement(0,0);
    } else {
      det=0;
      for(columnNumber=0;columnNumber<aMatrix.getColumns();columnNumber++) {
        plusMinus=1-columnNumber%2*2;
        det+=plusMinus*aMatrix.getElement(columnNumber,0)*
          det(smallerForDet(aMatrix,columnNumber,0));
      };
      return det;
    };
  };
  
  //Return determinant of this matrix
  public double det() {
    return det(this);
  };
  
  //Takes right half of the matrix
  private boolean takeLastHalf() {
    int rowNumber,columnNumber;
    Matrix oldMatrix;
    
    if(columns%2!=0) return false;
    else {
      oldMatrix=new Matrix(this);
      columns/=2;
      element=new double[columns][rows];
      for(rowNumber=0;rowNumber<rows;rowNumber++)
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          element[columnNumber][rowNumber]=
            oldMatrix.getElement(columns+columnNumber,rowNumber);
      return true;
    };
  };     
      
  //Swaps two rows in matrix
  private void swapRows(int a,int b) {
    int ctr;
    double swap;
    for(ctr=0;ctr<columns;ctr++) {
      swap=element[ctr][a];
      element[ctr][a]=element[ctr][b];
      element[ctr][b]=swap;
    };
  };
      
    
  
  //Calculate the inverse of this matrix
  public boolean inverse() {
    Matrix oldMatrix;
    int changeNumber,rowNumber,columnNumber,swapWith;
    double multiplier;
    boolean rowFound;
    if(det()==0)
      return false;
    else {
      this.augmentAB(new Matrix(columns,rows,Matrix.identity));
      for(changeNumber=0;changeNumber<rows;changeNumber++) {
        if(element[changeNumber][changeNumber]==0) { //Must swap rows
          rowFound=false;
          swapWith=changeNumber+1;
          while(swapWith<rows && rowFound==false) {
            if(element[changeNumber][swapWith]!=0) {
              swapRows(changeNumber,swapWith);
              rowFound=true;
            };
          };
          if(rowFound==false) return false;
        };
        //Set row,col to 1
        multiplier=element[changeNumber][changeNumber];
        for(columnNumber=0;columnNumber<columns;columnNumber++)
          element[columnNumber][changeNumber]/=multiplier;
        
        //Set rest of column to 0
        for(rowNumber=0;rowNumber<rows;rowNumber++)
          if(rowNumber!=changeNumber) {
            multiplier=element[changeNumber][rowNumber];
            for(columnNumber=changeNumber;columnNumber<columns;columnNumber++)
              element[columnNumber][rowNumber]-=
                element[columnNumber][changeNumber]*multiplier;
          };
      };
      takeLastHalf();
      return true;
    };
  };
    
  public String toString() {
    int rowNumber,columnNumber;
    String s;
    s="Matrix: "+rows+"R"+columns+"C =";
    s+="(";
    for(rowNumber=0;rowNumber<rows;rowNumber++) {
      s+="[";
      for(columnNumber=0;columnNumber<columns;columnNumber++) {
        s+=element[columnNumber][rowNumber];
        if(columnNumber<columns-1) s+=",";
      };
      s+="]";
    };
    s+=")";
    return s;
  };
    
};