Project: Bio-PEPA
package uk.ac.ed.inf.biopepa.core.analysis;
 
import java.util.ArrayList; 
import java.util.Iterator; 
import java.util.Vector; 
import Jama.Matrix;  
/**
 * Yet another matrix class, specifics are: fourier-motzkin to obtain invariants 
 * which implies uncommon operations to remove rows or add sums of other rows. 
 *  
 * @author peterkemper 
 * 
 */
 
public class IntegerMatrix implements Cloneable { 
 /**
  * matrix dimensions 
  */
 
 private int numRows = 0 ;  
 private int numCols = 0 ; 
 /**
  * internal storage format: a matrix is a vector of rows, each row is a array of int values 
  */
 
 private Vector<int[]> matrix ; 
 /**
  * Constructor 
  */
 
 public IntegerMatrix(int rows, int cols) 
 
  numRows = rows ; 
  numCols = cols ; 
  matrix = new Vector<int[]>(rows) ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   matrix.add(new int[cols]) ; 
  
 
 /**
  * private constructor for a more direct generation of a matrix 
  * @param rows 
  * @param cols 
  * @param entries 
  */
 
 /*
 private IntegerMatrix(int rows, int cols, Vector<int[]>entries) 
 
  numRows = rows ; 
  numCols = cols ; 
  matrix = entries ; 
 
 */
 
 /**
  * get number of rows 
  */
 
 public int getRowDimension() 
 
  return numRows ; 
 
 /**
  * get number of columns 
  */
 
 public int getColumnDimension() 
 
  return numCols ; 
 
 /**
  * get matrix entry 
  */
 
 public int get(int row, int col) 
 
  int[] d = matrix.get(row) ; 
  return d[col] ; 
 
 /**
  * set matrix entry 
  */
 
 public void set(int row, int col, int value) 
 
  int[] d = matrix.get(row) ; 
  d[col] = value ; 
 
 /**
  * add matrix entry 
  */
 
 public void add(int row, int col, int value) 
 
  int[] d = matrix.get(row) ; 
  d[col] += value ; 
 
 /**
  * sets all entries in the given row to zero 
  */
 
 public void setRowToZero(int row) 
 
  int[] d = matrix.get(row) ; 
  for (int i = 0 ;i < d.length ; i++) 
  
   d[i] = 0 ; 
  
 
 /**
  * sets all entries in the given column to zero 
  */
 
 public void setColumnToZero(int col) 
 
  int[] row ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   row[col] = 0 ; 
  
 
 /**
  * linear algebraic matrix matrix multiplication resulting in new matrix 
  * @return new matrix c = this * m  
  */
 
 public IntegerMatrix mult(IntegerMatrix m) 
 
  // defined iff this.numCols == m.numRows 
  if (numCols != m.getRowDimension()) 
  
   System.out.println("Matrix multiplication failed, dimensions don't match: try AxB with A: "  
     + numRows + "x" + numCols + " and B:" + m.getRowDimension() + "x" + m.getColumnDimension()) ; 
   return new IntegerMatrix(0,0) ; 
  
  // own dimension numRows x numCols 
  // m dimension m.numRows x m.numCols 
  // result has dimension numRows x m.numCols 
  int cNumRows = numRows ; 
  int cNumCols = m.getColumnDimension() ; 
  IntegerMatrix C = new IntegerMatrix(numRows,cNumCols) ; 
  // more common notation: C = A x B 
  // where A == this.matrix, B == m, C == result 
  int[] Ai = null ; 
  // for each row 
  for (int i = 0 ; i < cNumRows ; i++) 
  
    
   Ai = matrix.get(i) ; 
   // multiply with each column 
   for (int j = 0 ; j < cNumCols ; j++) 
   
    // new entry Cij = sum_k Aik * Bkj 
    for (int k = 0 ; k < numCols ; k++) 
    
     //int Aik = Ai[k] ; 
     //int Bkj = m.get(k, j) ; 
     //int Cij += Aik * Bkj ; 
     C.add(i, j, Ai[k] * m.get(k, j)) ; 
    
   
  
  return C ; 
 
 /**
  * Fourier Motzkin: transform this matrix to zero matrix,  
  * return effect of same transformations to identity matrix as a result. 
  * Note that if this matrix is (nxm) then the returned matrix is (unknown x n). 
  * @return new matrix with report on transformations applied to identity matrix 
  */
 
 public IntegerMatrix solveFourierMotzkin() 
 
