package com.mattrixwv.matrix; import java.util.Arrays; 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; import com.mattrixwv.matrix.exceptions.NullMatrixException; /** * Represents a matrix of integers and provides various matrix operations. */ public class IntegerMatrix{ /** * The grid that represents the matrix */ protected int[][] grid; //?Helper functions /** * Validates that all rows in the grid are of equal length. * * @param grid The 2D array to validate. * @throws InvalidRowSizeException If the rows of the matrix are not all the same length. */ private int[][] validateGrid(int[][] grid){ if(grid.length == 0){ return new int[0][0]; } else if(grid[0].length == 0){ return 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){ newGrid[rowCnt] = Arrays.copyOf(grid[rowCnt], grid[rowCnt].length); } //Save the new grid return newGrid; } } /** * Sets the matrix grid to the specified 2D array. Validates the input to ensure * all rows are of equal length. * * @param grid The 2D array to set as the matrix grid. * @throws InvalidRowSizeException If the rows of the matrix are not all the same length. */ protected void setGrid(int[][] grid){ this.grid = validateGrid(grid); } /** * Creates a deep copy of the matrix grid. * * @return A new IntegerMatrix instance containing the copied grid. */ protected int[][] copyGrid(){ if(getNumCols() == 0){ return new int[grid.length][0]; } //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){ newGrid[row] = Arrays.copyOf(grid[row], grid[row].length); } //Return the new grid return newGrid; } /** * Checks if the matrix is a square matrix (i.e., the number of rows equals the number of columns). * * @return {@code true} if the matrix is square, {@code false} otherwise. */ public boolean isSquare(){ if(getNumRows() == 0){ return false; } else{ return getNumRows() == getNumCols(); } } /** * Creates a new matrix with the specified row and column removed. * This matrix is smaller by one row and one column than the current matrix. * * @param row The index of the row to remove. * @param col The index of the column to remove. * @return A new IntegerMatrix instance with the specified row and column removed. * @throws InvalidGeometryException If the matrix is not square or too small for Laplace expansion. * @throws InvalidCoordinatesException If the row or column index is out of bounds. */ protected IntegerMatrix laplaceExpansionHelper(int row, int col){ //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 matrix is square if(!isSquare()){ throw new InvalidGeometryException("A matrix must be square for you to perform Laplace Expansion"); } //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 /** * Constructs an empty matrix (0x0). */ public IntegerMatrix(){ grid = new int[0][0]; } /** * Constructs a matrix with the specified grid. * * @param grid The 2D array to initialize the matrix with. */ public IntegerMatrix(int[][] grid){ this.grid = validateGrid(grid); } /** * Constructs a copy of the specified matrix. * * @param matrix The matrix to copy. */ public IntegerMatrix(IntegerMatrix matrix){ this.grid = validateGrid(matrix.grid); } /** * Constructs a matrix with the specified number of rows and columns, filled with the specified value. * * @param rows The number of rows. * @param cols The number of columns. * @param fill The value to fill the matrix with. * @throws InvalidGeometryException If the number of rows or columns is less than or equal to zero. */ 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 /** * Gets the value at the specified row and column. * * @param row The row index. * @param col The column index. * @return The value at the specified row and column. * @throws InvalidCoordinatesException If the row or column index is out of bounds. */ 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(row < 0){ throw new InvalidCoordinatesException("Row cannot be less than 0"); } else if(col >= grid[row].length){ throw new InvalidCoordinatesException("Column cannot be greater than the number of columns"); } else if(col < 0){ throw new InvalidCoordinatesException("Column cannot be less than 0"); } //Return the location in the grid return grid[row][col]; } /** * Returns a new matrix that is a copy of the specified row. * * @param row The index of the row to retrieve. * @return A new IntegerMatrix instance containing the specified row. * @throws InvalidCoordinatesException If the row index is out of bounds. */ 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]; newRow[0] = Arrays.copyOf(grid[row], grid[row].length); //Return the new matrix return new IntegerMatrix(newRow); } /** * Returns the number of rows in the matrix. * * @return The number of rows. */ public int getNumRows(){ return grid.length; } /** * Returns a new matrix that is a copy of the specified column. * * @param col The index of the column to retrieve. * @return A new IntegerMatrix instance containing the specified column. * @throws InvalidCoordinatesException If the column index is out of bounds. */ 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); } /** * Returns the number of columns in the matrix. * * @return The number of columns. */ public int getNumCols(){ if(grid.length > 0){ return grid[0].length; } else{ return 0; } } //?Sets /** * Sets the value at the specified row and column. * * @param row The row index. * @param col The column index. * @param value The value to set. * @throws InvalidCoordinatesException If the row or column index is out of bounds. */ 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; } /** * Sets the specified row with the given array of elements. * * @param row The row index. * @param elements The array of elements to set. * @throws InvalidCoordinatesException If the row index is out of bounds. * @throws InvalidGeometryException If the length of the elements array does not match the number of columns. */ 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 == null){ throw new InvalidGeometryException("Row cannot be null"); } else if(elements.length != getNumCols()){ throw new InvalidGeometryException(elements.length, getNumCols()); } //Save the elements grid[row] = Arrays.copyOf(elements, elements.length); } /** * Sets the specified row with the given matrix containing a single row. * * @param row The row index. * @param matrix The matrix containing a single row to set. * @throws NullMatrixException If the matrix is null. * @throws InvalidGeometryException If the matrix does not contain a single row. */ public void setRow(int row, IntegerMatrix matrix){ //Make sure the matrix isn't null if(matrix == null){ throw new NullMatrixException(); } //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]); } /** * Sets the specified column with the given array of elements. * * @param col The column index. * @param elements The array of elements to set. * @throws InvalidCoordinatesException If the column index is out of bounds. * @throws InvalidGeometryException If the length of the elements array does not match the number of rows. */ 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 == null){ throw new InvalidGeometryException("Column cannot be null"); } else if(elements.length != grid.length){ throw new InvalidGeometryException(elements.length, grid.length); } //Save the elements for(int row = 0;row < elements.length;++row){ grid[row][col] = elements[row]; } } /** * Sets the specified column with the given matrix containing a single column. * * @param col The column index. * @param matrix The matrix containing a single column to set. * @throws NullMatrixException If the matrix is null. * @throws InvalidGeometryException If the matrix does not contain a single column. */ public void setCol(int col, IntegerMatrix matrix){ //Make sure the matrix isn't null if(matrix == null){ throw new NullMatrixException(); } //Make sure the matrix has a single column else if(matrix.getNumCols() != 1){ throw new InvalidGeometryException("Setting a column by Matrix requires the matrix contain a single column"); } int[] vector = new int[matrix.getNumRows()]; for(int cnt = 0;cnt < matrix.getNumRows();++cnt){ vector[cnt] = matrix.grid[cnt][0]; } //Set the column setCol(col, vector); } //?Adds /** * Adds a new row with the specified array of elements to the matrix. * * @param elements The array of elements to add as a new row. * @throws NullMatrixException If the elements array is null. * @throws InvalidGeometryException If the length of the elements array does not match the number of columns. */ public void addRow(int[] elements){ //Make sure the matrix isn't null if(elements == null){ throw new NullMatrixException(); } //Make sure the number of columns is valid else 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] = Arrays.copyOf(grid[row], grid[row].length); } grid = newGrid; } else{ throw new InvalidGeometryException(elements.length, getNumCols()); } //Add all elements to the grid grid[grid.length - 1] = Arrays.copyOf(elements, elements.length); } /** * Adds a new row with the given matrix containing a single row to the matrix. * * @param matrix The matrix containing a single row to add. * @throws NullMatrixException If the matrix is null. * @throws InvalidGeometryException If the matrix does not contain a single row. */ public void addRow(IntegerMatrix matrix){ //Make sure the matrix isn't null if(matrix == null){ throw new NullMatrixException(); } //Make sure the matrix has a single row else 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]); } /** * Adds a new column with the specified array of elements to the matrix. * * @param elements The array of elements to add as a new column. * @throws NullMatrixException If the elements array is null. * @throws InvalidGeometryException If the length of the elements array does not match the number of rows. */ public void addCol(int[] elements){ //Make sure the matrix isn't null if(elements == null){ throw new NullMatrixException(); } //Make sure the number of rows is valid if(grid.length == 0){ grid = new int[elements.length][1]; for(int row = 0;row < grid.length;++row){ grid[row][0] = elements[row]; } } else if(grid.length == elements.length){ //Copy all existing data into the new grid for(int row = 0;row < grid.length;++row){ grid[row] = Arrays.copyOf(grid[row], grid[row].length + 1); grid[row][grid[row].length - 1] = elements[row]; } } else{ throw new InvalidGeometryException(elements.length, getNumCols()); } } /** * Adds a new column with the given matrix containing a single column to the matrix. * * @param matrix The matrix containing a single column to add. * @throws NullMatrixException If the matrix is null. * @throws InvalidGeometryException If the matrix does not contain a single column. */ public void addCol(IntegerMatrix matrix){ //Make sure the matrix isn't null if(matrix == null){ throw new NullMatrixException(); } //Make sure the matrix has a single column else if(matrix.getNumCols() != 1){ throw new InvalidGeometryException("Adding a column by Matrix requires the matrix contain a single column"); } int[] vector = new int[matrix.getNumRows()]; for(int cnt = 0;cnt < matrix.getNumRows();++cnt){ vector[cnt] = matrix.get(cnt, 0); } //Add the column addCol(vector); } /** * Appends the specified matrix to the right side of the current matrix. * * @param rightSide The matrix to append to the right side. * @return A new IntegerMatrix instance with the right-side matrix appended. * @throws NullMatrixException If the right-side matrix is null. * @throws InvalidGeometryException If the number of rows does not match. */ public IntegerMatrix appendRight(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have the same number of rows else 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 newGrid[row] = Arrays.copyOf(grid[row], grid[row].length + rightSide.grid[row].length); //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); } /** * Appends the specified matrix to the bottom of the current matrix. * * @param bottomSide The matrix to append to the bottom. * @return A new IntegerMatrix instance with the bottom matrix appended. * @throws NullMatrixException If the bottom matrix is null. * @throws InvalidGeometryException If the number of columns does not match. */ public IntegerMatrix appendBottom(IntegerMatrix bottomSide){ //Make sure the matrix isn't null if(bottomSide == null){ throw new NullMatrixException(); } //Make sure the matrices have the same number of columns else if(getNumCols() != bottomSide.getNumCols()){ throw new InvalidGeometryException("Invalid number of columns. " + bottomSide.getNumCols() + " must be " + getNumCols()); } //Traverse both matrices and set their values in the new matrix int[][] newGrid = new int[getNumRows() + bottomSide.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 < bottomSide.getNumRows();++row){ newGrid[getNumRows() + row][col] = bottomSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } //?Simple operations /** * Generates an identity matrix of the given size. * * @param size The size of the identity matrix. * @return A new IntegerMatrix instance representing the identity matrix. * @throws InvalidGeometryException If the size is less than or equal to zero. */ 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"); } } /** * Adds the specified matrix to the current matrix. * * @param rightSide The matrix to add. * @return A new IntegerMatrix instance with the result of the addition. * @throws InvalidGeometryException If the matrices do not have the same dimensions. */ public IntegerMatrix add(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have compatible 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 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); } /** * Adds the scalar to every element in the matrix. * * @param scalar The scalar to add. * @return A new IntegerMatrix instance with the result of the addition. */ 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); } /** * Subtracts the specified matrix from the current matrix. * * @param rightSide The matrix to subtract. * @return A new IntegerMatrix instance with the result of the subtraction. * @throws InvalidGeometryException If the matrices do not have the same dimensions. */ public IntegerMatrix subtract(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have compatible 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 gird int[][] newGrid = copyGrid(); //Subtract each element in the right matrix 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] -= rightSide.grid[row][col]; } } //Return the new matrix return new IntegerMatrix(newGrid); } /** * Subtracts the scalar from every element in the matrix. * * @param scalar The scalar to subtract. * @return A new IntegerMatrix instance with the result of the subtraction. */ 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); } /** * Multiplies the current matrix by the specified matrix. * * @param rightSide The matrix to multiply by. * @return A new IntegerMatrix instance with the result of the multiplication. * @throws InvalidGeometryException If the number of columns in the current matrix does not match the number of rows in the right-side matrix. */ public IntegerMatrix multiply(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have compatible 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); } /** * Multiplies every element in the matrix by the scalar. * * @param scalar the scalar to multiply * @return A new IntegerMatrix instance with the result of the multiplication */ 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); } /** * Multiplies the current matrix by itself the given number of times. * * @param power The number of times to multiply the matrix by itself * @return A new IntegerMatrix instance with the result of the multiplication * @throws InvalidScalarException If the power is negative * @throws InvalidGeometryException If the matrix is not square */ 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 = new IntegerMatrix(this); //Multiply the current grid power times for(int currentPower = 1;currentPower < power;++currentPower){ newMatrix = newMatrix.multiply(this); } //Return the new grid return newMatrix; } /** * Calculates the dot product of the two matrices. * * @param rightSide The matrix to use on the right side of the calculation * @return The dot product of the two matrices * @throws NullMatrixException If the right matrix is null * @throws InvalidGeometryException If the matrices do not have compatible dimensions */ public int dotProduct(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have compatible 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; } /** * Calculates the Hadamard product of the two matrices. * * @param rightSide The matrix to use on the right side of the calculation * @return The Hadamard product of the two matrices * @throws NullMatrixException If the right matrix is null * @throws InvalidGeometryException If the matrices do not have compatible dimensions */ public IntegerMatrix hadamardProduct(IntegerMatrix rightSide){ //Make sure the matrix isn't null if(rightSide == null){ throw new NullMatrixException(); } //Make sure the matrices have compatible 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 /** * Transposes the current matrix (i.e., swaps rows and columns). * * @return A new IntegerMatrix instance representing the transposed matrix. */ 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); } /** * Calculates the determinant of the matrix. For matrices 4x4 or larger, a recursive approach is used. * * @return The determinant of the matrix. * @throws InvalidGeometryException If the matrix is not square. * @see #determinant() */ public int det(){ return determinant(); } /** * Calculates the determinant of a 2x2 matrix. * * @return The determinant of the matrix. * @throws InvalidGeometryException If the matrix is not 2x2. */ private int det2(){ return (grid[0][0] * grid[1][1]) - (grid[0][1] * grid[1][0]); } /** * Calculates the determinant of a 3x3 matrix. * * @return The determinant of the matrix. * @throws InvalidGeometryException If the matrix is not 3x3. */ private int det3(){ return (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]); } /** * Calculates the determinant of a 4x4 or larger matrix. * * @return The determinant of the matrix. * @throws InvalidGeometryException If the matrix is not 4x4. */ private int det4(){ int det = 0; //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 det; } /** * Calculates the determinant of the matrix. For matrices 4x4 or larger, a recursive approach is used. * * @return The determinant of the matrix. * @throws InvalidGeometryException If the matrix is not square. */ 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 = det2(); break; //If the matrix is 3x3 use the formula case 3 : det = det3(); break; //If the matrix is larger break it down and try again default : det = det4(); } //Return the determinant return det; } /** * Calculates the cofactor matrix of the current matrix. * * @return A new IntegerMatrix instance representing the cofactor matrix. * @throws InvalidGeometryException If the matrix is not square. * @see #cofactor() */ public IntegerMatrix cof(){ return cofactor(); } /** * Calculates the cofactor matrix of the current matrix. * * @return A new IntegerMatrix instance representing the cofactor matrix. * @throws InvalidGeometryException If the matrix is not square. */ 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); } /** * Calculates the adjoint matrix of the current matrix. * * @return A new IntegerMatrix instance representing the adjoint matrix. * @throws InvalidGeometryException If the matrix is not square. * @see #adjoint() */ public IntegerMatrix adj(){ return adjoint(); } /** * Calculates the adjoint matrix of the current matrix. * * @return A new IntegerMatrix instance representing the adjoint matrix. * @throws InvalidGeometryException If the matrix is not square. */ public IntegerMatrix adjoint(){ return cofactor().transpose(); } /** * Calculates the inverse of the current matrix. * * @return A new IntegerMatrix instance representing the inverse matrix. * @throws InvalidGeometryException If the matrix is not square or if the determinant is 0. */ 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 functions /** * Determines whether the given object is equal to the current matrix. * Can determine equality using IntegerMatrix or int[][]. * * @param rightSide The object to compare to the current matrix. * @return True if the objects are equal, false otherwise. * @see #equals(IntegerMatrix) */ @Override public boolean equals(Object rightSide){ if(rightSide == null){ return false; } if(rightSide.getClass().equals(this.getClass())){ return equals((IntegerMatrix)rightSide); } else if(rightSide.getClass().equals(int[][].class)){ int[][] rightMatrix = (int[][])rightSide; return equals(new IntegerMatrix(rightMatrix)); } else{ return false; } } /** * Determines whether the given IntegerMatrix is equal to the current matrix. * * @param rightMatrix The IntegerMatrix to compare to the current matrix. * @return True if the matrices are equal, false otherwise. */ private boolean equals(IntegerMatrix rightMatrix){ //Make sure they have the same number of elements if((getNumRows() != rightMatrix.getNumRows()) || (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; } /** * Calculates a hash code for the current matrix. * * @return The hash code for the current matrix. */ @Override public int hashCode(){ return Arrays.hashCode(grid); } /** * Returns a string representation of the matrix, with rows and columns formatted for readability. * * @return A string representation of the matrix. */ @Override public String toString(){ StringJoiner matrix = new StringJoiner("\n").setEmptyValue("[]"); 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(); } }