Monday, 24 September 2018

Chapter 9 // Exercise 12 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 12



Change the representation of a Date to be the number of days since January 1, 1970 (known as day 0), represented as a long int, and re-implement the functions from section 9.8. Be sure to reject dates outside the range we can represent that was (feel free to reject days before day 0, i.e no negative days).

dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_

#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

enum class Day
{
 monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
};

//forward declaration
Month returnMonth(int month);
Day returnDay(int day);

//9.8 The Date Class
class Date
{
public:
 Date();
 ~Date();
 Date(int y, Month m, int d);  //check for valid date & initialise

 //non-modifying operations:
 int day() const { return d; };
 Month month() const { return m; }
 int year() const { return y; }

 //modifying operations:
 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

private:
 int y;
 Month m;
 int d;

 bool lastDay = false;
 bool endYear = false;
};

//helper functions
bool is_date(int y, Month m, int d);   //true for valid date
bool leapyear(int y);     //true if y is leap year

Day findDayOfWeek(const Date& d);   //returns the day for the given date
Day next_workday(const Date& d);   //retrieves the next work day
int week_of_the_year(const Date& d);   //returns the number of the week in the year
int howManyDaysSince(const Date& d);   //returns how many days since the start of the year
long int daysSince(const Date& date1, const Date& date2); //returns how many days between two given dates
void printDay(const Day& day);    //prints out day in written form

ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);

bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);

//to help with finding day for any given date
struct stringInt
{
 int monthDay;
 int key;
};

#endif // !_DATECLASS_H_


dateclass.cpp
//dateclass.cpp

#include "dateclass.h"

vector<stringInt> monthKey
{
 { 3, 1 },{ 4, 2 },{ 5, 3 },{ 6, 4 },{ 7, 5 },{ 8, 6 },{ 9, 7 },{ 10, 8 },{ 11, 9 },{ 12, 10 },{ 1, 11 },{ 2, 12 }
};
vector<stringInt> dayKey
{
 { 7, 0 },{ 1, 1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 5, 5 },{ 6, 6 }
};

//default Date Constructor
Date::Date() 
{
 y = 2001;
 m = Month::jan;
 d = 1;
}

//deconstructor
Date::~Date() {}

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (!is_date(y, m, d))
  cout << "Error, invalid date." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }

 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//switch to return correct day
Day returnDay(int day)
{
 switch (day)
 {
 case 7:
  return Day::sunday;
  break;
 case 1:
  return Day::monday;
  break;
 case 2:
  return Day::tuesday;
  break;
 case 3:
  return Day::wednesday;
  break;
 case 4:
  return Day::thursday;
  break;
 case 5:
  return Day::friday;
  break;
 case 6:
  return Day::saturday;
  break;
 default:
  cout << "Bad day" << endl;
 }
}

void printDay(const Day& day)
{
 switch (day)
 {
 case Day::sunday:
  cout << "Sunday\n";
  break;
 case Day::monday:
  cout << "Monday\n";
  break;
 case Day::tuesday:
  cout << "Tuesday\n";
  break;
 case Day::wednesday:
  cout << "Wednesday\n";
  break;
 case Day::thursday:
  cout << "Thursday\n";
  break;
 case Day::friday:
  cout << "Friday\n";
  break;
 case Day::saturday:
  cout << "Saturday\n";
  break;
 default:
  cout << "Bad day" << endl;
 }
}

//true for valid date
bool is_date(int y, Month m, int d)
{
 //check that y is valid
 if (y < 1900 || y > 2018)
 {
  cout << "Error, invalid year." << endl;
  return false;
 }

 //check that m is valid
 if (m < Month::jan || m > Month::dec)
  return false;

 //check that d is valid
 if (d <= 0)
  return false; //d must be positive

 int daysInMonth = 31;  //most months have 31 days

 switch (m)
 {
  case Month::feb:
   //if leapyear, make it 29, if not make it 28
   daysInMonth = (leapyear(y)) ? 29 : 28;
   break;
  case Month::apr: case Month::jun: case Month::sep: case Month::nov:
   daysInMonth = 30; //the rest have 30 days
   break;
 }

 if (daysInMonth < d)
  return false;

 return true;
}