  //throw new RuntimeException("FourierMotzkin: sorry not implemented yet") ; 
  // Fourier Motzkin:  
  // given matrix == A, identity matrix I 
  // consider A || I 
  // transform A step by step into a 0 matrix 
  // each steps considers a particular column j 
  // splits rows into groups minus (m), plus(p), neutral(n) according to its entry in column j being <0, >0, ==0 
  // keeps group n 
  // removes groups m and p 
  // for each pair of elements (m,p) add a new row to the result that is a linear combination with column entry j being 0 
  //return null ; 
  ArrayList<int[]> lminus = new ArrayList<int[]>() ; 
  ArrayList<int[]> lplus = new ArrayList<int[]>() ; 
  ArrayList<int[]> lneutral = new ArrayList<int[]>() ; 
  ArrayList<int[]> lresult = new ArrayList<int[]>() ; 
  // Method has exponential worst case for space and time, so we define a threshold to artificially 
  // limit calculations 
  final int threshold = 10000 ; 
  // initial set up 
  //System.out.println("Incidence matrix:"); 
  //System.out.println(printMatrix()); 
  for (int i = 0 ; i < numRows ; i++) 
  
   // create new vector:  
   int[] v = new int[numRows+numCols] ; 
   int[] row = matrix.get(i) ; 
   // copy corresponding row from matrix 
   for (int j = 0 ; j < numCols ; j++) 
   
    v[j] = row[j] ; 
   
   // add the identity matrix part at the end 
   v[numCols+i] = 1 ; 
   // new vector is completely initialized, now put it in the appropriate set 
   lneutral.add(v) ; 
    
  
  // iterate while first part is non-zero 
  try  
  
  int position = selectposition(lneutral,threshold) ; 
  for (int i = 0 ; (i < numCols) && (-1 != position) ; i++ ) 
  
   //System.out.println("FM Step:" + i); 
   split(lneutral,lplus,lminus, position) ; 
   addcombinations(lneutral,lplus,lminus,lresult,position) ; 
   position = selectposition(lneutral, threshold) ; 
   if (lneutral.size() > threshold) 
    throw new Exception() ; 
  
  // reduce vectors to appropriate size (remove initial zero part 
  lresult.addAll(lneutral) ; 
  
  catch(Exception ex) 
  
   // assume threshold is reached 
   System.out.println("WARNING: invariant calculation artificially terminated, intermediate solution exceeds threshold of " + threshold); 
   // invariants we obtained so far a perfectly fine results 
   return reducedMatrix(lresult) ; 
  
  return reducedMatrix(lresult) ; 
 
 /**
  * select the column the gives the least number of combinations 
  * @param lneutral 
  * @return 
  */
 
 private int selectposition(ArrayList<int[]> lneutral, int threshold) throws Exception 
 
  int min = Integer.MAX_VALUE ; 
  int position = -1 ; 
  int plus = 0 ; 
  int minus = 0 ; 
  int result = 0 ; 
  int[] v ; 
  for (int i = 0 ; i < numCols ; i++ ) 
  
   plus = 0 ; 
   minus = 0 ; 
   for (int j = 0 ; j < lneutral.size() ; j++) 
   
