radnelac 0.0.2

Calculations in a variety of different timekeeping systems.
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

#[cfg(feature = "display")]
mod display_logic {
    pub use num_traits::FromPrimitive;
    pub use num_traits::ToPrimitive;
    pub use proptest::prelude::TestCaseError;
    pub use proptest::prop_assume;
    pub use proptest::proptest;
    pub use radnelac::calendar::*;
    pub use radnelac::day_count::*;
    pub use radnelac::day_cycle::*;
    pub use radnelac::display::*;

    pub const MAX_4DIGIT: f64 = 8000.0 * 365.25;
    pub const MIN_4DIGIT: f64 = 1000.0 * 365.25;
    pub const FR_MIN_4DIGIT: f64 = 1795.0 * 365.25;
    pub const HL_MIN_5DIGIT: f64 = -9000.0 * 365.25;
    pub const HL_MAX_5DIGIT: f64 = 87000.0 * 365.25;
    pub const TQ_MIN_4DIGIT: f64 = 1975.0 * 365.25;

    pub fn ymd_order_raw<T: FromFixed + PresetDisplay + PartialOrd>(
        preset: PresetFormat,
        d0: T,
        d1: T,
    ) {
        let s0 = d0.preset_str(Language::EN, preset);
        let s1 = d1.preset_str(Language::EN, preset);
        assert_eq!(d0 < d1, s0 < s1, "<  {} {}", s0, s1);
        assert_eq!(d0 <= d1, s0 <= s1, "<= {} {}", s0, s1);
        assert_eq!(d0 == d1, s0 == s1, "== {} {}", s0, s1);
        assert_eq!(d0 >= d1, s0 >= s1, ">= {} {}", s0, s1);
        assert_eq!(d0 > d1, s0 > s1, "> {} {}", s0, s1);
    }

    pub fn ymd_order<T: FromFixed + PresetDisplay + Epoch + PartialOrd>(
        preset: PresetFormat,
        t0: f64,
        t1: f64,
    ) {
        let f0 = Fixed::new(t0).to_day();
        let f1 = Fixed::new(t1).to_day();
        let d0 = T::from_fixed(f0);
        let d1 = T::from_fixed(f1);
        ymd_order_raw::<T>(preset, d0, d1);
    }

    pub fn ymd_order_tq(preset: PresetFormat, t0: f64, t1: f64) -> Result<(), TestCaseError> {
        let f0 = Fixed::new(t0).to_day();
        let f1 = Fixed::new(t1).to_day();
        let d0 = TranquilityMoment::from_fixed(f0);
        let d1 = TranquilityMoment::from_fixed(f1);
        prop_assume!(d0.epagomenae().is_none() && d1.epagomenae().is_none());
        ymd_order_raw::<TranquilityMoment>(preset, d0, d1);
        Ok(())
    }

    pub fn ymd_vs_dmy_vs_mdy<T: FromFixed + PresetDisplay + Epoch + PartialOrd>(t: f64) {
        let d = T::from_fixed(Fixed::new(t).to_day());
        let ymd0 = d.preset_str(Language::EN, YYYYMMDD_SLASH);
        let ymd1 = d.preset_str(Language::EN, DDMMYYYY_SLASH);
        let ymd2 = d.preset_str(Language::EN, MMDDYYYY_SLASH);
        let ymd3 = d.preset_str(Language::EN, YYYYMMDD_DASH);
        assert_eq!(&ymd0[0..4], &ymd1[6..10]);
        assert_eq!(&ymd0[5..7], &ymd1[3..5]);
        assert_eq!(&ymd0[8..10], &ymd1[0..2]);
        assert_eq!(&ymd0[0..4], &ymd2[6..10]);
        assert_eq!(&ymd0[5..7], &ymd2[0..2]);
        assert_eq!(&ymd0[8..10], &ymd2[3..5]);
        assert_eq!(&ymd0[0..4], &ymd3[0..4]);
        assert_eq!(&ymd0[5..7], &ymd3[5..7]);
        assert_eq!(&ymd0[8..10], &ymd3[8..10]);
    }

    pub fn epoch_order<T: FromFixed + PresetDisplay + PartialOrd>(
        preset: PresetFormat,
        t0: f64,
        t1: f64,
    ) {
        let f0 = Fixed::new(t0).to_day();
        let f1 = Fixed::new(t1).to_day();
        let d0 = T::from_fixed(f0);
        let d1 = T::from_fixed(f1);
        let s0 = format!("{:0>100}", d0.preset_str(Language::EN, preset));
        let s1 = format!("{:0>100}", d1.preset_str(Language::EN, preset));
        assert_eq!(d0 < d1, s0 < s1, "<  {} {}", s0, s1);
        assert_eq!(d0 <= d1, s0 <= s1, "<= {} {}", s0, s1);
        assert_eq!(d0 == d1, s0 == s1, "== {} {}", s0, s1);
        assert_eq!(d0 >= d1, s0 >= s1, ">= {} {}", s0, s1);
        assert_eq!(d0 > d1, s0 > s1, "> {} {}", s0, s1);
    }

