astroceleste-engine 0.0.11

Astrological chart calculation engine on JPL ephemerides
Documentation
//! The lunar calendar from 1900 to 2100, embedded so that any date's lunar date is known
//! without a kernel. Generated from the engine's own calendar (`calendar::lunar_years`) by
//! the ignored test `chinese::lunar_table::tests::print_table`, and checked against it by
//! `tests::the_table_matches_the_calendar`.

use super::calendar::LunarYear;

/// First lunar year of the table.
pub(crate) const FIRST_YEAR: i32 = 1900;

/// Each lunar year: Julian day number of its New Year, its months' lengths (bit i set: the
/// i-th month, the leap month in its place, has 30 days) and the month its leap month
/// repeats (0 for none).
#[rustfmt::skip]
pub(crate) const LUNAR_YEARS: [(u32, u16, u8); 201] = [
    (2415051, 0b1011011010010, 8), // 1900
    (2415435, 0b0011101010010, 0), // 1901
    (2415789, 0b0111010100101, 0), // 1902
    (2416144, 0b1011001001010, 5), // 1903
    (2416527, 0b0011001001011, 0), // 1904
    (2416881, 0b0101010011011, 0), // 1905
    (2417236, 0b1010101011010, 4), // 1906
    (2417620, 0b0010101101010, 0), // 1907
    (2417974, 0b0101101011001, 0), // 1908
    (2418329, 0b1011101010010, 2), // 1909
    (2418713, 0b0011101010010, 0), // 1910
    (2419067, 0b1101100100101, 6), // 1911
    (2419451, 0b0101100100101, 0), // 1912
    (2419805, 0b0101001001011, 0), // 1913
    (2420159, 0b1010010101011, 5), // 1914
    (2420543, 0b0001010101101, 0), // 1915
    (2420897, 0b0010101101011, 0), // 1916
    (2421252, 0b0101101101001, 2), // 1917
    (2421636, 0b0110110101001, 0), // 1918
    (2421991, 0b1110110010010, 7), // 1919
    (2422375, 0b0111010010010, 0), // 1920
    (2422729, 0b0110100100101, 0), // 1921
    (2423083, 0b1101001001101, 5), // 1922
    (2423467, 0b0101001010110, 0), // 1923
    (2423821, 0b0001010110110, 0), // 1924
    (2424175, 0b1010110110101, 4), // 1925
    (2424560, 0b0011011010100, 0), // 1926
    (2424914, 0b0111010101001, 0), // 1927
    (2425269, 0b1111010010010, 2), // 1928
    (2425653, 0b0111010010010, 0), // 1929
    (2426007, 0b0110100100110, 6), // 1930
    (2426390, 0b0010100101011, 0), // 1931
    (2426744, 0b0101001010111, 0), // 1932
    (2427099, 0b1001010110110, 5), // 1933
    (2427483, 0b0101101011010, 0), // 1934
    (2427838, 0b0011011010100, 0), // 1935
    (2428192, 0b0111011001001, 3), // 1936
    (2428576, 0b0011101001001, 0), // 1937
    (2428930, 0b1011010010011, 7), // 1938
    (2429314, 0b0101010010011, 0), // 1939
    (2429668, 0b0010100101011, 0), // 1940
    (2430022, 0b0101001011011, 6), // 1941
    (2430406, 0b0101010101101, 0), // 1942
    (2430761, 0b0010101101010, 0), // 1943
    (2431115, 0b1101101010101, 4), // 1944
    (2431500, 0b0101110100100, 0), // 1945
    (2431854, 0b0101101001001, 0), // 1946
    (2432208, 0b1101010010011, 2), // 1947
    (2432592, 0b0101010010101, 0), // 1948
    (2432946, 0b1010100101101, 7), // 1949
    (2433330, 0b0010100110110, 0), // 1950
    (2433684, 0b0101010101101, 0), // 1951
    (2434039, 0b1010110101010, 5), // 1952
    (2434423, 0b0010110110010, 0), // 1953
    (2434777, 0b0110110100101, 0), // 1954
    (2435132, 0b1110101001010, 3), // 1955
    (2435516, 0b0110101001010, 0), // 1956
    (2435870, 0b0101010010101, 8), // 1957
    (2436253, 0b0101010010111, 0), // 1958
    (2436608, 0b0010101010110, 0), // 1959
    (2436962, 0b0101010110101, 6), // 1960
    (2437346, 0b0101011010101, 0), // 1961
    (2437701, 0b0011011010010, 0), // 1962
    (2438055, 0b0111010100101, 4), // 1963
    (2438439, 0b0111010100101, 0), // 1964
    (2438794, 0b0011001001010, 0), // 1965
    (2439147, 0b0110010010111, 3), // 1966
    (2439531, 0b0101010011011, 0), // 1967
    (2439886, 0b1010101011010, 7), // 1968
    (2440270, 0b0010101101010, 0), // 1969
    (2440624, 0b0101101101001, 0), // 1970
    (2440979, 0b1011101010010, 5), // 1971
    (2441363, 0b0101101010010, 0), // 1972
    (2441717, 0b0101100100101, 0), // 1973
    (2442071, 0b1011001001011, 4), // 1974
    (2442455, 0b0101001001011, 0), // 1975
    (2442809, 0b1010010101011, 8), // 1976
    (2443193, 0b0001010101101, 0), // 1977
    (2443547, 0b0010101101101, 0), // 1978
    (2443902, 0b0101101101001, 6), // 1979
    (2444286, 0b0110110101001, 0), // 1980
    (2444641, 0b0110110010010, 0), // 1981
    (2444995, 0b1110100100101, 4), // 1982
    (2445379, 0b0110100100101, 0), // 1983
    (2445733, 0b1101001001101, 10), // 1984
    (2446117, 0b0101001010110, 0), // 1985
    (2446471, 0b0001010110110, 0), // 1986
    (2446825, 0b0010110110101, 6), // 1987
    (2447209, 0b0011011010101, 0), // 1988
    (2447564, 0b0111010101001, 0), // 1989
    (2447919, 0b1111010010010, 5), // 1990
    (2448303, 0b0111010010010, 0), // 1991
    (2448657, 0b0110100100110, 0), // 1992
    (2449011, 0b0101001010110, 3), // 1993
    (2449394, 0b0101001010111, 0), // 1994
    (2449749, 0b1010011010110, 8), // 1995
    (2450133, 0b0001101011010, 0), // 1996
    (2450487, 0b0011011010101, 0), // 1997
    (2450842, 0b1011011001001, 5), // 1998
    (2451226, 0b0011101001001, 0), // 1999
    (2451580, 0b0011010010011, 0), // 2000
    (2451934, 0b1010100101011, 4), // 2001
    (2452318, 0b0010100101011, 0), // 2002
    (2452672, 0b0101001011011, 0), // 2003
    (2453027, 0b1010101011010, 2), // 2004
    (2453411, 0b0010101101010, 0), // 2005
    (2453765, 0b1101101010101, 7), // 2006
    (2454150, 0b0101110100100, 0), // 2007
    (2454504, 0b0101101001001, 0), // 2008
    (2454858, 0b1101010010011, 5), // 2009
    (2455242, 0b0101010010101, 0), // 2010
    (2455596, 0b0010100101101, 0), // 2011
    (2455950, 0b0101010101101, 4), // 2012
    (2456334, 0b0101010110101, 0), // 2013
    (2456689, 0b1010110101010, 9), // 2014
    (2457073, 0b0010111010010, 0), // 2015
    (2457427, 0b0110110100101, 0), // 2016
    (2457782, 0b1110101001010, 6), // 2017
    (2458166, 0b0110101001010, 0), // 2018
    (2458520, 0b0110010010101, 0), // 2019
    (2458874, 0b1010100101110, 4), // 2020
    (2459258, 0b0010101010110, 0), // 2021
    (2459612, 0b0101010110101, 0), // 2022
    (2459967, 0b1010110110010, 2), // 2023
    (2460351, 0b0011011010010, 0), // 2024
    (2460705, 0b0111010100101, 6), // 2025
    (2461089, 0b0011100100101, 0), // 2026
    (2461443, 0b0011001001011, 0), // 2027
    (2461797, 0b0110010010111, 5), // 2028
    (2462181, 0b0110010101011, 0), // 2029
    (2462536, 0b0010101011010, 0), // 2030
    (2462890, 0b0101011010110, 3), // 2031
    (2463274, 0b0101101101001, 0), // 2032
    (2463629, 0b1011101010010, 11), // 2033
    (2464013, 0b0101101010010, 0), // 2034
    (2464367, 0b0101100100101, 0), // 2035
    (2464721, 0b1101001001011, 6), // 2036
    (2465105, 0b0101001001011, 0), // 2037
    (2465459, 0b0010010101011, 0), // 2038
    (2465813, 0b0010101011011, 5), // 2039
    (2466197, 0b0010110101101, 0), // 2040
    (2466552, 0b0101101101010, 0), // 2041
    (2466907, 0b1101101010010, 2), // 2042
    (2467291, 0b0110110010010, 0), // 2043
    (2467645, 0b1110100100101, 7), // 2044
    (2468029, 0b0110100100101, 0), // 2045
    (2468383, 0b0101001010101, 0), // 2046
    (2468737, 0b1010010101101, 5), // 2047
    (2469121, 0b0010010110110, 0), // 2048
    (2469475, 0b0010110110101, 0), // 2049
    (2469830, 0b0110110101010, 3), // 2050
    (2470214, 0b0111011001001, 0), // 2051
    (2470569, 0b1111010010010, 8), // 2052
    (2470953, 0b0111010010010, 0), // 2053
    (2471307, 0b0110100100110, 0), // 2054
    (2471661, 0b0101001010110, 6), // 2055
    (2472044, 0b0101001010111, 0), // 2056
    (2472399, 0b0010011010110, 0), // 2057
    (2472753, 0b0011011010101, 4), // 2058
    (2473137, 0b0011101010101, 0), // 2059
    (2473492, 0b0011101001001, 0), // 2060
    (2473846, 0b0111010010011, 3), // 2061
    (2474230, 0b0011010010011, 0), // 2062
    (2474584, 0b1010100101011, 7), // 2063
    (2474968, 0b0010100101011, 0), // 2064
    (2475322, 0b0101001011011, 0), // 2065
    (2475677, 0b1010101011010, 5), // 2066
    (2476061, 0b0010101101010, 0), // 2067
    (2476415, 0b0101101100101, 0), // 2068
    (2476770, 0b1011101001010, 4), // 2069
    (2477154, 0b0101101001010, 0), // 2070
    (2477508, 0b1101010010101, 8), // 2071
    (2477892, 0b0101010010101, 0), // 2072
    (2478246, 0b0010100101101, 0), // 2073
    (2478600, 0b0101010101101, 6), // 2074
    (2478984, 0b0101010110101, 0), // 2075
    (2479339, 0b0010110101010, 0), // 2076
    (2479693, 0b0101110100101, 4), // 2077
    (2480077, 0b0110110100101, 0), // 2078
    (2480432, 0b0110101001010, 0), // 2079
    (2480786, 0b1110010010101, 3), // 2080
    (2481170, 0b0110010010110, 0), // 2081
    (2481524, 0b1100101001110, 7), // 2082
    (2481908, 0b0010101010110, 0), // 2083
    (2482262, 0b0101010110101, 0), // 2084
    (2482617, 0b1010110110010, 5), // 2085
    (2483001, 0b0011011010010, 0), // 2086
    (2483355, 0b0111010100101, 0), // 2087
    (2483710, 0b0111001001010, 4), // 2088
    (2484093, 0b0011010001011, 0), // 2089
    (2484447, 0b0110010010111, 8), // 2090
    (2484831, 0b0010010101011, 0), // 2091
    (2485185, 0b0010101011011, 0), // 2092
    (2485540, 0b0101011010110, 6), // 2093
    (2485924, 0b0101101101010, 0), // 2094
    (2486279, 0b0011101010010, 0), // 2095
    (2486633, 0b1011100100101, 4), // 2096
    (2487017, 0b0101100100101, 0), // 2097
    (2487371, 0b0101010001011, 0), // 2098
    (2487725, 0b1010010011011, 2), // 2099
    (2488109, 0b0010010101011, 0), // 2100
];

