use core::cmp;
use core::ops::{Div, Mul};
use core::time::Duration;
use cast;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u64)]
pub (crate) enum FreqRange {
MegaHertz = 1_000_000_000,
KiloHertz = 1_000_000,
Hertz = 1_000,
MilliHertz = 1,
}
#[derive(Clone, Copy, Debug)]
pub struct Frequency {
pub (crate) resolution: FreqRange,
pub (crate) numerator: u32,
pub (crate) denominator: u32,
}
impl PartialEq for Frequency {
fn eq(&self, other: &Frequency) -> bool {
let left = self.into_hertz();
let other = other.into_hertz();
left.numerator == other.numerator
}
}
impl Eq for Frequency {}
impl Ord for Frequency {
fn cmp(&self, other: &Frequency) -> cmp::Ordering {
let left = self.into_hertz();
let other = other.into_hertz();
left.numerator.cmp(&other.numerator)
}
}
impl PartialOrd for Frequency {
fn partial_cmp(&self, other: &Frequency) -> Option<cmp::Ordering> {
Some(self.cmp(&other))
}
}
macro_rules! into_x {
($name:ident, $range:ident) => {
pub fn $name(&self) -> Frequency {
if let Some(num) = (self.resolution as u32).checked_mul(self.numerator) {
Frequency {
resolution: FreqRange::$range,
numerator: (num / (FreqRange::$range as u32) / self.denominator),
denominator: 1,
}
} else {
Frequency {
resolution: FreqRange::$range,
numerator: cast::u32((self.resolution as u64 * self.numerator as u64) / (FreqRange::$range as u64 * self.denominator as u64)).unwrap(),
denominator: 1,
}
}
}
}
}
impl Frequency {
fn new(value: u32, resolution: FreqRange) -> Frequency {
Frequency {
resolution,
numerator: value,
denominator: 1,
}
}
pub const fn tick(&self) -> Duration {
Duration::from_nanos(1_000_000_000_000u64 * self.denominator as u64 / (self.numerator as u64 * self.resolution as u64))
}
pub const fn ticks_in(&self, d: Duration) -> u64 {
(d.as_secs() * 1_000_000_000u64 + d.subsec_nanos() as u64) * self.resolution as u64 * self.numerator as u64 / (self.denominator as u64 * 1_000_000_000_000u64)
}
into_x!(into_hertz, Hertz);
into_x!(into_kilo, KiloHertz);
into_x!(into_mega, MegaHertz);
into_x!(into_milli, MilliHertz);
}
impl Div<u32> for Frequency {
type Output = Frequency;
fn div(self, rhs: u32) -> Frequency {
assert!(rhs != 0);
Frequency {
resolution: self.resolution,
numerator: self.numerator,
denominator: self.denominator * rhs
}
}
}
impl Mul<u32> for Frequency {
type Output = Frequency;
fn mul(self, rhs: u32) -> Frequency {
Frequency {
resolution: self.resolution,
numerator: self.numerator * rhs,
denominator: self.denominator
}
}
}
impl Mul<Frequency> for u32 {
type Output = Frequency;
fn mul(self, rhs: Frequency) -> Frequency {
rhs * self
}
}
impl Div<Frequency> for Frequency {
type Output = u32;
fn div(self, rhs: Frequency) -> u32 {
assert!(rhs.numerator != 0);
(self.resolution as u32 / rhs.resolution as u32) * (self.numerator * rhs.denominator) / (self.denominator * rhs.numerator)
}
}
pub trait U32Ext {
fn hz(self) -> Frequency;
fn khz(self) -> Frequency;
fn mhz(self) -> Frequency;
fn ms(self) -> Duration;
fn us(self) -> Duration;
fn s(self) -> Duration;
fn clamp(self, min: u32, max: u32) -> u32;
}
impl U32Ext for u32 {
fn hz(self) -> Frequency {
Frequency::new(self, FreqRange::Hertz)
}
fn khz(self) -> Frequency {
Frequency::new(self, FreqRange::KiloHertz)
}
fn mhz(self) -> Frequency {
Frequency::new(self, FreqRange::MegaHertz)
}
fn s(self) -> Duration {
Duration::from_secs(self as u64)
}
fn ms(self) -> Duration {
Duration::from_millis(self as u64)
}
fn us(self) -> Duration {
Duration::from_micros(self as u64)
}
fn clamp(self, min: u32, max: u32) -> u32 {
cmp::min(cmp::max(self, min), max)
}
}
mod test {
#[allow(unused_imports)]
use super::{FreqRange, U32Ext};
#[test]
fn multiply() {
assert_eq!((1.mhz() * 2).numerator, 2);
assert_eq!((2 * 1.mhz()).numerator, 2);
assert_eq!((1.mhz() * 8000).numerator, 8000);
assert_eq!((1.khz() * 80000).into_mega().resolution, FreqRange::MegaHertz);
assert_eq!((1.khz() * 80000).into_mega().numerator, 80);
}
#[test]
fn divide() {
assert_eq!((1.mhz() / 2).into_kilo().numerator, 500);
assert_eq!((1.mhz() / 8000).into_hertz().resolution, FreqRange::Hertz);
assert_eq!((1.mhz() / 8000).into_hertz().numerator, 125);
assert_eq!((1.mhz() / 80000).into_hertz().resolution, FreqRange::Hertz);
assert_eq!((1.mhz() / 80000).into_hertz().numerator, 12);
}
#[test]
fn tick() {
assert_eq!(1.mhz().tick(), 1.us());
assert_eq!(1.khz().tick(), 1.ms());
assert_eq!(1.hz().tick(), 1.s());
assert_eq!(1.hz().ticks_in(2.s()), 2);
}
#[test]
fn ticks_in() {
assert_eq!(1.khz().ticks_in(2.s()), 2_000);
assert_eq!(1.mhz().ticks_in(2.s()), 2_000_000);
assert_eq!(2.mhz().ticks_in(2.s()), 4_000_000);
assert_eq!(2.mhz().ticks_in(2.us()), 4);
}
#[test]
fn scale() {
assert_eq!(1.mhz() / 500.khz(), 2);
assert_eq!(1.mhz() / 1000.khz(), 1);
assert_eq!(1.mhz() / 10000.khz(), 0);
}
#[test]
fn clamp() {
assert_eq!(2.clamp(1, 3), 2);
assert_eq!(20.clamp(1, 3), 3);
assert_eq!(0.clamp(1, 3), 1);
}
#[test]
fn comp() {
assert!(1.mhz() < 2000.khz());
assert!(1.mhz() == 1000.khz());
assert!(1.mhz() >= 1000.khz());
assert!(1.mhz() <= 1000.khz());
}
}