    pub fn perennial_week<S, T, U>(
        preset: PresetFormat,
        t0: f64,
        t1: f64,
    ) -> Result<(), TestCaseError>
    where
        S: FromPrimitive + ToPrimitive,
        T: FromPrimitive + ToPrimitive,
        U: FromFixed + Perennial<S, T> + PresetDisplay + PartialOrd,
    {
        let f0 = Fixed::new(t0).to_day();
        let f1 = Fixed::new(t1).to_day();
        let d0 = U::from_fixed(f0);
        let d1 = U::from_fixed(f1);
        prop_assume!(d0.weekday().is_some());
        prop_assume!(d1.weekday().is_some());
        let s0 = format!("{:0>20}", d0.preset_str(Language::EN, preset));
        let s1 = format!("{:0>20}", d1.preset_str(Language::EN, preset));
        assert_eq!(d0 < d1, s0 < s1, "<  {} {}", s0, s1);
        assert_eq!(d0 <= d1, s0 <= s1, "<= {} {}", s0, s1);
        assert_eq!(d0 == d1, s0 == s1, "== {} {}", s0, s1);
        assert_eq!(d0 >= d1, s0 >= s1, ">= {} {}", s0, s1);
        assert_eq!(d0 > d1, s0 > s1, "> {} {}", s0, s1);
        Ok(())
    }
}

#[cfg(feature = "display")]
use display_logic::*;

