use time::{Date, Month, OffsetDateTime}; /// The start of the COVID-19 lockdowns /// /// This is used as an epoch in order to convert from a given date to an integer seed. This is /// specified as part of the algorithm for randomly selecting from a weighted list. pub const COVID_EPOCH: Date = match Date::from_calendar_date(2020, Month::January, 26) { Ok(d) => d, Err(_) => Date::MIN, // This never runs, but we can't unwrap, so this is what we're stuck with }; /// A state to use as an initial state before seeding /// /// Think of this as a ::new() method for a pcg64 generator. Subsiquent calls should be /// made to the [`pcg64_seed()`] methods or the [`seed_with_date()`]/[`seed_with_today()`] /// methods pub const INITIAL_STATE: u128 = 1312_1312_1312; pub fn pcg64(state: &mut u128) -> u64 { let mut x = *state; pcg64_iterstate(state); let count = (x >> (128 - 6)) as u32; // Highest 6 bits x ^= x >> 35; ((x >> 58) as u64).rotate_right(count) } fn pcg64_iterstate(state: &mut u128) { const MULTIPLIER: u128 = 0xde92a69f6e2f9f25fd0d90f576075fbd; const INCREMENT: u128 = 621; *state = state.wrapping_mul(MULTIPLIER).wrapping_add(INCREMENT); } pub fn pcg64_seed(state: &mut u128, bytes: &[u8]) { for byte in bytes { *state = (*state ^ *byte as u128).rotate_right(8); } pcg64_iterstate(state); } /// Seed the generator with a given [`Date`] object pub fn seed_with_date(state: &mut u128, date: Date) { pcg64_seed( state, &( (date - COVID_EPOCH) .whole_days() as u32 ).to_le_bytes() ) } /// Seed the generator with the [`Date`] object representing today's date /// /// Uses the system's local time, or falls back to UTC pub fn seed_with_today(state: &mut u128) { seed_with_date( state, OffsetDateTime::now_local() .unwrap_or_else(|_| OffsetDateTime::now_utc()) .date() ) }