1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
package calendar
import "sort"
// efRankOrder orders EF (1960) ranks: class-1 highest, then class-2..4,
// commemoration, ferial lowest.
func efRankOrder(r Rank) int {
switch r {
case RankClass1:
return 5
case RankClass2:
return 4
case RankClass3:
return 3
case RankClass4:
return 2
case RankCommemoration:
return 1
default: // ferial / unset
return 0
}
}
// precedenceEF ranks an EF candidate for occurrence (lower = higher precedence):
// 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 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 {
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, 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
}
return p
}
// pickEF returns the observed EF celebration and the commemorations, ordered
// deterministically by precedence then slug.
func pickEF(cands []candidate) (candidate, []candidate) {
sorted := make([]candidate, len(cands))
copy(sorted, cands)
sort.SliceStable(sorted, func(i, j int) bool {
pi, pj := precedenceEF(sorted[i]), precedenceEF(sorted[j])
if pi != pj {
return pi < pj
}
return sorted[i].Cel.Slug < sorted[j].Cel.Slug
})
return sorted[0], sorted[1:]
}
|