    v = lneutral.get(j) ; 
    if (v[i] < 0
     minus++ ; 
    if (v[i]>0
     plus++ ; 
   
   if (0 == minus && 0 == plus) 
    continue ; // column has been resolved before, skip this one 
    
   result = minus*plus ; 
   //update min if necessary 
   if (result < min) 
   
    min = result ; 
    position = i ; 
   
   // check special case for early termination 
   if (1 >= result) 
   
    position = i ; // memo result 
    i = numCols ;// terminate loop 
   
     
  
   
  //System.out.println("suggest position " + position + " minus: " + minus + " plus: " + plus); 
  if (result > threshold) 
   throw new Exception() ; 
  return position; 
 
 /**
  * create a matrix from the last numCols ... numCols+numRows-1 entries in the vectors of the given list 
  * @param lneutral 
  * @return 
  */
 
 private IntegerMatrix reducedMatrix(ArrayList<int[]> lneutral) { 
  IntegerMatrix result = new IntegerMatrix(lneutral.size(),numRows) ; 
  // check special case of an empty set 
  if (0 == lneutral.size()) 
  
   // System.out.println("special case: set of computed invariants is empty") ; 
   return result ; 
  
  // ok, result is not empty and contains at least one row 
  Iterator<int[]> it = lneutral.iterator() ; 
  int[] v = null ; 
  int r = 0 ; // counter to access the current row in the result matrix 
  while (it.hasNext()) 
  
   v = it.next() ; 
   for (int i = 0 ; i < numRows; i++) 
   
    result.set(r, i, v[numCols+i]) ;  
   
   r++ ; 
  
  //System.out.println( " result of fourier motzkin:"); 
  //System.out.println(result.printMatrix()); 
  //return result ; 
  //System.out.println(" calling base of row vectors"); 
  return baseOfRowVectors(result) ; 
 
 /**
  * create a matrix with a maximal set of linear independent rows for the given matrix 
  * @param m input matrix 
  * @return matrix with a subset of rows copied from m 
  */
 
 private IntegerMatrix baseOfRowVectors(IntegerMatrix input) 
 
  int m = input.getRowDimension() ; 
  int n = input.getColumnDimension() ; 
  int[] support = new int[m] ; // number of nonzero entries per row, rows with few entries are preferable 
  int[] base = new int[m] ; // flag: 0: not selected, 1: selected, -1: linear dependent 
  int[] covered = new int[n] ; // flag: number of base(!) vectors that cover this column 
  int selected = 0 ; 
  // case 0: special case: 1 row only: all rows are linearly independent: copy matrix and return result 
  if (1 == m) 
  
   // System.out.println("Integermatrix.baseOfRowVectors: special case, single row must be lin. independent!"); 
   return input.clone() ; 
  
  // compute the rank of the input matrix first to see how many vectors we need to select 
  // compute the support of each row (number of nonzero entries in each row) 
  Matrix mat = new Matrix(m,n) ; 
  for (int i = 0 ; i < m ; i++) 
  
   for (int j=0 ; j < n ; j++) 
   
    if (input.get(i, j) != 0
    
     support[i] += 1 ; 
     mat.set(i, j, input.get(i, j)) ; // implicit type conversion int -> double for entry 
    
   
  
  int rank = mat.rank() ; 
  // System.out.println("matrix: " + m + " times " + n + ", rank computed: " + rank) ; 
  // case 1: all rows are linearly independent: copy matrix and return result 
  if (m == rank) 
  
   // System.out.println("Integermatrix.baseOfRowVectors: special case, all rows lin. independent!"); 
   return input.clone() ; 
  
  // select vectors that individually cover a particular column and have a small support 
  // due to the coverage, those vectors MUST be linearly independent 
  // for each column that is not covered pick a vector with small support 
  int pos = 0 ; 
  for (int j = 0 ; j < n ; j++) 
  
   if (0 < covered[j]) 
    continue ; // column is already covered 
   pos = findRowWithMinimumSupport(m, support, base, j, input); 
   // if there is a candidate row, select it, otherwise just continue 
   if (0 <= pos) 
   
    base[pos] = 1 ; // selected 
    selected++ ; 
    for (int k = 0 ; k < n; k++) 
    
     if (0 != input.get(pos, k)) 
      covered[k] +=1 ; // update coverage 
    
   
  
  // we have an initial selection that is linearly independent,  
  // check if we are done or if there is room for more invariants to add 
  // System.out.println("IntegerMatrix.baseOfRowVectors: initial selection yields " + selected + " out of " + rank + " vectors."); 
 
  // let's look for the rest 
  while (rank != selected) 
  
   // pick unchecked row with smallest support 
   pos = findRowWithMinimumSupport(m, support, base); 
   // if there is a candidate row, check it,  
   if (0 < pos) 
   
    // check if it is independent 
    base[pos] = 1 ; // only temporary for matrix extraction 
    selected++ ;  // only temporary for matrix extraction 
    Matrix matrix = extractSelectedRows2(input, m, n, base, selected) ;  
    int mrank = matrix.rank() ; 
    //System.out.println("Row " + pos + " gives rank " + mrank + " for total rows: " + selected) ; 
    if (selected == mrank) 
    
     // it is independent, so we can select it for the base 
     //System.out.println("adding row " + pos); 
     for (int k = 0 ; k < n; k++) 
     
      if (0 != input.get(pos, k)) 
       covered[k] +=1 ; // update coverage 
     
    
    else 
    
     // not selected, we need to take back previous assignments 
     //System.out.println("not adding row " + pos); 
     base[pos] = -1 ; 
     selected-- ; 
    
   
   // else we ran out of candidates and must stop! 
   else 
   
    if (rank != selected) 
    
     System.out.println("IntegerMatrix.baseOfRowVectors: no vectors left but rank " + rank + " does not match selection " + selected); 
     rank = selected ; // terminate loop 
    
   
  
  // at this point: (rank == selected) 
  // return selection 
  System.out.println("base of Row vectors: base has dimension " + selected ); 
  return extractSelectedRows(input, m, n, base, selected); 
 
 /**
  * private helper method to extract a subset of selected rows from an integer matrix 
  * @param input 
  * @param m 
  * @param n 
  * @param base 
  * @param selected 
  * @return 
  */
 
 private IntegerMatrix extractSelectedRows(IntegerMatrix input, int m, int n, int[] base, int selected)  
 
  IntegerMatrix result = new IntegerMatrix(selected,n) ; 
  int pos = 0 ; 
  for (int i=0 ; i< m ; i++) 
   
    if (base[i] != 1
     continue ; 
    // copy entries into result matrix 
    for (int j=0 ; j < n ;j++) 
    
     result.set(pos, j, input.get(i,j)) ; 
    
    pos++ ; 
   
  System.out.println("finally returned: " + pos + " for selected " + selected); 
  return result; 
 
 /**
  * private helper method to extract a subset of selected rows from an integer matrix 
  * @param input 
  * @param m 
  * @param n 
  * @param base 
  * @param selected 
  * @return 
  */
 
 private Matrix extractSelectedRows2(IntegerMatrix input, int m, int n, int[] base, int selected)  
 
  Matrix result = new Matrix(selected,n) ; 
  int pos = 0 ; 
  for (int i=0 ; i< m ; i++) 
   
    if (base[i] != 1
     continue ; 
    // copy entries into result matrix 
    for (int j=0 ; j < n ;j++) 
    
     result.set(pos, j, input.get(i,j)) ; // implicit type conversion: int -> double 
    
    pos++ ; 
   
  return result; 
 
 /**
  * private helper method 
  * @param m 
  * @param support 
  * @param base 
  * @return -1 if nothing is found, value >=0 for valid row index 
  */
 
 private int findRowWithMinimumSupport(int m, int[] support, int[] base) { 
  int min = Integer.MAX_VALUE ; 
  int pos = -1 ; 
  for (int i =0 ; i < m ; i++) 
  
   // pick row with smallest support that has not been selected yet (or recognized dependent) 
   if (0 < support[i] && support[i] < min && 0 == base[i]) 
   
    min = support[i] ; 
    pos = i ; 
   
  
  //System.out.println("Picking row " + pos + " due to minimal support " + min); 
  return pos; 
 
 private int findRowWithMinimumSupport(int m, int[] support, int[] base, int column, IntegerMatrix input) { 
  int min = Integer.MAX_VALUE ; 
  int pos = -1 ; 
  for (int i =0 ; i < m ; i++) 
  
   // pick row with smallest support that has not been selected yet (or recognized dependent) 
   if (0 < support[i] && support[i] < min && 0 == base[i] && 0 != input.get(i, column)) 
   
    min = support[i] ; 
    pos = i ; 
   
  
  //System.out.println("Picking row " + pos + " due to minimal support " + min + " for column " + column); 
  return pos; 
 
 /**
  *  
  * @param lneutral 
  * @param lplus 
  * @param lminus 
  * @param lresult 
  * @param position 
  */
 
 private void addcombinations(ArrayList<int[]> lneutral, 
   ArrayList<int[]> lplus, ArrayList<int[]> lminus, ArrayList<int[]> lresult, int position) { 
  // case 1: lminus is empty, then clean up lplus since there are no combinations to create 
  if (lminus.isEmpty()) 
  
   // clean up lplus in any case 
   lplus.clear() ; 
   return ; 
  
  // case 2: lplus is empty, then clean up lminus since there are no combinations to create 
  if (lplus.isEmpty()) 
  
   // clean up lminus in any case 
   lminus.clear() ; 
   return ; 
  
  // case 3: both lists are not empty, we need to create |lminus| * |lplus| combinations 
  Iterator<int[]> itminus = lminus.iterator() ; 
  Iterator<int[]> itplus = null ; 
  int[] vm ; 
  int[] vp ; 
  int gcd ; 
  int scalep ; 
  int scalem ; 
  int[] vn ; 
  boolean finished ; 
  while (itminus.hasNext()) 
  
   vm = itminus.next() ; 
   itplus = lplus.iterator() ; 
   while (itplus.hasNext()) 
   
    vp = itplus.next() ; 
    //System.out.println("vp: " + debugprinter(vp)); 
    //System.out.println("vm: " + debugprinter(vm)); 
    // get least common multiple 
    // gcd = P3InvariantImplementation.bigGCD(Math.abs(vm[position]), vp[position], false) ; 
    // gcd    = myGCD(Math.abs(vm[position]), vp[position], false); 
     
    gcd = euklidGCD(vm[position], vp[position]); 
    scalep = Math.abs(vm[position]) / gcd ; 
    scalem = vp[position] / gcd ; 
    //System.out.println("gcd:" + gcd + " scalep" + scalep + "scalem" + scalem); 
    vn = new int[vm.length] ; 
    // check correctness 
    if (0 != vm[position]*scalem + vp[position]*scalep ) 
     throw new RuntimeException("Calculation of combinations is wrong") ; 
    // since positions are selected in any order, we need to add all entries of the vectors 
    finished = true ;  
    for (int i = 0  ; i < vm.length ; i++) 
    
     vn[i] = vm[i]*scalem + vp[i]*scalep ; 
     if (i<numCols && 0 !=vn[i]) 
      finished = false ; 
    
    normalizeWithGCD(vn,0); 
    //System.out.println("vn: " + debugprinter(vn)); 
    // add only if really new 
    //add(lneutral,vn,position) ; 
    if (finished) 
     add(lresult,vn,0) ; 
    else 
     add(lneutral,vn,0) ; 
    //lneutral.add(vn) ; 
   
  
  lminus.clear() ; 
  lplus.clear() ; 
 
 private void add(ArrayList<int[]> lneutral, int[] vn, int startposition) { 
  // check if vector is really new 
   
  if (containsVector(lneutral, vn, startposition)) 
   return ; 
  // vector vn survived duplicate filter and needs to be added 
  lneutral.add(vn) ; 
  //System.out.println("vn added"); 
 
 /**
  * check if the given list already contains the given vector 
  * check entries only starting at the given startposition 
  * @param lneutral 
  * @param vn 
  * @param startposition 
  * @return 
  */
 
 private boolean containsVector(ArrayList<int[]> lneutral, int[] vn, 
   int startposition) { 
  int[] v; 
  boolean ok; 
  // check all existing vectors in lneutral 
  for (int i = 0 ; i < lneutral.size() ; i++) 
  
   v = lneutral.get(i) ; 
   ok = false ; 
   // we only need to consider parts that are nonzero  
   // which are known to start at startposition the earliest 
   for (int j=startposition ; j < v.length ; j++) 
   
    if (v[j] != vn[j]) 
    
     // so vectors are really different, stop the loop 
     ok = true ; 
     j = v.length ; 
    
   
   if (!ok) 
   
    // vector is a duplicate, skip 
    //System.out.println("skip duplicate vector"); 
    return true ; 
   
  
  return false ; 
 
  
 /*
  * My simple version of the greatest common divisor routine 
  * temporarily used to avoid the unresolved reference to 
  * P3InvariantImplementation.bigGCD 
  *  
  */
 
 private int myGCD(int a, int b, boolean notsure) { 
     if (b==0)  
       return a; 
     else 
       return myGCD(b, a % b, notsure); 
    }  
  
  
 private static int euklidGCD(int a, int b) { 
  // function gcd(a, b) 
  // while b ï¿Ω 0 
  // t := b 
  // b := a mod b 
  // a := t 
  // return a 
  // System.out.print("GCD (" + a + ", " + b + ") = ") ; 
  a = Math.abs(a); 
  b = Math.abs(b); 
  int t; 
  while (0 != b) { 
   t = b; 
   b = a % b; 
   a = t; 
  
  // System.out.println(a); 
  return a; 
 
  
 /**
  * compute the gcd and normalize the entries of the given vector 
  * consider only entries starting at the given startposition in the vector 
  * @param vn 
  * @param startposition 
  */
 
 private void normalizeWithGCD(int[] vn, int startposition) { 
  // compute the gcd 
  int gcd = 0 ; 
  for (int i = startposition ; i < vn.length ; i++) 
  
   if (0 != vn[i]) 
   
    if (0 == gcd) 
    
     // initialize 
     gcd = Math.abs(vn[i]) ; 
    
    else 
    
     // gcd = P3InvariantImplementation.bigGCD(gcd, Math.abs(vn[i]), false) ; 
     // gcd = myGCD(gcd, Math.abs(vn[i]), false); 
     gcd = euklidGCD(gcd, vn[i]); 
    
   
  
  // normalize  
  if (0 != gcd && 1 != gcd) 
  
   for (int i = startposition ; i < vn.length ; i++) 
   
    if (0 != vn[i]) 
    
     vn[i] /= gcd ; 
    
   
  
 
 /*
 private String debugprinter(int[] v) 
 
  String result = "" ; 
  for (int i=0 ; i <v.length ; i++) 
   result += v[i]+" " ; 
  return result ; 
 
 */
 
 /**
  * move elements from the lneutral list to lplus or lminus depending on the nonzero entries in the given column 
  * @param lneutral 
  * @param lplus 
  * @param lminus 
  * @param column 
  */
 
 private void split(ArrayList<int[]> lneutral, ArrayList<int[]> lplus, 
   ArrayList<int[]> lminus, int column)  
 
  int i = 0 ; 
  int[] v ; 
  while (i < lneutral.size()) 
  
   v = lneutral.get(i) ; 
   if (v[column] < 0
   
    lminus.add(v) ; 
    lneutral.remove(i) ; 
   
   else 
   
    if (v[column] > 0
    
     lplus.add(v) ; 
     lneutral.remove(i) ; 
    
    else 
    
     i++ ; 
    
   
  
  //System.out.println("FM Split gives neutral: " + lneutral.size() + " plus" + lplus.size() + " minus:" + lminus.size()); 
 
 /**
  * transpose matrix into new one 
  */
 
 public IntegerMatrix transpose() 
 
  IntegerMatrix result = new IntegerMatrix(numCols,numRows) ; 
  int[] row = null ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   for (int j = 0 ; j < numCols ; j++) 
   
    result.set(j, i, row[j]) ; 
   
  
  return result ; 
 
 /**
  * clones the whole matrix onto new memory 
  */
 
 public IntegerMatrix clone() 
 
  IntegerMatrix result = new IntegerMatrix(numRows,numCols) ; 
  int[] row = null ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   for (int j = 0 ; j < numCols ; j++) 
   
    result.set(i, j, row[j]) ; 
   
  
  return result ; 
 
 /**
  * checks equality of elements up to an epsilon 
  */
 
 public boolean equals(IntegerMatrix m) 
 
  return (0 == compare(m)) ; 
 
 /**
  * compares with matrix, equality up to an epsilon 
  */
 
 public int compare(IntegerMatrix m) 
 
  // check if dimensions match 
  if (numCols != m.getColumnDimension() || numRows != m.getRowDimension()) 
   throw new RuntimeException("Cannot compare matrices of different dimensions") ; 
  int[] row = null ; 
  int result = 0 ; // so far everything equal 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   for (int j = 0 ; j < numCols ; j++) 
   
    if (row[j]!= m.get(i,j)) 
    
     switch (result)  
     
     case -1 :  
      if (row[j] > m.get(i, j)) 
       throw new RuntimeException("Matrices are not comparable") ; 
      break ; 
     case 0 :  
      result = (row[j] < m.get(i, j)) ? -1 : 1 ; 
      break ;  
     case 1 :  
      if (row[j] < m.get(i, j)) 
       throw new RuntimeException("Matrices are not comparable") ; 
      break ; 
     default : 
      throw new RuntimeException("Reaching unreachable default case") ; 
     
    
   
  
  return result ; 
 
 /**
  * tell if this is a matrix with zero entries only 
  */
 
 public boolean isZeroMatrix() 
 
  // check if the result is the zero vector 
  int[] row = null ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   for (int j = 0 ; j < numCols ; j++) 
   
    if (0 != row[j]) 
     return false ; 
   
  
  return true ; 
 
 /**
  * tell if this is a matrix with zero entries only but for those on the main diagonal, 
  * works for rectangular matrices as well 
  */
 
 public boolean isIdentityMatrix() 
 
  // check if the result is the zero vector 
  int[] row = null ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   row = matrix.get(i) ; 
   for (int j = 0 ; j < numCols ; j++) 
   
    if (i == j) 
    
     if (1 != row[j]) 
      return false ; 
    
    else 
    
     if (0 != row[j]) 
      return false ; 
    
   
  
  return true ; 
 
 public final static String newline = System.getProperty("line.separator"); 
 /**
  * print matrix to see its content 
  */
 
 public String printMatrix() 
 
  String result = "Content of an " + numRows + " x " + numCols + " matrix:" + newline ; 
  for (int i = 0 ; i < numRows ; i++) 
  
   result += "row " + i + ": " ; 
   for (int j = 0 ; j< numCols ; j++) 
   
    result += " " + this.get(i, j) ; 
     
   
   result += newline ; 
  
  return result ; 
 
}