1use std::ops::{Add,Sub,Mul,Div,Neg};
2
3use std::cmp::Ordering;
4
5use arkley_traits::{
6 ArithmeticCore,
7 Abs,
8};
9
10#[derive(Debug,PartialEq,Clone)]
24pub enum Fraction<T> where T : ArithmeticCore + Clone {
25 NaN,
27 PositiveInfinity,
29 NegativeInfinity,
31 Proper(T, T),
33}
34
35impl<T> Fraction<T> where T : ArithmeticCore + Clone {
36 pub const fn new_unchecked(numerator : T,denominator : T) -> Self {
44 Fraction::Proper(numerator,denominator)
45 }
46
47 pub const fn numerator(&self) -> Option<&T>{
51 match self {
52 Fraction::Proper(numerator,_) => Some(&numerator),
53 Fraction::NaN => None,
54 Fraction::PositiveInfinity => None,
55 Fraction::NegativeInfinity => None,
56 }
57 }
58
59 pub const fn denominator(&self) -> Option<&T>{
63 match self {
64 Fraction::Proper(_,denomator) => Some(&denomator),
65 Fraction::NaN => None,
66 Fraction::PositiveInfinity => None,
67 Fraction::NegativeInfinity => None,
68 }
69 }
70
71 pub fn as_inverse(&self) -> Fraction<T> {
77 match self {
78 Fraction::Proper(numerator,denominator) => Fraction::new_unchecked(denominator.clone(),numerator.clone()),
79 Fraction::NaN => Fraction::NaN,
80 Fraction::PositiveInfinity => Fraction::NegativeInfinity,
81 Fraction::NegativeInfinity => Fraction::PositiveInfinity,
82 }
83 }
84}
85
86impl<T> Fraction<T> where T : ArithmeticCore + PartialOrd + Clone {
87 pub fn new(numerator : T,denominator : T) -> Self {
99 if !denominator.is_zero(){
100 return Fraction::new_unchecked_reduced(numerator,denominator);
101 };
102
103 if numerator.is_zero() {
104 return Fraction::NaN;
105 }
106
107 if numerator > T::zero() {
108 Fraction::PositiveInfinity
109 }
110 else {
111 Fraction::NegativeInfinity
112 }
113 }
114
115 pub fn new_unchecked_reduced(numerator : T,denominator : T) -> Fraction<T> {
127 let gcd = numerator.clone().gcd(denominator.clone());
128 let n = if denominator < T::zero() {
129 -numerator / gcd.clone()
130 } else {
131 numerator / gcd.clone()
132 };
133
134 let d = if denominator < T::zero() {
135 -denominator / gcd
136 } else {
137 denominator / gcd
138 };
139
140 Fraction::Proper(n,d)
141 }
142}
143
144impl<T> Abs for Fraction<T> where T : ArithmeticCore + Clone {
145 fn absolute(self) -> Self {
147 match self {
148 Fraction::Proper(numerator,denominator) => Fraction::new_unchecked(numerator.absolute(),denominator),
149 Fraction::NaN => Fraction::NaN,
150 Fraction::PositiveInfinity | Fraction::NegativeInfinity => Fraction::PositiveInfinity,
151 }
152 }
153}
154
155impl<T> std::fmt::Display for Fraction<T> where T : ArithmeticCore + std::fmt::Display + Clone{
156 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
157 match self {
158 Fraction::Proper(numerator,denominator) => {
159 let ns = numerator.to_string();
160 let ds = denominator.to_string();
161
162 let nr = ns.parse::<f64>();
163 let dr = ds.parse::<f64>();
164
165 let string = match (nr,dr) {
166 (Ok(_),Ok(_)) => format!("{}/{}",ns,ds),
167 (Ok(_),Err(_)) => format!("{}/({})",ns,ds),
168 (Err(_),Ok(_)) => format!("({})/{}",ns,ds),
169 _ => format!("({})/({})",ns,ds)
170 };
171
172 write!(f,"{}",string)
173 },
174 Fraction::NaN => write!(f,"NaN"),
175 Fraction::PositiveInfinity => write!(f,"+∞"),
176 Fraction::NegativeInfinity => write!(f,"-∞"),
177 }
178 }
179}
180
181impl<T> Neg for Fraction<T> where T: ArithmeticCore + Clone{
182 type Output = Self;
183
184 fn neg(self) -> Self::Output {
185 match self {
186 Fraction::Proper(numerator,denominator) => Fraction::new_unchecked(-numerator,denominator),
187 Fraction::NaN => Fraction::NaN,
188 Fraction::PositiveInfinity => Fraction::NegativeInfinity,
189 Fraction::NegativeInfinity => Fraction::PositiveInfinity,
190 }
191 }
192}
193
194impl<T> Add for Fraction<T> where T : ArithmeticCore + PartialEq + PartialOrd + Clone{
195 type Output = Self;
196
197 fn add(self, other: Self) -> Self::Output {
198 match (self,other) {
199 (Fraction::Proper(self_numerator , self_denominator),Fraction::Proper(other_numerator , other_denominator)) => {
200 if self_denominator == other_denominator {
201 return Fraction::new_unchecked(self_numerator + other_numerator,self_denominator);
202 }
203
204 let n1 = self_numerator * other_denominator.clone();
205 let n2 = other_numerator * self_denominator.clone();
206 let n = n1 + n2;
207
208 let d = self_denominator * other_denominator;
209
210 Fraction::new_unchecked_reduced(n,d)
211 }
212 (Fraction::NaN,_) | (_,Fraction::NaN) => Fraction::NaN,
213 (Fraction::PositiveInfinity,_) | (_,Fraction::PositiveInfinity) => Fraction::PositiveInfinity,
214 (Fraction::NegativeInfinity,_) | (_,Fraction::NegativeInfinity) => Fraction::NegativeInfinity,
215 }
216 }
217}
218
219impl<T> Sub for Fraction<T> where T : ArithmeticCore + PartialEq + PartialOrd + Clone{
220 type Output = Self;
221
222 fn sub(self, other: Self) -> Self::Output {
223 match (self,other) {
224 (Fraction::Proper(self_numerator , self_denominator),Fraction::Proper(other_numerator , other_denominator)) => {
225 if self_denominator == other_denominator {
226 return Fraction::Proper(self_numerator - other_numerator,self_denominator);
227 }
228
229 let n1 = self_numerator * other_denominator.clone();
230 let n2 = other_numerator * self_denominator.clone();
231 let n = n1 - n2;
232
233 let d = self_denominator * other_denominator;
234
235 Fraction::new_unchecked_reduced(n,d)
236 }
237 (Fraction::NaN,_) | (_,Fraction::NaN) => Fraction::NaN,
238 (Fraction::PositiveInfinity,_) | (_,Fraction::PositiveInfinity) => Fraction::PositiveInfinity,
239 (Fraction::NegativeInfinity,_) | (_,Fraction::NegativeInfinity) => Fraction::NegativeInfinity,
240 }
241 }
242}
243
244impl<T> Mul for Fraction<T> where T : ArithmeticCore + PartialOrd + Clone{
245 type Output = Self;
246
247 fn mul(self, other: Self) -> Self::Output {
248 match (self,other) {
249 (Fraction::Proper(self_numerator , self_denominator),Fraction::Proper(other_numerator , other_denominator)) =>
250 Fraction::new_unchecked_reduced(self_numerator * other_numerator,self_denominator * other_denominator),
251 (Fraction::NaN,_) | (_,Fraction::NaN) => Fraction::NaN,
252 (Fraction::PositiveInfinity,_) | (_,Fraction::PositiveInfinity) => Fraction::PositiveInfinity,
253 (Fraction::NegativeInfinity,_) | (_,Fraction::NegativeInfinity) => Fraction::NegativeInfinity,
254 }
255 }
256}
257
258impl<T> Div for Fraction<T> where T : ArithmeticCore + PartialOrd + Clone {
259 type Output = Self;
260
261 fn div(self, other: Self) -> Self::Output {
262 self * other.as_inverse()
263 }
264}
265
266impl<T> PartialOrd for Fraction<T> where T: ArithmeticCore + PartialOrd + Clone {
267 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
268 let ordering = match (self, other) {
269 (Fraction::NaN, Fraction::NaN) => Ordering::Equal,
270 (Fraction::NaN, _) => Ordering::Less,
271 (_, Fraction::NaN) => Ordering::Greater,
272
273 (Fraction::PositiveInfinity, Fraction::PositiveInfinity) => Ordering::Equal,
274 (Fraction::PositiveInfinity, Fraction::NegativeInfinity) => Ordering::Greater,
275 (Fraction::NegativeInfinity, Fraction::PositiveInfinity) => Ordering::Less,
276 (Fraction::NegativeInfinity, Fraction::NegativeInfinity) => Ordering::Equal,
277
278 (Fraction::PositiveInfinity, Fraction::Proper(_, _)) => Ordering::Greater,
279 (Fraction::NegativeInfinity, Fraction::Proper(_, _)) => Ordering::Less,
280
281 (Fraction::Proper(_, _), Fraction::PositiveInfinity) => Ordering::Less,
282 (Fraction::Proper(_, _), Fraction::NegativeInfinity) => Ordering::Greater,
283 (
284 Fraction::Proper(n1,d1),
285 Fraction::Proper(n2,d2),
286 ) => match d1.is_zero() && d2.is_zero() {
287 true => Ordering::Equal,
288 false => {
289 let lhs = n1.clone() * d2.clone();
290 let rhs = n2.clone() * d1.clone();
291
292 lhs.partial_cmp(&rhs).unwrap()
293 }
294 }
295 };
296
297 Some(ordering)
298 }
299}
300
301macro_rules! primitives {
302 (form => $($t:ty),*) => {
303 $(
304 impl<T> From<$t> for Fraction<T> where T : ArithmeticCore + Clone, $t : Into<T> {
305 fn from(value: $t) -> Self {
306 Fraction::new_unchecked(value.into(), 1.into())
307 }
308 }
309 )*
310 };
311
312 (try_from => $($t:ty),*) => {
313 $(
314 impl TryFrom<&str> for Fraction<$t> {
315 type Error = std::num::ParseIntError;
316 fn try_from(value : &str) -> Result<Self, Self::Error> {
317 match value.find('/') {
318 None => value.parse::<$t>().and_then(|number| Ok(number.into())),
319 Some(index) => {
320 let n : $t = value[..index].parse::<$t>()?;
321 let d : $t = value[index + 1..].parse::<$t>()?;
322 Ok(Fraction::new(n,d))
323 }
324 }
325 }
326 }
327 )*
328 };
329
330 (add; $($t:ty),*) => {
331 $(
332 impl<T> Add<$t> for Fraction<T> where T : ArithmeticCore + PartialEq + PartialOrd + Clone, $t : Into<Self> {
333 type Output = Self;
334
335 fn add(self, other: $t) -> Self::Output {
336 let rhs : Self = other.into();
337 self + rhs
338 }
339 }
340 )*
341 };
342
343 (sub; $($t:ty),*) => {
344 $(
345 impl<T> Sub<$t> for Fraction<T> where T : ArithmeticCore + PartialEq + PartialOrd + Clone, $t : Into<Self> {
346 type Output = Self;
347
348 fn sub(self, other: $t) -> Self::Output {
349 let rhs : Self = other.into();
350 self - rhs
351 }
352 }
353 )*
354 };
355
356 (div; $($t:ty),*) => {
357 $(
358 impl<T> Div<$t> for Fraction<T> where T : ArithmeticCore + PartialOrd + Clone, $t : Into<Self> {
359 type Output = Self;
360
361 fn div(self, other: $t) -> Self::Output {
362 let rhs : Self = other.into();
363 self / rhs
364 }
365 }
366 )*
367 };
368
369 (mul; $($t:ty),*) => {
370 $(
371 impl<T> Mul<$t> for Fraction<T> where T : ArithmeticCore + PartialOrd + Clone, $t : Into<Self> {
372 type Output = Self;
373
374 fn mul(self, other: $t) -> Self::Output {
375 let rhs : Self = other.into();
376 self * rhs
377 }
378 }
379 )*
380 };
381
382 (operations => $($t:ty),*) => {
383 $(
384 primitives!(add; $t);
385 primitives!(sub; $t);
386 primitives!(mul; $t);
387 primitives!(div; $t);
388 )*
389 };
390
391 (eq => $($t:ty),*) => {
392 $(
393 impl<T> PartialEq<$t> for Fraction<T> where T : ArithmeticCore + Clone, $t : Into<Self> {
394 fn eq(&self,other: &$t) -> bool {
395 let rhs : Self = (*other).into();
396 *self == rhs
397 }
398 }
399 )*
400 };
401
402 (ord => $($t:ty),*) => {
403 $(
404 impl<T> PartialOrd<$t> for Fraction<T> where
405 Self : PartialOrd,
406 T: ArithmeticCore + Clone,
407 $t : Into<Fraction<T>> {
408 fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
409 let rhs : Self = (*other).into();
410 self.partial_cmp(&rhs)
411 }
412 }
413 )*
414 };
415
416}
417
418primitives!(form => i8,i16,i32,i64);
419primitives!(try_from => i8,i16,i32,i64);
420primitives!(operations => i8, i16, i32, i64);
421primitives!(eq => i8,i16,i32,i64);
422primitives!(ord => i8,i16,i32,i64);
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn new_unchecked(){
430 let fraction1 = Fraction::new_unchecked(1, 2);
431 let fraction2 = Fraction::new_unchecked(3, 4);
432 let fraction3 = Fraction::new_unchecked(6,8);
433
434 assert_eq!(fraction1,Fraction::Proper(1,2));
435 assert_eq!(fraction2,Fraction::Proper(3,4));
436 assert_ne!(fraction3,fraction1);
437 }
438
439 #[test]
440 fn new_unchecked_reduced(){
441 let fraction1 = Fraction::new_unchecked_reduced(4,8);
442 let fraction2 = Fraction::new_unchecked_reduced(3, 4);
443 let fraction3 = Fraction::new_unchecked_reduced(6,8);
444
445 assert_eq!(fraction1,Fraction::Proper(1,2));
446 assert_eq!(fraction2,Fraction::Proper(3,4));
447 assert_ne!(fraction3,Fraction::NaN);
448 }
449
450 #[test]
451 fn new_fraction() {
452 let fraction1 = Fraction::new(2, 4);
454 assert_eq!(fraction1, Fraction::Proper(1, 2));
455
456 let fraction2 = Fraction::new(5, 15);
458 assert_eq!(fraction2, Fraction::Proper(1, 3));
459
460 let fraction3 = Fraction::new(-10, -20);
462 assert_eq!(fraction3, Fraction::Proper(1, 2));
463
464 let fraction4 = Fraction::new(0, 5);
466 assert_eq!(fraction4, Fraction::Proper(0, 1));
467
468 let fraction5 = Fraction::new(7, 0);
470 assert_eq!(fraction5, Fraction::PositiveInfinity);
471
472 let fraction6 = Fraction::new(0, 0);
474 assert_eq!(fraction6, Fraction::NaN);
475
476 let fraction7 = Fraction::new(-3, 0);
478 assert_eq!(fraction7, Fraction::NegativeInfinity);
479
480 let fraction8 = Fraction::new(10, 4);
481 assert_eq!(fraction8,Fraction::Proper(5, 2));
482 let fraction9 = Fraction::new(3, 4);
483 assert_eq!(fraction9,Fraction::Proper(3, 4));
484 }
485
486 #[test]
487 fn addition() {
488 let fraction1 = Fraction::new(1, 2);
490 let fraction2 = Fraction::new(3, 4);
491
492 let result = fraction1 + fraction2;
494
495 if let Fraction::Proper(numerator, denominator) = result {
497 assert_eq!(numerator, 5);
498 assert_eq!(denominator, 4);
499 } else {
500 assert!(false, "Expected top-heavy fraction");
501 }
502 }
503
504 #[test]
505 fn subtraction() {
506 let fraction1 = Fraction::new(2,4);
508 let fraction2 = Fraction::new(1,4);
509
510 let result = fraction1 - fraction2;
512
513 if let Fraction::Proper(numerator, denominator) = result {
515 assert_eq!(numerator,1);
516 assert_eq!(denominator,4);
517 } else {
518 assert!(false, "Expected top-heavy fraction");
519 }
520 }
521
522 #[test]
523 fn multiplication() {
524 let fraction1 = Fraction::new(2,4);
526 let fraction2 = Fraction::new(1,4);
527
528 let result = fraction1 * fraction2;
530
531 if let Fraction::Proper(numerator, denominator) = result {
533 assert_eq!(numerator,1);
534 assert_eq!(denominator,8);
535 } else {
536 assert!(false, "Expected top-heavy fraction");
537 }
538 }
539
540 #[test]
541 fn divide() {
542 let fraction1 = Fraction::new(1,1);
544 let fraction2 = Fraction::new(1,2);
545
546 let result = fraction1 / fraction2;
548
549 if let Fraction::Proper(numerator, denominator) = result {
551 assert_eq!(numerator,2);
552 assert_eq!(denominator,1);
553 } else {
554 assert!(false, "Expected top-heavy fraction");
555 }
556 }
557
558 #[test]
559 fn fraction_equality() {
560 let fraction1: Fraction<i32> = Fraction::new(2, 4);
561 let fraction2: Fraction<i32> = Fraction::new(1, 2);
562 let fraction3: Fraction<i32> = Fraction::new(4, 8);
563 let fraction4: Fraction<i32> = Fraction::NaN;
564
565 assert_eq!(fraction1, fraction2); assert_eq!(fraction1, fraction3); assert_ne!(fraction1, fraction4); assert_ne!(fraction2, fraction4); assert_ne!(fraction3, fraction4); }
571
572 #[test]
573 fn neg(){
574 let fraction1 = Fraction::new(1,2);
575 let result = -Fraction::new(1,2);
576 assert_ne!(fraction1,result);
577 assert_eq!(result,Fraction::new(-1,2));
578 }
579
580 #[test]
581 fn abs() {
582 let fraction1 = Fraction::new(-1, 2);
583 let abs_fraction1 = fraction1.absolute();
584 assert_eq!(abs_fraction1, Fraction::new(1, 2));
585
586 let fraction2 = Fraction::new(3, 4);
587 let abs_fraction2 = fraction2.clone().absolute();
588 assert_eq!(abs_fraction2, fraction2); }
590
591 #[test]
592 fn partial_ord_integers_vs_fractions() {
593 assert!(Fraction::new(1,1) >= Fraction::new(1,2));
594
595 let integer_values : [i32;4] = [1, 3, -2, 5];
597 let fraction_values = [
598 Fraction::new(1, 2),
599 Fraction::new(-3, 4),
600 Fraction::new(5, 1),
601 Fraction::new(-6, 2),
602 ];
603 let results = [
604 Some(Ordering::Less), Some(Ordering::Less), Some(Ordering::Greater), Some(Ordering::Less) ];
609
610 for x in 0..4 {
611 println!("f1 = {:?}",fraction_values[x]);
612 println!("i2 = {:?}",integer_values[x]);
613 println!("-------------");
614
615 assert_eq!(fraction_values[x].partial_cmp(&integer_values[x]), results[x]);
616 }
617 }
618
619 #[test]
620 fn partial_eq_integers_vs_fractions() {
621 let integer_values = [2, -3, 5, -1, 7];
623 let fraction_values = [
624 Fraction::new(2, 1),
625 Fraction::new(-3, 1),
626 Fraction::new(5, 1),
627 Fraction::new(-1, 1),
628 Fraction::new(7, 1),
629 ];
630
631 for x in 0..5 {
632 assert_eq!(fraction_values[x],integer_values[x]);
633 }
634 }
635
636 #[test]
638 fn try_from_i8() {
639 let input = "42";
640 let result = Fraction::<i8>::try_from(input);
641 assert!(result.is_ok());
642
643 }
645
646 #[test]
648 fn try_from_i16() {
649 let input = "12345";
650 let result = Fraction::<i16>::try_from(input);
651 assert!(result.is_ok());
652
653 }
655
656 #[test]
658 fn try_from_i32() {
659 let input = "100/25";
660 let result = Fraction::<i32>::try_from(input);
661 assert!(result.is_ok());
662
663 }
665
666 #[test]
668 fn try_from_i64() {
669 let input = "500/125";
670 let result = Fraction::<i64>::try_from(input);
671 assert!(result.is_ok());
672
673 }
675
676 #[test]
678 fn try_from_negative() {
679 let input = "-42";
680 let result = Fraction::<i32>::try_from(input);
681 assert!(result.is_ok());
682
683 }
685
686 #[test]
687 fn try_from_negative_fraction() {
688 let input = "-1/2";
689 let result = Fraction::<i64>::try_from(input);
690 assert!(result.is_ok());
691 }
693
694 #[test]
695 fn try_from_invalid_input() {
696 let input = "invalid"; let result = Fraction::<i16>::try_from(input);
698 assert!(result.is_err());
699
700 }
702}