1use crate::calendar_arithmetic::ArithmeticDate;
6use crate::calendar_arithmetic::DateFieldsResolver;
7use crate::calendar_arithmetic::PackWithMD;
8use crate::error::{
9 DateAddError, DateFromFieldsError, DateNewError, EcmaReferenceYearError, UnknownEraError,
10};
11use crate::options::DateFromFieldsOptions;
12use crate::options::{DateAddOptions, DateDifferenceOptions};
13use crate::types::DateFields;
14use crate::types::Month;
15use crate::{AsCalendar, RangeError};
16use crate::{Calendar, Date, types};
17use calendrical_calculations::rata_die::RataDie;
18use core::cmp::Ordering;
19use core::fmt::Debug;
20use icu_locale_core::preferences::extensions::unicode::keywords::{
21 CalendarAlgorithm, HijriCalendarAlgorithm,
22};
23use icu_provider::prelude::*;
24use tinystr::tinystr;
25
26#[path = "hijri/ummalqura_data.rs"]
27mod ummalqura_data;
28
29#[derive(Clone, Debug, Default, Copy)]
83#[allow(clippy::exhaustive_structs)] pub struct Hijri<S>(pub S);
85
86pub trait Rules: Clone + Debug + crate::cal::scaffold::UnstableSealed {
110 fn year(&self, extended_year: i32) -> HijriYear;
112
113 fn year_containing_rd(&self, rd: RataDie) -> HijriYear {
115 let extended_year = calendrical_calculations::islamic::tabular_year_from_fixed(
116 rd - 5,
118 calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY,
119 );
120
121 let mut year = self.year(extended_year);
122
123 if rd >= year.new_year() + year.packed.days_in_year() as i64 && extended_year < i32::MAX {
125 year = self.year(year.extended_year + 1)
126 }
127
128 year
129 }
130
131 fn ecma_reference_year(&self, _month: Month, _day: u8) -> Result<i32, EcmaReferenceYearError> {
146 Err(EcmaReferenceYearError::Unimplemented)
147 }
148
149 type DateCompatibilityError: Debug;
154
155 fn check_date_compatibility(&self, other: &Self) -> Result<(), Self::DateCompatibilityError>;
157
158 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
160 None
161 }
162
163 fn debug_name(&self) -> &'static str {
165 "Hijri (custom rules)"
166 }
167}
168
169#[derive(Copy, Clone, Debug, PartialEq, Eq)]
185#[non_exhaustive]
186#[deprecated(since = "2.2.0", note = "use `UmmAlQura`")]
187pub struct AstronomicalSimulation;
188
189#[allow(deprecated)]
190impl crate::cal::scaffold::UnstableSealed for AstronomicalSimulation {}
191#[allow(deprecated)]
192impl Rules for AstronomicalSimulation {
193 fn debug_name(&self) -> &'static str {
194 "Hijri (simulated, Mecca)"
195 }
196
197 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
198 None
199 }
200
201 fn year(&self, extended_year: i32) -> HijriYear {
202 UmmAlQura.year(extended_year)
203 }
204
205 fn ecma_reference_year(&self, month: Month, day: u8) -> Result<i32, EcmaReferenceYearError> {
206 UmmAlQura.ecma_reference_year(month, day)
207 }
208
209 fn year_containing_rd(&self, rd: RataDie) -> HijriYear {
210 UmmAlQura.year_containing_rd(rd)
211 }
212
213 type DateCompatibilityError = core::convert::Infallible;
214
215 fn check_date_compatibility(&self, &Self: &Self) -> Result<(), Self::DateCompatibilityError> {
216 Ok(())
217 }
218}
219
220#[derive(Copy, Clone, Debug, Default)]
234#[non_exhaustive]
235pub struct UmmAlQura;
236
237impl crate::cal::scaffold::UnstableSealed for UmmAlQura {}
238impl Rules for UmmAlQura {
239 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
240 Some(CalendarAlgorithm::Hijri(Some(
241 HijriCalendarAlgorithm::Umalqura,
242 )))
243 }
244
245 fn ecma_reference_year(&self, month: Month, day: u8) -> Result<i32, EcmaReferenceYearError> {
246 if month.is_leap() {
247 return Err(EcmaReferenceYearError::MonthNotInCalendar);
248 }
249
250 let extended_year = match (month.number(), day) {
251 (1, _) => 1392,
252 (2, 30..) => 1390,
253 (2, _) => 1392,
254 (3, 30..) => 1391,
255 (3, _) => 1392,
256 (4, _) => 1392,
257 (5, 30..) => 1391,
258 (5, _) => 1392,
259 (6, _) => 1392,
260 (7, 30..) => 1389,
261 (7, _) => 1392,
262 (8, _) => 1392,
263 (9, _) => 1392,
264 (10, 30..) => 1390,
265 (10, _) => 1392,
266 (11, ..26) => 1392,
267 (11, _) => 1391,
268 (12, 30..) => 1390,
269 (12, _) => 1391,
270 _ => return Err(EcmaReferenceYearError::MonthNotInCalendar),
271 };
272 Ok(extended_year)
273 }
274
275 fn debug_name(&self) -> &'static str {
276 "Hijri (Umm al-Qura)"
277 }
278
279 fn year(&self, extended_year: i32) -> HijriYear {
280 if let Some(data) = HijriYear::lookup(
281 extended_year,
282 ummalqura_data::STARTING_YEAR,
283 ummalqura_data::DATA,
284 ) {
285 data
286 } else {
287 TabularAlgorithm {
288 leap_years: TabularAlgorithmLeapYears::TypeII,
289 epoch: TabularAlgorithmEpoch::Friday,
290 }
291 .year(extended_year)
292 }
293 }
294
295 type DateCompatibilityError = core::convert::Infallible;
296
297 fn check_date_compatibility(&self, &Self: &Self) -> Result<(), Self::DateCompatibilityError> {
298 Ok(())
299 }
300}
301
302#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
315pub struct TabularAlgorithm {
316 pub(crate) leap_years: TabularAlgorithmLeapYears,
317 pub(crate) epoch: TabularAlgorithmEpoch,
318}
319
320impl TabularAlgorithm {
321 pub const fn new(leap_years: TabularAlgorithmLeapYears, epoch: TabularAlgorithmEpoch) -> Self {
323 Self { epoch, leap_years }
324 }
325}
326
327impl crate::cal::scaffold::UnstableSealed for TabularAlgorithm {}
328impl Rules for TabularAlgorithm {
329 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
330 Some(match (self.epoch, self.leap_years) {
331 (TabularAlgorithmEpoch::Friday, TabularAlgorithmLeapYears::TypeII) => {
332 CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Civil))
333 }
334 (TabularAlgorithmEpoch::Thursday, TabularAlgorithmLeapYears::TypeII) => {
335 CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Tbla))
336 }
337 })
338 }
339
340 fn ecma_reference_year(&self, month: Month, day: u8) -> Result<i32, EcmaReferenceYearError> {
341 if month.is_leap() {
342 return Err(EcmaReferenceYearError::MonthNotInCalendar);
343 }
344
345 Ok(match (month.number(), day) {
346 (1, _) => 1392,
347 (2, _) => 1392,
348 (3, _) => 1392,
349 (4, _) => 1392,
350 (5, _) => 1392,
351 (6, _) => 1392,
352 (7, _) => 1392,
353 (8, _) => 1392,
354 (9, _) => 1392,
355 (10, _) => 1392,
356 (11, ..26) if self.epoch == TabularAlgorithmEpoch::Friday => 1392,
357 (11, ..27) if self.epoch == TabularAlgorithmEpoch::Thursday => 1392,
358 (11, _) => 1391,
359 (12, 30..) => 1390,
360 (12, _) => 1391,
361 _ => return Err(EcmaReferenceYearError::MonthNotInCalendar),
362 })
363 }
364
365 fn debug_name(&self) -> &'static str {
366 match self.epoch {
367 TabularAlgorithmEpoch::Friday => "Hijri (civil)",
368 TabularAlgorithmEpoch::Thursday => "Hijri (astronomical)",
369 }
370 }
371
372 fn year(&self, extended_year: i32) -> HijriYear {
373 let start_day = calendrical_calculations::islamic::fixed_from_tabular_islamic(
374 extended_year,
375 1,
376 1,
377 self.epoch.rata_die(),
378 );
379 let month_lengths = core::array::from_fn(|m| {
380 m % 2 == 0
381 || m == 11
382 && match self.leap_years {
383 TabularAlgorithmLeapYears::TypeII => {
384 (14 + 11 * extended_year as i64).rem_euclid(30) < 11
385 }
386 }
387 });
388 HijriYear {
389 packed: PackedHijriYearData::new_unchecked(extended_year, month_lengths, start_day),
392 extended_year,
393 }
394 }
395
396 type DateCompatibilityError = crate::error::MismatchedCalendarError;
397
398 fn check_date_compatibility(&self, other: &Self) -> Result<(), Self::DateCompatibilityError> {
399 if self != other {
400 return Err(crate::error::MismatchedCalendarError);
401 }
402 Ok(())
403 }
404}
405
406#[allow(deprecated)]
407impl Hijri<AstronomicalSimulation> {
408 #[cfg(feature = "compiled_data")]
410 #[deprecated(since = "2.1.0", note = "use `Hijri::new_umm_al_qura`")]
411 pub const fn new_mecca() -> Self {
412 Self::new_simulated_mecca()
413 }
414
415 #[deprecated(since = "2.2.0", note = "use `Hijri::new_umm_al_qura`")]
421 pub const fn new_simulated_mecca() -> Self {
422 Self(AstronomicalSimulation)
423 }
424
425 #[cfg(feature = "serde")]
426 #[doc = icu_provider::gen_buffer_unstable_docs!(BUFFER,Self::new)]
427 #[deprecated(since = "2.1.0", note = "use `Hijri::new_umm_al_qura`")]
428 pub fn try_new_mecca_with_buffer_provider(
429 _provider: &(impl BufferProvider + ?Sized),
430 ) -> Result<Self, DataError> {
431 Ok(Self::new_simulated_mecca())
432 }
433
434 #[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::new_mecca)]
435 #[deprecated(since = "2.1.0", note = "use `Hijri::new_umm_al_qura`")]
436 pub fn try_new_mecca_unstable<D: ?Sized>(_provider: &D) -> Result<Self, DataError> {
437 Ok(Self::new_simulated_mecca())
438 }
439
440 #[deprecated(since = "2.1.0", note = "use `Hijri::new_umm_al_qura`")]
442 pub const fn new_mecca_always_calculating() -> Self {
443 Self::new_simulated_mecca()
444 }
445}
446
447impl Hijri<UmmAlQura> {
448 #[deprecated(since = "2.1.0", note = "use `Self::new_umm_al_qura`")]
450 pub const fn new() -> Self {
451 Self(UmmAlQura)
452 }
453
454 pub const fn new_umm_al_qura() -> Self {
456 Self(UmmAlQura)
457 }
458}
459
460#[non_exhaustive]
462#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
463pub enum TabularAlgorithmEpoch {
464 Thursday,
466 Friday,
468}
469
470impl TabularAlgorithmEpoch {
471 fn rata_die(self) -> RataDie {
472 match self {
473 Self::Thursday => calendrical_calculations::islamic::ISLAMIC_EPOCH_THURSDAY,
474 Self::Friday => calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY,
475 }
476 }
477}
478
479#[non_exhaustive]
484#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
485pub enum TabularAlgorithmLeapYears {
486 TypeII,
488}
489
490impl Hijri<TabularAlgorithm> {
491 #[deprecated(since = "2.1.0", note = "use `Hijri::new_tabular`")]
493 pub const fn new(leap_years: TabularAlgorithmLeapYears, epoch: TabularAlgorithmEpoch) -> Self {
494 Hijri::new_tabular(leap_years, epoch)
495 }
496
497 pub const fn new_tabular(
499 leap_years: TabularAlgorithmLeapYears,
500 epoch: TabularAlgorithmEpoch,
501 ) -> Self {
502 Self(TabularAlgorithm::new(leap_years, epoch))
503 }
504}
505
506#[derive(Copy, Clone, Debug)]
515pub struct HijriYear {
516 packed: PackedHijriYearData,
517 extended_year: i32,
518}
519
520impl PartialEq for HijriYear {
521 fn eq(&self, other: &Self) -> bool {
522 self.extended_year == other.extended_year
523 }
524}
525impl Eq for HijriYear {}
526impl core::hash::Hash for HijriYear {
527 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
528 self.extended_year.hash(state);
529 }
530}
531impl PartialOrd for HijriYear {
532 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
533 Some(self.cmp(other))
534 }
535}
536impl Ord for HijriYear {
537 fn cmp(&self, other: &Self) -> Ordering {
538 self.extended_year.cmp(&other.extended_year)
539 }
540}
541
542impl core::ops::Sub<HijriYear> for HijriYear {
543 type Output = i32;
544 #[inline]
545 fn sub(self, rhs: HijriYear) -> Self::Output {
546 self.extended_year - rhs.extended_year
547 }
548}
549
550impl HijriYear {
551 pub const fn try_new(extended_year: i32, month_starts: [RataDie; 13]) -> Option<Self> {
562 let mut month_lengths = [false; 12];
563
564 let mut i = 0;
565 #[allow(clippy::indexing_slicing)]
566 while i < 12 {
567 match month_starts[i + 1].to_i64_date() - month_starts[i].to_i64_date() {
568 29 => month_lengths[i] = false,
569 30 => month_lengths[i] = true,
570 _ => return None,
571 }
572 i += 1;
573 }
574
575 let Some(packed) =
576 PackedHijriYearData::try_new(extended_year, month_lengths, month_starts[0])
577 else {
578 return None;
579 };
580
581 Some(Self {
582 packed,
583 extended_year,
584 })
585 }
586
587 fn lookup(
588 extended_year: i32,
589 starting_year: i32,
590 data: &[PackedHijriYearData],
591 ) -> Option<Self> {
592 Some(extended_year)
593 .and_then(|e| usize::try_from(e.checked_sub(starting_year)?).ok())
594 .and_then(|i| data.get(i))
595 .map(|&packed| Self {
596 extended_year,
597 packed,
598 })
599 }
600
601 fn month_day_for(self, day_of_year: u16) -> (u8, u8) {
603 let mut month = ((day_of_year - 1) / 30) as u8 + 1;
606 let mut days_before_month = self.packed.days_before_month(month);
607 let mut last_day_of_month = self.packed.days_before_month(month + 1);
608
609 while day_of_year > last_day_of_month {
610 month += 1;
611 days_before_month = last_day_of_month;
612 last_day_of_month = self.packed.days_before_month(month + 1);
613 }
614
615 (month, (day_of_year - days_before_month) as u8)
616 }
617
618 fn new_year(self) -> RataDie {
619 self.packed.new_year(self.extended_year)
620 }
621}
622
623#[derive(Copy, Clone, Hash, Debug)]
647#[cfg_attr(test, derive(PartialEq))]
648struct PackedHijriYearData(u16);
649
650impl PackedHijriYearData {
651 const fn try_new(
652 extended_year: i32,
653 month_lengths: [bool; 12],
654 start_day: RataDie,
655 ) -> Option<Self> {
656 let start_offset @ -5..=5 = start_day.since(Self::mean_tabular_start_day(extended_year))
657 else {
658 return None;
659 };
660
661 let start_offset = start_offset as i8 & 0b1000_0111u8 as i8;
662
663 let mut all = 0u16;
664
665 let mut i = 0;
666 while i < 12 {
667 #[expect(clippy::indexing_slicing)]
668 if month_lengths[i] {
669 all |= 1 << i;
670 }
671 i += 1;
672 }
673
674 if start_offset < 0 {
675 all |= 1 << 12;
676 }
677 all |= (start_offset.unsigned_abs() as u16) << 13;
678 Some(Self(all))
679 }
680
681 const fn new_unchecked(
682 extended_year: i32,
683 month_lengths: [bool; 12],
684 start_day: RataDie,
685 ) -> Self {
686 let start_offset = start_day.since(Self::mean_tabular_start_day(extended_year));
687
688 let start_offset = start_offset as i8 & 0b1000_0111u8 as i8;
689
690 let mut all = 0u16;
691
692 let mut i = 0;
693 while i < 12 {
694 #[expect(clippy::indexing_slicing)]
695 if month_lengths[i] {
696 all |= 1 << i;
697 }
698 i += 1;
699 }
700
701 if start_offset < 0 {
702 all |= 1 << 12;
703 }
704 all |= (start_offset.unsigned_abs() as u16) << 13;
705 Self(all)
706 }
707
708 fn new_year(self, extended_year: i32) -> RataDie {
709 let start_offset = if (self.0 & 0b1_0000_0000_0000) != 0 {
710 -((self.0 >> 13) as i64)
711 } else {
712 (self.0 >> 13) as i64
713 };
714 Self::mean_tabular_start_day(extended_year) + start_offset
715 }
716
717 fn month_len(self, month: u8) -> u8 {
718 29 + (self.0 >> (month - 1) & 1) as u8
719 }
720
721 fn is_leap(self) -> bool {
722 (self.0 & ((1 << 12) - 1)).count_ones() == 7
723 }
724
725 fn days_before_month(self, month: u8) -> u16 {
727 let mut prev_month_lengths = 29 * (month - 1) as u16;
729 let long_month_bits = self.0 & ((1 << (month - 1) as u16) - 1);
733 prev_month_lengths += long_month_bits.count_ones().try_into().unwrap_or(0);
734 prev_month_lengths
735 }
736
737 fn days_in_year(self) -> u16 {
738 self.days_before_month(13)
739 }
740
741 const fn mean_tabular_start_day(extended_year: i32) -> RataDie {
742 calendrical_calculations::islamic::ISLAMIC_EPOCH_FRIDAY
744 .add((extended_year as i64 - 1) * (354 * 30 + 11) / 30)
745 }
746}
747
748impl PackWithMD for HijriYear {
749 type Packed = [u8; 6];
751
752 fn pack(self, month: u8, day: u8) -> Self::Packed {
753 let [a, b] = self.packed.0.to_le_bytes();
754 let [c, d, e, f] = self.extended_year.pack(month, day);
755 [a, b, c, d, e, f]
756 }
757
758 fn unpack_year([a, b, c, d, e, f]: Self::Packed) -> Self {
759 let packed = PackedHijriYearData(u16::from_le_bytes([a, b]));
760 let extended_year = i32::unpack_year([c, d, e, f]);
761 Self {
762 extended_year,
763 packed,
764 }
765 }
766
767 fn unpack_month([_, _, c, d, e, f]: Self::Packed) -> u8 {
768 i32::unpack_month([c, d, e, f])
769 }
770
771 fn unpack_day([_, _, c, d, e, f]: Self::Packed) -> u8 {
772 i32::unpack_day([c, d, e, f])
773 }
774}
775
776#[allow(deprecated)]
777impl<A: AsCalendar<Calendar = Hijri<AstronomicalSimulation>>> Date<A> {
778 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar`")]
780 pub fn try_new_simulated_hijri_with_calendar(
781 year: i32,
782 month: u8,
783 day: u8,
784 calendar: A,
785 ) -> Result<Date<A>, RangeError> {
786 Date::try_new_hijri_with_calendar(year, month, day, calendar)
787 }
788}
789
790#[test]
791fn computer_reference_years() {
792 fn compute_hijri_reference_year<R>(ordinal_month: u8, day: u8, rules: R) -> Option<HijriYear>
793 where
794 R: Rules + Copy,
795 {
796 let cal = Hijri(rules);
797 let dec_31 = Date::from_rata_die(
798 crate::cal::abstract_gregorian::LAST_DAY_OF_REFERENCE_YEAR,
799 cal,
800 );
801 debug_assert_eq!(dec_31.month().ordinal, 11);
803 let (y0, y1, y2, y3) =
804 if ordinal_month < 11 || (ordinal_month == 11 && day <= dec_31.day_of_month().0) {
805 (1389, 1390, 1391, 1392)
806 } else {
807 (1388, 1389, 1390, 1391)
808 };
809 let year_info = cal.0.year(y3);
810 if day <= Hijri::<R>::days_in_provided_month(year_info, ordinal_month) {
811 return Some(year_info);
812 }
813 let year_info = cal.0.year(y2);
814 if day <= Hijri::<R>::days_in_provided_month(year_info, ordinal_month) {
815 return Some(year_info);
816 }
817 let year_info = cal.0.year(y1);
818 if day <= Hijri::<R>::days_in_provided_month(year_info, ordinal_month) {
819 return Some(year_info);
820 }
821 let year_info = cal.0.year(y0);
822 if day <= Hijri::<R>::days_in_provided_month(year_info, ordinal_month) {
823 return Some(year_info);
824 }
825 None
826 }
827
828 fn compute_all_reference_years<R>(rules: R)
829 where
830 R: Rules + Copy + Debug,
831 {
832 println!("Reference years for {:?}", rules);
833 for month in 1..=12 {
834 let y_29 = compute_hijri_reference_year(month, 29, rules)
835 .expect("All Hijri calendars have d = 29")
836 .extended_year;
837 let y_30 = compute_hijri_reference_year(month, 30, rules).map(|y| y.extended_year);
838
839 if let Some(y_30) = y_30
840 && y_29 != y_30
841 {
842 println!("({month}, 30..) => {y_30},")
843 }
844 if month == 11 {
845 let y_1 = compute_hijri_reference_year(month, 1, rules)
846 .expect("All Hijri calendars have d = 1")
847 .extended_year;
848 for day in 1..29 {
849 let y_n = compute_hijri_reference_year(month, day, rules)
850 .expect("All Hijri calendars have d < 29")
851 .extended_year;
852 if y_n != y_1 {
853 println!("({month}, ..{day}) => {y_1},");
854 break;
855 }
856 }
857 }
858 println!("({month}, _) => {y_29},");
859 }
860 }
861
862 compute_all_reference_years(UmmAlQura);
863 compute_all_reference_years(TabularAlgorithm {
864 leap_years: TabularAlgorithmLeapYears::TypeII,
865 epoch: TabularAlgorithmEpoch::Friday,
866 });
867 compute_all_reference_years(TabularAlgorithm {
868 leap_years: TabularAlgorithmLeapYears::TypeII,
869 epoch: TabularAlgorithmEpoch::Thursday,
870 });
871}
872
873#[allow(clippy::derived_hash_with_manual_eq)] #[derive(Clone, Debug, Hash)]
875pub struct HijriDateInner<R: Rules>(ArithmeticDate<Hijri<R>>);
877
878impl<R: Rules> Copy for HijriDateInner<R> {}
879impl<R: Rules> PartialEq for HijriDateInner<R> {
880 fn eq(&self, other: &Self) -> bool {
881 self.0 == other.0
882 }
883}
884impl<R: Rules> Eq for HijriDateInner<R> {}
885impl<R: Rules> PartialOrd for HijriDateInner<R> {
886 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
887 Some(self.cmp(other))
888 }
889}
890impl<R: Rules> Ord for HijriDateInner<R> {
891 fn cmp(&self, other: &Self) -> Ordering {
892 self.0.cmp(&other.0)
893 }
894}
895
896impl<R: Rules> DateFieldsResolver for Hijri<R> {
897 type YearInfo = HijriYear;
898
899 fn days_in_provided_month(year: Self::YearInfo, month: u8) -> u8 {
900 year.packed.month_len(month)
901 }
902
903 #[inline]
904 fn extended_year_from_era_year_unchecked(
905 &self,
906 era: &[u8],
907 era_year: i32,
908 ) -> Result<i32, UnknownEraError> {
909 let extended_year = match era {
910 b"ah" => era_year,
911 b"bh" => 1 - era_year,
912 _ => return Err(UnknownEraError),
913 };
914 Ok(extended_year)
915 }
916
917 #[inline]
918 fn year_info_from_extended(&self, extended_year: i32) -> Self::YearInfo {
919 debug_assert!(crate::calendar_arithmetic::SAFE_YEAR_RANGE.contains(&extended_year));
920 self.0.year(extended_year)
921 }
922
923 #[inline]
924 fn extended_from_year_info(&self, year_info: Self::YearInfo) -> i32 {
925 year_info.extended_year
926 }
927
928 #[inline]
929 fn reference_year_from_month_day(
930 &self,
931 month: Month,
932 day: u8,
933 ) -> Result<Self::YearInfo, EcmaReferenceYearError> {
934 self.0
935 .ecma_reference_year(month, day)
936 .map(|y| self.0.year(y))
937 }
938
939 fn to_rata_die_inner(year: Self::YearInfo, month: u8, day: u8) -> RataDie {
940 year.new_year() + year.packed.days_before_month(month) as i64 + (day - 1) as i64
941 }
942}
943
944impl<R: Rules> crate::cal::scaffold::UnstableSealed for Hijri<R> {}
945impl<R: Rules> Calendar for Hijri<R> {
946 type DateInner = HijriDateInner<R>;
947 type Year = types::EraYear;
948 type DateCompatibilityError = R::DateCompatibilityError;
949
950 fn new_date(
951 &self,
952 year: types::YearInput,
953 month: Month,
954 day: u8,
955 ) -> Result<Self::DateInner, DateNewError> {
956 ArithmeticDate::from_input_year_month_code_day(year, month, day, self).map(HijriDateInner)
957 }
958
959 fn from_fields(
960 &self,
961 fields: DateFields,
962 options: DateFromFieldsOptions,
963 ) -> Result<Self::DateInner, DateFromFieldsError> {
964 ArithmeticDate::from_fields(fields, options, self).map(HijriDateInner)
965 }
966
967 fn from_rata_die(&self, rd: RataDie) -> Self::DateInner {
968 let year = self.0.year_containing_rd(rd);
969
970 let (month, day) = year.month_day_for((rd - year.new_year()) as u16 + 1);
971
972 HijriDateInner(ArithmeticDate::new_unchecked(year, month, day))
974 }
975
976 fn to_rata_die(&self, date: &Self::DateInner) -> RataDie {
977 date.0.to_rata_die()
978 }
979
980 fn has_cheap_iso_conversion(&self) -> bool {
981 false
982 }
983
984 fn months_in_year(&self, date: &Self::DateInner) -> u8 {
985 Self::months_in_provided_year(date.0.year())
986 }
987
988 fn days_in_year(&self, date: &Self::DateInner) -> u16 {
989 date.0.year().packed.days_in_year()
990 }
991
992 fn days_in_month(&self, date: &Self::DateInner) -> u8 {
993 Self::days_in_provided_month(date.0.year(), date.0.month())
994 }
995
996 fn add(
997 &self,
998 date: &Self::DateInner,
999 duration: types::DateDuration,
1000 options: DateAddOptions,
1001 ) -> Result<Self::DateInner, DateAddError> {
1002 date.0.added(duration, self, options).map(HijriDateInner)
1003 }
1004
1005 fn until(
1006 &self,
1007 date1: &Self::DateInner,
1008 date2: &Self::DateInner,
1009 options: DateDifferenceOptions,
1010 ) -> types::DateDuration {
1011 date1.0.until(&date2.0, self, options)
1012 }
1013
1014 fn check_date_compatibility(&self, other: &Self) -> Result<(), Self::DateCompatibilityError> {
1015 self.0.check_date_compatibility(&other.0)
1016 }
1017
1018 fn debug_name(&self) -> &'static str {
1019 self.0.debug_name()
1020 }
1021
1022 fn year_info(&self, date: &Self::DateInner) -> Self::Year {
1023 let extended_year = date.0.year().extended_year;
1024 if extended_year > 0 {
1025 types::EraYear {
1026 era: tinystr!(16, "ah"),
1027 era_index: Some(0),
1028 year: extended_year,
1029 extended_year,
1030 ambiguity: types::YearAmbiguity::CenturyRequired,
1031 }
1032 } else {
1033 types::EraYear {
1034 era: tinystr!(16, "bh"),
1035 era_index: Some(1),
1036 year: 1 - extended_year,
1037 extended_year,
1038 ambiguity: types::YearAmbiguity::CenturyRequired,
1039 }
1040 }
1041 }
1042
1043 fn is_in_leap_year(&self, date: &Self::DateInner) -> bool {
1044 date.0.year().packed.is_leap()
1045 }
1046
1047 fn month(&self, date: &Self::DateInner) -> types::MonthInfo {
1048 types::MonthInfo::new(self, date.0)
1049 }
1050
1051 fn day_of_month(&self, date: &Self::DateInner) -> types::DayOfMonth {
1052 types::DayOfMonth(date.0.day())
1053 }
1054
1055 fn day_of_year(&self, date: &Self::DateInner) -> types::DayOfYear {
1056 types::DayOfYear(
1057 date.0.year().packed.days_before_month(date.0.month()) + date.0.day() as u16,
1058 )
1059 }
1060
1061 fn calendar_algorithm(&self) -> Option<CalendarAlgorithm> {
1062 self.0.calendar_algorithm()
1063 }
1064}
1065
1066impl<A: AsCalendar<Calendar = Hijri<R>>, R: Rules> Date<A> {
1067 pub fn try_new_hijri_with_calendar(
1086 year: i32,
1087 month: u8,
1088 day: u8,
1089 calendar: A,
1090 ) -> Result<Self, RangeError> {
1091 ArithmeticDate::from_year_month_day(year, month, day, calendar.as_calendar())
1092 .map(HijriDateInner)
1093 .map(|inner| Date::from_raw(inner, calendar))
1094 }
1095}
1096
1097impl Date<Hijri<UmmAlQura>> {
1098 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar`")]
1100 pub fn try_new_ummalqura(year: i32, month: u8, day: u8) -> Result<Self, RangeError> {
1101 Date::try_new_hijri_with_calendar(year, month, day, Hijri::new_umm_al_qura())
1102 }
1103}
1104
1105impl<A: AsCalendar<Calendar = Hijri<TabularAlgorithm>>> Date<A> {
1106 #[deprecated(since = "2.1.0", note = "use `Date::try_new_hijri_with_calendar`")]
1108 pub fn try_new_hijri_tabular_with_calendar(
1109 year: i32,
1110 month: u8,
1111 day: u8,
1112 calendar: A,
1113 ) -> Result<Date<A>, RangeError> {
1114 Date::try_new_hijri_with_calendar(year, month, day, calendar)
1115 }
1116}
1117
1118#[cfg(test)]
1119mod test {
1120 use super::*;
1121
1122 const START_YEAR: i32 = -1245;
1123 const END_YEAR: i32 = 1518;
1124
1125 #[derive(Debug)]
1126 struct DateCase {
1127 year: i32,
1128 month: u8,
1129 day: u8,
1130 }
1131
1132 static TEST_RD: [i64; 33] = [
1133 -214193, -61387, 25469, 49217, 171307, 210155, 253427, 369740, 400085, 434355, 452605,
1134 470160, 473837, 507850, 524156, 544676, 567118, 569477, 601716, 613424, 626596, 645554,
1135 664224, 671401, 694799, 704424, 708842, 709409, 709580, 727274, 728714, 744313, 764652,
1136 ];
1137
1138 static UMMALQURA_CASES: [DateCase; 33] = [
1139 DateCase {
1140 year: -1245,
1141 month: 12,
1142 day: 9,
1143 },
1144 DateCase {
1145 year: -813,
1146 month: 2,
1147 day: 23,
1148 },
1149 DateCase {
1150 year: -568,
1151 month: 4,
1152 day: 1,
1153 },
1154 DateCase {
1155 year: -501,
1156 month: 4,
1157 day: 6,
1158 },
1159 DateCase {
1160 year: -157,
1161 month: 10,
1162 day: 17,
1163 },
1164 DateCase {
1165 year: -47,
1166 month: 6,
1167 day: 3,
1168 },
1169 DateCase {
1170 year: 75,
1171 month: 7,
1172 day: 13,
1173 },
1174 DateCase {
1175 year: 403,
1176 month: 10,
1177 day: 5,
1178 },
1179 DateCase {
1180 year: 489,
1181 month: 5,
1182 day: 22,
1183 },
1184 DateCase {
1185 year: 586,
1186 month: 2,
1187 day: 7,
1188 },
1189 DateCase {
1190 year: 637,
1191 month: 8,
1192 day: 7,
1193 },
1194 DateCase {
1195 year: 687,
1196 month: 2,
1197 day: 20,
1198 },
1199 DateCase {
1200 year: 697,
1201 month: 7,
1202 day: 7,
1203 },
1204 DateCase {
1205 year: 793,
1206 month: 7,
1207 day: 1,
1208 },
1209 DateCase {
1210 year: 839,
1211 month: 7,
1212 day: 6,
1213 },
1214 DateCase {
1215 year: 897,
1216 month: 6,
1217 day: 1,
1218 },
1219 DateCase {
1220 year: 960,
1221 month: 9,
1222 day: 30,
1223 },
1224 DateCase {
1225 year: 967,
1226 month: 5,
1227 day: 27,
1228 },
1229 DateCase {
1230 year: 1058,
1231 month: 5,
1232 day: 18,
1233 },
1234 DateCase {
1235 year: 1091,
1236 month: 6,
1237 day: 2,
1238 },
1239 DateCase {
1240 year: 1128,
1241 month: 8,
1242 day: 4,
1243 },
1244 DateCase {
1245 year: 1182,
1246 month: 2,
1247 day: 3,
1248 },
1249 DateCase {
1250 year: 1234,
1251 month: 10,
1252 day: 10,
1253 },
1254 DateCase {
1255 year: 1255,
1256 month: 1,
1257 day: 11,
1258 },
1259 DateCase {
1260 year: 1321,
1261 month: 1,
1262 day: 21,
1263 },
1264 DateCase {
1265 year: 1348,
1266 month: 3,
1267 day: 20,
1268 },
1269 DateCase {
1270 year: 1360,
1271 month: 9,
1272 day: 7,
1273 },
1274 DateCase {
1275 year: 1362,
1276 month: 4,
1277 day: 14,
1278 },
1279 DateCase {
1280 year: 1362,
1281 month: 10,
1282 day: 7,
1283 },
1284 DateCase {
1285 year: 1412,
1286 month: 9,
1287 day: 12,
1288 },
1289 DateCase {
1290 year: 1416,
1291 month: 10,
1292 day: 6,
1293 },
1294 DateCase {
1295 year: 1460,
1296 month: 10,
1297 day: 13,
1298 },
1299 DateCase {
1300 year: 1518,
1301 month: 3,
1302 day: 5,
1303 },
1304 ];
1305
1306 static ARITHMETIC_CASES: [DateCase; 33] = [
1307 DateCase {
1308 year: -1245,
1309 month: 12,
1310 day: 9,
1311 },
1312 DateCase {
1313 year: -813,
1314 month: 2,
1315 day: 23,
1316 },
1317 DateCase {
1318 year: -568,
1319 month: 4,
1320 day: 1,
1321 },
1322 DateCase {
1323 year: -501,
1324 month: 4,
1325 day: 6,
1326 },
1327 DateCase {
1328 year: -157,
1329 month: 10,
1330 day: 17,
1331 },
1332 DateCase {
1333 year: -47,
1334 month: 6,
1335 day: 3,
1336 },
1337 DateCase {
1338 year: 75,
1339 month: 7,
1340 day: 13,
1341 },
1342 DateCase {
1343 year: 403,
1344 month: 10,
1345 day: 5,
1346 },
1347 DateCase {
1348 year: 489,
1349 month: 5,
1350 day: 22,
1351 },
1352 DateCase {
1353 year: 586,
1354 month: 2,
1355 day: 7,
1356 },
1357 DateCase {
1358 year: 637,
1359 month: 8,
1360 day: 7,
1361 },
1362 DateCase {
1363 year: 687,
1364 month: 2,
1365 day: 20,
1366 },
1367 DateCase {
1368 year: 697,
1369 month: 7,
1370 day: 7,
1371 },
1372 DateCase {
1373 year: 793,
1374 month: 7,
1375 day: 1,
1376 },
1377 DateCase {
1378 year: 839,
1379 month: 7,
1380 day: 6,
1381 },
1382 DateCase {
1383 year: 897,
1384 month: 6,
1385 day: 1,
1386 },
1387 DateCase {
1388 year: 960,
1389 month: 9,
1390 day: 30,
1391 },
1392 DateCase {
1393 year: 967,
1394 month: 5,
1395 day: 27,
1396 },
1397 DateCase {
1398 year: 1058,
1399 month: 5,
1400 day: 18,
1401 },
1402 DateCase {
1403 year: 1091,
1404 month: 6,
1405 day: 2,
1406 },
1407 DateCase {
1408 year: 1128,
1409 month: 8,
1410 day: 4,
1411 },
1412 DateCase {
1413 year: 1182,
1414 month: 2,
1415 day: 3,
1416 },
1417 DateCase {
1418 year: 1234,
1419 month: 10,
1420 day: 10,
1421 },
1422 DateCase {
1423 year: 1255,
1424 month: 1,
1425 day: 11,
1426 },
1427 DateCase {
1428 year: 1321,
1429 month: 1,
1430 day: 21,
1431 },
1432 DateCase {
1433 year: 1348,
1434 month: 3,
1435 day: 19,
1436 },
1437 DateCase {
1438 year: 1360,
1439 month: 9,
1440 day: 8,
1441 },
1442 DateCase {
1443 year: 1362,
1444 month: 4,
1445 day: 13,
1446 },
1447 DateCase {
1448 year: 1362,
1449 month: 10,
1450 day: 7,
1451 },
1452 DateCase {
1453 year: 1412,
1454 month: 9,
1455 day: 13,
1456 },
1457 DateCase {
1458 year: 1416,
1459 month: 10,
1460 day: 5,
1461 },
1462 DateCase {
1463 year: 1460,
1464 month: 10,
1465 day: 12,
1466 },
1467 DateCase {
1468 year: 1518,
1469 month: 3,
1470 day: 5,
1471 },
1472 ];
1473
1474 static ASTRONOMICAL_CASES: [DateCase; 33] = [
1475 DateCase {
1476 year: -1245,
1477 month: 12,
1478 day: 10,
1479 },
1480 DateCase {
1481 year: -813,
1482 month: 2,
1483 day: 24,
1484 },
1485 DateCase {
1486 year: -568,
1487 month: 4,
1488 day: 2,
1489 },
1490 DateCase {
1491 year: -501,
1492 month: 4,
1493 day: 7,
1494 },
1495 DateCase {
1496 year: -157,
1497 month: 10,
1498 day: 18,
1499 },
1500 DateCase {
1501 year: -47,
1502 month: 6,
1503 day: 4,
1504 },
1505 DateCase {
1506 year: 75,
1507 month: 7,
1508 day: 14,
1509 },
1510 DateCase {
1511 year: 403,
1512 month: 10,
1513 day: 6,
1514 },
1515 DateCase {
1516 year: 489,
1517 month: 5,
1518 day: 23,
1519 },
1520 DateCase {
1521 year: 586,
1522 month: 2,
1523 day: 8,
1524 },
1525 DateCase {
1526 year: 637,
1527 month: 8,
1528 day: 8,
1529 },
1530 DateCase {
1531 year: 687,
1532 month: 2,
1533 day: 21,
1534 },
1535 DateCase {
1536 year: 697,
1537 month: 7,
1538 day: 8,
1539 },
1540 DateCase {
1541 year: 793,
1542 month: 7,
1543 day: 2,
1544 },
1545 DateCase {
1546 year: 839,
1547 month: 7,
1548 day: 7,
1549 },
1550 DateCase {
1551 year: 897,
1552 month: 6,
1553 day: 2,
1554 },
1555 DateCase {
1556 year: 960,
1557 month: 10,
1558 day: 1,
1559 },
1560 DateCase {
1561 year: 967,
1562 month: 5,
1563 day: 28,
1564 },
1565 DateCase {
1566 year: 1058,
1567 month: 5,
1568 day: 19,
1569 },
1570 DateCase {
1571 year: 1091,
1572 month: 6,
1573 day: 3,
1574 },
1575 DateCase {
1576 year: 1128,
1577 month: 8,
1578 day: 5,
1579 },
1580 DateCase {
1581 year: 1182,
1582 month: 2,
1583 day: 4,
1584 },
1585 DateCase {
1586 year: 1234,
1587 month: 10,
1588 day: 11,
1589 },
1590 DateCase {
1591 year: 1255,
1592 month: 1,
1593 day: 12,
1594 },
1595 DateCase {
1596 year: 1321,
1597 month: 1,
1598 day: 22,
1599 },
1600 DateCase {
1601 year: 1348,
1602 month: 3,
1603 day: 20,
1604 },
1605 DateCase {
1606 year: 1360,
1607 month: 9,
1608 day: 9,
1609 },
1610 DateCase {
1611 year: 1362,
1612 month: 4,
1613 day: 14,
1614 },
1615 DateCase {
1616 year: 1362,
1617 month: 10,
1618 day: 8,
1619 },
1620 DateCase {
1621 year: 1412,
1622 month: 9,
1623 day: 14,
1624 },
1625 DateCase {
1626 year: 1416,
1627 month: 10,
1628 day: 6,
1629 },
1630 DateCase {
1631 year: 1460,
1632 month: 10,
1633 day: 13,
1634 },
1635 DateCase {
1636 year: 1518,
1637 month: 3,
1638 day: 6,
1639 },
1640 ];
1641
1642 #[test]
1643 fn test_rd_from_hijri() {
1644 let calendar = Hijri::new_tabular(
1645 TabularAlgorithmLeapYears::TypeII,
1646 TabularAlgorithmEpoch::Friday,
1647 );
1648 for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
1649 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1650 .unwrap();
1651 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1652 }
1653 }
1654
1655 #[test]
1656 fn test_hijri_from_rd() {
1657 let calendar = Hijri::new_tabular(
1658 TabularAlgorithmLeapYears::TypeII,
1659 TabularAlgorithmEpoch::Friday,
1660 );
1661 for (case, f_date) in ARITHMETIC_CASES.iter().zip(TEST_RD.iter()) {
1662 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1663 .unwrap();
1664 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1665
1666 assert_eq!(date, date_rd, "{case:?}");
1667 }
1668 }
1669
1670 #[test]
1671 fn test_rd_from_hijri_tbla() {
1672 let calendar = Hijri::new_tabular(
1673 TabularAlgorithmLeapYears::TypeII,
1674 TabularAlgorithmEpoch::Thursday,
1675 );
1676 for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
1677 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1678 .unwrap();
1679 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1680 }
1681 }
1682
1683 #[test]
1684 fn test_hijri_tbla_from_rd() {
1685 let calendar = Hijri::new_tabular(
1686 TabularAlgorithmLeapYears::TypeII,
1687 TabularAlgorithmEpoch::Thursday,
1688 );
1689 for (case, f_date) in ASTRONOMICAL_CASES.iter().zip(TEST_RD.iter()) {
1690 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1691 .unwrap();
1692 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1693
1694 assert_eq!(date, date_rd, "{case:?}");
1695 }
1696 }
1697
1698 #[test]
1699 fn test_saudi_hijri_from_rd() {
1700 let calendar = Hijri::new_umm_al_qura();
1701 for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) {
1702 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1703 .unwrap();
1704 let date_rd = Date::from_rata_die(RataDie::new(*f_date), calendar);
1705
1706 assert_eq!(date, date_rd, "{case:?}");
1707 }
1708 }
1709
1710 #[test]
1711 fn test_rd_from_saudi_hijri() {
1712 let calendar = Hijri::new_umm_al_qura();
1713 for (case, f_date) in UMMALQURA_CASES.iter().zip(TEST_RD.iter()) {
1714 let date = Date::try_new_hijri_with_calendar(case.year, case.month, case.day, calendar)
1715 .unwrap();
1716 assert_eq!(date.to_rata_die(), RataDie::new(*f_date), "{case:?}");
1717 }
1718 }
1719
1720 #[ignore] #[test]
1722 fn test_days_in_provided_year_ummalqura() {
1723 let calendar = Hijri::new_umm_al_qura();
1724 let sum_days_in_year: i64 = (START_YEAR..END_YEAR)
1727 .map(|year| calendar.0.year(year).packed.days_in_year() as i64)
1728 .sum();
1729 let expected_number_of_days = Date::try_new_hijri_with_calendar(END_YEAR, 1, 1, calendar)
1730 .unwrap()
1731 .to_rata_die()
1732 - (Date::try_new_hijri_with_calendar(START_YEAR, 1, 1, calendar).unwrap())
1733 .to_rata_die(); assert_eq!(sum_days_in_year, expected_number_of_days);
1736 }
1737
1738 #[test]
1739 fn test_regression_3868() {
1740 let date = Date::try_new_iso(2011, 4, 4)
1742 .unwrap()
1743 .to_calendar(Hijri::new_umm_al_qura());
1744 assert_eq!(date.day_of_month().0, 30);
1746 assert_eq!(date.month().ordinal, 4);
1747 assert_eq!(date.era_year().year, 1432);
1748 }
1749
1750 #[test]
1751 fn test_regression_4914() {
1752 let dt = Hijri::new_umm_al_qura()
1754 .new_date(types::YearInput::EraYear("bh", 6824), Month::new(1), 1)
1755 .unwrap();
1756 assert_eq!(dt.0.day(), 1);
1757 assert_eq!(dt.0.month(), 1);
1758 assert_eq!(dt.0.year().extended_year, -6823);
1759 }
1760
1761 #[test]
1762 fn test_regression_7056() {
1763 let calendar = Hijri::new_tabular(
1765 TabularAlgorithmLeapYears::TypeII,
1766 TabularAlgorithmEpoch::Friday,
1767 );
1768 let _dt = Date::from_rata_die(
1769 calendrical_calculations::gregorian::fixed_from_gregorian(-62971, 3, 19),
1770 calendar,
1771 );
1772 }
1773
1774 #[test]
1775 fn test_regression_5069_uaq() {
1776 let calendar = Hijri::new_umm_al_qura();
1777
1778 let dt = Date::try_new_hijri_with_calendar(1391, 1, 29, calendar).unwrap();
1779
1780 assert_eq!(Date::from_rata_die(dt.to_rata_die(), calendar), dt);
1781 }
1782
1783 #[test]
1784 fn test_regression_6197() {
1785 let calendar = Hijri::new_umm_al_qura();
1786
1787 let date = Date::try_new_iso(2025, 2, 26)
1788 .unwrap()
1789 .to_calendar(calendar);
1790
1791 assert_eq!(
1793 (
1794 date.day_of_month().0,
1795 date.month().ordinal,
1796 date.era_year().year
1797 ),
1798 (27, 8, 1446)
1799 );
1800 }
1801
1802 #[test]
1803 fn test_hijri_packed_roundtrip() {
1804 #[track_caller]
1805 fn single_roundtrip(month_lengths: [bool; 12], start_day: RataDie) {
1806 let packed = PackedHijriYearData::try_new(1600, month_lengths, start_day).unwrap();
1807 for i in 0..12 {
1808 assert_eq!(packed.month_len(i + 1) == 30, month_lengths[i as usize]);
1809 }
1810 assert_eq!(packed.new_year(1600), start_day);
1811 }
1812
1813 let l = true;
1814 let s = false;
1815 let all_short = [s; 12];
1816 let all_long = [l; 12];
1817 let mixed1 = [l, s, l, s, l, s, l, s, l, s, l, s];
1818 let mixed2 = [s, s, l, l, l, s, l, s, s, s, l, l];
1819
1820 let start_1600 = PackedHijriYearData::mean_tabular_start_day(1600);
1821 single_roundtrip(all_short, start_1600);
1822 single_roundtrip(all_long, start_1600);
1823 single_roundtrip(mixed1, start_1600);
1824 single_roundtrip(mixed2, start_1600);
1825
1826 single_roundtrip(mixed1, start_1600 - 5);
1827 single_roundtrip(mixed2, start_1600 + 5);
1828 single_roundtrip(mixed2, start_1600 + 4);
1829 single_roundtrip(mixed2, start_1600 + 1);
1830 single_roundtrip(mixed2, start_1600 - 1);
1831 single_roundtrip(mixed2, start_1600 - 4);
1832 }
1833}