Skip to main content

kosher_rust/limudim/
mishna_yomis.rs

1use icu_calendar::{Date, cal::Hebrew};
2
3use crate::limudim::{
4    HebrewDateExt, Limud, MISHNA_YOMIS_CYCLE_DAYS,
5    cycle::Cycle,
6    interval::Interval,
7    limud::{CycleFinder, InternalLimud},
8    units::{Mishna, TRACTATES, Tractate},
9};
10
11fn initial_cycle_date() -> Date<Hebrew> {
12    #[allow(clippy::expect_used)]
13    let date = Date::try_new_gregorian(1947, 5, 20)
14        .expect("hard-coded Gregorian date should be valid")
15        .to_calendar(Hebrew);
16    date
17}
18
19const fn chapters(tractate: Tractate) -> usize {
20    match tractate {
21        Tractate::Berachos => 9,
22        Tractate::Peah => 8,
23        Tractate::Demai => 7,
24        Tractate::Kilayim => 9,
25        Tractate::Sheviis => 10,
26        Tractate::Terumos => 11,
27        Tractate::Maasros => 5,
28        Tractate::MaaserSheni => 5,
29        Tractate::Chalah => 4,
30        Tractate::Orlah => 3,
31        Tractate::Bikurim => 4,
32        Tractate::Shabbos => 24,
33        Tractate::Eruvin => 10,
34        Tractate::Pesachim => 10,
35        Tractate::Shekalim => 8,
36        Tractate::Yoma => 8,
37        Tractate::Sukkah => 5,
38        Tractate::Beitzah => 5,
39        Tractate::RoshHashanah => 4,
40        Tractate::Taanis => 4,
41        Tractate::Megillah => 4,
42        Tractate::MoedKatan => 3,
43        Tractate::Chagigah => 3,
44        Tractate::Yevamos => 16,
45        Tractate::Kesubos => 13,
46        Tractate::Nedarim => 11,
47        Tractate::Nazir => 9,
48        Tractate::Sotah => 9,
49        Tractate::Gitin => 9,
50        Tractate::Kiddushin => 4,
51        Tractate::BavaKamma => 10,
52        Tractate::BavaMetzia => 10,
53        Tractate::BavaBasra => 10,
54        Tractate::Sanhedrin => 11,
55        Tractate::Makkos => 3,
56        Tractate::Shevuos => 8,
57        Tractate::Eduyos => 8,
58        Tractate::AvodahZarah => 5,
59        Tractate::Avos => 6,
60        Tractate::Horiyos => 3,
61        Tractate::Zevachim => 14,
62        Tractate::Menachos => 13,
63        Tractate::Chullin => 12,
64        Tractate::Bechoros => 9,
65        Tractate::Arachin => 9,
66        Tractate::Temurah => 7,
67        Tractate::Kerisos => 6,
68        Tractate::Meilah => 6,
69        Tractate::Tamid => 7,
70        Tractate::Midos => 5,
71        Tractate::Kinnim => 3,
72        Tractate::Keilim => 30,
73        Tractate::Ohalos => 18,
74        Tractate::Negaim => 14,
75        Tractate::Parah => 12,
76        Tractate::Taharos => 10,
77        Tractate::Mikvaos => 10,
78        Tractate::Niddah => 10,
79        Tractate::Machshirin => 6,
80        Tractate::Zavim => 5,
81        Tractate::TevulYom => 4,
82        Tractate::Yadayim => 4,
83        Tractate::Uktzin => 3,
84    }
85}
86const fn chapter_length(tractate: Tractate, chapter: usize) -> u16 {
87    let chapter_index = chapter - 1;
88    match tractate {
89        Tractate::Berachos => [5, 8, 6, 7, 5, 8, 5, 8, 5][chapter_index],
90        Tractate::Peah => [6, 8, 8, 11, 8, 11, 8, 9][chapter_index],
91        Tractate::Demai => [4, 5, 6, 7, 11, 12, 8][chapter_index],
92        Tractate::Kilayim => [9, 11, 7, 9, 8, 9, 8, 6, 10][chapter_index],
93        Tractate::Sheviis => [8, 10, 10, 10, 9, 6, 7, 11, 9, 9][chapter_index],
94        Tractate::Terumos => [10, 6, 9, 13, 9, 6, 7, 12, 7, 12, 10][chapter_index],
95        Tractate::Maasros => [8, 8, 10, 6, 8][chapter_index],
96        Tractate::MaaserSheni => [7, 10, 13, 12, 15][chapter_index],
97        Tractate::Chalah => [9, 8, 10, 11][chapter_index],
98        Tractate::Orlah => [9, 17, 9][chapter_index],
99        Tractate::Bikurim => [11, 11, 12, 5][chapter_index],
100        Tractate::Shabbos => [11, 7, 6, 2, 4, 10, 4, 7, 7, 6, 6, 6, 7, 4, 3, 8, 8, 3, 6, 5, 3, 6, 5, 5][chapter_index],
101        Tractate::Eruvin => [10, 6, 9, 11, 9, 10, 11, 11, 4, 15][chapter_index],
102        Tractate::Pesachim => [7, 8, 8, 9, 10, 6, 13, 8, 11, 9][chapter_index],
103        Tractate::Shekalim => [7, 5, 4, 9, 6, 6, 7, 8][chapter_index],
104        Tractate::Yoma => [8, 7, 11, 6, 7, 8, 5, 9][chapter_index],
105        Tractate::Sukkah => [11, 9, 15, 10, 8][chapter_index],
106        Tractate::Beitzah => [10, 10, 8, 7, 7][chapter_index],
107        Tractate::RoshHashanah => [9, 9, 8, 9][chapter_index],
108        Tractate::Taanis => [7, 10, 9, 8][chapter_index],
109        Tractate::Megillah => [11, 6, 6, 10][chapter_index],
110        Tractate::MoedKatan => [10, 5, 9][chapter_index],
111        Tractate::Chagigah => [8, 7, 8][chapter_index],
112        Tractate::Yevamos => [4, 10, 10, 13, 6, 6, 6, 6, 6, 9, 7, 6, 13, 9, 10, 7][chapter_index],
113        Tractate::Kesubos => [10, 10, 9, 12, 9, 7, 10, 8, 9, 6, 6, 4, 11][chapter_index],
114        Tractate::Nedarim => [4, 5, 11, 8, 6, 10, 9, 7, 10, 8, 12][chapter_index],
115        Tractate::Nazir => [7, 10, 7, 7, 7, 11, 4, 2, 5][chapter_index],
116        Tractate::Sotah => [9, 6, 8, 5, 5, 4, 8, 7, 15][chapter_index],
117        Tractate::Gitin => [6, 7, 8, 9, 9, 7, 9, 10, 10][chapter_index],
118        Tractate::Kiddushin => [10, 10, 13, 14][chapter_index],
119        Tractate::BavaKamma => [4, 6, 11, 9, 7, 6, 7, 7, 12, 10][chapter_index],
120        Tractate::BavaMetzia => [8, 11, 12, 12, 11, 8, 11, 9, 13, 6][chapter_index],
121        Tractate::BavaBasra => [6, 14, 8, 9, 11, 8, 4, 8, 10, 8][chapter_index],
122        Tractate::Sanhedrin => [6, 5, 8, 5, 5, 6, 11, 7, 6, 6, 6][chapter_index],
123        Tractate::Makkos => [10, 8, 16][chapter_index],
124        Tractate::Shevuos => [7, 5, 11, 13, 5, 7, 8, 6][chapter_index],
125        Tractate::Eduyos => [14, 10, 12, 12, 7, 3, 9, 7][chapter_index],
126        Tractate::AvodahZarah => [9, 7, 10, 12, 12][chapter_index],
127        Tractate::Avos => [18, 16, 18, 22, 23, 11][chapter_index],
128        Tractate::Horiyos => [5, 7, 8][chapter_index],
129        Tractate::Zevachim => [4, 5, 6, 6, 8, 7, 6, 12, 7, 8, 8, 6, 8, 10][chapter_index],
130        Tractate::Menachos => [4, 5, 7, 5, 9, 7, 6, 7, 9, 9, 9, 5, 11][chapter_index],
131        Tractate::Chullin => [7, 10, 7, 7, 5, 7, 6, 6, 8, 4, 2, 5][chapter_index],
132        Tractate::Bechoros => [7, 9, 4, 10, 6, 12, 7, 10, 8][chapter_index],
133        Tractate::Arachin => [4, 6, 5, 4, 6, 5, 5, 7, 8][chapter_index],
134        Tractate::Temurah => [6, 3, 5, 4, 6, 5, 6][chapter_index],
135        Tractate::Kerisos => [7, 6, 10, 3, 8, 9][chapter_index],
136        Tractate::Meilah => [4, 9, 8, 6, 5, 6][chapter_index],
137        Tractate::Tamid => [4, 5, 9, 3, 6, 3, 4][chapter_index],
138        Tractate::Midos => [9, 6, 8, 7, 4][chapter_index],
139        Tractate::Kinnim => [4, 5, 6][chapter_index],
140        Tractate::Keilim => [
141            9, 8, 8, 4, 11, 4, 6, 11, 8, 8, 9, 8, 8, 8, 6, 8, 17, 9, 10, 7, 3, 10, 5, 17, 9, 9, 12, 10, 8, 4,
142        ][chapter_index],
143        Tractate::Ohalos => [8, 7, 7, 3, 7, 7, 6, 6, 16, 7, 9, 8, 6, 7, 10, 5, 5, 10][chapter_index],
144        Tractate::Negaim => [6, 5, 8, 11, 5, 8, 5, 10, 3, 10, 12, 7, 12, 13][chapter_index],
145        Tractate::Parah => [4, 5, 11, 4, 9, 5, 12, 11, 9, 6, 9, 11][chapter_index],
146        Tractate::Taharos => [9, 8, 8, 13, 9, 10, 9, 9, 9, 8][chapter_index],
147        Tractate::Mikvaos => [8, 10, 4, 5, 6, 11, 7, 5, 7, 8][chapter_index],
148        Tractate::Niddah => [7, 7, 7, 7, 9, 14, 5, 4, 11, 8][chapter_index],
149        Tractate::Machshirin => [6, 11, 8, 10, 11, 8][chapter_index],
150        Tractate::Zavim => [6, 4, 3, 7, 12][chapter_index],
151        Tractate::TevulYom => [5, 8, 6, 7][chapter_index],
152        Tractate::Yadayim => [5, 4, 5, 8][chapter_index],
153        Tractate::Uktzin => [6, 10, 12][chapter_index],
154    }
155}
156
157const fn mishna_count_in_tractate(tractate: Tractate) -> usize {
158    let mut count = 0;
159    let mut chapter = 1;
160    while chapter <= chapters(tractate) {
161        count += chapter_length(tractate, chapter) as usize;
162        chapter += 1;
163    }
164    count
165}
166
167fn mishna_at_offset(offset: usize) -> Option<Mishna> {
168    let mut remaining = offset;
169    for tractate in TRACTATES {
170        let count = mishna_count_in_tractate(tractate);
171        if remaining < count {
172            let mut index = remaining;
173            let mut chapter = 1;
174            while chapter <= chapters(tractate) {
175                let len = chapter_length(tractate, chapter) as usize;
176                if index < len {
177                    return Some(Mishna {
178                        tractate,
179                        chapter,
180                        mishna: (index + 1) as u16,
181                    });
182                }
183                index -= len;
184                chapter += 1;
185            }
186            return None;
187        }
188        remaining -= count;
189    }
190    None
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
194#[cfg_attr(feature = "defmt", derive(defmt::Format))]
195/// Represents a pair of mishnayos.
196pub struct Mishnas(pub Mishna, pub Mishna);
197
198#[derive(Default)]
199/// Calculates the Mishna Yomis schedule.
200pub struct MishnaYomis;
201fn mishna_yomis_for_date(limud_date: Date<Hebrew>) -> Option<Mishnas> {
202    let cycle = Cycle::from_cycle_initiation(initial_cycle_date(), MishnaYomis::cycle_end_calculation, limud_date)?;
203    if limud_date > cycle.end_date {
204        return None;
205    }
206    let iteration = cycle.start_date.days_until(&limud_date)? + 1;
207    mishnas_for_iteration(iteration)
208}
209
210impl InternalLimud<Mishnas> for MishnaYomis {
211    fn limud(&self, limud_date: Date<Hebrew>) -> Option<Mishnas> {
212        mishna_yomis_for_date(limud_date)
213    }
214
215    fn cycle_finder(&self) -> CycleFinder {
216        CycleFinder::Initial(initial_cycle_date())
217    }
218
219    fn unit_for_interval(&self, _interval: &Interval, limud_date: &Date<Hebrew>) -> Option<Mishnas> {
220        mishna_yomis_for_date(*limud_date)
221    }
222    fn cycle_end_calculation(hebrew_date: Date<Hebrew>, _iteration: Option<i32>) -> Option<Date<Hebrew>> {
223        hebrew_date.add_days(MISHNA_YOMIS_CYCLE_DAYS)
224    }
225}
226impl Limud<Mishnas> for MishnaYomis {}
227
228fn mishnas_for_iteration(iteration: u32) -> Option<Mishnas> {
229    let base = ((iteration - 1) * 2) as usize;
230    Some(Mishnas(mishna_at_offset(base)?, mishna_at_offset(base + 1)?))
231}
232
233#[cfg(test)]
234#[allow(clippy::expect_used)]
235mod tests {
236    use crate::limudim::from_gregorian_date;
237
238    use super::*;
239
240    #[test]
241    fn mishna_yomis_simple_date() {
242        let test_date = from_gregorian_date(2017, 12, 28);
243        let limud = MishnaYomis.limud(test_date).expect("limud exists");
244        // Python test expects: 'megillah 3:4-5'
245        assert_eq!(limud.0.tractate, Tractate::Megillah);
246        assert_eq!(limud.0.chapter, 3);
247        assert_eq!(limud.0.mishna, 4);
248        assert_eq!(limud.1.tractate, Tractate::Megillah);
249        assert_eq!(limud.1.chapter, 3);
250        assert_eq!(limud.1.mishna, 5);
251    }
252
253    #[test]
254    fn mishna_yomis_before_cycle_began() {
255        let test_date = from_gregorian_date(1947, 1, 1);
256        let limud = MishnaYomis.limud(test_date);
257        assert!(limud.is_none());
258    }
259
260    #[test]
261    fn mishna_yomis_first_day_of_cycle() {
262        let test_date = from_gregorian_date(2016, 3, 30);
263        let limud = MishnaYomis.limud(test_date).expect("limud exists");
264        // Python test expects: 'berachos 1:1-2'
265        assert_eq!(limud.0.tractate, Tractate::Berachos);
266        assert_eq!(limud.0.chapter, 1);
267        assert_eq!(limud.0.mishna, 1);
268        assert_eq!(limud.1.tractate, Tractate::Berachos);
269        assert_eq!(limud.1.chapter, 1);
270        assert_eq!(limud.1.mishna, 2);
271    }
272
273    #[test]
274    fn mishna_yomis_last_day_of_cycle() {
275        let test_date = from_gregorian_date(2016, 3, 29);
276        let limud = MishnaYomis.limud(test_date).expect("limud exists");
277        // Python test expects: 'uktzin 3:11-12'
278        assert_eq!(limud.0.tractate, Tractate::Uktzin);
279        assert_eq!(limud.0.chapter, 3);
280        assert_eq!(limud.0.mishna, 11);
281        assert_eq!(limud.1.tractate, Tractate::Uktzin);
282        assert_eq!(limud.1.chapter, 3);
283        assert_eq!(limud.1.mishna, 12);
284    }
285
286    #[test]
287    fn mishna_yomis_span_two_masechtos() {
288        let test_date = from_gregorian_date(2016, 4, 27);
289        let limud = MishnaYomis.limud(test_date).expect("limud exists");
290        // Python test expects: 'berachos 9:5 - peah 1:1'
291        assert_eq!(limud.0.tractate, Tractate::Berachos);
292        assert_eq!(limud.0.chapter, 9);
293        assert_eq!(limud.0.mishna, 5);
294        assert_eq!(limud.1.tractate, Tractate::Peah);
295        assert_eq!(limud.1.chapter, 1);
296        assert_eq!(limud.1.mishna, 1);
297    }
298
299    #[test]
300    fn mishna_yomis_span_two_perakim() {
301        let test_date = from_gregorian_date(2017, 12, 23);
302        let limud = MishnaYomis.limud(test_date).expect("limud exists");
303        // Python test expects: 'megillah 1:11-2:1'
304        assert_eq!(limud.0.tractate, Tractate::Megillah);
305        assert_eq!(limud.0.chapter, 1);
306        assert_eq!(limud.0.mishna, 11);
307        assert_eq!(limud.1.tractate, Tractate::Megillah);
308        assert_eq!(limud.1.chapter, 2);
309        assert_eq!(limud.1.mishna, 1);
310    }
311
312    #[test]
313    fn mishna_yomis_1947_05_20() {
314        let test_date = from_gregorian_date(1947, 5, 20);
315        let limud = MishnaYomis.limud(test_date).expect("limud exists");
316        assert_eq!(limud.0.tractate, Tractate::Berachos);
317        assert_eq!(limud.0.chapter, 1);
318        assert_eq!(limud.0.mishna, 1);
319        assert_eq!(limud.1.tractate, Tractate::Berachos);
320        assert_eq!(limud.1.chapter, 1);
321        assert_eq!(limud.1.mishna, 2);
322    }
323
324    #[test]
325    fn mishna_yomis_1950_01_01() {
326        let test_date = from_gregorian_date(1950, 1, 1);
327        let limud = MishnaYomis.limud(test_date).expect("limud exists");
328        assert_eq!(limud.0.tractate, Tractate::BavaKamma);
329        assert_eq!(limud.0.chapter, 1);
330        assert_eq!(limud.0.mishna, 1);
331        assert_eq!(limud.1.tractate, Tractate::BavaKamma);
332        assert_eq!(limud.1.chapter, 1);
333        assert_eq!(limud.1.mishna, 2);
334    }
335
336    #[test]
337    fn mishna_yomis_1960_01_01() {
338        let test_date = from_gregorian_date(1960, 1, 1);
339        let limud = MishnaYomis.limud(test_date).expect("limud exists");
340        assert_eq!(limud.0.tractate, Tractate::Eruvin);
341        assert_eq!(limud.0.chapter, 5);
342        assert_eq!(limud.0.mishna, 5);
343        assert_eq!(limud.1.tractate, Tractate::Eruvin);
344        assert_eq!(limud.1.chapter, 5);
345        assert_eq!(limud.1.mishna, 6);
346    }
347
348    #[test]
349    fn mishna_yomis_1970_01_01() {
350        let test_date = from_gregorian_date(1970, 1, 1);
351        let limud = MishnaYomis.limud(test_date).expect("limud exists");
352        assert_eq!(limud.0.tractate, Tractate::Mikvaos);
353        assert_eq!(limud.0.chapter, 10);
354        assert_eq!(limud.0.mishna, 6);
355        assert_eq!(limud.1.tractate, Tractate::Mikvaos);
356        assert_eq!(limud.1.chapter, 10);
357        assert_eq!(limud.1.mishna, 7);
358    }
359
360    #[test]
361    fn mishna_yomis_1980_01_01() {
362        let test_date = from_gregorian_date(1980, 1, 1);
363        let limud = MishnaYomis.limud(test_date).expect("limud exists");
364        assert_eq!(limud.0.tractate, Tractate::Bechoros);
365        assert_eq!(limud.0.chapter, 1);
366        assert_eq!(limud.0.mishna, 2);
367        assert_eq!(limud.1.tractate, Tractate::Bechoros);
368        assert_eq!(limud.1.chapter, 1);
369        assert_eq!(limud.1.mishna, 3);
370    }
371
372    #[test]
373    fn mishna_yomis_1990_01_01() {
374        let test_date = from_gregorian_date(1990, 1, 1);
375        let limud = MishnaYomis.limud(test_date).expect("limud exists");
376        assert_eq!(limud.0.tractate, Tractate::Sotah);
377        assert_eq!(limud.0.chapter, 9);
378        assert_eq!(limud.0.mishna, 14);
379        assert_eq!(limud.1.tractate, Tractate::Sotah);
380        assert_eq!(limud.1.chapter, 9);
381        assert_eq!(limud.1.mishna, 15);
382    }
383
384    #[test]
385    fn mishna_yomis_2000_01_01() {
386        let test_date = from_gregorian_date(2000, 1, 1);
387        let limud = MishnaYomis.limud(test_date).expect("limud exists");
388        assert_eq!(limud.0.tractate, Tractate::Shabbos);
389        assert_eq!(limud.0.chapter, 9);
390        assert_eq!(limud.0.mishna, 5);
391        assert_eq!(limud.1.tractate, Tractate::Shabbos);
392        assert_eq!(limud.1.chapter, 9);
393        assert_eq!(limud.1.mishna, 6);
394    }
395
396    #[test]
397    fn mishna_yomis_2010_01_01() {
398        let test_date = from_gregorian_date(2010, 1, 1);
399        let limud = MishnaYomis.limud(test_date).expect("limud exists");
400        assert_eq!(limud.0.tractate, Tractate::Taharos);
401        assert_eq!(limud.0.chapter, 4);
402        assert_eq!(limud.0.mishna, 12);
403        assert_eq!(limud.1.tractate, Tractate::Taharos);
404        assert_eq!(limud.1.chapter, 4);
405        assert_eq!(limud.1.mishna, 13);
406    }
407
408    #[test]
409    fn mishna_yomis_2016_04_01() {
410        let test_date = from_gregorian_date(2016, 4, 1);
411        let limud = MishnaYomis.limud(test_date).expect("limud exists");
412        // Python: 'berachos 1:5-2:1' - spans two chapters
413        assert_eq!(limud.0.tractate, Tractate::Berachos);
414        assert_eq!(limud.0.chapter, 1);
415        assert_eq!(limud.0.mishna, 5);
416        assert_eq!(limud.1.tractate, Tractate::Berachos);
417        assert_eq!(limud.1.chapter, 2);
418        assert_eq!(limud.1.mishna, 1);
419    }
420
421    #[test]
422    fn mishna_yomis_2016_05_01() {
423        let test_date = from_gregorian_date(2016, 5, 1);
424        let limud = MishnaYomis.limud(test_date).expect("limud exists");
425        assert_eq!(limud.0.tractate, Tractate::Peah);
426        assert_eq!(limud.0.chapter, 2);
427        assert_eq!(limud.0.mishna, 2);
428        assert_eq!(limud.1.tractate, Tractate::Peah);
429        assert_eq!(limud.1.chapter, 2);
430        assert_eq!(limud.1.mishna, 3);
431    }
432
433    #[test]
434    fn mishna_yomis_2017_01_01() {
435        let test_date = from_gregorian_date(2017, 1, 1);
436        let limud = MishnaYomis.limud(test_date).expect("limud exists");
437        assert_eq!(limud.0.tractate, Tractate::Chalah);
438        assert_eq!(limud.0.chapter, 2);
439        assert_eq!(limud.0.mishna, 3);
440        assert_eq!(limud.1.tractate, Tractate::Chalah);
441        assert_eq!(limud.1.chapter, 2);
442        assert_eq!(limud.1.mishna, 4);
443    }
444
445    #[test]
446    fn mishna_yomis_2017_06_01() {
447        let test_date = from_gregorian_date(2017, 6, 1);
448        let limud = MishnaYomis.limud(test_date).expect("limud exists");
449        assert_eq!(limud.0.tractate, Tractate::Eruvin);
450        assert_eq!(limud.0.chapter, 7);
451        assert_eq!(limud.0.mishna, 8);
452        assert_eq!(limud.1.tractate, Tractate::Eruvin);
453        assert_eq!(limud.1.chapter, 7);
454        assert_eq!(limud.1.mishna, 9);
455    }
456
457    #[test]
458    fn mishna_yomis_2017_12_01() {
459        let test_date = from_gregorian_date(2017, 12, 1);
460        let limud = MishnaYomis.limud(test_date).expect("limud exists");
461        assert_eq!(limud.0.tractate, Tractate::Taanis);
462        assert_eq!(limud.0.chapter, 1);
463        assert_eq!(limud.0.mishna, 1);
464        assert_eq!(limud.1.tractate, Tractate::Taanis);
465        assert_eq!(limud.1.chapter, 1);
466        assert_eq!(limud.1.mishna, 2);
467    }
468
469    #[test]
470    fn mishna_yomis_2018_01_01() {
471        let test_date = from_gregorian_date(2018, 1, 1);
472        let limud = MishnaYomis.limud(test_date).expect("limud exists");
473        assert_eq!(limud.0.tractate, Tractate::Megillah);
474        assert_eq!(limud.0.chapter, 4);
475        assert_eq!(limud.0.mishna, 6);
476        assert_eq!(limud.1.tractate, Tractate::Megillah);
477        assert_eq!(limud.1.chapter, 4);
478        assert_eq!(limud.1.mishna, 7);
479    }
480
481    #[test]
482    fn mishna_yomis_2020_01_01() {
483        let test_date = from_gregorian_date(2020, 1, 1);
484        let limud = MishnaYomis.limud(test_date).expect("limud exists");
485        assert_eq!(limud.0.tractate, Tractate::Menachos);
486        assert_eq!(limud.0.chapter, 8);
487        assert_eq!(limud.0.mishna, 2);
488        assert_eq!(limud.1.tractate, Tractate::Menachos);
489        assert_eq!(limud.1.chapter, 8);
490        assert_eq!(limud.1.mishna, 3);
491    }
492
493    #[test]
494    fn mishna_yomis_2016_03_31() {
495        let test_date = from_gregorian_date(2016, 3, 31);
496        let limud = MishnaYomis.limud(test_date).expect("limud exists");
497        assert_eq!(limud.0.tractate, Tractate::Berachos);
498        assert_eq!(limud.0.chapter, 1);
499        assert_eq!(limud.0.mishna, 3);
500        assert_eq!(limud.1.tractate, Tractate::Berachos);
501        assert_eq!(limud.1.chapter, 1);
502        assert_eq!(limud.1.mishna, 4);
503    }
504
505    #[test]
506    fn mishna_yomis_2016_04_02() {
507        let test_date = from_gregorian_date(2016, 4, 2);
508        let limud = MishnaYomis.limud(test_date).expect("limud exists");
509        assert_eq!(limud.0.tractate, Tractate::Berachos);
510        assert_eq!(limud.0.chapter, 2);
511        assert_eq!(limud.0.mishna, 2);
512        assert_eq!(limud.1.tractate, Tractate::Berachos);
513        assert_eq!(limud.1.chapter, 2);
514        assert_eq!(limud.1.mishna, 3);
515    }
516
517    #[test]
518    fn mishna_yomis_2017_12_24() {
519        let test_date = from_gregorian_date(2017, 12, 24);
520        let limud = MishnaYomis.limud(test_date).expect("limud exists");
521        assert_eq!(limud.0.tractate, Tractate::Megillah);
522        assert_eq!(limud.0.chapter, 2);
523        assert_eq!(limud.0.mishna, 2);
524        assert_eq!(limud.1.tractate, Tractate::Megillah);
525        assert_eq!(limud.1.chapter, 2);
526        assert_eq!(limud.1.mishna, 3);
527    }
528
529    #[test]
530    fn mishna_yomis_2017_12_25() {
531        let test_date = from_gregorian_date(2017, 12, 25);
532        let limud = MishnaYomis.limud(test_date).expect("limud exists");
533        assert_eq!(limud.0.tractate, Tractate::Megillah);
534        assert_eq!(limud.0.chapter, 2);
535        assert_eq!(limud.0.mishna, 4);
536        assert_eq!(limud.1.tractate, Tractate::Megillah);
537        assert_eq!(limud.1.chapter, 2);
538        assert_eq!(limud.1.mishna, 5);
539    }
540
541    #[test]
542    fn mishna_yomis_2017_12_26() {
543        let test_date = from_gregorian_date(2017, 12, 26);
544        let limud = MishnaYomis.limud(test_date).expect("limud exists");
545        // Python: 'megillah 2:6-3:1' - spans two chapters
546        assert_eq!(limud.0.tractate, Tractate::Megillah);
547        assert_eq!(limud.0.chapter, 2);
548        assert_eq!(limud.0.mishna, 6);
549        assert_eq!(limud.1.tractate, Tractate::Megillah);
550        assert_eq!(limud.1.chapter, 3);
551        assert_eq!(limud.1.mishna, 1);
552    }
553
554    #[test]
555    fn mishna_yomis_2017_12_27() {
556        let test_date = from_gregorian_date(2017, 12, 27);
557        let limud = MishnaYomis.limud(test_date).expect("limud exists");
558        assert_eq!(limud.0.tractate, Tractate::Megillah);
559        assert_eq!(limud.0.chapter, 3);
560        assert_eq!(limud.0.mishna, 2);
561        assert_eq!(limud.1.tractate, Tractate::Megillah);
562        assert_eq!(limud.1.chapter, 3);
563        assert_eq!(limud.1.mishna, 3);
564    }
565
566    #[test]
567    fn mishna_yomis_2012_02_26_rosh_hashanah_chapter_boundary() {
568        // Regression test: Rosh Hashanah chapter 2 has 9 mishnayot, not 8
569        // This date crosses from chapter 2 to chapter 3
570        let test_date = from_gregorian_date(2012, 2, 26);
571        let limud = MishnaYomis.limud(test_date).expect("limud exists");
572        assert_eq!(limud.0.tractate, Tractate::RoshHashanah);
573        assert_eq!(limud.0.chapter, 2);
574        assert_eq!(limud.0.mishna, 9);
575        assert_eq!(limud.1.tractate, Tractate::RoshHashanah);
576        assert_eq!(limud.1.chapter, 3);
577        assert_eq!(limud.1.mishna, 1);
578    }
579
580    #[test]
581    fn mishna_yomis_2012_02_27_rosh_hashanah_chapter_3() {
582        // Regression test: Day after crossing chapter boundary
583        let test_date = from_gregorian_date(2012, 2, 27);
584        let limud = MishnaYomis.limud(test_date).expect("limud exists");
585        assert_eq!(limud.0.tractate, Tractate::RoshHashanah);
586        assert_eq!(limud.0.chapter, 3);
587        assert_eq!(limud.0.mishna, 2);
588        assert_eq!(limud.1.tractate, Tractate::RoshHashanah);
589        assert_eq!(limud.1.chapter, 3);
590        assert_eq!(limud.1.mishna, 3);
591    }
592}