numb_rs/
fraction.rs

1//! Implements a fraction type for unsigned integers
2#![allow(dead_code)]
3
4use crate::numerics::{gcd, Unsigned};
5use std::fmt::{Display, Formatter};
6use std::ops::{Add, Div, Mul, Sub};
7
8// TODO: Implement Signs for Fraction, type casting etc.
9
10// PartialEq and Eq need to be manually implemented as 2/4 = 1/2 for instance
11#[derive(Debug, Eq, PartialEq)]
12pub struct Fraction<T: Unsigned> {
13    numerator: T,
14    denominator: T,
15}
16
17pub const HALF: Fraction<u32> = Fraction {
18    numerator: 1,
19    denominator: 2,
20};
21
22/// shorthand for creating u32 fractions
23macro_rules! frac32 {
24    ($num:expr , $den:expr) => {
25        Fraction::new($num as u32, $den as u32);
26    };
27}
28
29impl<T: Unsigned> Fraction<T> {
30    pub fn new(numerator: T, denominator: T) -> Self {
31        Fraction {
32            numerator,
33            denominator,
34        }
35    }
36
37    pub fn reciprocal(self) -> Self {
38        Fraction {
39            numerator: self.denominator,
40            denominator: self.numerator,
41        }
42    }
43
44    pub fn reduce(self) -> Self {
45        let gcd = gcd(self.numerator, self.denominator);
46        Fraction {
47            numerator: self.numerator / gcd,
48            denominator: self.denominator / gcd,
49        }
50    }
51}
52
53impl<T: Unsigned> Display for Fraction<T> {
54    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}/{}", self.numerator, self.denominator)
56    }
57}
58
59impl<T: Unsigned> Add for Fraction<T> {
60    type Output = Fraction<T>;
61
62    fn add(self, rhs: Self) -> Self::Output {
63        let denominator = self.denominator * rhs.denominator;
64        let numerator = self.numerator * rhs.denominator + rhs.numerator * self.denominator;
65
66        Fraction {
67            numerator,
68            denominator,
69        }
70    }
71}
72
73impl<T: Unsigned> Sub for Fraction<T> {
74    type Output = Fraction<T>;
75
76    fn sub(self, rhs: Self) -> Self::Output {
77        let denominator = self.denominator * rhs.denominator;
78        let numerator = self.numerator * rhs.denominator - rhs.numerator * self.denominator;
79
80        Fraction {
81            numerator,
82            denominator,
83        }
84    }
85}
86
87impl<T: Unsigned> Mul for Fraction<T> {
88    type Output = Fraction<T>;
89
90    fn mul(self, rhs: Self) -> Self::Output {
91        let denominator = self.denominator * rhs.denominator;
92        let numerator = self.numerator * rhs.numerator;
93
94        Fraction {
95            numerator,
96            denominator,
97        }
98    }
99}
100
101impl<T: Unsigned> Div for Fraction<T> {
102    type Output = Fraction<T>;
103
104    #[allow(clippy::suspicious_arithmetic_impl)]
105    fn div(self, rhs: Self) -> Self::Output {
106        self * rhs.reciprocal()
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn macro_test() {
116        let half = frac32!(1, 2);
117        assert_eq!(half.numerator, HALF.numerator);
118        assert_eq!(half.denominator, HALF.denominator);
119    }
120
121    #[test]
122    fn display_test() {
123        let a = frac32!(132, 203);
124        assert_eq!(format!("{}", a), "132/203".to_string())
125    }
126
127    #[test]
128    fn add_test() {
129        let a = frac32!(1, 2);
130        let b = frac32!(1, 4);
131
132        assert_eq!(a + b, frac32!(6, 8));
133    }
134
135    #[test]
136    fn sub_test() {
137        let a = frac32!(1, 2);
138        let b = frac32!(1, 4);
139
140        assert_eq!(a - b, frac32!(2, 8));
141    }
142
143    #[test]
144    fn mul_test() {
145        let a = frac32!(1, 2);
146        let b = frac32!(2, 5);
147
148        assert_eq!(a * b, frac32!(2, 10));
149    }
150
151    #[test]
152    fn div_test() {
153        let a = frac32!(1, 2);
154        let b = frac32!(1, 4);
155
156        assert_eq!(a / b, frac32!(4, 2));
157    }
158
159    #[test]
160    fn reduction_test() {
161        let a = frac32!(157684, 511974);
162        assert_eq!(a.reduce(), frac32!(158, 513))
163    }
164}