Skip to main content

kosher_rust/limudim/
amud_yomi_bavli_dirshu.rs

1use icu_calendar::{Date, cal::Hebrew};
2
3use crate::limudim::{
4    BAVLI_TOTAL_AMUDIM, HebrewDateExt, Limud,
5    cycle::Cycle,
6    interval::Interval,
7    limud::{CycleFinder, InternalLimud},
8    units::*,
9};
10
11fn initial_cycle_date() -> Date<Hebrew> {
12    #[allow(clippy::expect_used)]
13    let date = Date::try_new_gregorian(2023, 10, 16)
14        .expect("hard-coded Gregorian date should be valid")
15        .to_calendar(Hebrew);
16    date
17}
18
19pub const fn start_daf(tractate: Tractate) -> Amud {
20    match tractate {
21        Tractate::Kinnim => Amud::new(Tractate::Kinnim, 22, Side::Bet),
22        Tractate::Tamid => Amud::new(Tractate::Tamid, 25, Side::Bet),
23        Tractate::Midos => Amud::new(Tractate::Midos, 34, Side::Aleph),
24        _ => Amud::new(tractate, 2, Side::Aleph),
25    }
26}
27
28pub const fn end_daf(tractate: Tractate) -> Option<Amud> {
29    let amud = match tractate {
30        Tractate::Berachos => Amud::new(Tractate::Berachos, 64, Side::Aleph),
31        Tractate::Shabbos => Amud::new(Tractate::Shabbos, 157, Side::Bet),
32        Tractate::Eruvin => Amud::new(Tractate::Eruvin, 105, Side::Aleph),
33        Tractate::Pesachim => Amud::new(Tractate::Pesachim, 121, Side::Bet),
34        Tractate::Shekalim => Amud::new(Tractate::Shekalim, 22, Side::Bet),
35        Tractate::Yoma => Amud::new(Tractate::Yoma, 88, Side::Aleph),
36        Tractate::Sukkah => Amud::new(Tractate::Sukkah, 56, Side::Bet),
37        Tractate::Beitzah => Amud::new(Tractate::Beitzah, 40, Side::Bet),
38        Tractate::RoshHashanah => Amud::new(Tractate::RoshHashanah, 35, Side::Bet),
39        Tractate::Taanis => Amud::new(Tractate::Taanis, 31, Side::Aleph),
40        Tractate::Megillah => Amud::new(Tractate::Megillah, 32, Side::Aleph),
41        Tractate::MoedKatan => Amud::new(Tractate::MoedKatan, 29, Side::Aleph),
42        Tractate::Chagigah => Amud::new(Tractate::Chagigah, 27, Side::Aleph),
43        Tractate::Yevamos => Amud::new(Tractate::Yevamos, 122, Side::Bet),
44        Tractate::Kesubos => Amud::new(Tractate::Kesubos, 112, Side::Bet),
45        Tractate::Nedarim => Amud::new(Tractate::Nedarim, 91, Side::Bet),
46        Tractate::Nazir => Amud::new(Tractate::Nazir, 66, Side::Bet),
47        Tractate::Sotah => Amud::new(Tractate::Sotah, 49, Side::Bet),
48        Tractate::Gitin => Amud::new(Tractate::Gitin, 90, Side::Bet),
49        Tractate::Kiddushin => Amud::new(Tractate::Kiddushin, 82, Side::Bet),
50        Tractate::BavaKamma => Amud::new(Tractate::BavaKamma, 119, Side::Bet),
51        Tractate::BavaMetzia => Amud::new(Tractate::BavaMetzia, 119, Side::Aleph),
52        Tractate::BavaBasra => Amud::new(Tractate::BavaBasra, 176, Side::Bet),
53        Tractate::Sanhedrin => Amud::new(Tractate::Sanhedrin, 113, Side::Bet),
54        Tractate::Makkos => Amud::new(Tractate::Makkos, 24, Side::Bet),
55        Tractate::Shevuos => Amud::new(Tractate::Shevuos, 49, Side::Bet),
56        Tractate::AvodahZarah => Amud::new(Tractate::AvodahZarah, 76, Side::Bet),
57        Tractate::Horiyos => Amud::new(Tractate::Horiyos, 14, Side::Aleph),
58        Tractate::Zevachim => Amud::new(Tractate::Zevachim, 120, Side::Bet),
59        Tractate::Menachos => Amud::new(Tractate::Menachos, 110, Side::Aleph),
60        Tractate::Chullin => Amud::new(Tractate::Chullin, 142, Side::Aleph),
61        Tractate::Bechoros => Amud::new(Tractate::Bechoros, 61, Side::Aleph),
62        Tractate::Arachin => Amud::new(Tractate::Arachin, 34, Side::Aleph),
63        Tractate::Temurah => Amud::new(Tractate::Temurah, 34, Side::Aleph),
64        Tractate::Kerisos => Amud::new(Tractate::Kerisos, 28, Side::Bet),
65        Tractate::Meilah => Amud::new(Tractate::Meilah, 22, Side::Aleph),
66        Tractate::Kinnim => Amud::new(Tractate::Kinnim, 25, Side::Aleph),
67        Tractate::Tamid => Amud::new(Tractate::Tamid, 33, Side::Bet),
68        Tractate::Midos => Amud::new(Tractate::Midos, 37, Side::Bet),
69        Tractate::Niddah => Amud::new(Tractate::Niddah, 73, Side::Aleph),
70        _ => Amud::new(Tractate::Bechoros, 0, Side::Aleph),
71    };
72    if amud.page == 0 { None } else { Some(amud) }
73}
74
75#[derive(Default)]
76/// Calculates the Amud Yomi Bavli Dirshu schedule.
77pub struct AmudYomiBavliDirshu {}
78
79fn amud_yomi_bavli_dirshu_for_date(limud_date: Date<Hebrew>) -> Option<Amud> {
80    let cycle = Cycle::from_cycle_initiation(
81        initial_cycle_date(),
82        AmudYomiBavliDirshu::cycle_end_calculation,
83        limud_date,
84    )?;
85    if limud_date > cycle.end_date {
86        return None;
87    }
88    let offset = cycle.start_date.days_until(&limud_date)? as usize;
89    let mut remaining = offset;
90    for tractate in BAVLI_TRACTATES {
91        let start = start_daf(tractate);
92        let end = end_daf(tractate)?;
93        let count = start.amudim_to(end)? as usize;
94        if remaining < count {
95            return start.at_offset(remaining as i32);
96        }
97        remaining -= count;
98    }
99    None
100}
101
102impl InternalLimud<Amud> for AmudYomiBavliDirshu {
103    fn limud(&self, limud_date: Date<Hebrew>) -> Option<Amud> {
104        amud_yomi_bavli_dirshu_for_date(limud_date)
105    }
106
107    fn cycle_finder(&self) -> CycleFinder {
108        CycleFinder::Initial(initial_cycle_date())
109    }
110    fn cycle_end_calculation(hebrew_date: Date<Hebrew>, _iteration: Option<i32>) -> Option<Date<Hebrew>> {
111        hebrew_date.add_days(BAVLI_TOTAL_AMUDIM - 1)
112    }
113
114    fn unit_for_interval(&self, _interval: &Interval, limud_date: &Date<Hebrew>) -> Option<Amud> {
115        amud_yomi_bavli_dirshu_for_date(*limud_date)
116    }
117}
118impl Limud<Amud> for AmudYomiBavliDirshu {}
119
120#[cfg(test)]
121#[allow(clippy::expect_used)]
122mod tests {
123    use crate::limudim::from_gregorian_date;
124
125    use super::*;
126
127    #[test]
128    fn amud_yomi_bavli_dirshu_simple_date() {
129        let test_date = from_gregorian_date(2024, 5, 30);
130        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
131        assert_eq!(limud.page, 53);
132        assert_eq!(limud.side, Side::Aleph);
133        assert_eq!(limud.tractate, Tractate::Shabbos);
134    }
135
136    #[test]
137    fn amud_yomi_bavli_dirshu_before_cycle_began() {
138        let test_date = from_gregorian_date(2023, 1, 1);
139        let limud = AmudYomiBavliDirshu::default().limud(test_date);
140        assert!(limud.is_none());
141    }
142
143    #[test]
144    fn amud_yomi_bavli_dirshu_first_day_of_cycle() {
145        let test_date = from_gregorian_date(2038, 8, 5);
146        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
147        assert_eq!(limud.page, 2);
148        assert_eq!(limud.side, Side::Aleph);
149        assert_eq!(limud.tractate, Tractate::Berachos);
150    }
151
152    #[test]
153    fn amud_yomi_bavli_dirshu_last_day_of_cycle() {
154        let test_date = from_gregorian_date(2038, 8, 4);
155        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
156        assert_eq!(limud.page, 73);
157        assert_eq!(limud.side, Side::Aleph);
158        assert_eq!(limud.tractate, Tractate::Niddah);
159    }
160
161    #[test]
162    fn amud_yomi_bavli_dirshu_end_of_meilah() {
163        let test_date = from_gregorian_date(2038, 2, 10);
164        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
165        assert_eq!(limud.page, 21);
166        assert_eq!(limud.side, Side::Bet);
167        assert_eq!(limud.tractate, Tractate::Meilah);
168    }
169
170    #[test]
171    fn amud_yomi_bavli_dirshu_beginning_of_kinnim() {
172        let test_date = from_gregorian_date(2038, 2, 12);
173        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
174        assert_eq!(limud.page, 22);
175        assert_eq!(limud.side, Side::Bet);
176        assert_eq!(limud.tractate, Tractate::Kinnim);
177    }
178
179    #[test]
180    fn amud_yomi_bavli_dirshu_end_of_kinnim() {
181        let test_date = from_gregorian_date(2038, 2, 17);
182        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
183        assert_eq!(limud.page, 25);
184        assert_eq!(limud.side, Side::Aleph);
185        assert_eq!(limud.tractate, Tractate::Kinnim);
186    }
187
188    #[test]
189    fn amud_yomi_bavli_dirshu_beginning_of_tamid() {
190        let test_date = from_gregorian_date(2038, 2, 18);
191        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
192        assert_eq!(limud.page, 25);
193        assert_eq!(limud.side, Side::Bet);
194        assert_eq!(limud.tractate, Tractate::Tamid);
195    }
196
197    #[test]
198    fn amud_yomi_bavli_dirshu_end_of_tamid() {
199        let test_date = from_gregorian_date(2038, 3, 6);
200        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
201        assert_eq!(limud.page, 33);
202        assert_eq!(limud.side, Side::Bet);
203        assert_eq!(limud.tractate, Tractate::Tamid);
204    }
205
206    #[test]
207    fn amud_yomi_bavli_dirshu_beginning_of_midos() {
208        let test_date = from_gregorian_date(2038, 3, 7);
209        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
210        assert_eq!(limud.page, 34);
211        assert_eq!(limud.side, Side::Aleph);
212        assert_eq!(limud.tractate, Tractate::Midos);
213    }
214
215    #[test]
216    fn amud_yomi_bavli_dirshu_end_of_midos() {
217        let test_date = from_gregorian_date(2038, 3, 14);
218        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
219        assert_eq!(limud.page, 37);
220        assert_eq!(limud.side, Side::Bet);
221        assert_eq!(limud.tractate, Tractate::Midos);
222    }
223
224    #[test]
225    fn amud_yomi_bavli_dirshu_after_midos() {
226        let test_date = from_gregorian_date(2038, 3, 15);
227        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
228        assert_eq!(limud.page, 2);
229        assert_eq!(limud.side, Side::Aleph);
230        assert_eq!(limud.tractate, Tractate::Niddah);
231    }
232    #[test]
233    fn amud_yomi_bavli_2025_23_9() {
234        let test_date = from_gregorian_date(2025, 9, 23);
235        let limud = AmudYomiBavliDirshu::default().limud(test_date).expect("limud exists");
236        assert_eq!(limud.page, 34);
237        assert_eq!(limud.side, Side::Aleph);
238        assert_eq!(limud.tractate, Tractate::Pesachim);
239    }
240}