/// A lunar year of the table.
fn year_at(index: usize) -> LunarYear {
    let (start, mask, leap) = LUNAR_YEARS[index];
    let count = if leap > 0 { 13 } else { 12 };
    LunarYear {
        year: FIRST_YEAR + index as i32,
        start: i64::from(start),
        days: (0..count)
            .map(|i| if mask >> i & 1 == 1 { 30 } else { 29 })
            .collect(),
        leap,
    }
}

/// The lunar date of a Julian day number: (year, month, leap, day), or `None` outside the
/// table.
pub(crate) fn lunar_date_of(day: i64) -> Option<(i32, u8, bool, u8)> {
    let index = LUNAR_YEARS.partition_point(|row| i64::from(row.0) <= day);
    let year = year_at(index.checked_sub(1)?);
    let mut start = year.start;
    let mut number = 0u8;
    for (i, &days) in year.days.iter().enumerate() {
        let leap = year.leap > 0 && i == usize::from(year.leap);
        if !leap {
            number += 1;
        }
        if day < start + i64::from(days) {
            return Some((year.year, number, leap, (day - start + 1) as u8));
        }
        start += i64::from(days);
    }
    None
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::chinese::calendar::lunar_years;
    use crate::ephemeris::{Kernel, KernelSet, Spk};

    fn kernels() -> Option<KernelSet> {
        let path =
            std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../kernels/de440s.bsp");
        if !path.exists() {
            eprintln!("skipping: {} not found", path.display());
            return None;
        }
        let mut set = KernelSet::new();
        set.push(Kernel::new("de440s.bsp", Spk::open(path).unwrap()).unwrap());
        Some(set)
    }

    #[test]
    #[ignore = "generates the table: cargo test --release print_table -- --ignored --nocapture"]
    fn print_table() {
        let kernels = kernels().expect("kernels/de440s.bsp");
        for year in lunar_years(&kernels, FIRST_YEAR, 2100).unwrap() {
            let mask: u16 = year
                .days
                .iter()
                .enumerate()
                .map(|(i, &d)| u16::from(d == 30) << i)
                .sum();
            println!(
                "    ({}, 0b{:013b}, {}), // {}",
                year.start, mask, year.leap, year.year
            );
        }
    }

    #[test]
    fn the_table_matches_the_calendar() {
        let Some(kernels) = kernels() else { return };
        let last = FIRST_YEAR + LUNAR_YEARS.len() as i32 - 1;
        let computed = lunar_years(&kernels, FIRST_YEAR, last).unwrap();
        for (i, year) in computed.iter().enumerate() {
            assert_eq!(&year_at(i), year);
        }
        assert_eq!(LUNAR_YEARS.len(), 201);
    }

    #[test]
    fn lunar_dates() {
        // 2020-01-25 (JDN 2458874) is the New Year of 2020; 2023-04-10 is the 20th of the
        // leap second month.
        assert_eq!(lunar_date_of(2_458_874), Some((2020, 1, false, 1)));
        assert_eq!(lunar_date_of(2_460_045), Some((2023, 2, true, 20)));
        assert_eq!(lunar_date_of(2_400_000), None);
    }
}