//this was wrong in previous chapters, I've realised this now and changed it
//new version of leapyear
bool leapyear(int y)
{
 //if century year, must be divisible by 400
 if (y % 400 == 0)
  return true;
 
 //if normal year and divisible by 4
 if (y % 4 == 0)
  return true;

 //else not leap year
 else
  return false;
}

//find day of the week for any year using Zeller's Rule
Day findDayOfWeek(const Date& d)
{
 int day, month, year, century;
 
 //get day
 day = d.day();

 //get key for month
 for (int i = 0; i < monthKey.size(); ++i)
 {
  if (monthKey[i].monthDay == static_cast<int>(d.month()))
   month = monthKey[i].key;
 }

 //get last two digits of year
 year = d.year() % 100;
 if (month == 11 || month == 12)
  year -= 1;

 //get the century - first 2 digits of year
 string y = to_string(d.year());
 char y1 = y.at(0);
 char y2 = y.at(1);
 string y3;
 y3 += y1;
 y3 += y2;
 century = stoi(y3);

 int f = d.day() + ((13 * month - 1) / 5) + year + (year / 4) + (century / 4) - (2 * century);
 day = f % 7;

 //get which day it is
 for (int i = 0; i < dayKey.size(); ++i)
 {
  if (day == dayKey[i].key)
   day = dayKey[i].monthDay;
 }

 return returnDay(day);
}

//retrieves the next work day
Day next_workday(const Date& d)
{
 //find the current day
 Day current = findDayOfWeek(d);

 //find next day
 Date nextDay(d);
 nextDay.add_day(1);
 Day next = findDayOfWeek(nextDay);
 while (next == Day::saturday || next == Day::sunday)
 {
  nextDay.add_day(1);
  next = findDayOfWeek(nextDay);
 }

 return next;
}

//returns how many days since the start of the year
int howManyDaysSince(const Date& d)
{
 int day = d.day();
 int month = static_cast<int>(d.month());
 cout << "Month: " << month << endl;
 int year = d.year();
 vector<int> daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 vector<int> daysInMonthLeap = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

 int result = 0;
 if (leapyear(year))
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonthLeap[i];
  result += day;
 }
 else
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonth[i];
  result += day;
 }

 return result;
}

//finds out what number week of the year it is
int week_of_the_year(const Date& d)
{
 int daysSince1st = howManyDaysSince(d); //how many days have passed since jan 1st of given year?
 cout << "Days since 1st: " << daysSince1st << endl;
 int dow;     //day of week. sun = 0, mon = 1 etc..
 int dowJan1;     //day of week on jan 1st

 //find out day of week for current day
 Day day = findDayOfWeek(d);
 if (day == Day::sunday)
  dow = 0;
 else
  dow = static_cast<int>(day);

 //find out day of week for jan 1st of given year
 day = findDayOfWeek(Date(d.year(), Month::jan, 1));
 if (day == Day::sunday)
  dowJan1 = 0;
 else
  dowJan1 = static_cast<int>(day);

 //find week number
 int weekNum = ((daysSince1st + 6) / 7);
 if (dow < dowJan1)
  ++weekNum;
 return weekNum;
}

