\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{easter}[2026/08/26 Implementation of Meeus algorithms to compute Easter's date and related feasts according to Gregorian and Julian computus.]
%Algorithms source: https://fr.wikipedia.org/wiki/Calcul_de_la_date_de_P%C3%A2ques_selon_la_m%C3%A9thode_de_Meeus

\newif\ife@printdate\e@printdatetrue

\def\e@error#1{\typeout{^^J! ** Package easter error: #1 **^^J}}
\def\Easter{\e@error{please select a computus^^J\space\space before computing Easter-related dates.}}

\RequirePackage{datenumber}

%Defining Easter date counters
\newcounter{EasterYear}
\newcounter{EasterMonth}
\newcounter{EasterDay}

\newcounter{AscensionYear}
\newcounter{AscensionMonth}
\newcounter{AscensionDay}

\newcounter{PentecostYear}
\newcounter{PentecostMonth}
\newcounter{PentecostDay}

\newcounter{TransposedYear}
\newcounter{TransposedMonth}
\newcounter{TransposedDay}

\def\printDate#1{\csname the#1Year\endcsname%
	-\expandafter\two@digits\csname the#1Month\endcsname%
	-\expandafter\two@digits\csname the#1Day\endcsname}

%Compute the Euclidian division of #1 by #2,
%returning both the quotient (#3) and the remainder (#4).
%#3 and #4 are TeX counters.
%#1: dividend (input), #2: divisor (input), #3: quotient (output), #4: remainder (output)
\newcount\dividend
\newcount\divisor
\def\EuclidianDiv#1#2#3#4{%
	\dividend\numexpr#1\relax%
	\divisor\numexpr#2\relax%
%
	\newcount#3#3\dividend%
	\divide#3 by\divisor%
	\newcount#4#4\numexpr\dividend-\divisor*#3\relax}

\def\EasterGregorian{%
	\begingroup\@ifstar%
		{\global\e@printdatefalse\@EasterGregorian}%
		{\global\e@printdatetrue\@EasterGregorian}}

\newcommand{\@EasterGregorian}[1][\year]{%
	\ifnum#1<1583
		\e@error{invalid year #1 for Gregorian comput^^J\space\space(must be >=1583).}
	\else
		\setcounter{EasterYear}{\numexpr#1}%
		\setcounter{AscensionYear}{\theEasterYear}%
		\setcounter{PentecostYear}{\theEasterYear}%
%
		%Butcher-Meeus algorithm
		\EuclidianDiv{\theEasterYear}{19}{\void}{\n}%
		\EuclidianDiv{\theEasterYear}{100}{\c}{\u}%
		\EuclidianDiv{\c}{4}{\s}{\t}%
		\EuclidianDiv{\c+8}{25}{\p}{\void}%
		\EuclidianDiv{\c-\p+1}{3}{\q}{\void}%
		\EuclidianDiv{19*\n+\c-\s-\q+15}{30}{\void}{\e}%
		\EuclidianDiv{\u}{4}{\b}{\d}%
		\EuclidianDiv{2*\t+2*\b-\e-\d+32}{7}{\void}{\L}%
		\EuclidianDiv{\n+11*\e+22*\L}{451}{\h}{\void}%
		\EuclidianDiv{\e+\L-7*\h+114}{31}{\m}{\j}%
		\setcounter{EasterMonth}{\m}%
		\setcounter{EasterDay}{\numexpr\j+1}%
%
		%Ascension and Pentecost dates
		\setcounter{@startyear}{\theEasterYear}%
		\setdatenumber{\theEasterYear}{\theEasterMonth}{\theEasterDay}%
		\addtocounter{datenumber}{39}%
		\setmydatebynumber{\thedatenumber}{AscensionYear}{AscensionMonth}{AscensionDay}%
		\addtocounter{datenumber}{10}%
		\setmydatebynumber{\thedatenumber}{PentecostYear}{PentecostMonth}{PentecostDay}%
%
		\ife@printdate \printDate{Easter}\fi
	\fi\endgroup}

\def\EasterJulian{%
	\begingroup\@ifstar
		{\global\e@printdatefalse\@EasterJulian}%
		{\global\e@printdatetrue\@EasterJulian}}

\newcommand{\@EasterJulian}[1][\year]{%
	\ifnum#1<326
		\e@error{invalid year #1 for Julian comput^^J\space\space(must be >=326).}
	\else
		\setcounter{EasterYear}{\numexpr#1}%
		\setcounter{AscensionYear}{\theEasterYear}%
		\setcounter{PentecostYear}{\theEasterYear}%
%
		%Delambre-Meeus algorithm
		\EuclidianDiv{\theEasterYear}{19}{\void}{\A}%
		\EuclidianDiv{\theEasterYear}{7}{\void}{\B}%
		\EuclidianDiv{\theEasterYear}{4}{\void}{\C}%
		\EuclidianDiv{19*\A+15}{30}{\void}{\D}%
		\EuclidianDiv{2*\C+4*\B-\D+34}{7}{\void}{\E}%
		\EuclidianDiv{\D+\E+114}{31}{\F}{\G}%
		\setcounter{EasterMonth}{\F}%
		\setcounter{EasterDay}{\numexpr\G+1}%
%
		%Ascension and Pentecost dates
		\setcounter{@startyear}{\theEasterYear}%
		\setdatenumber{\theEasterYear}{\theEasterMonth}{\theEasterDay}%
		\addtocounter{datenumber}{39}%
		\setmydatebynumber{\thedatenumber}{AscensionYear}{AscensionMonth}{AscensionDay}%
		\addtocounter{datenumber}{10}%
		\setmydatebynumber{\thedatenumber}{PentecostYear}{PentecostMonth}{PentecostDay}%
%
		\ife@printdate \printDate{Easter}\fi
	\fi\endgroup}

%Computes de offset between calendars such that Julian + offset = Gregorian
%Offset for a single year is valid starting the day following February the 28th in both calendars.
\newcounter{calendaroffset}
\newcount\@tempA
\newcount\@tempB
\def\calendarOffset{\begingroup\@calendarOffset}
\newcommand{\@calendarOffset}[1][\year]{%
	\@tempA\numexpr#1\relax%
	\@tempB\@tempA
%
	\divide\@tempA by 100%
	\divide\@tempB by 400%
%
	\setcounter{calendaroffset}{\numexpr\@tempA-\@tempB-2}%
	\endgroup}

%Transpose dates from a calendar to the other.
%#1: year, #2: month, #3: day
\newcount\offsetYear
\def\toGregorian#1#2#3{%
	\offsetYear\numexpr#1\relax
	\ifnum#2<2
		\advance\offsetYear by -1\relax%
	\else\ifnum#2=2\ifnum#3>28\else
		\advance\offsetYear by -1\relax%
	\fi\fi\fi
%
	\setcounter{@startyear}{#1}%
	\setdatenumber{#1}{#2}{#3}%
	\calendarOffset[\offsetYear]%
	\addtocounter{datenumber}{\thecalendaroffset}%
	\setmydatebynumber{\thedatenumber}{TransposedYear}{TransposedMonth}{TransposedDay}}

\def\toJulian#1#2#3{%
	\offsetYear\numexpr#1\relax
	\ifnum#2<2
		\advance\offsetYear by -1\relax%
	\else\ifnum#2=2\ifnum#3>28\else
		\advance\offsetYear by -1\relax%
	\fi\fi\fi
%
	\setcounter{@startyear}{#1}%
	\setdatenumber{#1}{#2}{#3}%
	\calendarOffset[\offsetYear]%
	\addtocounter{datenumber}{-\thecalendaroffset}%
	\setmydatebynumber{\thedatenumber}{TransposedYear}{TransposedMonth}{TransposedDay}}

%Option processing: computus selection
\def\@gregorian{gregorian}
\def\@julian{julian}
\def\selectComputus#1{%
	\def\@computus{#1}
	\ifx\@computus\@gregorian \let\Easter\EasterGregorian
	\else\ifx\@computus\@julian \let\Easter\EasterJulian
	\fi\fi}

\DeclareOption{gregorian}{\selectComputus{gregorian}}
\DeclareOption{julian}{\selectComputus{julian}}
\ProcessOptions\relax

