#include "Matrix.h" #include //------------------------- // Private Helper Functions //------------------------- // Allocates memory for an r x c matrix and fills every element with 'fill' void Matrix::allocate(unsigned int r, unsigned int c, double fill) { rows = r; cols = c; data = new double*[rows]; for (unsigned int i = 0; i < rows; i++) { data[i] = new double[cols]; for (unsigned int j = 0; j < cols; j++) { data[i][j] = fill; } } } // Deallocates the memory used by the matrix void Matrix::deallocate() { if (data) { for (unsigned int i = 0; i < rows; i++) { delete [] data[i]; } delete [] data; } data = nullptr; rows = 0; cols = 0; } //------------------------- // Constructors & Destructor //------------------------- // Default constructor: creates an empty matrix (0 x 0) Matrix::Matrix() : rows(0), cols(0), data(nullptr) {} // Parameterized constructor: creates an r x c matrix filled with 'fill' // If either dimension is 0, an empty matrix is created. Matrix::Matrix(unsigned int r, unsigned int c, double fill) : rows(0), cols(0), data(nullptr) { if (r == 0 || c == 0) { // Create an empty matrix. rows = 0; cols = 0; data = nullptr; } else { allocate(r, c, fill); } } // Copy constructor Matrix::Matrix(const Matrix &other) : rows(0), cols(0), data(nullptr) { if (other.rows == 0 || other.cols == 0) { rows = 0; cols = 0; data = nullptr; } else { allocate(other.rows, other.cols, 0.0); for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { data[i][j] = other.data[i][j]; } } } } // Destructor Matrix::~Matrix() { deallocate(); } // Assignment operator Matrix& Matrix::operator=(const Matrix &other) { if (this == &other) return *this; deallocate(); if (other.rows == 0 || other.cols == 0) { rows = 0; cols = 0; data = nullptr; } else { allocate(other.rows, other.cols, 0.0); for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { data[i][j] = other.data[i][j]; } } } return *this; } //------------------------- // Dimension Accessors & Clear //------------------------- unsigned int Matrix::num_rows() const { return rows; } unsigned int Matrix::num_cols() const { return cols; } void Matrix::clear() { deallocate(); } //------------------------- // Safe Accessors & Modifiers //------------------------- // get(): If (row,col) is within bounds, set value and return true; otherwise, return false. bool Matrix::get(unsigned int row, unsigned int col, double &value) const { if (row >= rows || col >= cols) return false; value = data[row][col]; return true; } // set(): If (row,col) is valid, assign value and return true; else return false. bool Matrix::set(unsigned int row, unsigned int col, double value) { if (row >= rows || col >= cols) return false; data[row][col] = value; return true; } //------------------------- // Simple Matrix Operations //------------------------- // Multiplies every element in the matrix by the given coefficient. void Matrix::multiply_by_coefficient(double coefficient) { for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { data[i][j] *= coefficient; } } } // Swaps the entire contents of row1 and row2 if both indices are valid. bool Matrix::swap_row(unsigned int row1, unsigned int row2) { if (row1 >= rows || row2 >= rows) return false; double* temp = data[row1]; data[row1] = data[row2]; data[row2] = temp; return true; } // Transposes the matrix in place. // For non-square matrices, a new 2D array is allocated, the contents are transposed, // and the old memory is deallocated. void Matrix::transpose() { if (rows == 0 || cols == 0) return; // Allocate new array with swapped dimensions. double** newData = new double*[cols]; for (unsigned int i = 0; i < cols; i++) { newData[i] = new double[rows]; } // Transpose: newData[j][i] becomes data[i][j] for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { newData[j][i] = data[i][j]; } } // Free old data. for (unsigned int i = 0; i < rows; i++) { delete [] data[i]; } delete [] data; // Swap dimensions. unsigned int temp = rows; rows = cols; cols = temp; data = newData; } //------------------------- // Binary Matrix Operations //------------------------- // Adds the corresponding elements of other to this matrix. // Returns true if dimensions match, else returns false. bool Matrix::add(const Matrix &other) { if (rows != other.rows || cols != other.cols) return false; for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { data[i][j] += other.data[i][j]; } } return true; } // Subtracts the corresponding elements of other from this matrix. // Returns true if dimensions match, else returns false. bool Matrix::subtract(const Matrix &other) { if (rows != other.rows || cols != other.cols) return false; for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { data[i][j] -= other.data[i][j]; } } return true; } //------------------------- // Advanced Accessors //------------------------- // Returns a new dynamically allocated array containing the requested row. // Returns nullptr if the row index is out of bounds. double* Matrix::get_row(unsigned int row) const { if (row >= rows) return nullptr; double* rowArray = new double[cols]; for (unsigned int j = 0; j < cols; j++) { rowArray[j] = data[row][j]; } return rowArray; } // Returns a new dynamically allocated array containing the requested column. // Returns nullptr if the column index is out of bounds. double* Matrix::get_col(unsigned int col) const { if (col >= cols) return nullptr; double* colArray = new double[rows]; for (unsigned int i = 0; i < rows; i++) { colArray[i] = data[i][col]; } return colArray; } //------------------------- // Quarter Operation //------------------------- // Splits the matrix into four quadrants (UL, UR, LL, LR) and returns them in a new array. // All four quadrants will have the same dimensions. // If the matrix has fewer than 2 rows or 2 columns, returns four empty matrices. Matrix* Matrix::quarter() const { Matrix* quadrants = new Matrix[4]; if (rows < 2 || cols < 2) { // Return four empty matrices. return quadrants; } // Determine quadrant dimensions. // Using (dim + 1) / 2 ensures that if the dimension is odd the shared middle row/col is included. unsigned int quad_rows = (rows + 1) / 2; unsigned int quad_cols = (cols + 1) / 2; quadrants[0] = Matrix(quad_rows, quad_cols, 0.0); // Upper Left (UL) quadrants[1] = Matrix(quad_rows, quad_cols, 0.0); // Upper Right (UR) quadrants[2] = Matrix(quad_rows, quad_cols, 0.0); // Lower Left (LL) quadrants[3] = Matrix(quad_rows, quad_cols, 0.0); // Lower Right (LR) // Fill UL quadrant: rows 0 .. quad_rows-1, cols 0 .. quad_cols-1. for (unsigned int i = 0; i < quad_rows; i++) { for (unsigned int j = 0; j < quad_cols; j++) { quadrants[0].set(i, j, data[i][j]); } } // Fill UR quadrant: rows 0 .. quad_rows-1, cols (cols - quad_cols) .. (cols - 1). for (unsigned int i = 0; i < quad_rows; i++) { for (unsigned int j = 0; j < quad_cols; j++) { quadrants[1].set(i, j, data[i][(cols - quad_cols) + j]); } } // Fill LL quadrant: rows (rows - quad_rows) .. (rows - 1), cols 0 .. quad_cols-1. for (unsigned int i = 0; i < quad_rows; i++) { for (unsigned int j = 0; j < quad_cols; j++) { quadrants[2].set(i, j, data[(rows - quad_rows) + i][j]); } } // Fill LR quadrant: rows (rows - quad_rows) .. (rows - 1), cols (cols - quad_cols) .. (cols - 1). for (unsigned int i = 0; i < quad_rows; i++) { for (unsigned int j = 0; j < quad_cols; j++) { quadrants[3].set(i, j, data[(rows - quad_rows) + i][(cols - quad_cols) + j]); } } return quadrants; } //------------------------- // Equality Operators //------------------------- bool Matrix::operator==(const Matrix &other) const { if (rows != other.rows || cols != other.cols) return false; for (unsigned int i = 0; i < rows; i++) { for (unsigned int j = 0; j < cols; j++) { if (data[i][j] != other.data[i][j]) return false; } } return true; } bool Matrix::operator!=(const Matrix &other) const { return !(*this == other); } //------------------------- // Overloaded Output Operator //------------------------- std::ostream& operator<<(std::ostream &out, const Matrix &m) { out << m.rows << " x " << m.cols << " matrix:" << std::endl; out << "["; if (m.rows > 0 && m.cols > 0) { for (unsigned int i = 0; i < m.rows; i++) { out << " "; for (unsigned int j = 0; j < m.cols; j++) { out << m.data[i][j]; if (j < m.cols - 1) out << " "; } if (i < m.rows - 1) out << std::endl; } out << " ]"; } else { out << " ]"; } return out; }