//compares dates
bool operator==(const Date& a, const Date& b)
{
 return a.year() == b.year()
  && a.month() == b.month()
  && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b)
{
 return !(a == b);
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

//write into Date
istream& operator>>(istream& is, Date& dd)
{
 int y, m, d;
 char ch1, ch2, ch3, ch4;
 is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
 if (!is)
  return is;
 if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
 {
  //format error
  is.clear(ios_base::failbit);  //set the failbit
  return is;
 }

 dd = Date(y, Month(m), d);  //update dd
 return is;
}

//finds how many days have passed between two given dates
long int daysSince(const Date& date1, const Date& date2)
{
 long int daysSince = 0;
 Date tempDate = date1;

 //get how many years have passed between two dates
 int years = date2.year() - date1.year();

 //add number of days to daysSince for every year
 for (int i = years; i >= 1; --i)
 {
  //if leap year, add 366 days
  if (leapyear(tempDate.year()))
  {
   daysSince += 366;
  }
  //if not leap year only add 365
  else
   daysSince += 365;

  //increase tempDate so correct days are added
  tempDate.add_year(1);
 }

 //when we get to current year, calculate remaining days & add to total
 daysSince += howManyDaysSince(date2);

 return daysSince;
}

main.cpp
//main.cpp

#include "std_lib_facilities.h"
#include "dateclass.h"

int main()
{
 //intialise dates
 Date today(2018, Month::sep, 24);
 Date zero(1970, Month::jan, 1);

 //print how many days between 2 given dates, earliest date first
 cout << "Days since Jan 1st 1970: " << daysSince(zero, today) << endl;

 keep_window_open();

 return 0;
}


Ohhh kay. It's been a while. I didn't realise I had completely forgotten about this book over summer...whoops. Anyway, I'm going to try and do an exercise a night from now on. 

First things first, I realised that my implementation of leapyear() was completely wrong. I've now changed it and it's working properly now.

Now for the exercise, I didn't want to change the implementation of Date so instead I created a new function which takes two dates and works out how many days have passed since then. This new function is called daysSince(Date, Date). It takes two dates (no error checking just to keep code size down, so please put the earliest date first), then works out how many years are between the two. It then has a variable called daysSince which starts at 0. For every year either 365 or 366 (for leap years, checked using leapyear()) is added to it until we get to the current year. It then works out how many days since the start of the year using howManyDaysSince(Date) and adds that to the running total to get how many days between the two dates.

There are many sites on the internet which can work this out for you, however, they all seem to be out by a couple of days or even a month. I strongly believe my version is correct, if not, it is only out by one day. That I'm sure of. 

Sunday, 24 June 2018

Blog Update// Current State of Affairs

Well, the second year of my degree is officially over. I can't believe it's been two years already. It feels like yesterday when I decided out of the blue to become a programmer and start learning C++. I now have a foundation degree in Software Engineering & Game Development, but I'm doing the one year top-up to achieve a Bachelors degree. Even though I have the entire summer ahead of me, I've already started work on my dissertation as I've chosen something rather ambitious.

The past two years have been something of an eye opener for me. I initially chose the course because on the second year it included a module on modding games engines and for me this sounded like the perfect job. It made me think of game mods like the Skyrim Thomas the Tank Engine or various GTA5 mods (like this hilarious one showcased by Funhaus, I've watched this a thousand times and it never gets old). However, when I eventually got to this module after a year and half, I absolutely detested it. We had to use Unreal Engine 4 and create a game within a team of 5. I can now say with absolute certainty; that I detest Unreal Engine with a burning passion. I also don't particularly like 3D modelling or creating game assets which is half the point of being a games developer. I find these aspects to be tedious and boring. 

My problem I suppose is that the first language I learnt was C++ but in a C-style. Principles & Practice by Bjarne Stroustrup is an excellent book for learning C++, however, by learning it in a C-Style, I have come to hate object orientated programming and much prefer the old procedural style that had to be used with older languages. My favourite module of the second year was 3D games programming in which I used DirectX9 and C++ to create a simple StarFox (1992) clone. My tutor was exasperated with me because I created my own procedural engine instead of utilising his fully object-orientated engine. He likes to remind me that having my engine code and game logic so firmly tied to each other is not a good thing. I tend to counter with the fact that no-one but me should be using my engine so it shouldn't really matter. However, if I plan to get a job with a games company, my style of programming will clearly become a problem. 

So, all of these experiences have forced me re-evaluate exactly what it is I want to achieve from programming. I love writing code, I honestly cannot see myself doing anything else now that I've started, however, I'm not sure if I can see myself becoming a games developer (apart from in my spare time). That is why I have decided to go on after my degree and complete a master's degree in computer science to broaden my programming field.

For my dissertation, I've chosen to create a games engine for the Super Nintendo utilising the SuperFX chip, as I find it extremely fascinating. I've already started learning Assembly x86 and have found it to be a more enjoyable language than C++ because it has to be done procedurally. I actually really love it. After I have grasped the basics, I'll be going on to learn 65c816 ASM, which is the microprocessor used in the Super Nintendo (or well, the Ricoh 5A22 is used in the SNES but it's based on this particular chip). I've been told this is a particularly difficult language and should not be used to learn Assembly off the bat, so I chose x86 instead. 

So far I've found this task to be challenging and rewarding, far more so than choosing a more game-related subject. Therefore, I will continue to post exercises from Principles & Practice as I complete them but it is not going to be my main focus (as I thought it would be). Learning Assembly and finishing a DirectX11 engine in preparation for my final year is going to be my main priority. Thank you to all those, who visit this blog though and who knows where I'll be a couple more years down the line.

Saturday, 16 June 2018

Chapter 9 // Exercise 11 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 11



Design and implement a set of useful helper functions for the Date class with functions such as next_workday() (assume that any day that is not a Saturday or Sunday is a workday) and week_of_year() (assume that week 1 is the week with January 1 in it and that the first day of a week is Sunday).

(the above code on github is different to below as I re-did the book)

dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_

#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

enum class Day
{
 monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
};

//forward declaration
Month returnMonth(int month);
Day returnDay(int day);

//9.8 The Date Class
class Date
{
public:
 Date();
 ~Date();
 Date(int y, Month m, int d);  //check for valid date & initialise

 //non-modifying operations:
 int day() const { return d; };
 Month month() const { return m; }
 int year() const { return y; }

 //modifying operations:
 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

private:
 int y;
 Month m;
 int d;

 bool lastDay = false;
 bool endYear = false;
};

//helper functions
bool is_date(int y, Month m, int d);   //true for valid date
bool leapyear(int y);     //true if y is leap year

Day findDayOfWeek(const Date& d);   //returns the day for the given date
Day next_workday(const Date& d);   //retrieves the next work day
int week_of_the_year(const Date& d);   //returns the number of the week in the year
int howManyDaysSince(const Date& d);   //returns how many days since the start of the year

void printDay(const Day& day);    //prints out day in written form

ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);

bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);

//to help with finding day for any given date
struct stringInt
{
 int monthDay;
 int key;
};

#endif // !_DATECLASS_H_


dateclass.cpp
//dateclass.cpp

#include "dateclass.h"

vector<stringInt> monthKey
{
 { 3, 1 },{ 4, 2 },{ 5, 3 },{ 6, 4 },{ 7, 5 },{ 8, 6 },{ 9, 7 },{ 10, 8 },{ 11, 9 },{ 12, 10 },{ 1, 11 },{ 2, 12 }
};
vector<stringInt> dayKey
{
 { 7, 0 },{ 1, 1 },{ 2, 2 },{ 3, 3 },{ 4, 4 },{ 5, 5 },{ 6, 6 }
};

//default Date Constructor
Date::Date() 
{
 y = 2001;
 m = Month::jan;
 d = 1;
}

//deconstructor
Date::~Date() {}

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (!is_date(y, m, d))
  cout << "Error, invalid date." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }

 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//switch to return correct day
Day returnDay(int day)
{
 switch (day)
 {
 case 7:
  return Day::sunday;
  break;
 case 1:
  return Day::monday;
  break;
 case 2:
  return Day::tuesday;
  break;
 case 3:
  return Day::wednesday;
  break;
 case 4:
  return Day::thursday;
  break;
 case 5:
  return Day::friday;
  break;
 case 6:
  return Day::saturday;
  break;
 default:
  cout << "Bad day" << endl;
 }
}

