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