aboutsummaryrefslogtreecommitdiff
path: root/internal/calendar/precedence_ef.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/calendar/precedence_ef.go')
-rw-r--r--internal/calendar/precedence_ef.go86
1 files changed, 72 insertions, 14 deletions
diff --git a/internal/calendar/precedence_ef.go b/internal/calendar/precedence_ef.go
index 4fda626..9f5016f 100644
--- a/internal/calendar/precedence_ef.go
+++ b/internal/calendar/precedence_ef.go
@@ -1,6 +1,9 @@
package calendar
-import "sort"
+import (
+ "sort"
+ "strings"
+)
// efRankOrder orders EF (1960) ranks: class-1 highest, then class-2..4,
// commemoration, ferial lowest.
@@ -22,34 +25,89 @@ func efRankOrder(r Rank) int {
}
// precedenceEF ranks an EF candidate for occurrence (lower = higher precedence):
-// by class first, then a tie-break at equal class (1960 occurrence table).
+// by class first, then a tie-break at equal class (RG 91's Table of
+// Precedence, read entry-by-entry within each class).
//
-// The tie-break is asymmetric by season. An ORDINARY feria (per annum, Advent,
-// Septuagesima) yields to an equal-class feast — the feast is celebrated and the
-// feria commemorated (e.g. St Francis Xavier, Dec 3, on an Advent feria). The
-// ferias of Lent and Passiontide are privileged: they outrank an equal-class
-// feast, which is only commemorated. Sundays and named temporal feasts likewise
-// win their ties. The *2 class spacing means the ±1 tie-break never crosses a
-// class boundary.
+// The tie-break is asymmetric by season AND, at class 2, by whether the
+// temporal day is a Sunday or a privileged FERIA:
+// - III/IV class: an ORDINARY feria (per annum, Advent to 16 Dec,
+// Septuagesima) yields to an equal-class feast (entries 24/25 above the
+// feria) -- the feast is celebrated, the feria commemorated (e.g. St
+// Francis Xavier, Dec 3). The ferias of Lent and Passiontide are
+// privileged (entry 22, ABOVE entry 24): they outrank an equal-class
+// feast, which is only commemorated.
+// - II class: an ordinary SUNDAY wins its tie (entry 15, above entry 16's
+// feasts) -- but a II-class privileged FERIA (the Ember days, the
+// late-Advent 17-23 Dec ferias, entry 18) sits BELOW entry 16 and yields
+// to an equal-class feast, the opposite of a Sunday's own tie. This is
+// the same table shape as III class's Advent/per-annum ferias, just
+// inverted for which II-class temporal days count as "privileged".
+// - I class: Sundays (entry 6) and I-class ferias (entry 7, Ash
+// Wednesday/Holy Week) both sit above entry 11's feasts, so they always
+// win -- there is no I-class "ordinary feria yields" case at all.
+//
+// The *2 class spacing means the ±1 tie-break never crosses a class boundary.
func precedenceEF(c candidate) int {
p := (6 - efRankOrder(c.Cel.Rank)) * 2 // class-1 -> 2, ferial -> 12
if c.Temporal {
- ordinaryFeria := (c.Cel.Rank == RankClass3 || c.Cel.Rank == RankClass4) &&
- c.Season != Lent && c.Season != Passiontide
- if ordinaryFeria {
- p++ // an ordinary feria yields to an equal-class feast
+ yieldsToFeast := false
+ switch c.Cel.Rank {
+ case RankClass3, RankClass4:
+ yieldsToFeast = c.Season != Lent && c.Season != Passiontide
+ case RankClass2:
+ // A II-class Sunday (entry 15) wins; a II-class privileged feria --
+ // Ember days, the late-Advent 17-23 Dec ferias (entry 18) -- yields
+ // to an equal-class feast (entry 16), e.g. St Matthew (21 Sep)
+ // beating the September Ember Wednesday, or St Thomas (21 Dec)
+ // beating an Advent late feria.
+ yieldsToFeast = !c.Sunday
+ }
+ if yieldsToFeast {
+ p++
} else {
- p-- // Sundays, named feasts, and penitential ferias win their ties
+ p-- // Sundays, I-class ferias, and penitential (Lent/Passiontide) ferias win their ties
}
} else if c.Cel.Class == ClassLord && c.Cel.Rank == RankClass2 {
// A II class feast of the Lord takes the place of a II class Sunday it
// falls on (unlike a saint's feast, which is only commemorated). Give it
// the edge over the Sunday's tie-break bonus.
p -= 2
+ } else if c.Cel.Rank == RankClass1 && beatsClass1Sunday(c.Cel.Slug) {
+ // RG 91 entries 4 (Immaculate Conception, Assumption BVM) and 5
+ // (Vigil & Octave day of the Nativity) both sit ABOVE entry 6
+ // (Sundays of Advent/Lent/Passiontide, Low Sunday) -- unlike an
+ // ORDINARY I-class feast (entry 11, e.g. St Joseph, the Precious
+ // Blood), which yields to a I-class Sunday, these win the tie.
+ // Found while verifying defect 4 (Sunday ranks): before that fix,
+ // Advent/Lent Sundays were wrongly II class, so these feasts beat
+ // them outright on base class alone, accidentally right; once
+ // Sundays became I class the tie-break mattered, and without this
+ // branch the Sunday would wrongly win. Two live cases in the fixed
+ // calendar: the Immaculate Conception (8 December) on an Advent
+ // Sunday, and the Vigil of Christmas (24 December) on Advent IV --
+ // the latter is not merely a wrong winner but a WORSE bug without
+ // this branch: transferIfImpededEF has no way to re-place a
+ // transfer that crosses the Dec31/Jan1 boundary (celebrationDate
+ // re-resolves a fixed date using the YEAR OF THE DAY BEING QUERIED,
+ // so a walk landing in January of the following year can never
+ // match the query that produced it), so the Vigil simply vanished
+ // for the year instead of landing on the wrong day. The Assumption
+ // (15 August) never falls in Advent or Lent, so its own share of
+ // this branch is citation-complete but not live.
+ p -= 2
}
return p
}
+// beatsClass1Sunday reports whether slug is one of RG 91's entries 4 or 5 --
+// the Immaculate Conception, the Assumption, or the Vigil of Christmas --
+// all of which sit above entry 6's Sundays in the Table of Precedence.
+func beatsClass1Sunday(slug string) bool {
+ return strings.Contains(slug, "immaculate-conception") ||
+ strings.Contains(slug, "assumption-of-the-blessed-virgin-mary") ||
+ slug == "vigil-of-christmas"
+}
+
// pickEF returns the observed EF celebration and the commemorations, ordered
// deterministically by precedence then slug.
func pickEF(cands []candidate) (candidate, []candidate) {