void printDay(const Day& day)
{
 switch (day)
 {
 case Day::sunday:
  cout << "Sunday\n";
  break;
 case Day::monday:
  cout << "Monday\n";
  break;
 case Day::tuesday:
  cout << "Tuesday\n";
  break;
 case Day::wednesday:
  cout << "Wednesday\n";
  break;
 case Day::thursday:
  cout << "Thursday\n";
  break;
 case Day::friday:
  cout << "Friday\n";
  break;
 case Day::saturday:
  cout << "Saturday\n";
  break;
 default:
  cout << "Bad day" << endl;
 }
}

//true for valid date
bool is_date(int y, Month m, int d)
{
 //check that y is valid
 if (y < 1900 || y > 2018)
 {
  cout << "Error, invalid year." << endl;
  return false;
 }

 //check that m is valid
 if (m < Month::jan || m > Month::dec)
  return false;

 //check that d is valid
 if (d <= 0)
  return false; //d must be positive

 int daysInMonth = 31;  //most months have 31 days

 switch (m)
 {
  case Month::feb:
   //if leapyear, make it 29, if not make it 28
   daysInMonth = (leapyear(y)) ? 29 : 28;
   break;
  case Month::apr: case Month::jun: case Month::sep: case Month::nov:
   daysInMonth = 30; //the rest have 30 days
   break;
 }

 if (daysInMonth < d)
  return false;

 return true;
}

//checks for leap year (gregorian calender)
bool leapyear(int y)
{
 //if divisible by 4 but not 100 - then leap year
 if (y % 4 == 0 && y % 100 != 0)
  return true;
 //if divisible by 4, 100 and 400 - then leap year
 if (y % 4 == 0 && y % 100 == 0 && y % 400 == 0)
  return true;

 else
  return false;
}

//find day of the week for any year using Zeller's Rule
Day findDayOfWeek(const Date& d)
{
 int day, month, year, century;
 
 //get day
 day = d.day();

 //get key for month
 for (int i = 0; i < monthKey.size(); ++i)
 {
  if (monthKey[i].monthDay == static_cast<int>(d.month()))
   month = monthKey[i].key;
 }

 //get last two digits of year
 year = d.year() % 100;
 if (month == 11 || month == 12)
  year -= 1;

 //get the century - first 2 digits of year
 string y = to_string(d.year());
 char y1 = y.at(0);
 char y2 = y.at(1);
 string y3;
 y3 += y1;
 y3 += y2;
 century = stoi(y3);

 int f = d.day() + ((13 * month - 1) / 5) + year + (year / 4) + (century / 4) - (2 * century);
 day = f % 7;

 //get which day it is
 for (int i = 0; i < dayKey.size(); ++i)
 {
  if (day == dayKey[i].key)
   day = dayKey[i].monthDay;
 }

 return returnDay(day);
}

//retrieves the next work day
Day next_workday(const Date& d)
{
 //find the current day
 Day current = findDayOfWeek(d);

 //find next day
 Date nextDay(d);
 nextDay.add_day(1);
 Day next = findDayOfWeek(nextDay);
 while (next == Day::saturday || next == Day::sunday)
 {
  nextDay.add_day(1);
  next = findDayOfWeek(nextDay);
 }

 return next;
}

//returns how many days since the start of the year
int howManyDaysSince(const Date& d)
{
 int day = d.day();
 int month = static_cast<int>(d.month());
 cout << "Month: " << month << endl;
 int year = d.year();
 vector<int> daysInMonth = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 vector<int> daysInMonthLeap = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

 int result = 0;
 if (leapyear(year))
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonthLeap[i];
  result += day;
 }
 else
 {
  for (int i = 0; i < month - 1; ++i)
   result += daysInMonth[i];
  result += day;
 }

 return result;
}

//finds out what number week of the year it is
int week_of_the_year(const Date& d)
{
 int daysSince1st = howManyDaysSince(d); //how many days have passed since jan 1st of given year?
 cout << "Days since 1st: " << daysSince1st << endl;
 int dow;     //day of week. sun = 0, mon = 1 etc..
 int dowJan1;     //day of week on jan 1st

 //find out day of week for current day
 Day day = findDayOfWeek(d);
 if (day == Day::sunday)
  dow = 0;
 else
  dow = static_cast<int>(day);

 //find out day of week for jan 1st of given year
 day = findDayOfWeek(Date(d.year(), Month::jan, 1));
 if (day == Day::sunday)
  dowJan1 = 0;
 else
  dowJan1 = static_cast<int>(day);

 //find week number
 int weekNum = ((daysSince1st + 6) / 7);
 if (dow < dowJan1)
  ++weekNum;
 return weekNum;
}

//compares dates
bool operator==(const Date& a, const Date& b)
{
 return a.year() == b.year()
  && a.month() == b.month()
  && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b)
{
 return !(a == b);
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

//write into Date
istream& operator>>(istream& is, Date& dd)
{
 int y, m, d;
 char ch1, ch2, ch3, ch4;
 is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
 if (!is)
  return is;
 if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
 {
  //format error
  is.clear(ios_base::failbit);  //set the failbit
  return is;
 }

 dd = Date(y, Month(m), d);  //update dd
 return is;
}

main.cpp
//main.cpp

#include "std_lib_facilities.h"
#include "dateclass.h"

int main()
{
 //intialise dates
 Date today(2018, Month::jun, 14);
 Date tomorrow(today);

 //increase day by one
 tomorrow.add_day(1);
 tomorrow.add_month(2);
 tomorrow.add_year(2);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 //prints todays day
 cout << "Today is a: ";
 printDay(findDayOfWeek(today));

 //print next working day
 cout << "The next working day is: ";
 printDay(next_workday(today));

 //print week number
 cout << "The week number is: " << week_of_the_year(today) << endl;

 keep_window_open();

 return 0;
}


This took much longer than I expected, mainly because I honestly (and naively) expected to be implementing just two functions. I ended up creating quite a few more than that. 

First up, in order to find the next work day, the day of the week had to be figured out. We enter a number as the day, how does the date class know what day it is? 

I went on a rabbit hole trying to figure this out. I eventually ended up using a formula called Zeller's Rule:
http://mathforum.org/dr.math/faq/faq.calendar.html
This had a few problems though as I needed to separate the year into the first and last two digits as well as assign keys to months and days. The keys were assigned by creating a new struct which allowed for two ints. Then the month-key pairs and day-key pairs were inputted manually. The function findDayOfWeek() returns an int, however I created another function which converts the day int into the Day enum value and returns that.

Just when I thought the hard part was over, he then wanted us to find what week number of the year it was. There are many different ways of doing this online however I eventually went with a function that figures out how many days have passed since the 1st of Jan of the given year and then a formula adapted from here:
https://stackoverflow.com/questions/274861/how-do-i-calculate-the-week-number-given-a-date

Getting the howManyDaysSince() to work properly was a bit of headache as there isn't really much out there from a C++ perspective. There was an excellent yet crazy answer from a commenter called Mmars from this site:
https://www.epochconverter.com/daynumbers
however I found this to be ridiculous to look at or even try and read. I prefer my brute force solution. I did go a bit further though than Bjarne's suggestion of assuming that the 1st of jan was a sunday. 

I've purposefully left that function to take any date in as I may change it in the future to calculate how many days since any given date. However, it will need some tweaking.

Friday, 15 June 2018

Chapter 9 // Exercise 10 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 10



Implement leapyear() from section 9.8.

dateclass.h
//dateclass.h
#ifndef _DATECLASS_H_
#define _DATECLASS_H_

#include "std_lib_facilities.h"

enum class Month
{
 jan = 1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec
};

//forward declaration
Month returnMonth(int month);

//9.8 The Date Class
class Date
{
public:
 Date();
 ~Date();
 Date(int y, Month m, int d);  //check for valid date & initialise

          //non-modifying operations:
 int day() const { return d; };
 Month month() const { return m; }
 int year() const { return y; }

 //modifying operations:
 void add_day(int n);
 void add_month(int n);
 void add_year(int n);

private:
 int y;
 Month m;
 int d;

 bool lastDay = false;
 bool endYear = false;
};

//helper functions
bool is_date(int y, Month m, int d);  //true for valid date
bool leapyear(int y);    //true if y is leap year

ostream& operator<<(ostream& os, Date& d);
istream& operator>>(istream& is, Date& dd);

bool operator==(const Date& a, const Date& b);
bool operator!=(const Date& a, const Date& b);

#endif // !_DATECLASS_H_

dateclass.cpp
//dateclass.cpp

#include "dateclass.h"

//default Date Constructor
Date::Date() 
{
 y = 2001;
 m = Month::jan;
 d = 1;
}

//deconstructor
Date::~Date() {}

//Date Constructor - initialise the day
Date::Date(int y, Month m, int d)
{
 //check that y m d is a valid date
 if (!is_date(y, m, d))
  cout << "Error, invalid date." << endl;
 else
 {
  //if date is valid, initalise the date
  Date::y = y;
  Date::m = m;
  Date::d = d;
 }

 return;
}

//increment date by n days
void Date::add_day(int n)
{
 //if day goes above 31, increase month, set day to 1
 //if month goes above 12, increase year, set month to 1
 for (int i = 0; i < n; ++i)
 {
  //wrap days
  if (Date::d == 31)
   lastDay = true;
  Date::d = (Date::d == 31) ? 1 : ++Date::d;  //if day is equal to 31, make it 1, otherwise ++

  if (lastDay)
  {
   //wrap month
   lastDay = false;
   int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1); //if month is equal to 12, make it 1, otherwise ++
   Date::m = returnMonth(mon);
   if (static_cast<int>(Date::m) == 12)
    endYear = true;

   if (endYear)
   {
    //just increase year by one
    endYear = false;
    ++Date::y;
   }

  }

 }
}

