aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_ef/precedence_ef.mli
blob: 9fe8789f1bf87b0ee19063d540cc90c646acf83b (plain) (blame)
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
(** RG 91's Table of Precedence for the EF (1962) rite: ranks any candidate
    for a given day by its RG 91 entry number. See
    docs/research/rules-register.md §4, whose 28-entry transcription this
    module follows line by line. *)

open Colitur_kernel

(** The universal (base) sanctoral layer's {!Celebration.t}.layer id. A
    Sanctoral-origin candidate whose layer is anything else is an overlay:
    "proper" (RG 91 entries 12, 19, 23) unless its layer id also carries
    {!indult_prefix} ("indult", entries 13, 20). This id and the prefix are
    colitur's own data-modelling convention, not an RG citation -- RG 91
    prescribes the ranking, not a machine encoding for it. Whichever task
    loads the real EF sanctoral base layer and its overlays must either
    reuse these two constants or this classifier will misfile them. *)
val universal_layer : string

(** See {!universal_layer}. *)
val indult_prefix : string

(** Slug suffix marking a celebration as a vigil (RG 91 entries 21, 26),
    e.g. "ef-ascension-vigil". Also colitur's own convention, not an RG
    citation, exposed for the same reason as {!universal_layer}: only the
    Ascension Vigil exists today (rite_ef/temporal_ef.ml); the Assumption,
    John Baptist, Peter & Paul and Lawrence vigils arrive as sanctoral data
    in a future task, and must use this same suffix or {!band} will band
    them 16/24 (an ordinary feast of the same rank) instead of 21/26. *)
val vigil_suffix : string

(** Slug prefixes marking a celebration as one of RG 91 entry 18's three
    Ember-day sets (Advent, Lent, September -- the Pentecost/Whitsun set is
    I class and matched by entry 10 before this is ever consulted). Also
    colitur's own convention mirroring rite_ef/temporal_ef.ml's own "ef-<set>
    -ember-<day>" slug format, not re-derived from first principles; exposed
    so a rename of that format has somewhere to be caught other than a
    silently-wrong entry 18. *)
val ember_prefixes : string list

(** Returned for a candidate shape RG 91's 28-entry table has no row for --
    e.g. a [Class1] vigil that is not the Nativity or Pentecost (entries 5,
    9 are the only I-class vigils the table names), or a [Class4] candidate
    also marked as a vigil. Deliberately outside 1..28 and larger than any
    real entry, so an unclassified candidate can never win an occurrence
    contest by accident; a caller that sees it back knows the shape needs a
    new rule, not a silently wrong one. *)
val unclassified : int

(** [band ctx c]: RG 91's Table of Precedence. Returns the table's own entry
    number -- I class 1-13, II class 14-21, III class 22-26, IV class 27-28;
    lower wins (see {!Precedence.rules.band}) -- EXCEPT where the table's own
    text states an exception: entry 8 (All Souls, register line 334) reads
    "yields to an occurring Sunday", so on a Sunday this returns a value that
    loses to entry 15 rather than the literal integer 8 (see the comment on
    entry 8 in precedence_ef.ml for the exact value and why). Total over
    every candidate {!Precedence.resolve} or {!Calendar} can construct,
    including shapes the 1962 table itself does not describe (see
    {!unclassified}). *)
val band : Vocab_ef.season Precedence.context -> Vocab_ef.rank Precedence.candidate -> int

(** RG 33's marker: every Sunday slug this rite's temporal cycle produces
    (temporal_ef.ml's [named] and [sunday_slug]) contains this substring;
    nothing else {!band} classifies does. Also colitur's own convention, not
    an RG citation -- see {!universal_layer} -- exposed for the same reason
    as {!vigil_suffix}: a rename of temporal_ef's Sunday-slug format has
    somewhere to be caught other than a silently-wrong RG 33 disposition. *)
val sunday_marker : string

(** The placeholder {!Precedence.privilege} every [Commemorate] disposition
    below carries until Task 9 implements RG 109's closed list of privileged
    commemorations and RG 108-111's admission counts. Exposed so Task 9 (and
    any test wanting to assert on it explicitly) does not have to duplicate
    the literal [Precedence.Ordinary]. *)
val interim_privilege : Precedence.privilege

(** [disposition ~winner ~loser]: RG 92-95, 33, 94 (docs/research/
    rules-register.md §4, "Occurrence" and "Vigils"). What becomes of a
    losing candidate, decided by the LOSER's own rank and status (RG 95),
    except RG 33's vigil omission, which also reads the winner:
    - a {!Celebration.status} of [Commemoration_only] is always
      [Commemorate] (checked first: it can never win -- see
      {!Precedence.resolve} -- and, by that same status's own definition,
      already denotes an office with nothing left to translate, so it never
      transfers either; not itself a further RG citation beyond RG 93's
      general four-mechanism statement above);
    - a [Class1] or [Class2] loser whose slug marks it a vigil ({!vigil_suffix})
      is [Omit] when the winner is any Sunday ({!sunday_marker}) or itself
      [Class1] (RG 33 -- entirely omitted, not merely commemorated);
    - any other [Class1] loser is [Transfer] (RG 95 -- only I class has the
      right of translation; this is also what moves All Souls, register
      line 334, once it loses to an occurring Sunday -- WHERE it lands is
      {!Rite.t.transfer_target}'s job, not this function's);
    - everything else is [Commemorate], carrying {!interim_privilege} until
      Task 9 replaces it with RG 109's real per-day computation.

    Total over every winner/loser pair {!Precedence.resolve} or {!Calendar}
    can construct: [Vocab_ef.rank] (RG 8) and {!Celebration.status} are both
    closed variants, so the four cases above exhaust every representable
    shape -- there is no fifth, "unclassified" case the way {!band} needs
    one, because this function's own return type has no such slot to fall
    into by accident. *)
val disposition :
  winner:Vocab_ef.rank Precedence.candidate ->
  loser:Vocab_ef.rank Precedence.candidate ->
  Precedence.disposition