embassy_hal_internal/
ratio.rs

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