diff options
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 114 |
1 files changed, 64 insertions, 50 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 5688640..861bb1a 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -758,38 +758,47 @@ let disposition ~(winner : Vocab_ef.rank Precedence.candidate) carries everything this split needs, the same way [observed]'s used to before a competing office could occupy the Sunday's place. *) -(* RG 8's four-class dignity order, Class1 highest. Deliberately NOT [band] - (RG 91's much finer 28-entry table): [band] needs a [context] [admit] - does not have (see above), and Celebration.mli's own comment on [status] - -- "RG 111 orders admitted commemorations by dignity" -- names [rank] - itself as that dignity, not the finer occurrence-table entry. *) -let dignity = function - | Vocab_ef.Class1 -> 1 - | Vocab_ef.Class2 -> 2 - | Vocab_ef.Class3 -> 3 - | Vocab_ef.Class4 -> 4 +(* RG 113 -- CORRECTED, Task B (branch ef-rg16a): docs/research/rules- + register.md §4's RG 113 entry previously quoted only its FIRST sentence + ("commemoratio de Tempore fit primo loco"); its SECOND, load-bearing + sentence, primary-source-verified against two independent scans, is the + real rule for this function: *"In admittendis et ordinandis aliis + commemorationibus, servetur ordo tabellae praecedentiae"* -- in ADMITTING + and ORDERING the other commemorations, the order of the table of + precedence (RG 91, {!band}'s own 28-entry table) is to be kept. -(* Deterministic selection order for RG 111: dignity first, then slug -- - the same tie-break {!Precedence.compare_by} uses for [band] itself (the - brief: "break ties on slug"), so which candidate wins a shared rank never - depends on the order [comms] arrives in. *) -let compare_dignity (a, _) (b, _) = - let da = dignity a.Precedence.cel.Celebration.rank - and db = dignity b.Precedence.cel.Celebration.rank in - if da <> db then Int.compare da db + This REPLACES a previous [compare_dignity], which sorted by RG 8's coarse + four-class [rank] ("dignity") and broke same-rank ties alphabetically by + slug -- an engineering convention with no rubrical warrant, silently + deciding 66 days over 2005-2050 (e.g. 22 Sept 2027, an Ember Wednesday: + the alphabetical rule admitted "maurice-and-companions-martyrs" over + "thomas-of-villanova", both Class3/{!band} entry 24 -- missalemeum shows + Thomas). {!band} needs a [context] this function itself does not have + (date/season/weekday) -- unlike [dignity], which read [rank] alone -- + so {!Precedence.resolve} now computes each candidate's own [band] value + once, generically, and hands it to [admit] as the trailing [int] on each + input triple (see {!Precedence.rules.admit}'s own doc). [comms] below is + [(candidate * privilege * int) list], not the pair it used to be. *) +let compare_precedence (a, _, ba) (b, _, bb) = + if ba <> bb then Int.compare (ba : int) bb else Slug.compare a.Precedence.cel.Celebration.slug b.Precedence.cel.Celebration.slug let rec take n = function | [] -> [] | x :: xs -> if n <= 0 then [] else x :: take (n - 1) xs +(* [comms]'s own candidate/privilege pair, its [band] value dropped once a + selection has been made -- {!Precedence.rules.admit}'s return type is + still the pair, not the triple; only the INPUT carries [band]. *) +let drop_band (c, p, (_ : int)) = (c, p) + let admit ~(observed : Vocab_ef.rank Precedence.candidate) ~(temporal : Vocab_ef.rank Precedence.candidate) - (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list) : + (comms : (Vocab_ef.rank Precedence.candidate * Precedence.privilege * int) list) : (Vocab_ef.rank Precedence.candidate * Precedence.privilege) list = - (* Sorted once, by dignity then slug (see [compare_dignity]); every branch - below either takes a prefix of this list or filters it, so the RESULT - is always a sub-list of [comms] with its elements untouched -- never + (* Sorted once, by {!band} then slug (see [compare_precedence]); every + branch below either takes a prefix of this list or filters it, so the + RESULT is always built from [comms]'s own elements, untouched -- never rebuilt -- which matters beyond determinism: {!Precedence.resolve}'s own [dropped] computation tells an admitted candidate from a dropped one by physical equality (==) on the candidate value (Task 2's own @@ -801,9 +810,11 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) celebration would surface TWICE in the same day -- once in [commemorations] (the rebuilt copy) and once in [omitted] (the original, which nothing admitted matches). One admission, double-reported, and no - crash to announce it, which is exactly why this comment exists. *) - let sorted = List.stable_sort compare_dignity comms in - let is_privileged (_, p) = p = Precedence.Privileged in + crash to announce it, which is exactly why this comment exists. + [drop_band] only unwraps the pair back out of the triple -- it does not + rebuild [c] or [p] themselves, so this obligation still holds. *) + let sorted = List.stable_sort compare_precedence comms in + let is_privileged (_, p, _) = p = Precedence.Privileged in let observed_rank = observed.Precedence.cel.Celebration.rank in (* CORRECTED (fix round 1, RG16(a) task): read off [temporal], not [observed] -- see this function's own doc comment above for the full @@ -819,9 +830,9 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) | Class1, _ -> (* RG 111: "I class: none save one privileged." Ordinary commemorations never get a slot at all on a I-class day, no matter how many are - due; at most one privileged one does, the highest-dignity one if - several are. *) - (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ best ]) + due; at most one privileged one does, the highest-precedence one + (RG 113: {!band}'s own table order) if several are. *) + (match List.filter is_privileged sorted with [] -> [] | best :: _ -> [ drop_band best ]) | Class2, true -> (* RG 111(b), primary text, RE-VERIFIED word for word against the scan (final fix wave; this sentence is the sole textual basis for the @@ -835,17 +846,18 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) dropped if a privileged commemoration is due." Two clauses, not one: (i) a privileged commemoration, whenever due, categorically takes the day's one slot - -- not by comparing its dignity against the ordinary contender's, + -- not by comparing its precedence against the ordinary contender's, so an ordinary commemoration that would otherwise win on raw - dignity is still dropped once any privileged one is also due (the - asymmetric clause the brief and task report flag as deliberate, not - present at "other II class" below); (ii) failing that, the slot is - reserved SPECIFICALLY for a [Class2] candidate -- "de festo II - classis" is a RANK restriction, not merely "whichever ordinary - candidate has the best dignity": a III- or IV-class ordinary loser - (a plain commemoration-only saint with no privilege of its own) has - NO standing for this slot at all and must be entirely omitted, even - when it is the only candidate present. + table order is still dropped once any privileged one is also due + (the asymmetric clause the brief and task report flag as + deliberate, not present at "other II class" below); (ii) failing + that, the slot is reserved SPECIFICALLY for a [Class2] candidate -- + "de festo II classis" is a RANK restriction, not merely "whichever + ordinary candidate has the best table position": a III- or + IV-class ordinary loser (a plain commemoration-only saint with no + privilege of its own) has NO standing for this slot at all and + must be entirely omitted, even when it is the only candidate + present. Fix, Task 16 (primary-source-verified + missalemeum-confirmed): previously this fell back to "the best of [sorted], whatever its @@ -857,21 +869,21 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) shows him "displaced" (omitted), never commemorated; the pre-fix code admitted him regardless. *) (match List.filter is_privileged sorted with - | best :: _ -> [ best ] + | best :: _ -> [ drop_band best ] | [] -> ( - match List.filter (fun (c, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with + match List.filter (fun (c, _, _) -> c.Precedence.cel.Celebration.rank = Class2) sorted with | [] -> [] - | best :: _ -> [ best ])) + | best :: _ -> [ drop_band best ])) | Class2, false -> (* RG 111: "other II class: one" -- no privilege-override clause here, unlike the Sunday case immediately above, so the day's one slot - goes to whichever candidate outranks the rest by dignity alone, - privileged or not. *) - (match sorted with [] -> [] | best :: _ -> [ best ]) + goes to whichever candidate outranks the rest by RG 113's own + table-of-precedence order ({!band}), privileged or not. *) + (match sorted with [] -> [] | best :: _ -> [ drop_band best ]) | (Class3 | Class4), _ -> - (* RG 111: "III-IV class: at most two" -- by dignity, same as the - non-Sunday II-class case, just with room for two. *) - take 2 sorted + (* RG 111: "III-IV class: at most two" -- by RG 113's table order, same + as the non-Sunday II-class case, just with room for two. *) + List.map drop_band (take 2 sorted) (* Task 11: RG 96 -- where an impeded I-class feast lands (docs/research/ rules-register.md §4, "Transfer/translation"). [band] decides who is @@ -884,9 +896,11 @@ let admit ~(observed : Vocab_ef.rank Precedence.candidate) RG 96's own text, register-transcribed: "the next following day that is not I or II class." [is_blocking] reads that off [Vocab_ef.rank] -- - RG 96 speaks of the day's CLASS (RG 8's four-way dignity), not [band]'s - finer 28-entry occurrence-table row, the same distinction {!admit} above - already draws for RG 111 ({!dignity}, not [band]). *) + RG 96 speaks of the day's CLASS (RG 8's four-way "dignity"), not [band]'s + finer 28-entry occurrence-table row -- unlike {!admit} above, which (RG + 113, Task B/ef-rg16a) now DOES use [band] itself for its own selection + order; RG 96's own text has no such finer-table reading, so [is_blocking] + stays on [Vocab_ef.rank] alone. *) let is_blocking (rank : Vocab_ef.rank) = rank = Vocab_ef.Class1 || rank = Vocab_ef.Class2 (* RG 96's own named exception (docs/research/rules-register.md §4, |
