From 4054554203a3a6552f7451be91d1ec187fdbf922 Mon Sep 17 00:00:00 2001 From: JamesFlare1212 Date: Mon, 10 Feb 2025 20:49:15 -0500 Subject: [PATCH] add hw3 solution --- .vscode/settings.json | 3 +- hws/matrix_class/Matrix.cpp | 292 +++++++++++++++++++++++++++++++ hws/matrix_class/Matrix.h | 76 ++++++++ hws/matrix_class/README.txt | 37 ++-- hws/matrix_class/matrix_main.cpp | 122 ++++++++++++- 5 files changed, 513 insertions(+), 17 deletions(-) create mode 100644 hws/matrix_class/Matrix.cpp create mode 100644 hws/matrix_class/Matrix.h diff --git a/.vscode/settings.json b/.vscode/settings.json index ce1abfa..5c12af3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,7 @@ "sstream": "cpp", "stdexcept": "cpp", "streambuf": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "cassert": "cpp" } } \ No newline at end of file diff --git a/hws/matrix_class/Matrix.cpp b/hws/matrix_class/Matrix.cpp new file mode 100644 index 0000000..dcd06cb --- /dev/null +++ b/hws/matrix_class/Matrix.cpp @@ -0,0 +1,292 @@ +#include "Matrix.h" +#include +#include + +//helper function to allocate memory for a matrix of size r x c and fill it with "fill" +void Matrix::allocateMemory(unsigned int r, unsigned int c, double fill) { + if(r == 0 || c == 0) { + rows = 0; + cols = 0; + data = nullptr; + return; + } + 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; + } + } +} + +//helper function to deallocate memory +void Matrix::deallocateMemory() { + if(data != nullptr) { + for (unsigned int i = 0; i < rows; i++) { + delete [] data[i]; + } + delete [] data; + data = nullptr; + } + rows = 0; + cols = 0; +} + +//default constructor: creates an empty 0 x 0 matrix +Matrix::Matrix() : rows(0), cols(0), data(nullptr) { } + +//parameterized constructor +Matrix::Matrix(unsigned int r, unsigned int c, double fill) : rows(0), cols(0), data(nullptr) { + allocateMemory(r, c, fill); +} + +//copy constructor +Matrix::Matrix(const Matrix& other) : rows(0), cols(0), data(nullptr) { + allocateMemory(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() { + deallocateMemory(); +} + +//assignment operator +Matrix& Matrix::operator=(const Matrix& other) { + if (this == &other) + return *this; + deallocateMemory(); + allocateMemory(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; +} + +//returns the number of rows +unsigned int Matrix::num_rows() const { + return rows; +} + +//returns the number of columns +unsigned int Matrix::num_cols() const { + return cols; +} + +//clears the matrix by deallocating its memory +void Matrix::clear() { + deallocateMemory(); +} + +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; +} + +//modifier +bool Matrix::set(unsigned int row, unsigned int col, double value) { + if(row >= rows || col >= cols) { + return false; + } + data[row][col] = value; + return true; +} + +//equality operator +bool Matrix::operator==(const Matrix& other) const { + //two matrices are equal if dimensions match + //and every element is equal within a small epsilon + 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 (fabs(data[i][j] - other.data[i][j]) > 1e-10) + return false; + } + } + return true; +} + +bool Matrix::operator!=(const Matrix& other) const { + return !(*this == other); +} + +//multiplies every element by the given coefficient +void Matrix::multiply_by_coefficient(double coeff) { + for (unsigned int i = 0; i < rows; i++) { + for (unsigned int j = 0; j < cols; j++) { + data[i][j] *= coeff; + } + } +} + +//swaps two rows of the matrix (by swapping the row pointers) +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 +void Matrix::transpose() { + if(rows == 0 || cols == 0) + return; + unsigned int newRows = cols; + unsigned int newCols = rows; + double** newData = new double*[newRows]; + for (unsigned int i = 0; i < newRows; i++) { + newData[i] = new double[newCols]; + for (unsigned int j = 0; j < newCols; j++) { + newData[i][j] = data[j][i]; + } + } + deallocateMemory(); + rows = newRows; + cols = newCols; + data = newData; +} + +//adds another matrix to this one element-wise +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 another matrix from this one element-wise +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; +} + +//returns a dynamically allocated copy of the specified row +//caller must delete[] the returned array +double* Matrix::get_row(unsigned int row) const { + if(row >= rows) + return NULL; + double* rowArray = new double[cols]; + for (unsigned int j = 0; j < cols; j++) { + rowArray[j] = data[row][j]; + } + return rowArray; +} + +//returns a dynamically allocated copy of the specified column +//caller must delete[] the returned array +double* Matrix::get_col(unsigned int col) const { + if(col >= cols) + return NULL; + double* colArray = new double[rows]; + for (unsigned int i = 0; i < rows; i++) { + colArray[i] = data[i][col]; + } + return colArray; +} + +//divides the matrix into four quadrants and returns a pointer to an array of four matrices +//each quadrant size = ceil(rows/2) x ceil(cols/2) +Matrix* Matrix::quarter() const { + if (rows == 0 || cols == 0) { + return nullptr; + } + // Determine quadrant size so that all four quadrants are identical. + // For overlapping, use ceil for both dimensions. + unsigned int q_rows = (rows % 2 == 0) ? (rows / 2) : (rows / 2 + 1); + unsigned int q_cols = (cols % 2 == 0) ? (cols / 2) : (cols / 2 + 1); + + // For an overlapping quarter, the top quadrants start at row 0, + // the bottom quadrants start at floor(rows/2), similarly for columns. + unsigned int start_row_bottom = rows / 2; + unsigned int start_col_right = cols / 2; + + Matrix* quadrants = new Matrix[4]{ + Matrix(q_rows, q_cols, 0.0), // Upper Left + Matrix(q_rows, q_cols, 0.0), // Upper Right + Matrix(q_rows, q_cols, 0.0), // Lower Left + Matrix(q_rows, q_cols, 0.0) // Lower Right + }; + + // Fill Upper Left quadrant from original (starting at (0,0)). + for (unsigned int i = 0; i < q_rows; i++) { + for (unsigned int j = 0; j < q_cols; j++) { + double value; + if (i < rows && j < cols && get(i, j, value)) + quadrants[0].set(i, j, value); + } + } + // Fill Upper Right quadrant from original (starting at (0, start_col_right)). + for (unsigned int i = 0; i < q_rows; i++) { + for (unsigned int j = 0; j < q_cols; j++) { + double value; + if (i < rows && (j + start_col_right) < cols && get(i, j + start_col_right, value)) + quadrants[1].set(i, j, value); + } + } + // Fill Lower Left quadrant from original (starting at (start_row_bottom, 0)). + for (unsigned int i = 0; i < q_rows; i++) { + for (unsigned int j = 0; j < q_cols; j++) { + double value; + if ((i + start_row_bottom) < rows && j < cols && get(i + start_row_bottom, j, value)) + quadrants[2].set(i, j, value); + } + } + // Fill Lower Right quadrant from original (starting at (start_row_bottom, start_col_right)). + for (unsigned int i = 0; i < q_rows; i++) { + for (unsigned int j = 0; j < q_cols; j++) { + double value; + if ((i + start_row_bottom) < rows && (j + start_col_right) < cols && + get(i + start_row_bottom, j + start_col_right, value)) + quadrants[3].set(i, j, value); + } + } + return quadrants; +} + + +//overloaded output operator to print the matrix +// 4 x 4 matrix: +// [ 14 14 14 14 +// 14 14 14 14 +// 14 9 14 14 +// 14 14 14 13 ] +std::ostream& operator<<(std::ostream& out, const Matrix& m) { + out << m.rows << " x " << m.cols << " matrix:" << std::endl; + out << "[ "; + for (unsigned int i = 0; i < m.rows; i++) { + 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 << " ]"; + return out; +} diff --git a/hws/matrix_class/Matrix.h b/hws/matrix_class/Matrix.h new file mode 100644 index 0000000..13eac96 --- /dev/null +++ b/hws/matrix_class/Matrix.h @@ -0,0 +1,76 @@ +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include + +class Matrix { +private: + unsigned int rows; + unsigned int cols; + double** data; + //allocate memory and fill with a given value + void allocateMemory(unsigned int r, unsigned int c, double fill); + //deallocate memory + void deallocateMemory(); + +public: + ////Constructors + Matrix(); + Matrix(unsigned int r, unsigned int c, double fill); + //copy constructor + Matrix(const Matrix& other); + + ////Destructor + ~Matrix(); + + ////Accessors + Matrix& operator=(const Matrix& other); + unsigned int num_rows() const; + unsigned int num_cols() const; + //deallocates memory and resets rows/cols to 0) + void clear(); + //if (row, col) is within bounds, stores the element in `value` and returns true; + //otherwise, returns false + bool get(unsigned int row, unsigned int col, double &value) const; + + ////Modifier + //if (row, col) is within bounds, sets the element to `value` and returns true; + //otherwise, returns false. + bool set(unsigned int row, unsigned int col, double value); + //multiplies every element in the matrix by the provided coefficient + void multiply_by_coefficient(double coeff); + //swaps two rows of the matrix. Returns true if both indices are valid, + //false otherwise + bool swap_row(unsigned int row1, unsigned int row2); + //transposes the matrix in place (switches rows and columns). + void transpose(); + //adds another matrix to this one element-wise (if dimensions match) and returns true; + //otherwise, returns false. + bool add(const Matrix& other); + //subtracts another matrix from this one element-wise (if dimensions match) + //and returns true; otherwise, returns false. + bool subtract(const Matrix& other); + //returns a new dynamically allocated array (of size num_cols) + //containing the elements in the specified row. + double* get_row(unsigned int row) const; + //returns a new dynamically allocated array (of size num_rows) + //containing the elements in the specified column. + double* get_col(unsigned int col) const; + //divides the matrix into four quadrants and returns a pointer to an array of + //4 Matrix objects: Upper Left, Upper Right, Lower Left, Lower Right. + //each quadrant is of size ceil(rows/2) x ceil(cols/2) + //and the quadrants overlap when the dimensions are odd. + Matrix* quarter() const; + + ////Operators + //equality operator: two matrices are equal if they have the same dimensions and + //every corresponding element differs by no more than a small epsilon. + bool operator==(const Matrix& other) const; + //inequality operator + bool operator!=(const Matrix& other) const; + //overloaded output operator for printing the matrix + friend std::ostream& operator<<(std::ostream& out, const Matrix& m); +}; + +#endif diff --git a/hws/matrix_class/README.txt b/hws/matrix_class/README.txt index 76eb4b7..0cce457 100644 --- a/hws/matrix_class/README.txt +++ b/hws/matrix_class/README.txt @@ -1,7 +1,7 @@ HOMEWORK 3: MATRIX CLASS -NAME: < insert name > +NAME: Jinshan Zhou COLLABORATORS AND OTHER RESOURCES: @@ -10,13 +10,13 @@ List the names of everyone you talked to about this assignment LMS, etc.), and all of the resources (books, online reference material, etc.) you consulted in completing this assignment. -< insert collaborators / resources > +cmath fabs method. Lecture notes about ** pointers and destructors. Remember: Your implementation for this assignment must be done on your own, as described in "Academic Integrity for Homework" handout. -ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: < insert # hours > +ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: 14 hr @@ -27,25 +27,25 @@ number of columns. You should assume that calling new [] or delete [] on an array will take time proportional to the number of elements in the array. -get +get -> O(1) -set +set -> O(1) -num_rows +num_rows -> O(1) -get_column +get_column -> O(m) -operator<< +operator<< -> O(m*n) -quarter +quarter -> O(m*n) -operator== +operator== -> O(m*n) -operator!= +operator!= -> O(m*n) -swap_rows +swap_rows -> O(1) -rref (provided in matrix_main.cpp) +rref (provided in matrix_main.cpp) -> O(m^2 * n) @@ -55,6 +55,17 @@ What tools did you use (gdb/lldb/Visual Studio debugger, Valgrind/Dr. Memory, std::cout & print, etc.)? How did you test the "corner cases" of your Matrix class design & implementation? +I used gdb inside VSCode to debug my program. I also used the information +from the submitty autograder (Dr. Memory) to help me find bugs in my code. +I basiclly find what's name and function of the methods of Martix class, +and change my code bit by bit to fit the expected output. + MISC. COMMENTS TO GRADER: (optional, please be concise!) +REFLECTION: +As the pointer adding up, the complexity of the program will increase. +I think it is important to understand where do pointers points to and +delete them when they are not needed anymore. It's quiet tricky and I +got a lot of memory leaks in my code at first. But the error message in +Dr. Memory shows which line caused the problem, so I was able to fix it. \ No newline at end of file diff --git a/hws/matrix_class/matrix_main.cpp b/hws/matrix_class/matrix_main.cpp index 3b76b6b..1a336f6 100644 --- a/hws/matrix_class/matrix_main.cpp +++ b/hws/matrix_class/matrix_main.cpp @@ -33,10 +33,8 @@ int main(){ std::cout << "Completed all simple tests." << std::endl; //Uncomment this to allocate a lot of 100x100 matrices so leaks will be bigger. - /* BatchTest(100,0.1,100,100,50); std::cout << "Completed all batch tests." << std::endl; - */ StudentTest(); std::cout << "Completed all student tests." << std::endl; @@ -199,10 +197,128 @@ void SimpleTest(){ //well behaved getrow/read after } //Write your own test cases here -void StudentTest(){ +void StudentTest() { + //Test transpose + Matrix m(2, 3, 0); + m.set(0, 0, 1); + m.set(0, 1, 2); + m.set(0, 2, 3); + m.set(1, 0, 4); + m.set(1, 1, 5); + m.set(1, 2, 6); + m.transpose(); + assert(m.num_rows() == 3); + assert(m.num_cols() == 2); + double val; + m.get(0, 0, val); + assert(double_compare(val, 1.0)); + m.get(0, 1, val); + assert(double_compare(val, 4.0)); + m.get(1, 0, val); + assert(double_compare(val, 2.0)); + m.get(2, 1, val); + assert(double_compare(val, 6.0)); + //Test quarter with odd dimensions + Matrix q(5, 5, 1); + Matrix* quarters = q.quarter(); + assert(quarters != nullptr); + //each quadrant should be 3x3 (ceiling: (5+1)/2 == 3) + assert(quarters[0].num_rows() == 3); + assert(quarters[0].num_cols() == 3); + assert(quarters[1].num_rows() == 3); + assert(quarters[1].num_cols() == 3); + assert(quarters[2].num_rows() == 3); + assert(quarters[2].num_cols() == 3); + assert(quarters[3].num_rows() == 3); + assert(quarters[3].num_cols() == 3); + //verify that the quadrants hold the expected values. + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + quarters[0].get(i, j, val); + assert(double_compare(val, 1.0)); + quarters[1].get(i, j, val); + assert(double_compare(val, 1.0)); + quarters[2].get(i, j, val); + assert(double_compare(val, 1.0)); + quarters[3].get(i, j, val); + assert(double_compare(val, 1.0)); + } + } + delete[] quarters; + + //Test add and subtract + Matrix a(2, 2, 2); + Matrix b(2, 2, 3); + assert(a.add(b)); // Now a is all 5's. + double v; + a.get(0, 0, v); + assert(double_compare(v, 5.0)); + assert(a.subtract(b)); // Now a is back to all 2's. + a.get(0, 0, v); + assert(double_compare(v, 2.0)); + + //Test multiply by coefficient + a.multiply_by_coefficient(2.5); //Now a is all 5's (2*2.5). + a.get(0, 0, v); + assert(double_compare(v, 5.0)); + + //Test get_row + double* row = a.get_row(0); + assert(row != nullptr); + assert(double_compare(row[0], 5.0)); + assert(double_compare(row[1], 5.0)); + delete[] row; + + // Test get_col + double* col = a.get_col(1); + assert(col != nullptr); + assert(double_compare(col[0], 5.0)); + assert(double_compare(col[1], 5.0)); + delete[] col; + + //Test clear + a.clear(); + assert(a.num_rows() == 0 && a.num_cols() == 0); + + //Test swap_row + Matrix s(3, 2, 0); + s.set(0, 0, 1); s.set(0, 1, 2); + s.set(1, 0, 3); s.set(1, 1, 4); + s.set(2, 0, 5); s.set(2, 1, 6); + //swap row 0 and row 2 + assert(s.swap_row(0, 2)); + s.get(0, 0, val); + assert(double_compare(val, 5.0)); + s.get(0, 1, val); + assert(double_compare(val, 6.0)); + s.get(2, 0, val); + assert(double_compare(val, 1.0)); + s.get(2, 1, val); + assert(double_compare(val, 2.0)); + //invalid swap should return false. + assert(!s.swap_row(0, 3)); + + //Test copy constructor and assignment operator. + Matrix orig(2, 3, 7); + Matrix copy(orig); // Using copy constructor. + Matrix assign; + assign = orig; // Using assignment operator. + //change orig to ensure copy and assign remain unchanged. + orig.set(0, 0, 10); + orig.get(0, 0, val); + assert(double_compare(val, 10.0)); + copy.get(0, 0, val); + assert(double_compare(val, 7.0)); + assign.get(0, 0, val); + assert(double_compare(val, 7.0)); + + //Test out-of-bound get and set + assert(!orig.get(5, 5, val)); + assert(!orig.set(5, 5, 3.0)); } + ////////////////Utility functions////////////////////// /* Function that quickly populates a rows x cols matrix with values from