Project Euler 19

http://projecteuler.net/index.php?section=problems&id=19


曜日の定義は1900年なのに数えるのは1901年から。何度でも騙される。
1日の曜日を出すiterableを定義する。そして、iterableの長さを得る関数を作った。数えてもいいが、zipを使えば1行で書ける。パフォーマンス的には恐らくそんなに問題ない。

    template<typename T>
    int length(T& g) {
        return fst(last(zip(count<>(1), g)));
    }


itertools.h

#include <iostream>
#include "itertools.h"

using namespace std;
using namespace itertools;

int num_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

bool is_leap_year(int y) {
    return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}

typedef tuple<int,int,int>  triplet;

// Monday is 1
class first_weekday {
    int y, m, w;
    
public:
    first_weekday() : y(1900), m(1), w(1) { }
    triplet next() {
        auto    t = triplet(y, m, w);
        w = next_weekday();
        next_month();
        return t;
    }
    bool exists_next() { return true; }
    int getYear() { return y; }
    
private:
    int next_weekday() {
        if(m == 2)
            return (w + (is_leap_year(y) ? 29 : 28)) % 7;
        else
            return (w + num_days[m-1]) % 7;
    }
    void next_month() {
        if(m == 12) {
            y++;
            m = 1;
        }
        else {
            m++;
        }
    }
};

int main() {
    cout << length(filter([] (triplet x) {
            return get<0>(x) >= 1901 && get<2>(x) == 0; },
            takewhile([] (triplet x) { return get<0>(x) <= 2000; },
            first_weekday()))) << endl;
}