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
|
package calendar
import "sort"
// candidate is one celebration competing for a given day: the temporal day, or
// a sanctoral celebration landing on it. The flags come from the temporal
// builder (Task 4) or are empty for sanctoral entries.
type candidate struct {
Cel Celebration
Temporal bool
Privileged bool // band 1-2 temporal day
PrivFeria bool // band 9 privileged feria
Sunday bool // Ordinary/Christmas Sunday (band 6)
Season Season
}
// precedence maps a candidate to its band in the Table of Liturgical Days
// (Universal Norms, 1969) — lower = higher precedence. "Proper" celebrations
// (a non-universal, non-temporal layer) sit one band below their General
// Calendar counterpart.
func precedence(c candidate) int {
proper := c.Cel.Layer != "" && c.Cel.Layer != "universal" && c.Cel.Layer != "temporal"
if c.Temporal {
switch {
case c.Season == Triduum:
return 1
case c.Privileged:
return 2
case c.Sunday: // Ordinary/Christmas Sunday (checked before the solemnity-rank fallback)
return 6
case c.Cel.Rank == RankSolemnity: // Trinity, Corpus Christi, Sacred Heart, Christ the King
return 3
case c.Cel.Rank == RankFeast: // feasts of the Lord (Baptism, Holy Family)
return 5
case c.PrivFeria:
return 9
default:
return 13
}
}
switch c.Cel.Rank {
case RankSolemnity:
if proper {
return 4
}
return 3
case RankFeast:
switch {
case proper:
return 8
case c.Cel.Class == ClassLord: // feasts of the Lord in the General Calendar
return 5
default:
return 7
}
case RankMemorial:
if proper {
return 11
}
return 10
case RankOptional:
return 12
default:
return 13
}
}
// pick returns the highest-precedence candidate as observed and the rest as
// others (commemorations / optional memorials). Ordering is deterministic:
// by precedence band, then by slug, so equal-band collisions resolve the same
// way on every run.
func pick(cands []candidate) (candidate, []candidate) {
sorted := make([]candidate, len(cands))
copy(sorted, cands)
sort.SliceStable(sorted, func(i, j int) bool {
pi, pj := precedence(sorted[i]), precedence(sorted[j])
if pi != pj {
return pi < pj
}
return sorted[i].Cel.Slug < sorted[j].Cel.Slug
})
return sorted[0], sorted[1:]
}
|