#+TITLE: Task 1: Perpetual calendar
#+AUTHOR: JirR02

* Task

A perpetual calendar can be used to determine the weekday (Monday, ..., Sunday) of any given date. You may for example know that the Berlin wall came down on November 9, 1989, but what was the weekday? It was a Thursday. Or what is the weekday of the 1000th anniversary of the Swiss confederation, to be celebrated on August 1, 2291? It will be a Saturday. The task of this exercise is to write a program that outputs the weekday of a given input date.

Your program will read the date from the input. The input is given as three integer values in the following order: Day, month, year. First the program must validate the input. Pay attention to special cases of February (leap years!) also, the date must be greater or equal to the reference date. If a date is invalid output invalid date. If the date is valid, calculate the weekday of this day. This can be done by calculating how many days lie between the date in question and a reference date whose weekday is known. As reference date use /Monday, 1st January 1900/. Finally, output the weekday as one word in English.

**Approach**: The goal of this exercise is to learn the usage of functions. For that, we split the program into the following sub tasks given as function declarations. Your task is to **implement the provided functions** in the calendar.cpp file, so that they perform the action that is specified in their post condition.

**Important: There is a well-known mathematical function to calculate the weekday of a date. Using this function is an incorrect solution as it defeats the purpose of this exercise.**

#+begin_src cpp
// PRE:  a year greater or equal than 1900
// POST: returns whether that year was a leap year
bool is_leap_year(int year);

// PRE:  a year greater or equal than 1900
// POST: returns the number of days in that year
int count_days_in_year(int year);

// PRE:  a month between 1 and 12 and a year greater or equal than 1900
// POST: returns the number of days in the month of that year
int count_days_in_month(int month, int year);

// PRE:  n/a
// POST: returns whether the given values represent a valid date
bool is_valid_date(int day, int month, int year);

// PRE:  the given values represent a valid date
// POST: returns the number of days between January 1, 1900 and this date (excluding this date)
int count_days(int day, int month, int year);

// PRE: the given values represent a (potentially invalid) date
// POST: prints the weekday if the date is valid or "invalid date" otherwise.
//      Everything must be printed in lowercase.
void print_weekday(int day, int month, int year);
#+end_src

To complete the task, you have to provide the definition of the aforementioned functions.

The =calendar.cpp= file contains skeletons of the functions to be implemented. The =main.cpp= file contains testing functions and the main function. In the main function, a menu is printed to select the function that has to be tested.

**Important** the main is not editable. Required functions must be implemented in =calendar.cpp=.

**Additional notes:**

1. For function arguments that do **not** fulfill the precondition, the behavior is undefined.

1. There are a few opportunities here to use switch statements. Use them if they result in better readable code.

1. A leap year is defined as follows: It is an integer multiple of 4, except for years evenly divisible by 100, which are not leap years unless evenly divisible by 400. (Source: [[https://en.wikipedia.org/wiki/Leap_year][Wikipedia]])

* Solution

#+begin_src cpp
#include "calendar.h"
#include <string>

// PRE: a year greater or equal than 1900
// POST: returns whether that year was a leap year
bool is_leap_year(int year) {
  if (year >= 1900) {
    if (year % 4 == 0 && year % 100 != 0)
      return true;
    else if (year % 4 == 0 && year % 400 == 0)
      return true;
  }
  return false;
}

// PRE: a year greater or equal than 1900
// POST: returns the number of days in that year
int count_days_in_year(int year) {
  if (year >= 1900) {
    if (is_leap_year(year) == true)
      return 366;
    return 365;
  }
  return 0;
}

// PRE: a month between 1 and 12 and a year greater or equal than 1900
// POST: returns the number of days in the month of that year
int count_days_in_month(int month, int year) {
  if (year >= 1900 && month > 0 && month <= 12) {
    if (is_leap_year(year) == true) {
      if (month % 2 != 0 && month != 2 && month <= 7)
        return 31;
      else if (month % 2 == 0 && month > 7)
        return 31;
      else if (month == 2)
        return 29;
      else
        return 30;
    } else {
      if (month % 2 != 0 && month != 2 && month <= 7)
        return 31;
      else if (month % 2 == 0 && month > 7)
        return 31;
      else if (month == 2)
        return 28;
      else
        return 30;
    }
  }
  return 0;
}

// PRE:  n/a
// POST: returns whether the given values represent a valid date
bool is_valid_date(int day, int month, int year) {
  if (day > 0 && day <= count_days_in_month(month, year) && month > 0 &&
      month <= 12 && year >= 1900)
    return true;
  return false;
}

// PRE: the given values represent a valid date
// POST: returns the number of days between January 1, 1900 and this date
// (excluding this date)
int count_days(int day, int month, int year) {
  int res_day = 0;
  if (is_valid_date(day, month, year) == true) {
    for (int j = 1900; j < year; ++j) {
      res_day += count_days_in_year(j);
    }
    for (int j = 1; j < month; ++j) {
      res_day += count_days_in_month(j, year);
    }
    res_day += (day - 1);
  }
  return res_day;
}

// PRE: the given values represent a (potentially invalid) date
// POST: prints the weekday if the date is valid or "invalid date" otherwise.
//      Everything must be printed in lowercase.
void print_weekday(int day, int month, int year) {
  if (is_valid_date(day, month, year) == true) {
    std::string days[7] = {"sunday",   "monday", "tuesday", "wednesday",
                           "thursday", "friday", "saturday"};
    std::cout << days[((count_days(day, month, year) + 1) % 7)];
  } else
    std::cout << "invalid date";
}
#+end_src

-----

Made by JirR02 in Switzerland 🇨🇭
