Computing Easter for a given year is a classic computational problem.
It is also one of the few cases when code seems to just have to live with magic numbers.
Alternative to magic numbers: Decoding Gauss' Easter Algorithm
So I thought today, I'd do something positive and make an Easter bar graph.
C99 Review goal: General coding comments, style, etc.
// easter.h // chux: April 15, 2019 #ifndef EASTER_H #define EASTER_H #define EASTER_EPOCH_YEAR 33 #define EASTER_JULIAN_YEAR EASTER_EPOCH_YEAR #define EASTER_GREGORIAN_EPOCH_YEAR 1582 /* 15 October 1582 */ #define EASTER_JULIAN_PERIOD 532 #define EASTER_GREGORIAN_PERIOD 5700000 typedef struct ymd { int y, m, d; } ymd; ymd Easter_DateJulian(int year); ymd Easter_DateGregorian(int year); ymd Easter_Date(int year); #endif // easter.c /* * Anonymous Gregorian algorithm: Meeus/Jones/Butcher * * Dates of Easter * Astronomical Algorithms 1991 * Jean Meeus * https://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm * * Meeus's Julian algorithm * https://en.wikipedia.org/wiki/Computus#Meeus's_Julian_algorithm */ ymd Easter_DateJulian(int year) { if (year < EASTER_EPOCH_YEAR) { return (ymd) {0, 0, 0}; } int a = year % 4; int b = year % 7; int c = year % 19; int d = (19 * c + 15) % 30; int e = (2 * a + 4 * b - d + 34) % 7; int f = (d + e + 114) / 31; int g = (d + e + 114) % 31; return (ymd) {year, f, g + 1}; } ymd Easter_DateGregorian(int year) { if (year <= EASTER_GREGORIAN_EPOCH_YEAR) { return (ymd) {0, 0, 0}; } int a = year%19; int b = year/100; int c = year%100; int d = b/4; int e = b%4; int f = (b+8)/25; int g = (b-f+1)/3; int h = (19*a + b - d - g + 15)%30; int i = c/4; int k = c%4; int l = (32 + 2*e + 2*i - h - k)%7; int m = (a+11*h + 22*l) / 451; int n = (h + l - 7 *m + 114)/31; int p = (h + l - 7 *m + 114)%31; return (ymd) {year, n, p+1}; } ymd Easter_Date(int year) { return (year > EASTER_GREGORIAN_EPOCH_YEAR) ? Easter_DateGregorian(year) : Easter_DateJulian(year); } Test
// main.c // **Alternate code used as a check** // Find easter on any given year // https://codereview.stackexchange.com/questions/193847/find-easter-on-any-given-year // Decoding Gauss' Easter Algorithm // https://math.stackexchange.com/q/896954/83175 static ymd Easter(int year) { int a = year%19; int b = year/100; int c = (b - (b/4) - ((8*b + 13)/25) + (19*a) + 15)%30; int d = c - (c/28)*(1 - (c/28)*(29/(c + 1))*((21 - a)/11)); int e = d - ((year + (year/4) + d + 2 - b + (b/4))%7); int month = 3 + ((e + 40)/44); int day = e + 28 - (31*(month/4)); return (ymd) {year, month , day}; } #include <assert.h> #include <stdio.h> int main(void) { int count[5][32] = { 0 }; for (int year = EASTER_GREGORIAN_EPOCH_YEAR + 1; year <= EASTER_GREGORIAN_EPOCH_YEAR + EASTER_GREGORIAN_PERIOD; year++) { ymd e1 = Easter_Date(year); ymd e2 = Easter(year); if (e1.d != e2.d) { printf("%5d-%02d-%02d ", e1.y, e1.m, e1.d); printf("%5d-%02d-%02d ", e2.y, e2.m, e2.d); puts(""); } assert(e1.m >= 3 && e1.m <=4); assert(e1.d >= 1 && e1.d <=31); count[e1.m][e1.d]++; } for (int m = 3; m <= 4; m++) { for (int d = 1; d <= 31; d++) { if (count[m][d]) { double permill = round(1000.0*count[m][d]/EASTER_GREGORIAN_PERIOD); printf("%d, %2d, %3.1f%%, %0*d\n", m, d, permill/10, (int) permill, 0); } } } return 0; } Output: Month, Day, Percentage, Graph
3, 22, 0.5%, 00000 3, 23, 1.0%, 0000000000 3, 24, 1.4%, 00000000000000 3, 25, 1.9%, 0000000000000000000 3, 26, 2.3%, 00000000000000000000000 3, 27, 2.9%, 00000000000000000000000000000 3, 28, 3.3%, 000000000000000000000000000000000 3, 29, 3.4%, 0000000000000000000000000000000000 3, 30, 3.3%, 000000000000000000000000000000000 3, 31, 3.3%, 000000000000000000000000000000000 4, 1, 3.4%, 0000000000000000000000000000000000 4, 2, 3.3%, 000000000000000000000000000000000 4, 3, 3.4%, 0000000000000000000000000000000000 4, 4, 3.3%, 000000000000000000000000000000000 4, 5, 3.4%, 0000000000000000000000000000000000 4, 6, 3.3%, 000000000000000000000000000000000 4, 7, 3.3%, 000000000000000000000000000000000 4, 8, 3.4%, 0000000000000000000000000000000000 4, 9, 3.3%, 000000000000000000000000000000000 4, 10, 3.4%, 0000000000000000000000000000000000 4, 11, 3.3%, 000000000000000000000000000000000 4, 12, 3.4%, 0000000000000000000000000000000000 4, 13, 3.3%, 000000000000000000000000000000000 4, 14, 3.3%, 000000000000000000000000000000000 4, 15, 3.4%, 0000000000000000000000000000000000 4, 16, 3.3%, 000000000000000000000000000000000 4, 17, 3.4%, 0000000000000000000000000000000000 4, 18, 3.5%, 00000000000000000000000000000000000 4, 19, 3.9%, 000000000000000000000000000000000000000 4, 20, 3.3%, 000000000000000000000000000000000 <-- 2019 4, 21, 2.9%, 00000000000000000000000000000 4, 22, 2.4%, 000000000000000000000000 4, 23, 1.9%, 0000000000000000000 4, 24, 1.5%, 000000000000000 4, 25, 0.7%, 0000000