Problem 19 "Counting Sundays"

You are given the following information, but you may prefer to do some research for yourself.

  • 1 Jan 1900 was a Monday.
  • Thirty days has September,
    April, June and November.
    All the rest have thirty-one,
    Saving February alone,
    Which has twenty-eight, rain or shine.
    And on leap years, twenty-nine.
  • A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.

How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

問 19 「日曜日の数え上げ」

次の情報が与えられている.

  • 1900年1月1日は月曜日である.

  • 9月, 4月, 6月, 11月は30日まであり, 2月を除く他の月は31日まである.

  • 2月は28日まであるが, うるう年のときは29日である.

  • うるう年は西暦が4で割り切れる年に起こる. しかし, 西暦が400で割り切れず100で割り切れる年はうるう年でない.

20世紀(1901年1月1日から2000年12月31日)中に月の初めが日曜日になるのは何回あるか?


https://drive.google.com/file/d/1nu4YU5BIEr_aZi8Ne0QbwkFOYDg6l6Fd/view

fn length_of_february(year: u16) -> u8 {
    if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 {
        29
    } else {
        28
    }
}

struct FirstDayOfMonth {
    year: u16,
    month: u8,
    day_count: u64,
    sunday_count: u32,
}

impl FirstDayOfMonth {
    fn new() -> Self {
        Self {
            year: 1900,
            month: 1,
            day_count: 0,
            sunday_count: 0,
        }
    }
    fn is_sunday(&self) -> bool {
        self.day_count % 7 == 6
    }
    fn next_month(&mut self) {
        self.day_count += match self.month {
            2 => length_of_february(self.year) as u64,
            4 | 6 | 9 | 11 => 30,
            _ => 31,
        };
        if self.month == 12 {
            self.year += 1;
            self.month = 1;
        } else {
            self.month += 1;
        }
        if self.year != 1900 && self.is_sunday() {
            self.sunday_count += 1;
        }
    }
}

fn main() {
    let mut cal = FirstDayOfMonth::new();
    while !(cal.year == 2000 && cal.month == 12) {
        cal.next_month();
    }
    let sum = cal.sunday_count;

    println!("{}", sum);
    assert_eq!(sum, 171);
}

#[derive(PartialEq)]
enum Weekday {
    Mon,
    Tue,
    Wed,
    Thu,
    Fri,
    Sat,
    Sun,
}

fn zeller_congruence(mut y: u32, mut m: u32, d: u32) -> Weekday {
    if m == 1 || m == 2 {
        m += 12;
        y -= 1;
    }
    let yd = y / 100;
    let ym = y % 100;
    match (d + (26 * (m + 1)) / 10 + ym + ym / 4 + yd / 4 + 5 * yd) % 7 {
        0 => Weekday::Sat,
        1 => Weekday::Sun,
        2 => Weekday::Mon,
        3 => Weekday::Tue,
        4 => Weekday::Wed,
        5 => Weekday::Thu,
        6 => Weekday::Fri,
        _ => panic!(),
    }
}

fn main() {
    let mut sum = 0u32;
    for y in 1901u32..=2000 {
        for m in 1u32..=12 {
            let weekday = zeller_congruence(y, m, 1);
            if weekday == Weekday::Sun {
                sum += 1;
            }
        }
    }

    println!("{}", sum);
    assert_eq!(sum, 171);
}