diff --git a/meson.build b/meson.build index 197a617..2039a22 100644 --- a/meson.build +++ b/meson.build @@ -21,3 +21,4 @@ c_2_lab_3 = executable('c_2_lab_3', 'src/chapter_2/lab_3/main.cpp', install: tru c_2_program_1 = executable('c_2_program_1', 'src/chapter_2/program_1/main.cpp', install: true) c_3_lab_1 = executable('c_3_lab_1', 'src/chapter_3/lab_1/main.cpp', install: true) +c_3_program_1 = executable('c_3_program_1', 'src/chapter_3/program_1/main.cpp', install: true) diff --git a/src/chapter_3/program_1/main.cpp b/src/chapter_3/program_1/main.cpp new file mode 100644 index 0000000..5470f04 --- /dev/null +++ b/src/chapter_3/program_1/main.cpp @@ -0,0 +1,278 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Date { +private: + unsigned day; + unsigned month; + string monthName; + unsigned year; + +public: + // creates the date January 1st, 2000. + Date(); + + /* parameterized constructor: month number, day, year + - e.g. (3, 1, 2010) will construct the date March 1st, 2010 + + If any of the arguments are invalid (e.g. 15 for month or 32 for day) + then the constructor will construct instead a valid Date as close + as possible to the arguments provided - e.g. in above example, + Date(15, 32, 2010), the Date would be corrected to Dec 31st, 2010. + In case of such invalid input, the constructor will issue a console error + message: + + Invalid date values: Date corrected to 12/31/2010. + (with a newline at the end). + */ + Date(unsigned m, unsigned d, unsigned y); + + /* parameterized constructor: month name, day, year + - e.g. (December, 15, 2012) will construct the date December 15th, 2012 + + If the constructor is unable to recognize the string argument as a valid + month name, then it will issue a console error message: + + Invalid month name: the Date was set to 1/1/2000. + (with a newline at the end). + + If the day argument is invalid for the given month (but the month name was + valid), then the constructor will handle this error in the same manner as + the other parameterized constructor. + + This constructor will recognize both "december" and "December" + as month name. + */ + Date(const string &mn, unsigned d, unsigned y); + + /* Outputs to the console (cout) a Date exactly in the format "3/15/2012". + Does not output a newline at the end. + */ + void printNumeric() const; + + /* Outputs to the console (cout) a Date exactly in the format "March 15, + 2012". The first letter of the month name is upper case, and the month name + is printed in full - January, not Jan, jan, or january. Does not output a + newline at the end. + */ + void printAlpha() const; + +private: + /* Returns true if the year passed in is a leap year, otherwise returns false. + */ + bool isLeap(unsigned y) const; + + /* Returns number of days allowed in a given month + - e.g. daysPerMonth(9, 2000) returns 30. + Calculates February's days for leap and non-leap years, + thus, the reason year is also a parameter. + */ + unsigned daysPerMonth(unsigned m, unsigned y) const; + + /* Returns the name of a given month + - e.g. name(12) returns the string "December" + */ + string name(unsigned m) const; + + /* Returns the number of a given named month + - e.g. number("March") returns 3 + */ + unsigned number(const string &mn) const; +}; + +// Implement the Date member functions here +Date::Date() { + this->day = 1; + this->month = 1; + this->year = 2000; +} + +bool Date::isLeap(unsigned y) const { + return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0); +} + +unsigned Date::daysPerMonth(unsigned m, unsigned y) const { + const unsigned int ODD_MONTHS[] = {1, 3, 5, 7, 8, 10, 12}; + const unsigned int EVEN_MONTHS[] = {4, 6, 9, 11}; + + if (find(begin(ODD_MONTHS), end(ODD_MONTHS), m) != end(ODD_MONTHS)) { + return 31; + } + if (find(begin(EVEN_MONTHS), end(EVEN_MONTHS), m) != end(EVEN_MONTHS)) { + return 30; + } + + // leap year + if (this->isLeap(y)) { + return 29; + } + + return 28; +}; + +string Date::name(unsigned m) const { + unordered_map months; + + months[1] = "January"; + months[2] = "February"; + months[3] = "March"; + months[4] = "April"; + months[5] = "May"; + months[6] = "June"; + months[7] = "July"; + months[8] = "August"; + months[9] = "September"; + months[10] = "October"; + months[11] = "November"; + months[12] = "December"; + + try { + return (months.at(m)); + } catch (std::out_of_range const &) { + return "NONE"; + } +}; + +unsigned Date::number(const string &mn) const { + string mnLowered = mn; + mnLowered.at(0) = tolower(mn.at(0)); + + unordered_map months; + + months["january"] = 1; + months["february"] = 2; + months["march"] = 3; + months["april"] = 4; + months["may"] = 5; + months["june"] = 6; + months["july"] = 7; + months["august"] = 8; + months["september"] = 9; + months["october"] = 10; + months["november"] = 11; + months["december"] = 12; + + try { + return (months.at(mnLowered)); + } catch (std::out_of_range const &) { + return 999; + } +}; + +Date::Date(unsigned m, unsigned d, unsigned y) { + this->day = d; + this->month = m; + this->year = y; + + if (m < 1) { + this->month = 1; + } + + if (m > 12) { + this->month = 12; + } + + if (d < 1) { + this->day = 1; + } + + if (d > this->daysPerMonth(this->month, y)) { + this->day = this->daysPerMonth(this->month, y); + } + + if (this->day != d || this->month != m || this->year != y) { + cout << "Invalid date values: Date corrected to " << this->month << "/" + << this->day << "/" << this->year << "." << endl; + } +} + +Date::Date(const string &mn, unsigned d, unsigned y) { + Date date; + auto m = this->number(mn); + + if (m != 999) { + date = Date(m, d, y); + this->day = date.day; + this->month = date.month; + this->year = date.year; + } else { + cout << "Invalid month name: the Date was set to 1/1/2000." << endl; + this->day = 1; + this->month = 1; + this->year = 2000; + } +} + +void Date::printNumeric() const { + cout << this->month << "/" << this->day << "/" << this->year; +}; + +void Date::printAlpha() const { + cout << this->name(this->month) << " " << this->day << ", " << this->year; +}; + +// Don't change the code below this line. +// You may comment them out if you want to build your own test harness +// while in develope mode, but you will need these to pass tets in submit mode. + +Date getDate(); + +int main() { + + Date testDate; + testDate = getDate(); + cout << endl; + cout << "Numeric: "; + testDate.printNumeric(); + cout << endl; + cout << "Alpha: "; + testDate.printAlpha(); + cout << endl; + + return 0; +} + +Date getDate() { + int choice; + unsigned monthNumber, day, year; + string monthName; + + cout << "Which Date constructor? (Enter 1, 2, or 3)" << endl + << "1 - Month Number" << endl + << "2 - Month Name" << endl + << "3 - default" << endl; + cin >> choice; + cout << endl; + + if (choice == 1) { + cout << "month number? "; + cin >> monthNumber; + cout << endl; + cout << "day? "; + cin >> day; + cout << endl; + cout << "year? "; + cin >> year; + cout << endl; + return Date(monthNumber, day, year); + } else if (choice == 2) { + cout << "month name? "; + cin >> monthName; + cout << endl; + cout << "day? "; + cin >> day; + cout << endl; + cout << "year? "; + cin >> year; + cout << endl; + return Date(monthName, day, year); + } else { + return Date(); + } +}