feat(api): add Time trait with Untimed and i64 impls
Foundation for generic History time axis. Untimed is the ZST case (no drift across slices); i64 is the standard timestamp case. Additional impls (time::OffsetDateTime, chrono) can be added behind feature flags in follow-up work. The trait is not yet wired into History — that happens in Task 7 along with generifying Drift over T. Part of T2 of docs/superpowers/specs/2026-04-23-trueskill-engine-redesign-design.md.
This commit is contained in:
@@ -6,6 +6,7 @@ use std::{
|
|||||||
#[cfg(feature = "approx")]
|
#[cfg(feature = "approx")]
|
||||||
mod approx;
|
mod approx;
|
||||||
pub(crate) mod arena;
|
pub(crate) mod arena;
|
||||||
|
mod time;
|
||||||
mod time_slice;
|
mod time_slice;
|
||||||
pub use time_slice::TimeSlice;
|
pub use time_slice::TimeSlice;
|
||||||
mod competitor;
|
mod competitor;
|
||||||
@@ -31,6 +32,7 @@ pub use key_table::KeyTable;
|
|||||||
use matrix::Matrix;
|
use matrix::Matrix;
|
||||||
pub use rating::Rating;
|
pub use rating::Rating;
|
||||||
pub use schedule::ScheduleReport;
|
pub use schedule::ScheduleReport;
|
||||||
|
pub use time::{Time, Untimed};
|
||||||
|
|
||||||
pub const BETA: f64 = 1.0;
|
pub const BETA: f64 = 1.0;
|
||||||
pub const MU: f64 = 0.0;
|
pub const MU: f64 = 0.0;
|
||||||
|
|||||||
54
src/time.rs
Normal file
54
src/time.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//! Generic time axis for `History`.
|
||||||
|
//!
|
||||||
|
//! Users pick the `Time` type based on their domain: `Untimed` when no
|
||||||
|
//! time axis is meaningful, `i64` for integer day/second timestamps.
|
||||||
|
//! Additional impls can be added behind feature flags.
|
||||||
|
|
||||||
|
/// A timestamp on the global ordering axis.
|
||||||
|
///
|
||||||
|
/// Must be `Ord + Copy` so slices can sort events, and `'static` so
|
||||||
|
/// `History` can store it by value without lifetimes.
|
||||||
|
pub trait Time: Copy + Ord + 'static {
|
||||||
|
/// How much time elapsed between `self` and `later`.
|
||||||
|
///
|
||||||
|
/// Used by `Drift<T>::variance_delta` to compute skill drift. Returning
|
||||||
|
/// zero means no drift accumulates between the two points. Return value
|
||||||
|
/// must be non-negative for `self <= later`.
|
||||||
|
fn elapsed_to(&self, later: &Self) -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Zero-sized type representing "no time axis."
|
||||||
|
///
|
||||||
|
/// Used as the default `Time` when events are unordered. Elapsed is always 0,
|
||||||
|
/// so no drift accumulates across slices.
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Untimed;
|
||||||
|
|
||||||
|
impl Time for Untimed {
|
||||||
|
fn elapsed_to(&self, _later: &Self) -> i64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Time for i64 {
|
||||||
|
fn elapsed_to(&self, later: &Self) -> i64 {
|
||||||
|
later - self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn untimed_elapsed_is_zero() {
|
||||||
|
assert_eq!(Untimed.elapsed_to(&Untimed), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_elapsed_is_difference() {
|
||||||
|
assert_eq!(5i64.elapsed_to(&10), 5);
|
||||||
|
assert_eq!(10i64.elapsed_to(&5), -5);
|
||||||
|
assert_eq!(0i64.elapsed_to(&0), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user