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