1#![allow(dead_code)]
3
4use crate::numerics::{gcd, Unsigned};
5use std::fmt::{Display, Formatter};
6use std::ops::{Add, Div, Mul, Sub};
7
8#[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
22macro_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}