1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use core::ops::{Add, Div, Mul};
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul};

/// Represents the ratio between two numbers.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Ratio<T> {
    /// Numerator.
    numer: T,
    /// Denominator.
    denom: T,
}

impl<T> Ratio<T> {
    /// Creates a new `Ratio`.
    #[inline(always)]
    pub(crate) const fn new_raw(numer: T, denom: T) -> Ratio<T> {
        Ratio { numer, denom }
    }

    /// Gets an immutable reference to the numerator.
    #[inline(always)]
    pub const fn numer(&self) -> &T {
        &self.numer
    }

    /// Gets an immutable reference to the denominator.
    #[inline(always)]
    pub const fn denom(&self) -> &T {
        &self.denom
    }
}

impl<T: CheckedDiv> Ratio<T> {
    /// Converts to an integer, rounding towards zero.
    #[inline(always)]
    pub fn to_integer(&self) -> T {
        unwrap!(self.numer().checked_div(self.denom()))
    }
}

impl<T: CheckedMul> Div<T> for Ratio<T> {
    type Output = Self;

    #[inline(always)]
    fn div(mut self, rhs: T) -> Self::Output {
        self.denom = unwrap!(self.denom().checked_mul(&rhs));
        self
    }
}

impl<T: CheckedMul> Mul<T> for Ratio<T> {
    type Output = Self;

    #[inline(always)]
    fn mul(mut self, rhs: T) -> Self::Output {
        self.numer = unwrap!(self.numer().checked_mul(&rhs));
        self
    }
}

impl<T: CheckedMul + CheckedAdd> Add<T> for Ratio<T> {
    type Output = Self;

    #[inline(always)]
    fn add(mut self, rhs: T) -> Self::Output {
        self.numer = unwrap!(unwrap!(self.denom().checked_mul(&rhs)).checked_add(self.numer()));
        self
    }
}

macro_rules! impl_from_for_float {
    ($from:ident) => {
        impl From<Ratio<$from>> for f32 {
            #[inline(always)]
            fn from(r: Ratio<$from>) -> Self {
                (r.numer as f32) / (r.denom as f32)
            }
        }

        impl From<Ratio<$from>> for f64 {
            #[inline(always)]
            fn from(r: Ratio<$from>) -> Self {
                (r.numer as f64) / (r.denom as f64)
            }
        }
    };
}

impl_from_for_float!(u8);
impl_from_for_float!(u16);
impl_from_for_float!(u32);
impl_from_for_float!(u64);
impl_from_for_float!(u128);
impl_from_for_float!(i8);
impl_from_for_float!(i16);
impl_from_for_float!(i32);
impl_from_for_float!(i64);
impl_from_for_float!(i128);

impl<T: core::fmt::Display> core::fmt::Display for Ratio<T> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        core::write!(f, "{} / {}", self.numer(), self.denom())
    }
}

#[cfg(test)]
mod tests {
    use super::Ratio;

    #[test]
    fn basics() {
        let mut r = Ratio::new_raw(1, 2) + 2;
        assert_eq!(*r.numer(), 5);
        assert_eq!(*r.denom(), 2);
        assert_eq!(r.to_integer(), 2);

        r = r * 2;
        assert_eq!(*r.numer(), 10);
        assert_eq!(*r.denom(), 2);
        assert_eq!(r.to_integer(), 5);

        r = r / 2;
        assert_eq!(*r.numer(), 10);
        assert_eq!(*r.denom(), 4);
        assert_eq!(r.to_integer(), 2);
    }
}