use sim_kernel::{Error, Result};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant {
numerator: i128,
denominator: i128,
}
impl Instant {
pub fn new(numerator: i128, denominator: i128) -> Result<Self> {
if denominator == 0 {
return Err(Error::Eval(
"instant denominator must be non-zero".to_owned(),
));
}
let mut numerator = numerator;
let mut denominator = denominator;
if denominator < 0 {
numerator = numerator
.checked_neg()
.ok_or_else(|| Error::Eval("instant numerator overflowed".to_owned()))?;
denominator = denominator
.checked_neg()
.ok_or_else(|| Error::Eval("instant denominator overflowed".to_owned()))?;
}
if numerator < 0 {
return Err(Error::Eval(
"stream instants must be non-negative".to_owned(),
));
}
let divisor = gcd(numerator, denominator);
Ok(Self {
numerator: numerator / divisor,
denominator: denominator / divisor,
})
}
pub fn seconds(seconds: u64) -> Self {
Self {
numerator: i128::from(seconds),
denominator: 1,
}
}
pub fn numerator(self) -> i128 {
self.numerator
}
pub fn denominator(self) -> i128 {
self.denominator
}
pub fn checked_add(self, other: Self) -> Result<Self> {
let numerator = self
.numerator
.checked_mul(other.denominator)
.and_then(|left| {
other
.numerator
.checked_mul(self.denominator)
.and_then(|right| left.checked_add(right))
})
.ok_or_else(|| Error::Eval("instant addition overflowed".to_owned()))?;
let denominator = self
.denominator
.checked_mul(other.denominator)
.ok_or_else(|| Error::Eval("instant denominator overflowed".to_owned()))?;
Self::new(numerator, denominator)
}
pub fn checked_sub(self, other: Self) -> Result<Self> {
let numerator = self
.numerator
.checked_mul(other.denominator)
.and_then(|left| {
other
.numerator
.checked_mul(self.denominator)
.and_then(|right| left.checked_sub(right))
})
.ok_or_else(|| Error::Eval("instant subtraction overflowed".to_owned()))?;
let denominator = self
.denominator
.checked_mul(other.denominator)
.ok_or_else(|| Error::Eval("instant denominator overflowed".to_owned()))?;
Self::new(numerator, denominator)
}
}
fn gcd(mut left: i128, mut right: i128) -> i128 {
left = left.abs();
right = right.abs();
while right != 0 {
let remainder = left % right;
left = right;
right = remainder;
}
left.max(1)
}