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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
(* RG 91's Table of Precedence (docs/research/rules-register.md §4). Each
branch below is one of the table's 28 entries, checked in the table's own
numeric order -- lower wins, and because occasional entries are true
exceptions to a later, broader one (RG 91 entry 18's Ember days are an
exception carved out of entry 22's Lent ferias; entry 21/26's vigils are
an exception carved out of the generic Class2/Class3 sanctoral-feast
entries that would otherwise also match), checking in table order and
returning on the first match is what makes the exception actually win
without a separate exclusion for every later entry it pre-empts.
Two kinds of evidence decide an entry:
- The temporal cycle's own office (Nativity, a Sunday, a feria, a vigil of
the Lord) is identified structurally, from the context's date/season/
weekday and the day's Easter offset -- never from its slug, which is
just a label. [origin = Temporal] gates every such entry so a sanctoral
candidate that happens to share a date (Immaculate Conception can never
coincide with the movable cycle, but nothing stops a future rite bug
from producing one) cannot be mistaken for the office itself.
- A sanctoral feast's entry (11-13 I class, 14/16/19/20 II class, 23/24
III class) is decided by its [rank] plus, per the brief's structural
insight, its {!Celebration.t}.layer: a celebration whose layer is not
the universal base is an overlay, hence "proper" or "indult" rather
than the universal entry (see precedence_ef.mli). [origin = Sanctoral]
gates these for the same reason: temporal-origin celebrations carry the
literal layer id "temporal" (rite_ef/temporal_ef.ml's [build]), which is
not [universal_layer] either, and would otherwise be misread as
"proper" by the layer test alone.
Vigils (21, 26) are the one shape neither of those two kinds fully
describes on their own: a II/III-class vigil can be temporal-origin (the
Ascension Vigil, produced by temporal_ef today) or sanctoral-origin (a
saint's vigil, not yet loaded by any task), so its entry cannot be gated
on [origin] at all. Nothing in the day's other fields marks "this is a
vigil, not an ordinary office of the same rank" either, so this reads it
off the temporal cycle's own slug convention (a "-vigil" suffix -- see
[named] in temporal_ef.ml) rather than guessing a new one. *)
open Colitur_kernel
(* Not an RG citation -- RG 91 ranks proper and indult feasts, it does not
encode how a computer tells them apart. See precedence_ef.mli. *)
let universal_layer = "ef-universal"
let indult_prefix = "indult:"
let unclassified = max_int
let is_indult layer = String.starts_with ~prefix:indult_prefix layer
let is_universal layer = String.equal layer universal_layer
(* Ember days are identified by the temporal cycle's own slug convention
(rite_ef/temporal_ef.ml's [ember]: "ef-<set>-ember-<day>"), not
re-derived here: the September anchor in particular is one of the more
contested dates in the 1962 calendar (temporal_ef.ml's own comment on
[third_sunday_of_september]), and re-deriving it a second time would only
create a second place for that same uncertainty to drift. Only the
Advent, Lent and September sets are listed: RG 91 entry 18 names exactly
those three; the Whitsun (Pentecost) set is I class and falls inside the
Pentecost octave, entry 10, matched below before this is ever reached. *)
let is_ember_18 slug =
String.starts_with ~prefix:"ef-advent-ember-" slug
|| String.starts_with ~prefix:"ef-lent-ember-" slug
|| String.starts_with ~prefix:"ef-september-ember-" slug
let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedence.candidate) :
int =
let cel = c.Precedence.cel in
let rank = cel.Celebration.rank in
let subject = cel.Celebration.subject in
let layer = cel.Celebration.layer in
let slug = Slug.to_string cel.Celebration.slug in
let is_temporal = c.Precedence.origin = Precedence.Temporal in
let is_vigil = String.ends_with ~suffix:"-vigil" slug in
let date = ctx.Precedence.date in
let season = ctx.Precedence.season in
let weekday = ctx.Precedence.weekday in
let is_sunday = weekday = Date.Sun in
let m = Date.month date and d = Date.day date in
(* Easter offset, the same convention as temporal_ef.ml's [days_between
easter d]: 0 is Easter itself, negative before, positive after. *)
let off = Date.to_rata date - Date.to_rata (Computus.gregorian_easter (Date.year date)) in
let open Vocab_ef in
(* 1: Nativity, Easter Sunday, Pentecost Sunday (I class w/ octave). *)
if is_temporal && rank = Class1 && ((m = 12 && d = 25) || off = 0 || off = 49) then 1
(* 2: Sacred Triduum (Thu-Sat of Holy Week). *)
else if is_temporal && rank = Class1 && off >= -3 && off <= -1 then 2
(* 3: Epiphany, Ascension, Holy Trinity, Corpus Christi, Sacred Heart,
Christ the King. *)
else if is_temporal && rank = Class1
&& ((m = 1 && d = 6) (* Epiphany *)
|| off = 39 (* Ascension *) || off = 56 (* Trinity *)
|| off = 60 (* Corpus Christi *) || off = 68 (* Sacred Heart *)
|| Date.compare date (Temporal_ef.christ_the_king (Date.year date)) = 0)
then 3
(* 4: Immaculate Conception, Assumption BVM. *)
else if (not is_temporal) && rank = Class1 && ((m = 12 && d = 8) || (m = 8 && d = 15)) then 4
(* 5: Vigil & Octave day of the Nativity. *)
else if is_temporal && rank = Class1 && ((m = 12 && d = 24) || (m = 1 && d = 1)) then 5
(* 6: Sundays of Advent, Lent, Passiontide, and Low Sunday. *)
else if is_temporal && rank = Class1 && is_sunday
&& (season = Advent || season = Lent || season = Passiontide || off = 7)
then 6
(* 7: I-class ferias not above -- Ash Wednesday; Mon/Tue/Wed of Holy Week.
Thu-Sat of Holy Week are the Triduum, entry 2 above, not this entry. *)
else if is_temporal && rank = Class1 && (off = -46 || (off >= -6 && off <= -4)) then 7
(* 8: All Souls. *)
else if (not is_temporal) && rank = Class1 && m = 11 && d = 2 then 8
(* 9: Vigil of Pentecost. *)
else if is_temporal && rank = Class1 && off = 48 then 9
(* 10: Days within the Octaves of Easter and Pentecost. *)
else if is_temporal && rank = Class1 && ((off >= 1 && off <= 6) || (off >= 50 && off <= 55))
then 10
(* 11: I-class feasts of the universal Church not above. *)
else if (not is_temporal) && (not is_vigil) && rank = Class1 && is_universal layer then 11
(* 12: Proper I-class feasts. *)
else if (not is_temporal) && (not is_vigil) && rank = Class1 && not (is_indult layer) then 12
(* 13: Indult I-class feasts. By elimination once 11 and 12 have failed:
not the universal layer (11), and marked as an indult overlay (12's
"not indult" test having just failed). *)
else if (not is_temporal) && (not is_vigil) && rank = Class1 then 13
(* 14: Feasts of the Lord, II class. *)
else if (not is_temporal) && (not is_vigil) && rank = Class2 && is_universal layer
&& subject = Subject.Lord
then 14
(* 15: Sundays, II class (every Sunday not already named at 6). *)
else if is_temporal && rank = Class2 && is_sunday then 15
(* 16: II-class feasts of the universal Church, not of the Lord. *)
else if (not is_temporal) && (not is_vigil) && rank = Class2 && is_universal layer then 16
(* 17: Days within the Octave of the Nativity (26-28 Dec are Stephen,
John, the Innocents -- sanctoral, not this entry). *)
else if is_temporal && rank = Class2 && m = 12 && (d = 29 || d = 30 || d = 31) then 17
(* 18: II-class ferias -- Advent 17-23 Dec; Ember days of Advent, Lent,
September. *)
else if is_temporal && rank = Class2
&& ((season = Advent && m = 12 && d >= 17 && d <= 23) || is_ember_18 slug)
then 18
(* 19: Proper II-class feasts. *)
else if (not is_temporal) && (not is_vigil) && rank = Class2 && not (is_indult layer) then 19
(* 20: Indult II-class feasts. By elimination, as at 13. *)
else if (not is_temporal) && (not is_vigil) && rank = Class2 then 20
(* 21: II-class vigils (Ascension, Assumption, John Baptist, Peter & Paul
-- can be temporal- or sanctoral-origin, see the file comment above). *)
else if rank = Class2 && is_vigil then 21
(* 22: Ferias of Lent and Passiontide (Thursday after Ash Wednesday to the
Saturday before Palm Sunday), except the Ember days (18 above). *)
else if is_temporal && rank = Class3 && (season = Lent || season = Passiontide) then 22
(* 23: III-class feasts in particular calendars. Unlike 11/12 and 14/16
above, the universal entry (24) is the HIGHER number here -- RG 91's
own table ranks a particular-calendar III-class feast ahead of a
universal one, the reverse of the I/II-class ordering. Transcribed as
the register states it, not "corrected" into the other classes'
pattern. RG 91 has no indult sub-rank at III class, so every non-base
layer lands here, not split further. *)
else if (not is_temporal) && (not is_vigil) && rank = Class3 && not (is_universal layer) then 23
(* 24: III-class feasts in the universal calendar. *)
else if (not is_temporal) && (not is_vigil) && rank = Class3 then 24
(* 25: Ferias of Advent to 16 Dec, except the Ember days (18 above). *)
else if is_temporal && rank = Class3 && season = Advent then 25
(* 26: III-class vigils (St Lawrence). *)
else if rank = Class3 && is_vigil then 26
(* 27: Office of the BVM on Saturday -- every otherwise-unoccupied IV-class
Saturday, per the historical default that fills it; ordinary Mass
propers still make Rogation Mon/Tue/Wed proper without changing the
Office (RG 88, see temporal_ef.ml's [temporal]), so those never carry
this entry unless they happen to fall on the Saturday itself. Excludes
vigils for the same reason 11-13/14/16/19/20/23/24 do: RG 91 has no
IV-class vigil at all (its own vigil list, register lines 381-384,
stops at III class), so one would be an anomaly, not this entry. *)
else if is_temporal && (not is_vigil) && rank = Class4 && weekday = Date.Sat then 27
(* 28: IV-class ferias -- the unqualified catch-all (temporal_ef.ml's own
comment on [ferial_rank] cites the same primary text, "Feriae IV
classis"). Excludes vigils for the same reason as 27 above: a IV-class
"feria" that is also a vigil is not a feria RG 91 describes. *)
else if (not is_vigil) && rank = Class4 then 28
else unclassified
|