Skip to content

Instantly share code, notes, and snippets.

@if1live

if1live/find_13_friday.cpp

Last active Aug 29, 2015
Embed
What would you like to do?
13일의 금요일 with C++ Template Meta Programming
/**
* https://twitter.com/Code_60/status/577062012250796032
* 오늘의 주제는 "13일의 금요일" 입니다.
* Input : 년 수
* Output : 해당 년도의 13일의 금요일이 포함된 월
* ex) input : 2015 output : 3
*/
#include <cstdio>
#include <vector>
#include <cassert>
enum {
DAY_MON = 0,
DAY_TUE,
DAY_WED,
DAY_THU,
DAY_FRI,
DAY_SAT,
DAY_SUN
};
template<int Y>
struct LeapYear {
enum {
value = ((Y % 4 == 0) && (Y % 100 != 0)) || (Y % 400 == 0),
total = value ? 366 : 365
};
};
// 서력 기원 연수가 4로 나누어 떨어지는 해는 윤년으로 한다.(2004년, 2008년, 2012년, 2016년…)
static_assert(LeapYear<2004>::value == true, "");
static_assert(LeapYear<2008>::value == true, "");
static_assert(LeapYear<2012>::value == true, "");
static_assert(LeapYear<2016>::value == true, "");
// 이 중에서 100으로 나누어 떨어지는 해는 평년으로 한다.(1900년, 2100년, 2200년, 2300년…)
static_assert(LeapYear<1900>::value == false, "");
static_assert(LeapYear<2100>::value == false, "");
static_assert(LeapYear<2200>::value == false, "");
static_assert(LeapYear<2300>::value == false, "");
// 그중에 400으로 나누어 떨어지는 해는 윤년으로 둔다.(1600년, 2000년, 2400년 …)
static_assert(LeapYear<1600>::value == true, "");
static_assert(LeapYear<2000>::value == true, "");
static_assert(LeapYear<2400>::value == true, "");
template<int idx, int I, int... Remainder>
struct ElementAt {
enum {
value = ElementAt<idx - 1, Remainder...>::value
};
};
template<int I, int... Remainder>
struct ElementAt <0, I, Remainder...> {
enum {
value = I
};
};
static_assert(ElementAt<0, 1, 2, 4>::value == 1, ""); //[1, 2, 4][0]
static_assert(ElementAt<1, 1, 2, 4>::value == 2, ""); //[1, 2, 4][1]
static_assert(ElementAt<2, 1, 2, 4>::value == 4, ""); //[1, 2, 4][2]
template<int Month>
struct NormalYearDayCount {
enum {
value = ElementAt<Month, 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31>::value
};
};
template<int Month>
struct LeapYearDayCount {
enum {
value = ElementAt<Month, 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31>::value
};
};
static_assert(NormalYearDayCount<2>::value == 28, "");
static_assert(LeapYearDayCount<2>::value == 29, "");
template<int Year, int Month>
struct YearDayCount {
enum {
value = LeapYear<Year>::value ? LeapYearDayCount<Month>::value : NormalYearDayCount<Month>::value
};
};
static_assert(YearDayCount<2015, 2>::value == 28, "");
static_assert(YearDayCount<2016, 2>::value == 29, "");
template<int Year, int Month, int Day>
struct DayCount {
enum {
value = DayCount<Year, Month, 1>::value + Day - 1
};
};
template<int Year, int Month>
struct DayCount < Year, Month, 1 > {
enum {
value = DayCount<Year, Month - 1, 1>::value + YearDayCount<Year, Month - 1>::value
};
};
template<int Year>
struct DayCount <Year, 1, 1> {
enum {
value = 0
};
};
static_assert(DayCount<2015, 1, 1>::value == 0, "");
static_assert(DayCount<2015, 1, 11>::value == 10, "");
static_assert(DayCount<2015, 2, 1>::value == 31, "");
static_assert(DayCount<2015, 3, 1>::value == 31 + 28, "");
static_assert(DayCount<2015, 4, 1>::value == 31 + 28 + 31, "");
template<int Year>
struct YearTotalDay {
enum {
value = YearTotalDay<Year - 1>::value + LeapYear<Year - 1>::total
};
};
template<>
struct YearTotalDay <2000> {
// 2000/01/01 = SAT
enum {
value = DAY_SAT
};
};
static_assert(YearTotalDay<2000>::value == DAY_SAT, "");
static_assert(YearTotalDay<2001>::value % 7 == DAY_MON, "");
static_assert(YearTotalDay<2002>::value % 7 == DAY_TUE, "");
template<int Y, int M, int D>
struct Weekday {
enum {
start = YearTotalDay<Y>::value % 7,
month_start = (DayCount<Y, M, 1>::value + start) % 7,
value = (DayCount<Y, M, D>::value + start) % 7
};
};
static_assert(Weekday<2001, 1, 1>::value == DAY_MON, "");
static_assert(Weekday<2002, 1, 1>::value == DAY_TUE, "");
static_assert(Weekday<2003, 1, 1>::value == DAY_WED, "");
static_assert(Weekday<2015, 1, 1>::value == DAY_THU, "");
static_assert(Weekday<2015, 1, 1>::month_start == DAY_THU, "");
static_assert(Weekday<2015, 2, 1>::month_start == DAY_SUN, "");
static_assert(Weekday<2015, 3, 1>::month_start == DAY_SUN, "");
static_assert(Weekday<2015, 1, 13>::value == DAY_TUE, "");
static_assert(Weekday<2015, 2, 13>::value == DAY_FRI, "");
static_assert(Weekday<2015, 3, 13>::value == DAY_FRI, "");
template<int v>
struct Int2Type {
enum { value = v };
};
template<int Year, int Month>
struct EvilYear {
typedef EvilYear<Year, Month + 1> Next;
enum {
Weekday = Weekday<2015, Month, 13>::value,
value = Weekday == DAY_FRI
};
static void run(std::vector<int> &result) {
run(result, Int2Type<value>());
}
static void run(std::vector<int> &result, Int2Type<true>) {
result.push_back(Month);
Next::run(result);
}
static void run(std::vector<int> &result, Int2Type<false>) {
Next::run(result);
}
};
template<int Year>
struct EvilYear<Year, 12> {
enum {
Month = 12,
Weekday = Weekday<2015, Month, 13>::value,
value = Weekday == DAY_FRI
};
static void run(std::vector<int> &result) {
run(result, Int2Type<value>());
}
static void run(std::vector<int> &result, Int2Type<true>) {
result.push_back(Month);
}
static void run(std::vector<int> &result, Int2Type<false>) {
}
};
template<int Year>
struct EvilYearRunner {
typedef EvilYear<Year, 1> Start;
static void run(std::vector<int> &result) {
Start::run(result);
}
};
int main()
{
const int Year = 2015;
std::vector<int> retval;
EvilYearRunner<Year>::run(retval);
if (Year == 2015) {
assert(retval.size() == 3);
assert(retval[0] == 2); // 2015/02/13
assert(retval[1] == 3); // 2015/03/13
assert(retval[2] == 11); // 2015/11/13
}
for (int month : retval) {
printf("%d ", month);
}
getchar();
return 0;
}
@if1live

This comment has been minimized.

Copy link
Owner Author

@if1live if1live commented Apr 12, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment