use crate::{frequency::units::Hertz, ConversionError, TimeInt};
use core::{cmp, convert, ops};
use num::{rational::Ratio, CheckedDiv, CheckedMul};
#[derive(Debug)]
pub struct Period<T = u32>(Ratio<T>);
impl<T> Period<T> {
pub const fn new(numerator: T, denominator: T) -> Self {
Self(Ratio::new_raw(numerator, denominator))
}
pub const fn numerator(&self) -> &T {
self.0.numer()
}
pub const fn denominator(&self) -> &T {
self.0.denom()
}
}
impl<T: TimeInt> Period<T> {
pub fn new_reduce(numerator: T, denominator: T) -> Result<Self, ConversionError> {
if !denominator.is_zero() {
Ok(Self(Ratio::new(numerator, denominator)))
} else {
Err(ConversionError::DivByZero)
}
}
pub fn to_integer(&self) -> T {
self.0.to_integer()
}
pub fn from_integer(value: T) -> Self {
Self(Ratio::from_integer(value))
}
pub fn recip(self) -> Self {
Self(self.0.recip())
}
pub fn checked_mul(&self, v: &Self) -> Result<Self, ConversionError> {
Ok(Self(
self.0.checked_mul(&v.0).ok_or(ConversionError::Overflow)?,
))
}
pub fn checked_div(&self, v: &Self) -> Result<Self, ConversionError> {
Ok(Self(
self.0.checked_div(&v.0).ok_or(ConversionError::Overflow)?,
))
}
pub fn checked_mul_integer(&self, multiplier: T) -> Result<Self, ConversionError> {
Ok(Self(
Ratio::checked_mul(&self.0, &Ratio::from_integer(multiplier))
.ok_or(ConversionError::Overflow)?,
))
}
pub fn checked_div_integer(&self, divisor: T) -> Result<Self, ConversionError> {
Ok(Self(
Ratio::checked_div(&self.0, &Ratio::from_integer(divisor))
.ok_or(ConversionError::Overflow)?,
))
}
}
impl<T> ops::Mul for Period<T>
where
T: TimeInt,
{
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self.checked_mul(&rhs).unwrap()
}
}
impl<T> ops::Div for Period<T>
where
T: TimeInt,
{
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
self.checked_div(&rhs).unwrap()
}
}
impl<T: TimeInt> convert::TryFrom<Hertz<T>> for Period<T> {
type Error = ConversionError;
fn try_from(freq: Hertz<T>) -> Result<Self, Self::Error> {
if !freq.0.is_zero() {
Ok(Self(Ratio::from_integer(freq.0).recip()))
} else {
Err(ConversionError::DivByZero)
}
}
}
impl<T: TimeInt> PartialOrd for Period<T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: TimeInt> PartialEq for Period<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}