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