void Date::add_month(int n)
{
 for (int i = 0; i < n; ++i)
 {
  //if month is equal to 12, make it 1, otherwise ++
  int mon = (static_cast<int>(Date::m) == 12) ? 1 : (static_cast<int>(Date::m) + 1);
  Date::m = returnMonth(mon);
  if (static_cast<int>(Date::m) == 12)
   endYear = true;

  if (endYear)
  {
   //just increase year by one
   endYear = false;
   ++Date::y;
  }
 }
}

void Date::add_year(int n)
{
 for (int i = 0; i < n; ++i)
  ++Date::y;
}

//switch to return correct type of Month
Month returnMonth(int month)
{
 switch (month)
 {
 case 1:
  return Month::jan;
  break;
 case 2:
  return Month::feb;
  break;
 case 3:
  return Month::mar;
  break;
 case 4:
  return Month::apr;
  break;
 case 5:
  return Month::may;
  break;
 case 6:
  return Month::jun;
  break;
 case 7:
  return Month::jul;
  break;
 case 8:
  return Month::aug;
  break;
 case 9:
  return Month::sep;
  break;
 case 10:
  return Month::oct;
  break;
 case 11:
  return Month::nov;
  break;
 case 12:
  return Month::dec;
  break;
 default:
  cout << "Bad month" << endl;
 }
}

//true for valid date
bool is_date(int y, Month m, int d)
{
 //check that y is valid
 if (y < 1900 || y > 2018)
 {
  cout << "Error, invalid year." << endl;
  return false;
 }

 //check that m is valid
 if (m < Month::jan || m > Month::dec)
  return false;

 //check that d is valid
 if (d <= 0)
  return false; //d must be positive

 int daysInMonth = 31;  //most months have 31 days

 switch (m)
 {
  case Month::feb:
   //if leapyear, make it 29, if not make it 28
   daysInMonth = (leapyear(y)) ? 29 : 28;
   break;
  case Month::apr: case Month::jun: case Month::sep: case Month::nov:
   daysInMonth = 30; //the rest have 30 days
   break;
 }

 if (daysInMonth < d)
  return false;

 return true;
}

//checks for leap year (gregorian calender)
bool leapyear(int y)
{
 //if divisible by 4 but not 100 - then leap year
 if (y % 4 == 0 && y % 100 != 0)
  return true;
 //if divisible by 4, 100 and 400 - then leap year
 if (y % 4 == 0 && y % 100 == 0 && y % 400 == 0)
  return true;

 else
  return false;
}

