stm32wlxx_hal/
ratio.rs

1use core::ops::{Add, Div, Mul};
2use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
3
4/// Represents the ratio between two numbers.
5#[derive(Copy, Clone, Debug)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub struct Ratio<T> {
8    /// Numerator.
9    numer: T,
10    /// Denominator.
11    denom: T,
12}
13
14impl<T> Ratio<T> {
15    /// Creates a new `Ratio`.
16    #[inline(always)]
17    pub(crate) const fn new_raw(numer: T, denom: T) -> Ratio<T> {
18        Ratio { numer, denom }
19    }
20
21    /// Gets an immutable reference to the numerator.
22    #[inline(always)]
23    pub const fn numer(&self) -> &T {
24        &self.numer
25    }
26
27    /// Gets an immutable reference to the denominator.
28    #[inline(always)]
29    pub const fn denom(&self) -> &T {
30        &self.denom
31    }
32}
33
34impl<T: CheckedDiv> Ratio<T> {
35    /// Converts to an integer, rounding towards zero.
36    #[inline(always)]
37    pub fn to_integer(&self) -> T {
38        unwrap!(self.numer().checked_div(self.denom()))
39    }
40}
41
42impl<T: CheckedMul> Div<T> for Ratio<T> {
43    type Output = Self;
44
45    #[inline(always)]
46    fn div(mut self, rhs: T) -> Self::Output {
47        self.denom = unwrap!(self.denom().checked_mul(&rhs));
48        self
49    }
50}
51
52impl<T: CheckedMul> Mul<T> for Ratio<T> {
53    type Output = Self;
54
55    #[inline(always)]
56    fn mul(mut self, rhs: T) -> Self::Output {
57        self.numer = unwrap!(self.numer().checked_mul(&rhs));
58        self
59    }
60}
61
62impl<T: CheckedMul + CheckedAdd> Add<T> for Ratio<T> {
63    type Output = Self;
64
65    #[inline(always)]
66    fn add(mut self, rhs: T) -> Self::Output {
67        self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer()));
68        self
69    }
70}
71
72macro_rules! impl_from_for_float {
73    ($from:ident) => {
74        impl From<Ratio<$from>> for f32 {
75            #[inline(always)]
76            fn from(r: Ratio<$from>) -> Self {
77                (r.numer as f32) / (r.denom as f32)
78            }
79        }
80
81        impl From<Ratio<$from>> for f64 {
82            #[inline(always)]
83            fn from(r: Ratio<$from>) -> Self {
84                (r.numer as f64) / (r.denom as f64)
85            }
86        }
87    };
88}
89
90impl_from_for_float!(u8);
91impl_from_for_float!(u16);
92impl_from_for_float!(u32);
93impl_from_for_float!(u64);
94impl_from_for_float!(u128);
95impl_from_for_float!(i8);
96impl_from_for_float!(i16);
97impl_from_for_float!(i32);
98impl_from_for_float!(i64);
99impl_from_for_float!(i128);
100
101impl<T: core::fmt::Display> core::fmt::Display for Ratio<T> {
102    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103        core::write!(f, "{} / {}", self.numer(), self.denom())
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::Ratio;
110
111    #[test]
112    fn basics() {
113        let mut r = Ratio::new_raw(1, 2) + 2;
114        assert_eq!(*r.numer(), 5);
115        assert_eq!(*r.denom(), 2);
116        assert_eq!(r.to_integer(), 2);
117
118        r = r * 2;
119        assert_eq!(*r.numer(), 10);
120        assert_eq!(*r.denom(), 2);
121        assert_eq!(r.to_integer(), 5);
122
123        r = r / 2;
124        assert_eq!(*r.numer(), 10);
125        assert_eq!(*r.denom(), 4);
126        assert_eq!(r.to_integer(), 2);
127    }
128}