//Matrix/src/main/java/com/mattrixwv/IntegerMatrix.java //Mattrixwv // Created: 02-01-22 //Modified: 02-17-22 package com.mattrixwv.matrix; import java.util.StringJoiner; import com.mattrixwv.matrix.exceptions.InvalidCoordinatesException; import com.mattrixwv.matrix.exceptions.InvalidGeometryException; import com.mattrixwv.matrix.exceptions.InvalidRowSizeException; import com.mattrixwv.matrix.exceptions.InvalidScalarException; public class IntegerMatrix{ protected int[][] grid; //Helper functions protected void setGrid(int[][] grid){ if(grid.length == 0){ this.grid = new int[0][0]; } else if(grid[0].length == 0){ this.grid = new int[grid.length][0]; } else{ //Make sure all rows are the same length int length = grid[0].length; for(int cnt = 1;cnt < grid.length;++cnt){ if(grid[cnt].length != length){ throw new InvalidRowSizeException("All rows in a matrix must be the same size"); } } //Copy every element over to a new grid int[][] newGrid = new int[grid.length][grid[0].length]; for(int rowCnt = 0;rowCnt < grid.length;++rowCnt){ for(int colCnt = 0;colCnt < grid[0].length;++colCnt){ newGrid[rowCnt][colCnt] = grid[rowCnt][colCnt]; } } //Save the new grid this.grid = newGrid; } } protected int[][] copyGrid(){ //Allocate memory for the new grid int[][] newGrid = new int[grid.length][grid[0].length]; //Copy every element from the current grid to the new one for(int row = 0;row < grid.length;++row){ for(int col = 0;col < grid[0].length;++col){ newGrid[row][col] = grid[row][col]; } } //Return the new grid return newGrid; } public boolean isSquare(){ if(getNumRows() == 0){ return false; } else{ return getNumRows() == getNumCols(); } } //Returns a matrix with the supplied row and column removed protected IntegerMatrix laplaceExpansionHelper(int row, int col){ //Make sure the matrix is square if(!isSquare()){ throw new InvalidGeometryException("A matrix must be square for you to perform Laplace Expansion"); } //Make sure the matrix is large enough to have this operation performed if((getNumRows() <= 1) || (getNumCols() <= 1)){ throw new InvalidGeometryException("A matrix must be at least 2x2 for you to perform Laplace Expansion: " + getNumRows() + "x" + getNumCols()); } //Make sure the row is valid if((row < 0) || (row >= getNumRows())){ throw new InvalidCoordinatesException("An invalid row number was supplied: " + row + ". The matrix contains " + getNumRows() + " rows"); } //Make sure the col is valid if((col < 0) || (col >= getNumCols())){ throw new InvalidCoordinatesException("An invalid column number was supplied: " + col + ". The matrix contains " + getNumCols() + " columns"); } //Create a new matrix that is one row and column smaller than the existing row int[][] newGrid = new int[getNumRows() - 1][getNumCols() - 1]; //Traverse the matrix and set the values in the new matrix, skipping elements in the given row and col for(int workingRow = 0, newRow = 0;workingRow < getNumRows();++workingRow){ //Skip the row we are expanding if(workingRow == row){ continue; } for(int workingCol = 0, newCol = 0;workingCol < getNumCols();++workingCol){ //Skip the column we are expanding if(workingCol == col){ continue; } newGrid[newRow][newCol] = grid[workingRow][workingCol]; ++newCol; } ++newRow; } //Return the new matrix return new IntegerMatrix(newGrid); } //Constructors public IntegerMatrix(){ grid = new int[0][0]; } public IntegerMatrix(int[][] grid){ setGrid(grid); } public IntegerMatrix(IntegerMatrix matrix){ setGrid(matrix.grid); } public IntegerMatrix(int rows, int cols, int fill){ if(rows <= 0){ throw new InvalidGeometryException("A filled matrix must have at least 1 row"); } else if(cols <= 0){ throw new InvalidGeometryException("A filled matrix must have at least 1 column"); } else{ grid = new int[rows][cols]; for(int row = 0;row < rows;++row){ for(int col = 0;col < cols;++col){ grid[row][col] = fill; } } } } //Gets public int get(int row, int col){ //Make sure the row and column are valid if(row >= grid.length){ throw new InvalidCoordinatesException("Row cannot be greater than the number of rows"); } else if(col >= grid[0].length){ throw new InvalidCoordinatesException("Column cannot be greater than the number of columns"); } //Return the location in the grid return grid[row][col]; } public IntegerMatrix getRow(int row){ //Make sure the row number is valid if((row < 0) || (row >= grid.length)){ throw new InvalidCoordinatesException("The row number " + row + " is not valid"); } //Generate a copy of the row int[][] newRow = new int[1][grid[row].length]; for(int col = 0;col < grid[row].length;++col){ newRow[0][col] = grid[row][col]; } //Return the new matrix return new IntegerMatrix(newRow); } public int getNumRows(){ if(grid == null){ return 0; } else{ return grid.length; } } public IntegerMatrix getCol(int col){ //Make sure the column number is valid if((col < 0) || (grid.length == 0) || (col > grid[0].length)){ throw new InvalidCoordinatesException("The column number " + col + " is not valid"); } //Generate a copy of the col int[][] newColumn = new int[grid.length][1]; for(int row = 0;row < grid.length;++row){ newColumn[row][0] = grid[row][col]; } //Return the new matrix return new IntegerMatrix(newColumn); } public int getNumCols(){ if(grid.length > 0){ return grid[0].length; } else{ return 0; } } //Sets public void set(int row, int col, int value){ //Make sure the row number is valid if((row < 0) || (row >= grid.length)){ throw new InvalidCoordinatesException("Invalid row number " + row); } //Make sure the column number is valid if((col < 0) || (col >= getNumCols())){ throw new InvalidCoordinatesException("Invalid column number " + col); } //Save the element grid[row][col] = value; } public void setRow(int row, int[] elements){ //Make sure the row number is valid if((row < 0) || (row >= grid.length)){ throw new InvalidCoordinatesException("Invalid row number " + row); } //Make sure the number of elements is valid if(elements.length != getNumCols()){ throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols()); } //Save the elements for(int col = 0;col < elements.length;++col){ grid[row][col] = elements[col]; } } public void setRow(int row, IntegerMatrix matrix){ //Make sure the matrix has a single row if(matrix.getNumRows() != 1){ throw new InvalidGeometryException("Setting a row by Matrix requires the matrix contain a single row"); } //Set the row setRow(row, matrix.grid[0]); } public void setCol(int col, int[] elements){ //Make sure the column number is valid if((col < 0) || (col >= getNumCols())){ throw new InvalidCoordinatesException("Invalid column number " + col); } //Make sure the number of elements is valid if(elements.length != grid.length){ throw new InvalidCoordinatesException("Invalid number of elements " + elements.length + " must be " + grid.length); } //Save the elements for(int row = 0;row < elements.length;++row){ grid[row][col] = elements[row]; } } public void setCol(int col, IntegerMatrix matrix){ //Make sure the matrix has a single column if(matrix.getNumCols() != 1){ throw new InvalidGeometryException("Setting a column by Matrix requires the matrix contain a single column"); } //Set the column setCol(col, matrix.getCol(0)); } //Adds public void addRow(int[] elements){ //Make sure the number of columns is valid if((grid.length == 0) || (getNumCols() == elements.length)){ int[][] newGrid = new int[grid.length + 1][elements.length]; //Copy all existing data into the new grid for(int row = 0;row < grid.length;++row){ newGrid[row] = grid[row]; } grid = newGrid; } else{ throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols()); } //Add all elements to the grid for(int col = 0;col < elements.length;++col){ grid[grid.length - 1][col] = elements[col]; } } public void addRow(IntegerMatrix matrix){ //Make sure the matrix has a single row if(matrix.getNumRows() != 1){ throw new InvalidGeometryException("Adding a row by Matrix requires the matrix contain a single row"); } //Add the row addRow(matrix.grid[0]); } public void addCol(int[] elements){ //Make sure the number of rows is valid if(grid.length == 0){ grid = new int[elements.length][1]; } else if(grid.length == elements.length){ //Copy all existing data into the new grid for(int row = 0;row < grid.length;++row){ int[] workingRow = new int[grid[row].length + 1]; for(int workingCol = 0;workingCol < grid[row].length;++workingCol){ workingRow[workingCol] = grid[row][workingCol]; } grid[row] = workingRow; } } else{ throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols()); } //Add all elements to the grid for(int row = 0;row < elements.length;++row){ grid[row][grid[row].length - 1] = elements[row]; } } public void addCol(IntegerMatrix matrix){ //Make sure the matrix has a single column if(matrix.getNumCols() != 1){ throw new InvalidGeometryException("Adding a column by Matrix requires the matrix contain a single column"); } //Add the column addCol(matrix.getCol(0)); } public IntegerMatrix appendRight(IntegerMatrix rightSide){ //Make sure the matrices have the same number of rows if(getNumRows() != rightSide.getNumRows()){ throw new InvalidGeometryException("Invalid number of rows. " + rightSide.getNumRows() + " must be " + getNumRows()); } //Traverse both matrices and set their values in the new matrix int[][] newGrid = new int[getNumRows()][getNumCols() + rightSide.getNumCols()]; for(int row = 0;row < getNumRows();++row){ //Set all elements from the current grid's row for(int col = 0;col < getNumCols();++col){ newGrid[row][col] = grid[row][col]; } //Set all elements from the right side grid's row for(int col = 0;col < rightSide.getNumCols();++col){ newGrid[row][getNumCols() + col] = rightSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix appendBottom(IntegerMatrix rightSide){ //Make sure the matrices have the same number of columns if(getNumCols() != rightSide.getNumCols()){ throw new InvalidGeometryException("Invalid number of columns. " + rightSide.getNumCols() + " must be " + getNumCols()); } //Traverse both matrices and set their values in the new matrix int[][] newGrid = new int[getNumRows() + rightSide.getNumRows()][getNumCols()]; for(int col = 0;col < getNumCols();++col){ //Set all elements from the current grid's column for(int row = 0;row < getNumRows();++row){ newGrid[row][col] = grid[row][col]; } //Set all elements from the right side grid's column for(int row = 0;row < rightSide.getNumRows();++row){ newGrid[getNumRows() + row][col] = rightSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } //Simple operations public static IntegerMatrix generateIdentity(int size){ //Make sure the size is valid if(size > 0){ //Create a grid with the correct size int[][] newGrid = new int[size][size]; //Go through every element and make sure it is set correctly for(int row = 0;row < size;++row){ for(int col = 0;col < size;++col){ if(col == row){ newGrid[row][col] = 1; } else{ newGrid[row][col] = 0; } } } //Return the new matrix return new IntegerMatrix(newGrid); } else{ throw new InvalidGeometryException("An identity matrix must have a size > 0"); } } public IntegerMatrix add(IntegerMatrix rightSide){ //Make sure the matrices have compatable geometry if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){ throw new InvalidGeometryException("Both matrices must have the same number of rows and columns: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols()); } //Create a new grid with the same elements as the current grid int[][] newGrid = copyGrid(); //Add each element in the righ tmatrix to the corresponding element in the left matrix for(int row = 0;row < newGrid.length;++row){ for(int col = 0;col < newGrid[0].length;++col){ newGrid[row][col] += rightSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix add(int scalar){ //Create a new grid with the same elements as the current grid int[][] newGrid = copyGrid(); //Add the scalar to each element in the grid for(int row = 0;row < newGrid.length;++row){ for(int col = 0;col < newGrid[0].length;++col){ newGrid[row][col] += scalar; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix subtract(IntegerMatrix rightSide){ //Make sure the matrices have compatable geometry if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){ throw new InvalidGeometryException("Both matrices must have the same number of rows and columsn: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols()); } //Create a new grid with the same elements as the current gird int[][] newGrid = copyGrid(); //Subtract each element in the righ tmatrix from the corresponding element in the left matrix for(int row = 0;row < newGrid.length;++row){ for(int col = 0;col < newGrid[0].length;++col){ newGrid[row][col] -= grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix subtract(int scalar){ //Create a new grid with the same elements as the current grid int[][] newGrid = copyGrid(); //Subtract the scalar from each element in the grid for(int row = 0;row < newGrid.length;++row){ for(int col = 0;col < newGrid[0].length;++col){ newGrid[row][col] -= scalar; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix multiply(IntegerMatrix rightSide){ //Make sure the matrices have compatable geometry if(getNumCols() != rightSide.getNumRows()){ throw new InvalidGeometryException("The left matrix must have the same number of columns as the right matrix has rows: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols()); } //Create a new grid with the same elements as the current grid int[][] newGrid = new int[getNumRows()][rightSide.getNumCols()]; //Multiply each row in the left matrix with each column int he right matrix to come up with the current element for(int leftRow = 0;leftRow < grid.length;++leftRow){ for(int rightCol = 0;rightCol < rightSide.getNumCols();++rightCol){ //Get a sum of the product of each column in the current row (left) and each row in the current column (right) int elementProductSum = 0; for(int incrementCounter = 0;incrementCounter < grid.length;++incrementCounter){ elementProductSum += grid[leftRow][incrementCounter] * rightSide.grid[incrementCounter][rightCol]; } newGrid[leftRow][rightCol] = elementProductSum; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix multiply(int scalar){ //Create a new grid with the same elements as the current grid int[][] newGrid = copyGrid(); //Multiply every element in the grid by the scalar for(int row = 0;row < grid.length;++row){ for(int col = 0;col < grid[0].length;++col){ newGrid[row][col] *= scalar; } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix pow(int power){ //Make sure the matrix is square so it can be multiplied if(!isSquare()){ throw new InvalidGeometryException("The matrix must be square to raise it to a power"); } //Make sure the power is positive if(power < 0){ throw new InvalidScalarException("The power must be >= 0"); } else if(power == 0){ return new IntegerMatrix(getNumRows(), getNumCols(), 1); } //Create a new matrix for the product IntegerMatrix newMatrix = clone(); //Multiply the current grid power times for(int currentPower = 1;currentPower < power;++currentPower){ newMatrix = newMatrix.multiply(this); } //Return the new grid return newMatrix; } public int dotProduct(IntegerMatrix rightSide){ //Make sure the matrices have compatable geometry if(getNumCols() != rightSide.getNumRows()){ throw new InvalidGeometryException("The left matrix must have the same number of columns as the right matrix has rows: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols()); } //Multiply each row in the left matrix with each column in the right matrix to come up with the current element //?Would be simpler, but slower, to get the product matrix to come up with the current element int sum = 0; for(int leftRow = 0;leftRow < grid.length;++leftRow){ for(int rightCol = 0;rightCol < rightSide.getNumCols();++rightCol){ for(int incrementCounter = 0;incrementCounter < grid.length;++incrementCounter){ sum += grid[leftRow][incrementCounter] * rightSide.grid[incrementCounter][rightCol]; } } } //Return the sum return sum; } public IntegerMatrix hadamardProduct(IntegerMatrix rightSide){ //Make sure the matrices have compatable geometry if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){ throw new InvalidGeometryException("Both matrices must have the same number of rows and columns: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols()); } //Create a new grid with the same element as the current grid int[][] newGrid = copyGrid(); //Multiply each element in the right matrix to the corresponding element in the left matrix for(int row = 0;row < newGrid.length;++row){ for(int col = 0;col < newGrid[0].length;++col){ newGrid[row][col] *= rightSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } //Complex operations public IntegerMatrix transpose(){ //Create a new grid int[][] newGrid = new int[getNumCols()][getNumRows()]; //Traverse every element in the existing grid and add each column to the new grid as a row for(int col = 0;col < getNumCols();++col){ for(int row = 0;row < getNumRows();++row){ newGrid[col][row] = grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } public int det(){ return determinant(); } public int determinant(){ //Make sure the matrix is square if(!isSquare()){ throw new InvalidGeometryException("A matrix must be square for it to have a determinant"); } //?Don't have to worry about a matrix existing. isSquare takes care of 0x0 matrix //Determine the formula do use for the determinant int det = 0; switch(getNumRows()){ //If the matrix is 1x1 return the number case 1 : det = grid[0][0]; break; //If the matrix is 2x2 use the formula case 2 : { det = (grid[0][0] * grid[1][1]) - (grid[0][1] * grid[1][0]); } break; //If the matrix is 3x3 use the formula case 3 : { det = (grid[0][0] * grid[1][1] * grid[2][2]) + (grid[0][1] * grid[1][2] * grid[2][0]) + (grid[0][2] * grid[1][0] * grid[2][1]) - (grid[2][0] * grid[1][1] * grid[0][2]) - (grid[2][1] * grid[1][2] * grid[0][0]) - (grid[2][2] * grid[1][0] * grid[0][1]); } break; //If the matrix is larger break it down and try again default : { //Find the row/column with the largest number of 0's int zerosLocation = 0; int maxNumZeros = 0; boolean zerosRow = true; //Check the rows for(int row = 0;row < getNumRows();++row){ int numZeros = 0; for(int col = 0;col < getNumCols();++col){ if(grid[row][col] == 0){ ++numZeros; } } if(numZeros > maxNumZeros){ maxNumZeros = numZeros; zerosLocation = row; zerosRow = true; } } //Check the columns for(int col = 0;col < getNumCols();++col){ int numZeros = 0; for(int row = 0;row < getNumRows();++row){ if(grid[row][col] == 0){ ++numZeros; } } if(numZeros > maxNumZeros){ maxNumZeros = numZeros; zerosLocation = col; zerosRow = false; } } //If the largest number of zeros were found in a row if(zerosRow){ //Set a variable to make sure the appropriate + or - is applied to the scalar int multiplier = 1; if((zerosLocation % 2) == 1){ multiplier = -1; } //Go through every column in the optimal row using the formula for(int col = 0;col < getNumCols();++col){ if(grid[zerosLocation][col] != 0){ det += (multiplier * grid[zerosLocation][col] * laplaceExpansionHelper(zerosLocation, col).determinant()); } multiplier = -multiplier; } } //If the largest number of zeros were found in a column else{ //Set a variable to make sure the appropriate + or - is applied to the scalar int multiplier = 1; if((zerosLocation % 2) == 1){ multiplier = -1; } //Go through every row in the coptimal column using the formula for(int row = 0;row < getNumRows();++row){ if(grid[row][zerosLocation] != 0){ det += (multiplier * grid[row][zerosLocation] * laplaceExpansionHelper(row, zerosLocation).determinant()); } multiplier = -multiplier; } } } } //Return the determinant return det; } public IntegerMatrix cof(){ return cofactor(); } public IntegerMatrix cofactor(){ //Make sure the matrix is square if(!isSquare()){ throw new InvalidGeometryException("A matrix must be square to find the cofactor matrix"); } //Create a new grid int[][] newGrid = new int[getNumRows()][getNumCols()]; //If the grid is 1x1 return the grid if(getNumRows() == 1){ newGrid[0][0] = 1; } //Use the formula to find the cofactor matrix else{ for(int row = 0;row < getNumRows();++row){ int multiplier = ((row % 2) == 0) ? 1 : -1; for(int col = 0;col < getNumCols();++col){ newGrid[row][col] = multiplier * laplaceExpansionHelper(row, col).determinant(); multiplier = -multiplier; } } } //Return the new matrix return new IntegerMatrix(newGrid); } public IntegerMatrix adj(){ return adjoint(); } public IntegerMatrix adjoint(){ return cofactor().transpose(); } public IntegerMatrix inverse(){ //Make sure the matrix is square if(!isSquare()){ throw new InvalidGeometryException("A matrix must be square for it to have an inverse"); } //Make sure the determinant is not 0 int determinant = determinant(); if(determinant == 0){ throw new InvalidScalarException("The determinant cannot be 0"); } //Return the new matrix return adjoint().multiply(1 / determinant); } //Object funtions @Override public boolean equals(Object rightSide){ if(rightSide.getClass().equals(this.getClass())){ IntegerMatrix rightMatrix = (IntegerMatrix)rightSide; //Make sure they have the same number of elements if(getNumRows() != rightMatrix.getNumRows()){ return false; } else if(getNumCols() != rightMatrix.getNumCols()){ return false; } //Check every element for(int row = 0;row < getNumRows();++row){ for(int col = 0;col < getNumCols();++col){ if(grid[row][col] != rightMatrix.grid[row][col]){ return false; } } } //If false hasn't been returned yet then they are equal return true; } else if(rightSide.getClass().equals(int[][].class)){ int[][] rightMatrix = (int[][])rightSide; return equals(new IntegerMatrix(rightMatrix)); } else{ return false; } } @Override public int hashCode(){ return grid.hashCode(); } @Override public String toString(){ StringJoiner matrix = new StringJoiner("\n"); for(int rowCnt = 0;rowCnt < getNumRows();++rowCnt){ StringJoiner row = new StringJoiner(",", "[", "]"); for(int colCnt = 0;colCnt < getNumCols();++colCnt){ row.add(Integer.toString(grid[rowCnt][colCnt])); } matrix.add(row.toString()); } return matrix.toString(); } @Override public IntegerMatrix clone(){ return new IntegerMatrix(grid); } }