#[cfg(feature = "display")]
proptest! {
    #[test]
    fn armenian(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Armenian>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Armenian>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Armenian>(t0);
        ymd_vs_dmy_vs_mdy::<Armenian>(t1);
    }

    #[test]
    fn armenian_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Armenian>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Armenian>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn coptic(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Coptic>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Coptic>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Coptic>(t0);
        ymd_vs_dmy_vs_mdy::<Coptic>(t1);
    }

    #[test]
    fn coptic_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Coptic>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Coptic>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn cotsworth(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Cotsworth>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Cotsworth>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Cotsworth>(t0);
        ymd_vs_dmy_vs_mdy::<Cotsworth>(t1);
    }

    #[test]
    fn cotsworth_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Cotsworth>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Cotsworth>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn cotsworth_week(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        perennial_week::<CotsworthMonth, Weekday, Cotsworth>(YEAR_WEEK_DAY, t0, t1)?;
    }

    #[test]
    fn egyptian(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Egyptian>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Egyptian>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Egyptian>(t0);
        ymd_vs_dmy_vs_mdy::<Egyptian>(t1);
    }

    #[test]
    fn egyptian_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Egyptian>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Egyptian>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn ethiopic(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Ethiopic>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Ethiopic>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Ethiopic>(t0);
        ymd_vs_dmy_vs_mdy::<Ethiopic>(t1);
    }

    #[test]
    fn ethiopic_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Ethiopic>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Ethiopic>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn french_rev_arith(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<FrenchRevArith<true>>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<FrenchRevArith<false>>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<FrenchRevArith<true>>(YYYYOOO_DASH, t0, t1);
        ymd_order::<FrenchRevArith<false>>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<FrenchRevArith<true>>(t0);
        ymd_vs_dmy_vs_mdy::<FrenchRevArith<false>>(t1);
        ymd_vs_dmy_vs_mdy::<FrenchRevArith<true>>(t0);
        ymd_vs_dmy_vs_mdy::<FrenchRevArith<false>>(t1);
    }

    #[test]
    fn french_rev_epoch(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<FrenchRevArith<false>>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<FrenchRevArith<false>>(EPOCH_DAYS_ONLY, t0, t1);
        epoch_order::<FrenchRevArith<true>>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<FrenchRevArith<true>>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn french_rev_week(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        perennial_week::<FrenchRevMonth, FrenchRevWeekday, FrenchRevArith<false>>(YEAR_WEEK_DAY, t0, t1)?;
        perennial_week::<FrenchRevMonth, FrenchRevWeekday, FrenchRevArith<true>>(YEAR_WEEK_DAY, t0, t1)?;
    }

    #[test]
    fn gregorian(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Gregorian>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Gregorian>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Gregorian>(t0);
        ymd_vs_dmy_vs_mdy::<Gregorian>(t1);
    }

    #[test]
    fn gregorian_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Gregorian>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Gregorian>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn holocene(t0 in HL_MIN_5DIGIT..HL_MAX_5DIGIT, t1 in HL_MIN_5DIGIT..HL_MAX_5DIGIT) {
        ymd_order::<Holocene>(YYYYYMMDD_DASH, t0, t1);
        ymd_order::<Holocene>(YYYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Holocene>(t0);
        ymd_vs_dmy_vs_mdy::<Holocene>(t1);
    }

    #[test]
    fn holocene_epoch(t0 in HL_MIN_5DIGIT..HL_MAX_5DIGIT, t1 in HL_MIN_5DIGIT..HL_MAX_5DIGIT) {
        epoch_order::<Holocene>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Holocene>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn iso(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<ISO>(YEAR_WEEK_DAY, t0, t1);
    }

    #[test]
    fn iso_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<ISO>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<ISO>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn positivist(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Positivist>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Positivist>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Positivist>(t0);
        ymd_vs_dmy_vs_mdy::<Positivist>(t1);
    }

    #[test]
    fn positivist_epoch(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Positivist>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Positivist>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn positivist_week(t0 in FR_MIN_4DIGIT..MAX_4DIGIT, t1 in FR_MIN_4DIGIT..MAX_4DIGIT) {
        perennial_week::<PositivistMonth, Weekday, Positivist>(YEAR_WEEK_DAY, t0, t1)?;
    }

    #[test]
    fn julian(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Julian>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Julian>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Julian>(t0);
        ymd_vs_dmy_vs_mdy::<Julian>(t1);
    }

    #[test]
    fn julian_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Julian>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Julian>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn symmetry(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order::<Symmetry454>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Symmetry010>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Symmetry454Solstice>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Symmetry010Solstice>(YYYYMMDD_DASH, t0, t1);
        ymd_order::<Symmetry454>(YYYYOOO_DASH, t0, t1);
        ymd_order::<Symmetry010>(YYYYOOO_DASH, t0, t1);
        ymd_order::<Symmetry454Solstice>(YYYYOOO_DASH, t0, t1);
        ymd_order::<Symmetry010Solstice>(YYYYOOO_DASH, t0, t1);
        ymd_vs_dmy_vs_mdy::<Symmetry010>(t0);
        ymd_vs_dmy_vs_mdy::<Symmetry010>(t1);
        ymd_vs_dmy_vs_mdy::<Symmetry454>(t0);
        ymd_vs_dmy_vs_mdy::<Symmetry454>(t1);
        ymd_vs_dmy_vs_mdy::<Symmetry010Solstice>(t0);
        ymd_vs_dmy_vs_mdy::<Symmetry010Solstice>(t1);
        ymd_vs_dmy_vs_mdy::<Symmetry454Solstice>(t0);
        ymd_vs_dmy_vs_mdy::<Symmetry454Solstice>(t1);
    }

    #[test]
    fn symmetry_epoch(t0 in MIN_4DIGIT..MAX_4DIGIT, t1 in MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<Symmetry454>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Symmetry454>(EPOCH_DAYS_ONLY, t0, t1);
        epoch_order::<Symmetry010>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Symmetry010>(EPOCH_DAYS_ONLY, t0, t1);
        epoch_order::<Symmetry454Solstice>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Symmetry454Solstice>(EPOCH_DAYS_ONLY, t0, t1);
        epoch_order::<Symmetry010Solstice>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<Symmetry010Solstice>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn tranquility(t0 in TQ_MIN_4DIGIT..MAX_4DIGIT, t1 in TQ_MIN_4DIGIT..MAX_4DIGIT) {
        ymd_order_tq(YYYYMMDD_DASH, t0, t1)?;
        ymd_order_tq(YYYYOOO_DASH, t0, t1)?;
        ymd_vs_dmy_vs_mdy::<TranquilityMoment>(t0);
        ymd_vs_dmy_vs_mdy::<TranquilityMoment>(t1);
    }

    #[test]
    fn tranquility_epoch(t0 in TQ_MIN_4DIGIT..MAX_4DIGIT, t1 in TQ_MIN_4DIGIT..MAX_4DIGIT) {
        epoch_order::<TranquilityMoment>(EPOCH_SECONDS_ONLY, t0, t1);
        epoch_order::<TranquilityMoment>(EPOCH_DAYS_ONLY, t0, t1);
    }

    #[test]
    fn tranquility_week(t0 in TQ_MIN_4DIGIT..MAX_4DIGIT, t1 in TQ_MIN_4DIGIT..MAX_4DIGIT) {
        perennial_week::<TranquilityMonth, Weekday, TranquilityMoment>(YEAR_WEEK_DAY, t0, t1)?;
    }
}