344 lines
9.6 KiB
C++
344 lines
9.6 KiB
C++
#include "Matrix.h"
|
|
#include <iostream>
|
|
|
|
//-------------------------
|
|
// 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;
|
|
}
|