//compares dates
bool operator==(const Date& a, const Date& b)
{
 return a.year() == b.year()
  && a.month() == b.month()
  && a.day() == b.day();
}

bool operator!=(const Date& a, const Date& b)
{
 return !(a == b);
}

//print to screen
ostream& operator<<(ostream& os, Date& d)
{
 return os << d.day() << ", " << static_cast<int>(d.month()) << ", " << d.year() << endl;
}

//write into Date
istream& operator>>(istream& is, Date& dd)
{
 int y, m, d;
 char ch1, ch2, ch3, ch4;
 is >> ch1 >> y >> ch2 >> m >> ch3 >> d >> ch4;
 if (!is)
  return is;
 if (ch1 != '(' || ch2 != ',' || ch3 != ',' || ch4 != ')')
 {
  //format error
  is.clear(ios_base::failbit);  //set the failbit
  return is;
 }

 dd = Date(y, Month(m), d);  //update dd
 return is;
}

main.cpp
//main.cpp

#include "std_lib_facilities.h"
#include "dateclass.h"

int main()
{
 //intialise dates
 Date today(2016, Month::feb, 29);
 Date tomorrow(today);

 //increase day by one
 tomorrow.add_day(1);
 tomorrow.add_month(2);
 tomorrow.add_year(2);

 //print out results
 cout << "Today: " << today << endl;
 cout << "Tomorrow: " << tomorrow << endl;

 keep_window_open();

 return 0;
}


At first I wasn't sure how to go about this. However after googling "what determines a leap year" there is some very handy psuedocode on wikipedia to determine whether a year is a leap year or not.

https://en.wikipedia.org/wiki/Leap_year

Turns out a leap year can be determined if the year is divisible by 4 or 100. However centurial years must be divisible by 400 to be a leap year.

Please be aware in this exercise though that the add_day() function still only wraps at 31 days as a later exercise instructs us to create this function so I haven't fixed it yet. 

Thursday, 14 June 2018

Chapter 9 // Exercise 9 - Principles & Practice Using C++

In this exercise I am using Visual Studio Community 2017 and the header file "std_lib_facilities.h" which can be found here:

http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


Chapter 9 // Exercise 9



Create a Library class. Include vectors of Books and Patrons. Include a struct called Transaction. Have it include a Book, a Patron, and a Date from the chapter. Make a vector of Transactions. Create functions to add books to the library, add patrons to the library, and check out books. Whenever a user checks out a book, have the library make sure that both the user and the book are in the library. if they aren't, report an error. Then check to make sure the the user owes no fees. if the user does, report and error. If not, create a Transaction, and place it in the vector of Transactions. Also write a function that will return a vector that contains the names of all the Patrons who owe fees.

Link to Project:
Exercise 9 Project


This exercise has been provided as the full project as there are several different files included. It has been separated into the following classes:

  • Book Class
  • Date Class (based off the last drill in exercise 9)
  • Patron Class
  • Library Class
  • Main
The Book, Date and Patron classes can only be accessed from within the Library class. The library class is created in main() in which books can be checked out by passing the ISBN number of the book and the card number of the patron. 

The error checking isn't great but it works for the scenario he has given us. I also refrained from adding extra features that he hadn't explicitly laid out in the exercise. 

You can create a library class in main() and from there 3 functions are available: checkOutBook(isbn, cardNum), printFeesOwed() and printLibrary().

checkOutBook(isbn, cardNum) takes in an isbn in the format n-n-n-x and a set of numbers and checks to see if these correspond with books and patrons added to the library. If they do and the patron has no fees it creates a transaction. If they don't match or the patron has a fee, no transactions are created and an error message is displayed.

New books and patrons can be added to the library, however currently they must be hard-coded in. Although it would be easy to create a new function in library which takes in the required fields and creates a new book. The same can be done with patrons.

I tried not to go to crazy on this, although I kind of wanted to.