From 903e76ef1fa29025a2933b2b0c117e6748b5ffd0 Mon Sep 17 00:00:00 2001 From: JamesFlare1212 Date: Wed, 12 Mar 2025 10:00:49 -0400 Subject: [PATCH] add solution for hw 5 --- .vscode/launch.json | 18 ++ hws/matrix_class/Matrix.cpp | 355 +++++++++++++++----------- hws/matrix_class/Matrix.h | 112 ++++----- hws/matrix_class/matrix_main.cpp | 186 ++++++-------- hws/online_dating/README.txt | 12 +- hws/online_dating/nydate.cpp | 415 +++++++++++++++++++++++++++++++ 6 files changed, 772 insertions(+), 326 deletions(-) create mode 100644 hws/online_dating/nydate.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 93fecc4..6426eb1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -70,6 +70,24 @@ "MIMode": "gdb", "miDebuggerPath": "/usr/bin/gdb", "preLaunchTask": "C/C++: g++ build active file" + }, + { + "name": "nydate", + "type": "cppdbg", + "request": "launch", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": [ + "users_medium2.txt", + "output.txt", + "958-473-3053", + "unmatch", + "267-674-2523" + ], + "cwd": "${fileDirname}", + "environment": [], + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "C/C++: g++ build active file" } ] } diff --git a/hws/matrix_class/Matrix.cpp b/hws/matrix_class/Matrix.cpp index dcd06cb..bc9a64f 100644 --- a/hws/matrix_class/Matrix.cpp +++ b/hws/matrix_class/Matrix.cpp @@ -1,15 +1,12 @@ #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; - } +//------------------------- +// 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]; @@ -21,149 +18,180 @@ void Matrix::allocateMemory(unsigned int r, unsigned int c, double fill) { } } -//helper function to deallocate memory -void Matrix::deallocateMemory() { - if(data != nullptr) { +// 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; } + data = nullptr; rows = 0; cols = 0; } -//default constructor: creates an empty 0 x 0 matrix -Matrix::Matrix() : rows(0), cols(0), data(nullptr) { } +//------------------------- +// Constructors & Destructor +//------------------------- -//parameterized constructor +// 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) { - allocateMemory(r, c, fill); + 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) { - 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]; +// 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 +// Destructor Matrix::~Matrix() { - deallocateMemory(); + deallocate(); } -//assignment operator -Matrix& Matrix::operator=(const Matrix& other) { +// 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]; + + 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; } -//returns the number of rows +//------------------------- +// Dimension Accessors & Clear +//------------------------- + 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(); + 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) { + if (row >= rows || col >= cols) return false; - } value = data[row][col]; return true; } -//modifier +// 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) { + 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; +//------------------------- +// 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++) { - 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; + data[i][j] *= coefficient; } } } -//swaps two rows of the matrix (by swapping the row pointers) +// 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) { + 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 +// 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) + 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]; + + // 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]; } } - deallocateMemory(); - rows = newRows; - cols = newCols; + // 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; } -//adds another matrix to this one element-wise -bool Matrix::add(const Matrix& other) { - if(rows != other.rows || cols != other.cols) +//------------------------- +// 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++) { @@ -173,9 +201,10 @@ bool Matrix::add(const Matrix& other) { return true; } -//subtracts another matrix from this one element-wise -bool Matrix::subtract(const Matrix& other) { - if(rows != other.rows || cols != other.cols) +// 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++) { @@ -185,11 +214,15 @@ bool Matrix::subtract(const Matrix& other) { return true; } -//returns a dynamically allocated copy of the specified row -//caller must delete[] the returned array +//------------------------- +// 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 NULL; + if (row >= rows) + return nullptr; double* rowArray = new double[cols]; for (unsigned int j = 0; j < cols; j++) { rowArray[j] = data[row][j]; @@ -197,11 +230,11 @@ double* Matrix::get_row(unsigned int row) const { return rowArray; } -//returns a dynamically allocated copy of the specified column -//caller must delete[] the returned array +// 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 NULL; + if (col >= cols) + return nullptr; double* colArray = new double[rows]; for (unsigned int i = 0; i < rows; i++) { colArray[i] = data[i][col]; @@ -209,84 +242,102 @@ double* Matrix::get_col(unsigned int col) const { 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) +//------------------------- +// 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 { - if (rows == 0 || cols == 0) { - return nullptr; + Matrix* quadrants = new Matrix[4]; + if (rows < 2 || cols < 2) { + // Return four empty matrices. + return quadrants; } - // 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; + // 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; - 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 - }; + 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 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 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 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 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 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 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 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); + + // 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 +//------------------------- -//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 << " "; +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; } - if(i < m.rows - 1) - out << std::endl << " "; } - out << " ]"; + 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; } diff --git a/hws/matrix_class/Matrix.h b/hws/matrix_class/Matrix.h index 13eac96..3787fdd 100644 --- a/hws/matrix_class/Matrix.h +++ b/hws/matrix_class/Matrix.h @@ -2,75 +2,63 @@ #define MATRIX_H #include -#include class Matrix { +public: + // Constructors & Destructor + Matrix(); // Default constructor (creates an empty 0 x 0 matrix) + Matrix(unsigned int rows, unsigned int cols, double fill); + Matrix(const Matrix &other); + ~Matrix(); + + Matrix& operator=(const Matrix &other); + + // Accessors for dimensions + unsigned int num_rows() const; + unsigned int num_cols() const; + + // Clears the matrix (deallocates any memory and sets size to 0 x 0) + void clear(); + + // Safe accessor and modifier methods + bool get(unsigned int row, unsigned int col, double &value) const; + bool set(unsigned int row, unsigned int col, double value); + + // Simple matrix operations + void multiply_by_coefficient(double coefficient); + bool swap_row(unsigned int row1, unsigned int row2); + void transpose(); + + // Binary matrix operations (modifies this matrix) + bool add(const Matrix &other); + bool subtract(const Matrix &other); + + // Advanced accessors: returns a dynamic array with the requested row or column. + // The caller is responsible for deleting the returned array. + double* get_row(unsigned int row) const; + double* get_col(unsigned int col) const; + + // Quarter the matrix into four equally sized quadrants. + // The four matrices are returned in a dynamically allocated array in the order: + // UL, UR, LL, LR. + // If the matrix is too small (i.e. less than 2 rows or 2 cols), returns four empty matrices. + Matrix* quarter() const; + + // Equality operators + bool operator==(const Matrix &other) const; + bool operator!=(const Matrix &other) const; + + // Friend overloaded output operator for printing the matrix. + friend std::ostream& operator<<(std::ostream &out, const Matrix &m); + 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); + // Helper functions to allocate and deallocate the 2D array. + void allocate(unsigned int r, unsigned int c, double fill); + void deallocate(); }; #endif diff --git a/hws/matrix_class/matrix_main.cpp b/hws/matrix_class/matrix_main.cpp index 1a336f6..b7617c2 100644 --- a/hws/matrix_class/matrix_main.cpp +++ b/hws/matrix_class/matrix_main.cpp @@ -198,124 +198,96 @@ void SimpleTest(){ //well behaved getrow/read after //Write your own test cases here 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 1: Transpose a non-square matrix. + Matrix m(2, 3, 1.0); + m.set(0, 0, 1.0); m.set(0, 1, 2.0); m.set(0, 2, 3.0); + m.set(1, 0, 4.0); m.set(1, 1, 5.0); m.set(1, 2, 6.0); + m.transpose(); // Now m should be 3 x 2. + assert(m.num_rows() == 3 && m.num_cols() == 2); + m.get(0, 0, val); assert(val == 1.0); + m.get(0, 1, val); assert(val == 4.0); + m.get(1, 0, val); assert(val == 2.0); + m.get(1, 1, val); assert(val == 5.0); + m.get(2, 0, val); assert(val == 3.0); + m.get(2, 1, val); assert(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)); + // Test 2: Multiply matrix by a coefficient. + Matrix m2(2, 2, 2.0); + m2.multiply_by_coefficient(3.0); + m2.get(0, 0, val); assert(val == 6.0); + m2.get(1, 1, val); assert(val == 6.0); + + // Test 3: get_col() functionality. + Matrix m3(3, 3, 0.0); + int counter = 1; + for (unsigned int i = 0; i < 3; i++) { + for (unsigned int j = 0; j < 3; j++) { + m3.set(i, j, counter++); } } - delete[] quarters; + double* col1 = m3.get_col(1); + // Expecting column 1 to be: 2, 5, 8. + assert(col1[0] == 2); + assert(col1[1] == 5); + assert(col1[2] == 8); + delete [] col1; - //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 4: swap_row(). + Matrix m4(2, 3, 0.0); + m4.set(0, 0, 1); m4.set(0, 1, 2); m4.set(0, 2, 3); + m4.set(1, 0, 4); m4.set(1, 1, 5); m4.set(1, 2, 6); + m4.swap_row(0, 1); + m4.get(0, 0, val); assert(val == 4); + m4.get(1, 0, val); assert(val == 1); - //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 5: subtract(). + Matrix m5(2, 2, 10.0); + Matrix m6(2, 2, 3.0); + bool success = m5.subtract(m6); + assert(success); + m5.get(0, 0, val); assert(val == 7.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 6: quarter() on an even-dimensioned matrix. + Matrix m7(4, 4, 0.0); + counter = 1; + for (unsigned int i = 0; i < 4; i++) { + for (unsigned int j = 0; j < 4; j++) { + m7.set(i, j, counter++); + } + } + Matrix* quads = m7.quarter(); + // For a 4 x 4 matrix, quadrant size should be (4+1)/2 = 2 (integer division) + assert(quads[0].num_rows() == 2 && quads[0].num_cols() == 2); + // Upper Left quadrant should be: + // [ 1 2 ] + // [ 5 6 ] + quads[0].get(0, 0, val); assert(val == 1); + quads[0].get(0, 1, val); assert(val == 2); + quads[0].get(1, 0, val); assert(val == 5); + quads[0].get(1, 1, val); assert(val == 6); + delete [] quads; - // 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 7: clear() method. + Matrix m8(3, 3, 9.0); + m8.clear(); + assert(m8.num_rows() == 0 && m8.num_cols() == 0); - //Test clear - a.clear(); - assert(a.num_rows() == 0 && a.num_cols() == 0); + // Test 8: Self-assignment. + Matrix m9(2, 2, 7.0); + m9 = m9; + m9.get(0, 0, val); assert(val == 7.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 9: Binary add() with mismatched dimensions. + Matrix m10(2, 3, 1.0); + Matrix m11(3, 2, 1.0); + bool res = m10.add(m11); + assert(res == false); - //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)); + // Test 10: Binary subtract() with mismatched dimensions. + res = m10.subtract(m11); + assert(res == false); } diff --git a/hws/online_dating/README.txt b/hws/online_dating/README.txt index 3fe7cef..01905ab 100644 --- a/hws/online_dating/README.txt +++ b/hws/online_dating/README.txt @@ -1,7 +1,7 @@ HOMEWORK 5: Online Dating -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 > +Stack Overflow' cases about some pointer further examples 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: 8 hr MISC. COMMENTS TO GRADER: @@ -33,5 +33,7 @@ What parts of the assignment did you find challenging? Is there anything that finally "clicked" for you in the process of working on this assignment? How well did the development and testing process go for you? -< insert reflection > - +For this assignment, it combine a lot of concept from serval lecture. Like I used +class, pointer, the iostream library, etc. It was quite challenging to me at +first. Since the link list is not very familiar to me, I spend some time on +the lab 5 assignment and online resource to understand it better. \ No newline at end of file diff --git a/hws/online_dating/nydate.cpp b/hws/online_dating/nydate.cpp new file mode 100644 index 0000000..436ff2a --- /dev/null +++ b/hws/online_dating/nydate.cpp @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include +#include + +// Node representing a user in the linked list +class User { +public: + std::string name; + int age; + std::string gender; + std::string phone; + std::string profession; + std::string school; + double latitude; + double longitude; + bool premium; + int prefMinAge; + int prefMaxAge; + int maxDistance; + std::string prefGender; + std::vector likes; // list of phone numbers this user liked + User* next; +}; + +// Helper function to convert underscores to spaces in a string +std::string formatString(const std::string &s) { + std::string result = ""; + for (std::size_t i = 0; i < s.size(); i++) { + if (s[i] == '_') + result.push_back(' '); + else + result.push_back(s[i]); + } + return result; +} + +// Calculate the distance between two coordinates using the Haversine formula +double calculateDistance(double lat1, double lon1, double lat2, double lon2) { + const double radiusOfEarth = 6371.0; // Earth's radius in kilometers + + // Convert degrees to radians + lat1 = lat1 * M_PI / 180.0; + lon1 = lon1 * M_PI / 180.0; + lat2 = lat2 * M_PI / 180.0; + lon2 = lon2 * M_PI / 180.0; + + double dLat = lat2 - lat1; + double dLon = lon2 - lon1; + double a = sin(dLat / 2.0) * sin(dLat / 2.0) + + cos(lat1) * cos(lat2) * + sin(dLon / 2.0) * sin(dLon / 2.0); + double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a)); + double distanceKM = radiusOfEarth * c; + double distanceMiles = distanceKM * 0.621371; + return distanceMiles; +} + +// Splits a string by the underscore delimiter +std::vector splitLikes(const std::string &s) { + std::vector result; + std::istringstream iss(s); + std::string token; + while (std::getline(iss, token, '_')) { + result.push_back(token); + } + return result; +} + +// Loads the users from the given file and returns the head of the linked list +User* loadUsers(const std::string &filename) { + std::ifstream fin(filename.c_str()); + if (!fin) { + std::cerr << "Error opening file: " << filename << std::endl; + return 0; + } + User* head = 0; + User* tail = 0; + std::string line; + while (std::getline(fin, line)) { + if (line.empty()) + continue; + std::istringstream iss(line); + User* newUser = new User; + newUser->next = 0; + std::string premiumStr, likesStr; + // Read the 14 fields + iss >> newUser->name + >> newUser->age + >> newUser->gender + >> newUser->phone + >> newUser->profession + >> newUser->school + >> newUser->latitude + >> newUser->longitude + >> premiumStr + >> newUser->prefMinAge + >> newUser->prefMaxAge + >> newUser->maxDistance + >> newUser->prefGender + >> likesStr; + // Convert premium field + newUser->premium = (premiumStr == "true"); + // Process likes: if "null", then empty; else split by underscore + if (likesStr != "null") { + newUser->likes = splitLikes(likesStr); + } + // Append to linked list + if (head == 0) { + head = newUser; + tail = newUser; + } else { + tail->next = newUser; + tail = newUser; + } + } + fin.close(); + return head; +} + +// Frees the memory allocated for the linked list +void freeUsers(User* head) { + while (head) { + User* temp = head; + head = head->next; + delete temp; + } +} + +// Finds and returns the pointer to the user with the given phone number +User* findUser(User* head, const std::string &phone) { + User* curr = head; + while (curr) { + if (curr->phone == phone) + return curr; + curr = curr->next; + } + return 0; +} + +// Checks whether a given phone number exists in the likes vector of a user +bool isNumberInLikes(const User* user, const std::string &phone) { + std::vector::const_iterator it; + for (it = user->likes.begin(); it != user->likes.end(); ++it) { + if (*it == phone) + return true; + } + return false; +} + +// Removes the given phone number from the user's likes vector +void removeLike(User* user, const std::string &phone) { + std::vector::iterator it; + for (it = user->likes.begin(); it != user->likes.end(); ++it) { + if (*it == phone) { + user->likes.erase(it); + break; + } + } +} + +// Checks whether candidate matches the current user's preference +bool matchesPreference(const User* current, const User* candidate) { + // Exclude self + if (current->phone == candidate->phone) + return false; + // Check age + if (candidate->age < current->prefMinAge || candidate->age > current->prefMaxAge) + return false; + // Check gender preference + if (current->prefGender != "Both" && candidate->gender != current->prefGender) + return false; + // Check distance + double dist = calculateDistance(current->latitude, current->longitude, candidate->latitude, candidate->longitude); + if (dist > current->maxDistance) + return false; + return true; +} + +// Prints a single profile to the output stream +void printProfile(std::ofstream &out, const User* user) { + out << user->name << " " << user->age; + if (user->profession != "Undisclosed") + out << std::endl << formatString(user->profession); + if (user->school != "Undisclosed") + out << std::endl << formatString(user->school); + out << std::endl; +} + +// Returns a vector of users who match the current user's preference +// If excludePhone is nonempty, then any user with that phone is skipped +std::vector getMatchingProfiles(const User* current, const User* head, const std::string &excludePhone = "") { + std::vector result; + const User* curr = head; + while (curr) { + if (excludePhone != "" && curr->phone == excludePhone) { + curr = curr->next; + continue; + } + if (matchesPreference(current, curr)) + result.push_back(const_cast(curr)); + curr = curr->next; + } + return result; +} + +// Returns a vector of users who mutually match with the current user +// A mutual match is defined as current liking candidate and candidate liking current +std::vector getMatches(const User* current, const User* head) { + std::vector result; + const User* curr = head; + while (curr) { + if (current->phone == curr->phone) { + curr = curr->next; + continue; + } + if (isNumberInLikes(current, curr->phone) && isNumberInLikes(curr, current->phone)) + result.push_back(const_cast(curr)); + curr = curr->next; + } + return result; +} + +// Returns a vector of users who liked the current user +std::vector getLikes(const User* current, const User* head) { + std::vector result; + const User* curr = head; + while (curr) { + if (current->phone == curr->phone) { + curr = curr->next; + continue; + } + if (isNumberInLikes(curr, current->phone)) + result.push_back(const_cast(curr)); + curr = curr->next; + } + return result; +} + +// Command: show profiles (or for block command, with an exclusion) +void commandShowProfiles(const User* current, const User* head, std::ofstream &out, const std::string &excludePhone = "") { + std::vector profiles = getMatchingProfiles(current, head, excludePhone); + if (profiles.empty()) { + out << "There are no users matching with your preference at this moment." << std::endl; + return; + } + const User* curr = head; + bool first = true; + while (curr) { + if ((excludePhone == "" || curr->phone != excludePhone) && matchesPreference(current, curr)) { + if (!first) out << std::endl; + printProfile(out, curr); + first = false; + } + curr = curr->next; + } +} + +// Command: show matches. Matches should be sorted by phone (increasing order) +void commandShowMatches(const User* current, const User* head, std::ofstream &out) { + std::vector matches = getMatches(current, head); + if (matches.empty()) { + out << "You do not have any matches at this moment."; + return; + } + std::sort(matches.begin(), matches.end(), + [](User* a, User* b) -> bool { return a->phone < b->phone; }); + for (size_t i = 0; i < matches.size(); ++i) { + if (i > 0) out << std::endl; + printProfile(out, matches[i]); + } +} + +// Command: show likes. Only premium users can use this +void commandShowLikes(const User* current, const User* head, std::ofstream &out) { + if (!current->premium) { + out << "Only premium users can view who liked you."; + return; + } + std::vector likes = getLikes(current, head); + if (likes.empty()) { + out << "You have not received any likes so far."; + return; + } + const User* curr = head; + bool first = true; + while (curr) { + if (current->phone != curr->phone && isNumberInLikes(curr, current->phone)) { + if (!first) out << std::endl; + printProfile(out, curr); + first = false; + } + curr = curr->next; + } +} + +// Command: unmatch someone. Remove mutual like between current and other, +// then print both users' match lists (sorted by phone number) +void commandUnmatch(User* current, User* other, const User* head, std::ofstream &out) { + removeLike(current, other->phone); + removeLike(other, current->phone); + std::vector currentMatches = getMatches(current, head); + std::vector otherMatches = getMatches(other, head); + std::sort(currentMatches.begin(), currentMatches.end(), + [](User* a, User* b) -> bool { return a->phone < b->phone; }); + std::sort(otherMatches.begin(), otherMatches.end(), + [](User* a, User* b) -> bool { return a->phone < b->phone; }); + + out << current->name << "'s match list:" << std::endl << std::endl; + if (currentMatches.empty()) { + out << "You do not have any matches at this moment." << std::endl; + } else { + for (size_t i = 0; i < currentMatches.size(); ++i) { + if (i > 0) out << std::endl; + printProfile(out, currentMatches[i]); + } + } + out << std::endl << "======" << std::endl << std::endl; + out << other->name << "'s match list:" << std::endl << std::endl; + if (otherMatches.empty()) { + out << "You do not have any matches at this moment." << std::endl; + } else { + for (size_t i = 0; i < otherMatches.size(); ++i) { + if (i > 0) out << std::endl; + printProfile(out, otherMatches[i]); + } + } +} + +// Command: block someone. For the current user and the other user, print profiles +// that match their preference but exclude the blocked user +void commandBlock(const User* current, const User* other, const User* head, std::ofstream &out) { + out << "profiles shown to " << current->name << ":" << std::endl << std::endl; + commandShowProfiles(current, head, out, other->phone); + out << std::endl << "======" << std::endl << std::endl; + out << "profiles shown to " << other->name << ":" << std::endl << std::endl; + commandShowProfiles(other, head, out, current->phone); +} + +int main(int argc, char* argv[]) { + if (argc < 5) { + std::cerr << "Usage: nydate.exe users.txt output.txt phoneNumber command [phoneNumberOther]" << std::endl; + return 1; + } + + std::string usersFile = argv[1]; + std::string outputFile = argv[2]; + std::string currentPhone = argv[3]; + std::string command = argv[4]; + + User* head = loadUsers(usersFile); + if (head == 0) { + return 1; + } + + User* currentUser = findUser(head, currentPhone); + if (currentUser == 0) { + std::cerr << "User with phone " << currentPhone << " not found." << std::endl; + freeUsers(head); + return 1; + } + + std::ofstream fout(outputFile.c_str()); + if (!fout) { + std::cerr << "Error opening output file: " << outputFile << std::endl; + freeUsers(head); + return 1; + } + + // Process commands + if (command == "profile") { + commandShowProfiles(currentUser, head, fout); + } else if (command == "match") { + commandShowMatches(currentUser, head, fout); + } else if (command == "like") { + commandShowLikes(currentUser, head, fout); + } else if (command == "unmatch") { + if (argc < 6) { + std::cerr << "unmatch command requires a second phone number." << std::endl; + freeUsers(head); + return 1; + } + std::string otherPhone = argv[5]; + User* otherUser = findUser(head, otherPhone); + if (otherUser == 0) { + std::cerr << "User with phone " << otherPhone << " not found." << std::endl; + freeUsers(head); + return 1; + } + commandUnmatch(currentUser, otherUser, head, fout); + } else if (command == "block") { + if (argc < 6) { + std::cerr << "block command requires a second phone number." << std::endl; + freeUsers(head); + return 1; + } + std::string otherPhone = argv[5]; + User* otherUser = findUser(head, otherPhone); + if (otherUser == 0) { + std::cerr << "User with phone " << otherPhone << " not found." << std::endl; + freeUsers(head); + return 1; + } + commandBlock(currentUser, otherUser, head, fout); + } else { + std::cerr << "Unknown command: " << command << std::endl; + } + + fout.close(); + freeUsers(head); + return 0; +} \ No newline at end of file