add solution for hw 5

This commit is contained in:
JamesFlare1212
2025-03-12 10:00:49 -04:00
parent c20ce7c2c3
commit 903e76ef1f
6 changed files with 772 additions and 326 deletions

18
.vscode/launch.json vendored
View File

@@ -70,6 +70,24 @@
"MIMode": "gdb", "MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb", "miDebuggerPath": "/usr/bin/gdb",
"preLaunchTask": "C/C++: g++ build active file" "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"
} }
] ]
} }

View File

@@ -1,15 +1,12 @@
#include "Matrix.h" #include "Matrix.h"
#include <iostream> #include <iostream>
#include <iomanip>
//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) { // Private Helper Functions
if(r == 0 || c == 0) { //-------------------------
rows = 0;
cols = 0; // Allocates memory for an r x c matrix and fills every element with 'fill'
data = nullptr; void Matrix::allocate(unsigned int r, unsigned int c, double fill) {
return;
}
rows = r; rows = r;
cols = c; cols = c;
data = new double*[rows]; data = new double*[rows];
@@ -21,149 +18,180 @@ void Matrix::allocateMemory(unsigned int r, unsigned int c, double fill) {
} }
} }
//helper function to deallocate memory // Deallocates the memory used by the matrix
void Matrix::deallocateMemory() { void Matrix::deallocate() {
if(data != nullptr) { if (data) {
for (unsigned int i = 0; i < rows; i++) { for (unsigned int i = 0; i < rows; i++) {
delete [] data[i]; delete [] data[i];
} }
delete [] data; delete [] data;
data = nullptr;
} }
data = nullptr;
rows = 0; rows = 0;
cols = 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) { 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 // Copy constructor
Matrix::Matrix(const Matrix& other) : rows(0), cols(0), data(nullptr) { Matrix::Matrix(const Matrix &other) : rows(0), cols(0), data(nullptr) {
allocateMemory(other.rows, other.cols, 0.0); 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 i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
data[i][j] = other.data[i][j]; data[i][j] = other.data[i][j];
} }
} }
}
} }
//destructor // Destructor
Matrix::~Matrix() { Matrix::~Matrix() {
deallocateMemory(); deallocate();
} }
//assignment operator // Assignment operator
Matrix& Matrix::operator=(const Matrix& other) { Matrix& Matrix::operator=(const Matrix &other) {
if (this == &other) if (this == &other)
return *this; return *this;
deallocateMemory();
allocateMemory(other.rows, other.cols, 0.0); 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 i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
data[i][j] = other.data[i][j]; data[i][j] = other.data[i][j];
} }
} }
}
return *this; return *this;
} }
//returns the number of rows //-------------------------
// Dimension Accessors & Clear
//-------------------------
unsigned int Matrix::num_rows() const { unsigned int Matrix::num_rows() const {
return rows; return rows;
} }
//returns the number of columns
unsigned int Matrix::num_cols() const { unsigned int Matrix::num_cols() const {
return cols; return cols;
} }
//clears the matrix by deallocating its memory
void Matrix::clear() { 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 { bool Matrix::get(unsigned int row, unsigned int col, double &value) const {
if(row >= rows || col >= cols) { if (row >= rows || col >= cols)
return false; return false;
}
value = data[row][col]; value = data[row][col];
return true; 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) { bool Matrix::set(unsigned int row, unsigned int col, double value) {
if(row >= rows || col >= cols) { if (row >= rows || col >= cols)
return false; return false;
}
data[row][col] = value; data[row][col] = value;
return true; return true;
} }
//equality operator //-------------------------
bool Matrix::operator==(const Matrix& other) const { // Simple Matrix Operations
//two matrices are equal if dimensions match //-------------------------
//and every element is equal within a small epsilon
if(rows != other.rows || cols != other.cols) // Multiplies every element in the matrix by the given coefficient.
return false; void Matrix::multiply_by_coefficient(double coefficient) {
for (unsigned int i = 0; i < rows; i++) { for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
if (fabs(data[i][j] - other.data[i][j]) > 1e-10) data[i][j] *= coefficient;
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) // Swaps the entire contents of row1 and row2 if both indices are valid.
bool Matrix::swap_row(unsigned int row1, unsigned int row2) { bool Matrix::swap_row(unsigned int row1, unsigned int row2) {
if(row1 >= rows || row2 >= rows) { if (row1 >= rows || row2 >= rows)
return false; return false;
}
double* temp = data[row1]; double* temp = data[row1];
data[row1] = data[row2]; data[row1] = data[row2];
data[row2] = temp; data[row2] = temp;
return true; 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() { void Matrix::transpose() {
if(rows == 0 || cols == 0) if (rows == 0 || cols == 0)
return; return;
unsigned int newRows = cols;
unsigned int newCols = rows; // Allocate new array with swapped dimensions.
double** newData = new double*[newRows]; double** newData = new double*[cols];
for (unsigned int i = 0; i < newRows; i++) { for (unsigned int i = 0; i < cols; i++) {
newData[i] = new double[newCols]; newData[i] = new double[rows];
for (unsigned int j = 0; j < newCols; j++) { }
newData[i][j] = data[j][i]; // 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(); // Free old data.
rows = newRows; for (unsigned int i = 0; i < rows; i++) {
cols = newCols; delete [] data[i];
}
delete [] data;
// Swap dimensions.
unsigned int temp = rows;
rows = cols;
cols = temp;
data = newData; data = newData;
} }
//adds another matrix to this one element-wise //-------------------------
bool Matrix::add(const Matrix& other) { // Binary Matrix Operations
if(rows != other.rows || cols != other.cols) //-------------------------
// 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; return false;
for (unsigned int i = 0; i < rows; i++) { for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
@@ -173,9 +201,10 @@ bool Matrix::add(const Matrix& other) {
return true; return true;
} }
//subtracts another matrix from this one element-wise // Subtracts the corresponding elements of other from this matrix.
bool Matrix::subtract(const Matrix& other) { // Returns true if dimensions match, else returns false.
if(rows != other.rows || cols != other.cols) bool Matrix::subtract(const Matrix &other) {
if (rows != other.rows || cols != other.cols)
return false; return false;
for (unsigned int i = 0; i < rows; i++) { for (unsigned int i = 0; i < rows; i++) {
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
@@ -185,11 +214,15 @@ bool Matrix::subtract(const Matrix& other) {
return true; 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 { double* Matrix::get_row(unsigned int row) const {
if(row >= rows) if (row >= rows)
return NULL; return nullptr;
double* rowArray = new double[cols]; double* rowArray = new double[cols];
for (unsigned int j = 0; j < cols; j++) { for (unsigned int j = 0; j < cols; j++) {
rowArray[j] = data[row][j]; rowArray[j] = data[row][j];
@@ -197,11 +230,11 @@ double* Matrix::get_row(unsigned int row) const {
return rowArray; return rowArray;
} }
//returns a dynamically allocated copy of the specified column // Returns a new dynamically allocated array containing the requested column.
//caller must delete[] the returned array // Returns nullptr if the column index is out of bounds.
double* Matrix::get_col(unsigned int col) const { double* Matrix::get_col(unsigned int col) const {
if(col >= cols) if (col >= cols)
return NULL; return nullptr;
double* colArray = new double[rows]; double* colArray = new double[rows];
for (unsigned int i = 0; i < rows; i++) { for (unsigned int i = 0; i < rows; i++) {
colArray[i] = data[i][col]; colArray[i] = data[i][col];
@@ -209,84 +242,102 @@ double* Matrix::get_col(unsigned int col) const {
return colArray; 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 { Matrix* Matrix::quarter() const {
if (rows == 0 || cols == 0) { Matrix* quadrants = new Matrix[4];
return nullptr; 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, // Determine quadrant dimensions.
// the bottom quadrants start at floor(rows/2), similarly for columns. // Using (dim + 1) / 2 ensures that if the dimension is odd the shared middle row/col is included.
unsigned int start_row_bottom = rows / 2; unsigned int quad_rows = (rows + 1) / 2;
unsigned int start_col_right = cols / 2; unsigned int quad_cols = (cols + 1) / 2;
Matrix* quadrants = new Matrix[4]{ quadrants[0] = Matrix(quad_rows, quad_cols, 0.0); // Upper Left (UL)
Matrix(q_rows, q_cols, 0.0), // Upper Left quadrants[1] = Matrix(quad_rows, quad_cols, 0.0); // Upper Right (UR)
Matrix(q_rows, q_cols, 0.0), // Upper Right quadrants[2] = Matrix(quad_rows, quad_cols, 0.0); // Lower Left (LL)
Matrix(q_rows, q_cols, 0.0), // Lower Left quadrants[3] = Matrix(quad_rows, quad_cols, 0.0); // Lower Right (LR)
Matrix(q_rows, q_cols, 0.0) // Lower Right
};
// Fill Upper Left quadrant from original (starting at (0,0)). // Fill UL quadrant: rows 0 .. quad_rows-1, cols 0 .. quad_cols-1.
for (unsigned int i = 0; i < q_rows; i++) { for (unsigned int i = 0; i < quad_rows; i++) {
for (unsigned int j = 0; j < q_cols; j++) { for (unsigned int j = 0; j < quad_cols; j++) {
double value; quadrants[0].set(i, j, data[i][j]);
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++) { // Fill UR quadrant: rows 0 .. quad_rows-1, cols (cols - quad_cols) .. (cols - 1).
for (unsigned int j = 0; j < q_cols; j++) { for (unsigned int i = 0; i < quad_rows; i++) {
double value; for (unsigned int j = 0; j < quad_cols; j++) {
if (i < rows && (j + start_col_right) < cols && get(i, j + start_col_right, value)) quadrants[1].set(i, j, data[i][(cols - quad_cols) + j]);
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++) { // Fill LL quadrant: rows (rows - quad_rows) .. (rows - 1), cols 0 .. quad_cols-1.
for (unsigned int j = 0; j < q_cols; j++) { for (unsigned int i = 0; i < quad_rows; i++) {
double value; for (unsigned int j = 0; j < quad_cols; j++) {
if ((i + start_row_bottom) < rows && j < cols && get(i + start_row_bottom, j, value)) quadrants[2].set(i, j, data[(rows - quad_rows) + i][j]);
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++) { // Fill LR quadrant: rows (rows - quad_rows) .. (rows - 1), cols (cols - quad_cols) .. (cols - 1).
for (unsigned int j = 0; j < q_cols; j++) { for (unsigned int i = 0; i < quad_rows; i++) {
double value; for (unsigned int j = 0; j < quad_cols; j++) {
if ((i + start_row_bottom) < rows && (j + start_col_right) < cols && quadrants[3].set(i, j, data[(rows - quad_rows) + i][(cols - quad_cols) + j]);
get(i + start_row_bottom, j + start_col_right, value))
quadrants[3].set(i, j, value);
} }
} }
return quadrants; return quadrants;
} }
//-------------------------
// Equality Operators
//-------------------------
//overloaded output operator to print the matrix bool Matrix::operator==(const Matrix &other) const {
// 4 x 4 matrix: if (rows != other.rows || cols != other.cols)
// [ 14 14 14 14 return false;
// 14 14 14 14 for (unsigned int i = 0; i < rows; i++) {
// 14 9 14 14 for (unsigned int j = 0; j < cols; j++) {
// 14 14 14 13 ] if (data[i][j] != other.data[i][j])
std::ostream& operator<<(std::ostream& out, const Matrix& m) { 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 << m.rows << " x " << m.cols << " matrix:" << std::endl;
out << "[ "; out << "[";
if (m.rows > 0 && m.cols > 0) {
for (unsigned int i = 0; i < m.rows; i++) { for (unsigned int i = 0; i < m.rows; i++) {
out << " ";
for (unsigned int j = 0; j < m.cols; j++) { for (unsigned int j = 0; j < m.cols; j++) {
out << m.data[i][j]; out << m.data[i][j];
if(j < m.cols - 1) if (j < m.cols - 1)
out << " "; out << " ";
} }
if(i < m.rows - 1) if (i < m.rows - 1)
out << std::endl << " "; out << std::endl;
} }
out << " ]"; out << " ]";
} else {
out << " ]";
}
return out; return out;
} }

View File

@@ -2,75 +2,63 @@
#define MATRIX_H #define MATRIX_H
#include <iostream> #include <iostream>
#include <cmath>
class Matrix { 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: private:
unsigned int rows; unsigned int rows;
unsigned int cols; unsigned int cols;
double** data; 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: // Helper functions to allocate and deallocate the 2D array.
////Constructors void allocate(unsigned int r, unsigned int c, double fill);
Matrix(); void deallocate();
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 #endif

View File

@@ -198,124 +198,96 @@ void SimpleTest(){ //well behaved getrow/read after
//Write your own test cases here //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; 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 // Test 1: Transpose a non-square matrix.
Matrix q(5, 5, 1); Matrix m(2, 3, 1.0);
Matrix* quarters = q.quarter(); m.set(0, 0, 1.0); m.set(0, 1, 2.0); m.set(0, 2, 3.0);
assert(quarters != nullptr); m.set(1, 0, 4.0); m.set(1, 1, 5.0); m.set(1, 2, 6.0);
//each quadrant should be 3x3 (ceiling: (5+1)/2 == 3) m.transpose(); // Now m should be 3 x 2.
assert(quarters[0].num_rows() == 3); assert(m.num_rows() == 3 && m.num_cols() == 2);
assert(quarters[0].num_cols() == 3); m.get(0, 0, val); assert(val == 1.0);
assert(quarters[1].num_rows() == 3); m.get(0, 1, val); assert(val == 4.0);
assert(quarters[1].num_cols() == 3); m.get(1, 0, val); assert(val == 2.0);
assert(quarters[2].num_rows() == 3); m.get(1, 1, val); assert(val == 5.0);
assert(quarters[2].num_cols() == 3); m.get(2, 0, val); assert(val == 3.0);
assert(quarters[3].num_rows() == 3); m.get(2, 1, val); assert(val == 6.0);
assert(quarters[3].num_cols() == 3);
//verify that the quadrants hold the expected values. // Test 2: Multiply matrix by a coefficient.
for (int i = 0; i < 3; i++) { Matrix m2(2, 2, 2.0);
for (int j = 0; j < 3; j++) { m2.multiply_by_coefficient(3.0);
quarters[0].get(i, j, val); m2.get(0, 0, val); assert(val == 6.0);
assert(double_compare(val, 1.0)); m2.get(1, 1, val); assert(val == 6.0);
quarters[1].get(i, j, val);
assert(double_compare(val, 1.0)); // Test 3: get_col() functionality.
quarters[2].get(i, j, val); Matrix m3(3, 3, 0.0);
assert(double_compare(val, 1.0)); int counter = 1;
quarters[3].get(i, j, val); for (unsigned int i = 0; i < 3; i++) {
assert(double_compare(val, 1.0)); 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 // Test 4: swap_row().
Matrix a(2, 2, 2); Matrix m4(2, 3, 0.0);
Matrix b(2, 2, 3); m4.set(0, 0, 1); m4.set(0, 1, 2); m4.set(0, 2, 3);
assert(a.add(b)); // Now a is all 5's. m4.set(1, 0, 4); m4.set(1, 1, 5); m4.set(1, 2, 6);
double v; m4.swap_row(0, 1);
a.get(0, 0, v); m4.get(0, 0, val); assert(val == 4);
assert(double_compare(v, 5.0)); m4.get(1, 0, val); assert(val == 1);
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 // Test 5: subtract().
a.multiply_by_coefficient(2.5); //Now a is all 5's (2*2.5). Matrix m5(2, 2, 10.0);
a.get(0, 0, v); Matrix m6(2, 2, 3.0);
assert(double_compare(v, 5.0)); bool success = m5.subtract(m6);
assert(success);
m5.get(0, 0, val); assert(val == 7.0);
//Test get_row // Test 6: quarter() on an even-dimensioned matrix.
double* row = a.get_row(0); Matrix m7(4, 4, 0.0);
assert(row != nullptr); counter = 1;
assert(double_compare(row[0], 5.0)); for (unsigned int i = 0; i < 4; i++) {
assert(double_compare(row[1], 5.0)); for (unsigned int j = 0; j < 4; j++) {
delete[] row; 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 // Test 7: clear() method.
double* col = a.get_col(1); Matrix m8(3, 3, 9.0);
assert(col != nullptr); m8.clear();
assert(double_compare(col[0], 5.0)); assert(m8.num_rows() == 0 && m8.num_cols() == 0);
assert(double_compare(col[1], 5.0));
delete[] col;
//Test clear // Test 8: Self-assignment.
a.clear(); Matrix m9(2, 2, 7.0);
assert(a.num_rows() == 0 && a.num_cols() == 0); m9 = m9;
m9.get(0, 0, val); assert(val == 7.0);
//Test swap_row // Test 9: Binary add() with mismatched dimensions.
Matrix s(3, 2, 0); Matrix m10(2, 3, 1.0);
s.set(0, 0, 1); s.set(0, 1, 2); Matrix m11(3, 2, 1.0);
s.set(1, 0, 3); s.set(1, 1, 4); bool res = m10.add(m11);
s.set(2, 0, 5); s.set(2, 1, 6); assert(res == false);
//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. // Test 10: Binary subtract() with mismatched dimensions.
Matrix orig(2, 3, 7); res = m10.subtract(m11);
Matrix copy(orig); // Using copy constructor. assert(res == false);
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));
} }

View File

@@ -1,7 +1,7 @@
HOMEWORK 5: Online Dating HOMEWORK 5: Online Dating
NAME: < insert name > NAME: Jinshan Zhou
COLLABORATORS AND OTHER RESOURCES: 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 LMS, etc.), and all of the resources (books, online reference
material, etc.) you consulted in completing this assignment. 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 Remember: Your implementation for this assignment must be done on your
own, as described in "Academic Integrity for Homework" handout. 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: 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 finally "clicked" for you in the process of working on this assignment? How well
did the development and testing process go for you? 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.

View File

@@ -0,0 +1,415 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cmath>
#include <vector>
#include <algorithm>
// 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<std::string> 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<std::string> splitLikes(const std::string &s) {
std::vector<std::string> 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<std::string>::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<std::string>::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<User*> getMatchingProfiles(const User* current, const User* head, const std::string &excludePhone = "") {
std::vector<User*> 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<User*>(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<User*> getMatches(const User* current, const User* head) {
std::vector<User*> 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<User*>(curr));
curr = curr->next;
}
return result;
}
// Returns a vector of users who liked the current user
std::vector<User*> getLikes(const User* current, const User* head) {
std::vector<User*> 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<User*>(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<User*> 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<User*> 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<User*> 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<User*> currentMatches = getMatches(current, head);
std::vector<User*> 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;
}