arkley_numerics/
fraction.rs

1use std::ops::{Add,Sub,Mul,Div,Neg};
2
3use std::cmp::Ordering;
4
5use arkley_traits::{
6    ArithmeticCore,
7    Abs,
8};
9
10/// The `Fraction` struct represents a fraction with a numerator and denominator.
11///
12/// # Overview
13///
14/// `Fraction` is designed to be a precise and lossless drop-in replacement for floating-point types.
15/// It provides infinite precision and guarantees no loss of information during calculations.
16/// The numerator and denominator can be of different types that implements the necessary traits.
17///
18/// # Usage
19///
20/// The `Fraction` struct can be used to perform arithmetic operations on fractions,
21/// such as addition, subtraction, multiplication, and division.
22/// It also supports comparison operations, simplification, conversion to other types and other mathematical operations.
23#[derive(Debug,PartialEq,Clone)]
24pub enum Fraction<T> where T : ArithmeticCore + Clone {
25    /// Represents an undefined or "Not a Number" fraction.
26    NaN,
27    /// Represents positive infinity.
28    PositiveInfinity,
29    /// Represents negative infinity.
30    NegativeInfinity,
31    /// Represents a proper fraction with a numerator and a denominator.
32    Proper(T, T),
33}
34
35impl<T> Fraction<T> where T : ArithmeticCore + Clone {
36    /// Constructs a new `Fraction` instance with the given numerator and denominator.
37    ///
38    /// # Safety
39    ///
40    /// This method does not perform any validation or simplification of the fraction.
41    /// It assumes that the numerator and denominator are valid and correctly provided. 
42    /// `Note` : If these conditions are not method then operations like PartialEq or PartialOrd maybe not function correctly so use this at your own risk. However if operation like + - * / or ^ is performed on self then this will be fixed
43    pub const fn new_unchecked(numerator : T,denominator : T) -> Self  {
44        Fraction::Proper(numerator,denominator)
45    }
46
47    /// Returns an option containing a reference to the numerator of the fraction.
48    ///
49    /// Returns `Some` if the fraction is in the `Fraction::Proper` variant, otherwise returns `None`.
50    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    /// Returns an option containing a reference to the denominator of the fraction.
60    ///
61    /// Returns `Some` if the fraction is in the `Fraction::Proper` variant, otherwise returns `None`.
62    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    /// Returns a new `Fraction` instance that represents the inverse of the current fraction.
72    ///
73    /// If the current fraction is in the `Fraction::Proper` variant, this method swaps the
74    /// numerator and denominator to create the inverse fraction. For other variant types, it returns
75    /// `NaN` for `NaN`, swaps `PositiveInfinity` to `NegativeInfinity`, and vice versa.
76    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    /// Creates a new fraction with the given numerator and denominator.
88    ///
89    /// # Arguments
90    ///
91    /// * `numerator` - The numerator of the fraction.
92    /// * `denominator` - The denominator of the fraction.
93    ///
94    /// # Returns
95    ///
96    /// A new `Fraction` instance based on the provided numerator and denominator.
97    ///
98    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    /// Creates a new `Fraction` with the given numerator and denominator.
116    /// The fraction is reduced to its simplest form, where the numerator and denominator have no common divisors.
117    ///
118    /// # Arguments
119    ///
120    /// * `numerator`: The numerator of the fraction.
121    /// * `denominator`: The denominator of the fraction.
122    ///
123    /// # Returns
124    ///
125    /// The reduced `Fraction` with the provided numerator and denominator.
126    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    // Required method
146    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        // Test case 1: Create a new fraction with a reduced numerator and denominator
453        let fraction1 = Fraction::new(2, 4);
454        assert_eq!(fraction1, Fraction::Proper(1, 2));
455
456        // Test case 2: Create a new fraction with an unreduced numerator and denominator
457        let fraction2 = Fraction::new(5, 15);
458        assert_eq!(fraction2, Fraction::Proper(1, 3));
459
460        // Test case 3: Create a new fraction with a negative numerator and denominator
461        let fraction3 = Fraction::new(-10, -20);
462        assert_eq!(fraction3, Fraction::Proper(1, 2));
463
464        // Test case 4: Create a new fraction with a zero numerator and non-zero denominator
465        let fraction4 = Fraction::new(0, 5);
466        assert_eq!(fraction4, Fraction::Proper(0, 1));
467
468        // Test case 5: Create a new fraction with a non-zero numerator and zero denominator
469        let fraction5 = Fraction::new(7, 0);
470        assert_eq!(fraction5, Fraction::PositiveInfinity);
471
472        // Test case 6: Create a new fraction with both numerator and denominator as zero
473        let fraction6 = Fraction::new(0, 0);
474        assert_eq!(fraction6, Fraction::NaN);
475
476        // Test case 7: Create a new fraction with a negative numerator and zero denominator
477        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        // Create fractions
489        let fraction1 = Fraction::new(1, 2);
490        let fraction2 = Fraction::new(3, 4);
491
492        // Add fractions
493        let result = fraction1 + fraction2;
494
495        // Verify the result
496        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        // Create fractions
507        let fraction1 = Fraction::new(2,4);
508        let fraction2 = Fraction::new(1,4);
509
510        // Add fractions
511        let result = fraction1 - fraction2;
512
513        // Verify the result
514        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        // Create fractions
525        let fraction1 = Fraction::new(2,4);
526        let fraction2 = Fraction::new(1,4);
527
528        // Add fractions
529        let result = fraction1 * fraction2;
530
531        // Verify the result
532        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        // Create fractions
543        let fraction1 = Fraction::new(1,1);
544        let fraction2 = Fraction::new(1,2);
545
546        // Add fractions
547        let result = fraction1 / fraction2;
548
549        // Verify the result
550        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);  // Fractions with equivalent values should be equal
566        assert_eq!(fraction1, fraction3);  // Fractions with equivalent values should be equal
567        assert_ne!(fraction1, fraction4);  // Different variants should not be equal
568        assert_ne!(fraction2, fraction4);  // Different variants should not be equal
569        assert_ne!(fraction3, fraction4);  // Different variants should not be equal
570    }
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); // Absolute value of a positive fraction is the fraction itself
589    }
590
591    #[test]
592    fn partial_ord_integers_vs_fractions() {
593        assert!(Fraction::new(1,1) >= Fraction::new(1,2));
594
595        // Test integers vs fractions using PartialOrd
596        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), // 1/2 is less than 1
605            Some(Ordering::Less), // -3/4 is less than 3
606            Some(Ordering::Greater), // 5/1 is greater than -2
607            Some(Ordering::Less)   // -6/2 is less than 5
608        ];
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        // Test integers vs fractions using PartialEq
622        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 for i8
637    #[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        // Add more test cases specific to i8 if needed
644    }
645
646    // Test for i16
647    #[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        // Add more test cases specific to i16 if needed
654    }
655
656    // Test for i32
657    #[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        // Add more test cases specific to i32 if needed
664    }
665
666    // Test for i64
667    #[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        // Add more test cases specific to i64 if needed
674    }
675
676    // Additional Test Cases
677    #[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        // Add more test cases with negative numbers if needed
684    }
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        // Add more test cases with negative fractions if needed
692    }
693
694    #[test]
695    fn try_from_invalid_input() {
696        let input = "invalid"; // Not a valid integer or fraction
697        let result = Fraction::<i16>::try_from(input);
698        assert!(result.is_err());
699
700        // Add more test cases with invalid input strings if needed
701    }
702}