embedded_time/fraction.rs
1//! Fractional/Rational values
2use crate::ConversionError;
3use core::ops;
4use num::{rational::Ratio, CheckedDiv, CheckedMul, Zero};
5
6/// A fractional value
7///
8/// Used primarily to define the _scaling factor_ for the [`Duration`], [`Rate`], [`Instant`] and
9/// [`Clock`] traits and types.
10///
11/// [`Duration`]: duration/trait.Duration.html
12/// [`Rate`]: rate/trait.Rate.html
13/// [`Clock`]: clock/trait.Clock.html
14/// [`Instant`]: instant/struct.Instant.html
15#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
16pub struct Fraction(Ratio<u32>);
17
18impl Fraction {
19 /// Construct a new `Fraction`.
20 ///
21 /// A reduction is **not** performed. Also there is no check for a denominator of `0`. If these
22 /// features are needed, use [`Fraction::new_reduce()`]
23 pub const fn new(numerator: u32, denominator: u32) -> Self {
24 Self(Ratio::new_raw(numerator, denominator))
25 }
26
27 /// Return the numerator of the fraction
28 pub const fn numerator(&self) -> &u32 {
29 self.0.numer()
30 }
31
32 /// Return the denominator of the fraction
33 pub const fn denominator(&self) -> &u32 {
34 self.0.denom()
35 }
36}
37
38impl Fraction {
39 /// Construct a new `Fraction`.
40 ///
41 /// A reduction and `denominator == 0` check **are** performed.
42 ///
43 /// # Errors
44 ///
45 /// [`ConversionError::DivByZero`] : A `0` denominator was detected
46 // TODO: add example
47 pub fn new_reduce(numerator: u32, denominator: u32) -> Result<Self, ConversionError> {
48 if !denominator.is_zero() {
49 Ok(Self(Ratio::new(numerator, denominator)))
50 } else {
51 Err(ConversionError::DivByZero)
52 }
53 }
54
55 /// Returns the value truncated to an integer
56 pub fn to_integer(&self) -> u32 {
57 self.0.to_integer()
58 }
59
60 /// Constructs a `Fraction` from an integer.
61 ///
62 /// Equivalent to `Fraction::new(value,1)`.
63 pub fn from_integer(value: u32) -> Self {
64 Self(Ratio::from_integer(value))
65 }
66
67 /// Returns the reciprocal of the fraction
68 pub fn recip(self) -> Self {
69 Self(self.0.recip())
70 }
71
72 /// Checked `Fraction` × `Fraction` = `Fraction`
73 ///
74 /// Returns [`None`] for any errors
75 ///
76 /// # Examples
77 ///
78 /// ```rust
79 /// # use embedded_time::{fraction::Fraction, ConversionError};
80 /// #
81 /// assert_eq!(Fraction::new(1000, 1).checked_mul(&Fraction::new(5,5)),
82 /// Some(Fraction::new(5_000, 5)));
83 ///
84 /// assert_eq!(Fraction::new(u32::MAX, 1).checked_mul(&Fraction::new(2,1)),
85 /// None);
86 /// ```
87 pub fn checked_mul(&self, v: &Self) -> Option<Self> {
88 self.0.checked_mul(&v.0).map(Self)
89 }
90
91 /// Checked `Fraction` / `Fraction` = `Fraction`
92 ///
93 /// Returns [`None`] for any errors
94 ///
95 /// # Examples
96 ///
97 /// ```rust
98 /// # use embedded_time::{fraction::Fraction, ConversionError};
99 /// #
100 /// assert_eq!(Fraction::new(1000, 1).checked_div(&Fraction::new(10, 1000)),
101 /// Some(Fraction::new(1_000_000, 10)));
102 ///
103 /// assert_eq!(Fraction::new(1, u32::MAX).checked_div(&Fraction::new(2,1)),
104 /// None);
105 /// ```
106 pub fn checked_div(&self, v: &Self) -> Option<Self> {
107 self.0.checked_div(&v.0).map(Self)
108 }
109}
110
111impl ops::Mul<Fraction> for u32 {
112 type Output = Self;
113
114 /// Panicky u32 × `Fraction` = u32
115 fn mul(self, rhs: Fraction) -> Self::Output {
116 (rhs.0 * self).to_integer()
117 }
118}
119
120impl ops::Div<Fraction> for u32 {
121 type Output = Self;
122
123 /// Panicky u32 / `Fraction` = u32
124 #[allow(clippy::suspicious_arithmetic_impl)]
125 fn div(self, rhs: Fraction) -> Self::Output {
126 (rhs.0.recip() * self).to_integer()
127 }
128}
129
130impl ops::Mul<Fraction> for u64 {
131 type Output = Self;
132
133 /// Panicky u64 × `Fraction` = u64
134 fn mul(self, rhs: Fraction) -> Self::Output {
135 (Ratio::new_raw((*rhs.numerator()).into(), (*rhs.denominator()).into()) * self).to_integer()
136 }
137}
138
139impl ops::Div<Fraction> for u64 {
140 type Output = Self;
141
142 /// Panicky u64 / `Fraction` = u64
143 #[allow(clippy::suspicious_arithmetic_impl)]
144 fn div(self, rhs: Fraction) -> Self::Output {
145 (Ratio::new_raw((*rhs.denominator()).into(), (*rhs.numerator()).into()) * self).to_integer()
146 }
147}
148
149impl ops::Mul for Fraction {
150 type Output = Self;
151
152 /// Panicky `Fraction` × `Fraction` = `Fraction`
153 ///
154 /// # Panics
155 ///
156 /// The same reason the integer operation would panic. Namely, if the
157 /// result overflows the type.
158 fn mul(self, rhs: Self) -> Self::Output {
159 Self(self.0 * rhs.0)
160 }
161}
162
163impl ops::Div for Fraction {
164 type Output = Self;
165
166 /// Panicky `Fraction` / `Fraction` = `Fraction`
167 ///
168 /// # Panics
169 ///
170 /// The same reason the integer operation would panic. Namely, if the
171 /// result overflows the type.
172 fn div(self, rhs: Self) -> Self::Output {
173 Self(self.0 / rhs.0)
174 }
175}
176
177impl Default for Fraction {
178 fn default() -> Self {
179 Self::new(1, 1)
180 }
181}
182
183#[cfg(test)]
184mod tests {}