use core::{
fmt::{self, Alignment},
ops::{Add, AddAssign, Div, Mul, Sub, SubAssign},
};
use num::NumCast;
const ONE_MHZ: u64 = 1_000_000;
const ONE_GHZ: u64 = ONE_MHZ * 1000;
#[repr(transparent)]
#[derive(Default, Clone, Copy)]
pub struct Frequency {
hz: u64,
}
impl Frequency {
pub const fn new(hz: u64) -> Self {
Self { hz }
}
pub fn from_hz<T: NumCast>(val: T) -> Self {
Self {
hz: val.to_u64().unwrap(),
}
}
pub fn from_mhz<T: NumCast>(val: T) -> Self {
Self {
hz: (val.to_f64().unwrap() * ONE_MHZ as f64) as u64,
}
}
pub fn from_ghz<T: NumCast>(val: T) -> Self {
Self {
hz: (val.to_f64().unwrap() * ONE_GHZ as f64) as u64,
}
}
pub fn to_hz<T: NumCast>(&self) -> T {
T::from(self.hz).unwrap()
}
pub fn to_mhz<T: NumCast>(&self) -> T {
T::from(self.hz as f64 / ONE_MHZ as f64).unwrap()
}
pub fn to_ghz<T: NumCast>(&self) -> T {
T::from(self.hz as f64 / ONE_GHZ as f64).unwrap()
}
}
impl Add for Frequency {
type Output = Frequency;
fn add(self, rhs: Self) -> Self::Output {
Self {
hz: self.hz + rhs.hz,
}
}
}
impl AddAssign for Frequency {
fn add_assign(&mut self, rhs: Self) {
self.hz += rhs.hz;
}
}
impl Sub for Frequency {
type Output = Frequency;
fn sub(self, rhs: Self) -> Self::Output {
Self {
hz: self.hz - rhs.hz,
}
}
}
impl SubAssign for Frequency {
fn sub_assign(&mut self, rhs: Self) {
self.hz -= rhs.hz;
}
}
impl<N: NumCast> Mul<N> for Frequency {
type Output = Frequency;
fn mul(self, rhs: N) -> Self::Output {
Self {
hz: self.hz * rhs.to_u64().unwrap(),
}
}
}
impl Div<f32> for Frequency {
type Output = Frequency;
fn div(self, rhs: f32) -> Self::Output {
Self {
hz: ((self.hz as f32) / rhs) as u64,
}
}
}
impl Div<f64> for Frequency {
type Output = Frequency;
fn div(self, rhs: f64) -> Self::Output {
Self {
hz: ((self.hz as f64) / rhs) as u64,
}
}
}
macro_rules! impl_div_num {
($T: ty) => {
impl Div<$T> for Frequency {
type Output = Frequency;
fn div(self, rhs: $T) -> Self::Output {
Self {
hz: self.hz / rhs as u64,
}
}
}
};
}
impl_div_num!(u8);
impl_div_num!(i8);
impl_div_num!(u16);
impl_div_num!(i16);
impl_div_num!(u32);
impl_div_num!(i32);
impl_div_num!(usize);
impl_div_num!(isize);
impl_div_num!(u64);
impl_div_num!(i64);
impl Div<Frequency> for Frequency {
type Output = f64;
fn div(self, rhs: Frequency) -> Self::Output {
self.hz as f64 / rhs.hz as f64
}
}
impl PartialEq for Frequency {
fn eq(&self, other: &Self) -> bool {
self.hz == other.hz
}
}
impl PartialOrd for Frequency {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.hz.partial_cmp(&other.hz)
}
}
impl core::fmt::Debug for Frequency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Frequency Hz ({})", self.hz)
}
}
impl std::fmt::Display for Frequency {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut value = self.hz as f64;
let mut unit = " Hz";
let mut precision = f.precision().unwrap_or(3);
if self.hz >= ONE_GHZ {
value = value / ONE_GHZ as f64;
unit = "GHz";
} else if self.hz >= ONE_MHZ {
value = value / ONE_MHZ as f64;
unit = "MHz";
} else {
precision = 0;
}
if let Some(width) = f.width() {
if f.align().unwrap_or(Alignment::Left) == Alignment::Left {
write!(f, "{:<width$.precision$}", value)?;
} else {
write!(f, "{:>width$.precision$}", value)?;
}
} else {
f.write_fmt(format_args!("{:.precision$}", value))?;
}
f.write_str(&unit)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_freq() {
let freq = Frequency::from_mhz(1.0);
let a: f64 = freq.to_mhz();
let b: i32 = freq.to_hz();
assert_eq!(a, 1.0);
assert_eq!(b, 1_000_000);
println!("{}", freq);
}
#[test]
fn test_freq_add() {
let freq = Frequency::from_mhz(1.0);
let freq2 = Frequency::from_mhz(2.0);
let freq3 = freq + freq2;
let a: f64 = freq3.to_mhz();
assert_eq!(a, 3.0);
}
#[test]
fn test_freq_div() {
let freq = Frequency::from_mhz(1.0);
let freq2 = Frequency::from_mhz(2.0);
let p = freq / freq2;
assert_eq!(p, 0.5);
let freq = Frequency::from_mhz(3.0);
let b = 2isize;
let p = freq / b;
let want = Frequency::from_mhz(1.5);
assert_eq!(p, want);
let freq = Frequency::from_mhz(1.0);
let b = 0.5f32;
let p = freq / b;
let want = Frequency::from_mhz(2);
assert_eq!(p, want);
}
#[test]
fn test_print() {
let freq = Frequency::from_mhz(12.3456789);
assert_eq!(format!("{:<10.2}", freq), "12.35 MHz");
let freq = Frequency::from_hz(12);
println!("{:<10.2}", freq);
}
}