add solution for hw 5
This commit is contained in:
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
for (unsigned int i = 0; i < rows; i++) {
|
rows = 0;
|
||||||
for (unsigned int j = 0; j < cols; j++) {
|
cols = 0;
|
||||||
data[i][j] = other.data[i][j];
|
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() {
|
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();
|
||||||
for (unsigned int i = 0; i < rows; i++) {
|
|
||||||
for (unsigned int j = 0; j < cols; j++) {
|
if (other.rows == 0 || other.cols == 0) {
|
||||||
data[i][j] = other.data[i][j];
|
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;
|
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;
|
||||||
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 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;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
// Test 1: Transpose a non-square matrix.
|
||||||
m.get(0, 1, val);
|
Matrix m(2, 3, 1.0);
|
||||||
assert(double_compare(val, 4.0));
|
m.set(0, 0, 1.0); m.set(0, 1, 2.0); m.set(0, 2, 3.0);
|
||||||
m.get(1, 0, val);
|
m.set(1, 0, 4.0); m.set(1, 1, 5.0); m.set(1, 2, 6.0);
|
||||||
assert(double_compare(val, 2.0));
|
m.transpose(); // Now m should be 3 x 2.
|
||||||
m.get(2, 1, val);
|
assert(m.num_rows() == 3 && m.num_cols() == 2);
|
||||||
assert(double_compare(val, 6.0));
|
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
|
// Test 2: Multiply matrix by a coefficient.
|
||||||
Matrix q(5, 5, 1);
|
Matrix m2(2, 2, 2.0);
|
||||||
Matrix* quarters = q.quarter();
|
m2.multiply_by_coefficient(3.0);
|
||||||
assert(quarters != nullptr);
|
m2.get(0, 0, val); assert(val == 6.0);
|
||||||
//each quadrant should be 3x3 (ceiling: (5+1)/2 == 3)
|
m2.get(1, 1, val); assert(val == 6.0);
|
||||||
assert(quarters[0].num_rows() == 3);
|
|
||||||
assert(quarters[0].num_cols() == 3);
|
// Test 3: get_col() functionality.
|
||||||
assert(quarters[1].num_rows() == 3);
|
Matrix m3(3, 3, 0.0);
|
||||||
assert(quarters[1].num_cols() == 3);
|
int counter = 1;
|
||||||
assert(quarters[2].num_rows() == 3);
|
for (unsigned int i = 0; i < 3; i++) {
|
||||||
assert(quarters[2].num_cols() == 3);
|
for (unsigned int j = 0; j < 3; j++) {
|
||||||
assert(quarters[3].num_rows() == 3);
|
m3.set(i, j, counter++);
|
||||||
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;
|
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
415
hws/online_dating/nydate.cpp
Normal file
415
hws/online_dating/nydate.cpp
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user