1use core::ops::{Add, Div, Mul};
2use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};
3
4#[derive(Copy, Clone, Debug)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub struct Ratio<T> {
8 numer: T,
10 denom: T,
12}
13
14impl<T> Ratio<T> {
15 #[inline(always)]
17 pub(crate) const fn new_raw(numer: T, denom: T) -> Ratio<T> {
18 Ratio { numer, denom }
19 }
20
21 #[inline(always)]
23 pub const fn numer(&self) -> &T {
24 &self.numer
25 }
26
27 #[inline(always)]
29 pub const fn denom(&self) -> &T {
30 &self.denom
31 }
32}
33
34impl<T: CheckedDiv> Ratio<T> {
35 #[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}