* Make some fields and functions non-public where possible. * Enable doc warnings for missing public API docs.
		
			
				
	
	
		
			131 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
//! Types for dealing with rational numbers.
 | 
						|
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 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);
 | 
						|
    }
 | 
						|
}
 |