1186 lines
39 KiB
Java
1186 lines
39 KiB
Java
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();
|
|
}
|
|
}
|