Skip to main content

taco_threshold_automaton/expressions/
fraction.rs

1//! This module contains the implementation of the [`Fraction`] type and its
2//! operations.
3//!
4//! Fractions are used to represent rational numbers in the threshold automaton,
5//! for example, in [`crate::lia_threshold_automaton::LIAThresholdAutomaton`]
6//! Fractions are always stored in their simplified form.
7
8use std::{
9    cmp::Ordering,
10    fmt::{Debug, Display},
11    ops::{self, Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
12};
13
14use crate::expressions::{Atomic, IntegerExpression};
15
16/// Type to representing a fraction
17#[derive(Debug, Clone, Copy, PartialEq, Hash)]
18pub struct Fraction {
19    /// True if the fraction is smaller than 0
20    negated: bool,
21    /// Numerator
22    numerator: u32,
23    /// Denominator
24    denominator: u32,
25}
26
27impl Display for Fraction {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        debug_assert!(self.is_simplified());
30
31        if let Ok(c) = i64::try_from(*self) {
32            write!(f, "{c}")
33        } else {
34            if self.negated {
35                write!(f, "-")?;
36            }
37            write!(f, "{}/{}", self.numerator, self.denominator)
38        }
39    }
40}
41
42impl Fraction {
43    /// Returns true if the fraction is smaller than 0
44    ///
45    /// # Example
46    ///
47    /// ```rust
48    /// use taco_threshold_automaton::expressions::fraction::Fraction;
49    ///
50    /// // 1/2
51    /// let f = Fraction::new(1, 2, false);
52    /// assert_eq!(f.is_negative(), false);
53    ///
54    /// // -1/2
55    /// let f = Fraction::new(1, 2, true);
56    /// assert_eq!(f.is_negative(), true);
57    /// ```
58    pub fn is_negative(&self) -> bool {
59        debug_assert!(self.is_simplified());
60        self.negated
61    }
62
63    /// Check whether the fraction is equal to 0
64    ///
65    /// # Example
66    ///
67    /// ```rust
68    /// use taco_threshold_automaton::expressions::fraction::Fraction;
69    ///
70    /// // 0/5 is simplified to 0/1 upon creation
71    /// let f = Fraction::new(0, 5, false);
72    /// assert_eq!(f.is_zero(), true);
73    ///
74    /// let f = Fraction::new(1, 2, false);
75    /// assert_eq!(f.is_zero(), false);
76    /// ```
77    pub fn is_zero(&self) -> bool {
78        self.numerator == 0
79    }
80
81    /// Check whether the fraction represents an integer
82    ///
83    /// # Example
84    ///
85    /// ```rust
86    /// use taco_threshold_automaton::expressions::fraction::Fraction;
87    ///
88    /// // 1/2 cannot be represented as an integer
89    /// let f = Fraction::new(1, 2, false);
90    /// assert_eq!(f.is_integer(), false);
91    ///
92    /// // 2/2 -> 1
93    /// let f = Fraction::new(2, 2, false);
94    /// assert_eq!(f.is_integer(), true);
95    /// ```
96    pub fn is_integer(&self) -> bool {
97        self.numerator.is_multiple_of(self.denominator)
98    }
99
100    /// Check if the fraction is simplified
101    fn is_simplified(&self) -> bool {
102        num::integer::gcd(self.numerator, self.denominator) == 1
103    }
104
105    /// Create a new fraction
106    ///
107    /// Create a new fraction with the given numerator and denominator. Upon
108    /// creation, the fraction is simplified to the maximal possible extent.
109    ///
110    /// # Example
111    ///
112    /// ```rust
113    /// use taco_threshold_automaton::expressions::fraction::Fraction;
114    ///
115    /// let f = Fraction::new(42, 2, false);
116    ///
117    /// assert_eq!(i64::try_from(f).unwrap(), 21);
118    /// assert_eq!(f.is_negative(), false);
119    /// assert_eq!(f.numerator(), 21);
120    /// assert_eq!(f.denominator(), 1);
121    /// ```
122    pub fn new(numerator: u32, denominator: u32, negated: bool) -> Self {
123        Self {
124            negated,
125            numerator,
126            denominator,
127        }
128        .canonicalize()
129    }
130
131    /// Simplify the fraction to the maximal possible extent and canonicalize its representation
132    fn canonicalize(self) -> Self {
133        // canonicalize when numerator = 0
134        if self.numerator == 0 {
135            return Self {
136                negated: false,
137                numerator: 0,
138                denominator: 1,
139            };
140        }
141
142        // canonicalize when denominator = 0
143        if self.denominator == 0 {
144            return Self {
145                negated: false,
146                numerator: 1,
147                denominator: 0,
148            };
149        }
150
151        // simplify the fraction
152        let mut numerator = self.numerator;
153        let mut denominator = self.denominator;
154        let gcd = num::integer::gcd(self.numerator, self.denominator);
155
156        numerator /= gcd;
157        denominator /= gcd;
158
159        Self {
160            negated: self.negated,
161            numerator,
162            denominator,
163        }
164    }
165
166    /// Compute the inverse of the fraction
167    ///
168    /// Panics if the numerator of the fraction is 0
169    ///
170    /// # Example
171    ///
172    /// ```rust
173    /// use taco_threshold_automaton::expressions::fraction::Fraction;
174    ///
175    /// let f1 = Fraction::new(13, 2, false).inverse();
176    /// let f2 = Fraction::new(2, 13, false);
177    /// assert_eq!(f1, f2);
178    /// ```
179    pub fn inverse(&self) -> Self {
180        if self.numerator() == 0 {
181            panic!("Division by zero undefined!");
182        }
183
184        Self {
185            negated: self.negated,
186            numerator: self.denominator,
187            denominator: self.numerator,
188        }
189    }
190
191    /// Get the denominator of the fraction
192    ///
193    /// # Example
194    ///
195    /// ```rust
196    /// use taco_threshold_automaton::expressions::fraction::Fraction;
197    ///
198    /// let f = Fraction::new(13, 2, false);
199    /// assert_eq!(f.denominator(), 2);
200    /// ```
201    pub fn denominator(&self) -> u32 {
202        debug_assert!(self.is_simplified());
203        self.denominator
204    }
205
206    /// Get the numerator of the fraction
207    ///
208    /// # Example
209    ///
210    /// ```rust
211    /// use taco_threshold_automaton::expressions::fraction::Fraction;
212    ///
213    /// let f = Fraction::new(42, 1, false);
214    /// assert_eq!(f.numerator(), 42);
215    /// ```
216    pub fn numerator(&self) -> u32 {
217        debug_assert!(self.is_simplified());
218        self.numerator
219    }
220
221    /// Get the next closest integer while always rounding up
222    ///
223    /// Returns `None` for negative fractions
224    ///
225    /// # Example
226    ///
227    /// ```rust
228    /// use taco_threshold_automaton::expressions::fraction::Fraction;
229    ///
230    /// // ceil(5/2) = 3
231    /// let f = Fraction::new(5, 2, false);
232    /// assert_eq!(f.get_ceil(), Some(3));
233    ///
234    /// // ceil(6/2) = 3
235    /// let f = Fraction::new(6, 2, false);
236    /// assert_eq!(f.get_ceil(), Some(3));
237    ///
238    /// // negative fractions cannot be rounded to a non-negative integer
239    /// let f = Fraction::new(5, 2, true);
240    /// assert_eq!(f.get_ceil(), None);
241    /// ```
242    pub fn get_ceil(&self) -> Option<u32> {
243        debug_assert!(self.is_simplified());
244        if self.is_negative() {
245            return None;
246        }
247
248        let mut c = self.numerator / self.denominator;
249        if !self.numerator.is_multiple_of(self.denominator) {
250            c += 1;
251        }
252
253        Some(c)
254    }
255
256    /// Get the next closest integer while always rounding down
257    ///
258    /// Returns `None` for negative fractions
259    ///
260    /// # Example
261    ///
262    /// ```rust
263    /// use taco_threshold_automaton::expressions::fraction::Fraction;
264    ///
265    /// // floor(5/2) = 2
266    /// let f = Fraction::new(5, 2, false);
267    /// assert_eq!(f.get_floor(), Some(2));
268    ///
269    /// // floor(6/2) = 3
270    /// let f = Fraction::new(6, 2, false);
271    /// assert_eq!(f.get_floor(), Some(3));
272    ///
273    /// // negative fractions cannot be rounded to a non-negative integer
274    /// let f = Fraction::new(5, 2, true);
275    /// assert_eq!(f.get_floor(), None);
276    /// ```
277    pub fn get_floor(&self) -> Option<u32> {
278        debug_assert!(self.is_simplified());
279        if self.is_negative() {
280            return None;
281        }
282
283        Some(self.numerator / self.denominator)
284    }
285}
286
287impl ops::Neg for Fraction {
288    type Output = Self;
289
290    fn neg(self) -> Self::Output {
291        debug_assert!(self.is_simplified());
292        Self {
293            negated: !self.negated,
294            numerator: self.numerator,
295            denominator: self.denominator,
296        }
297    }
298}
299
300impl Eq for Fraction {}
301
302impl Add for Fraction {
303    type Output = Self;
304
305    fn add(self, rhs: Self) -> Self::Output {
306        if self.negated == rhs.negated {
307            Fraction::new(
308                self.numerator * rhs.denominator + rhs.numerator * self.denominator,
309                self.denominator * rhs.denominator,
310                self.negated,
311            )
312        } else {
313            let denominator = self.denominator * rhs.denominator;
314            let (negated, non_negated) = if self.negated {
315                (self, rhs)
316            } else {
317                (rhs, self)
318            };
319
320            if non_negated.numerator * negated.denominator
321                >= negated.numerator * non_negated.denominator
322            {
323                Fraction::new(
324                    non_negated.numerator * negated.denominator
325                        - negated.numerator * non_negated.denominator,
326                    denominator,
327                    false,
328                )
329            } else {
330                Fraction::new(
331                    negated.numerator * non_negated.denominator
332                        - non_negated.numerator * negated.denominator,
333                    denominator,
334                    true,
335                )
336            }
337        }
338    }
339}
340
341impl AddAssign for Fraction {
342    fn add_assign(&mut self, rhs: Self) {
343        *self = *self + rhs;
344    }
345}
346
347impl Sub for Fraction {
348    type Output = Self;
349
350    fn sub(self, rhs: Self) -> Self::Output {
351        self + (-rhs)
352    }
353}
354
355impl SubAssign for Fraction {
356    fn sub_assign(&mut self, rhs: Self) {
357        *self = *self - rhs;
358    }
359}
360
361impl Mul for Fraction {
362    type Output = Self;
363
364    fn mul(self, rhs: Self) -> Self::Output {
365        Fraction::new(
366            self.numerator * rhs.numerator,
367            self.denominator * rhs.denominator,
368            self.negated ^ rhs.negated,
369        )
370    }
371}
372
373impl MulAssign for Fraction {
374    fn mul_assign(&mut self, rhs: Self) {
375        *self = *self * rhs;
376    }
377}
378
379impl Div for Fraction {
380    type Output = Self;
381
382    #[allow(clippy::suspicious_arithmetic_impl)]
383    fn div(self, rhs: Self) -> Self::Output {
384        self * Fraction::new(rhs.denominator, rhs.numerator, rhs.negated)
385    }
386}
387
388impl DivAssign for Fraction {
389    fn div_assign(&mut self, rhs: Self) {
390        *self = *self / rhs;
391    }
392}
393
394impl From<u32> for Fraction {
395    fn from(value: u32) -> Self {
396        Fraction {
397            negated: false,
398            numerator: value,
399            denominator: 1,
400        }
401    }
402}
403
404impl PartialOrd for Fraction {
405    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
406        Some(self.cmp(other))
407    }
408}
409
410impl Ord for Fraction {
411    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
412        let a = self.numerator * other.denominator;
413        let b = other.numerator * self.denominator;
414
415        if self.negated && !other.negated {
416            return Ordering::Less;
417        }
418
419        if !self.negated && other.negated {
420            return Ordering::Greater;
421        }
422
423        if self.negated && other.negated {
424            // For negative fractions, reverse the comparison
425            return a.cmp(&b).reverse();
426        }
427        a.cmp(&b)
428    }
429}
430
431impl TryFrom<Fraction> for i64 {
432    type Error = ();
433
434    /// Try to convert the fraction into an integer    
435    fn try_from(value: Fraction) -> Result<Self, Self::Error> {
436        if value.numerator.is_multiple_of(value.denominator) {
437            let mut res = value.numerator as i64 / value.denominator as i64;
438
439            if value.negated {
440                res = -res;
441            }
442
443            Ok(res)
444        } else {
445            Err(())
446        }
447    }
448}
449
450impl<T: Atomic> From<Fraction> for IntegerExpression<T> {
451    fn from(value: Fraction) -> Self {
452        // attempt conversion into an integer value first
453        if let Ok(c) = i64::try_from(value) {
454            if c < 0 {
455                return -IntegerExpression::Const(-c as u32);
456            } else {
457                return IntegerExpression::Const(c as u32);
458            }
459        }
460
461        // only return fractions when necessary
462        let mut expr =
463            IntegerExpression::Const(value.numerator) / IntegerExpression::Const(value.denominator);
464
465        if value.negated {
466            expr = -expr;
467        }
468
469        expr
470    }
471}
472
473#[cfg(test)]
474mod tests {
475
476    use crate::expressions::Variable;
477
478    use super::*;
479
480    #[test]
481    fn test_fraction_getters() {
482        let f = Fraction::new(4, 8, false);
483        assert_eq!(f.numerator(), 1);
484        assert_eq!(f.denominator(), 2);
485        assert!(!f.is_negative());
486
487        let f = Fraction::new(12, 9, true);
488        assert_eq!(f.numerator(), 4);
489        assert_eq!(f.denominator(), 3);
490    }
491
492    #[test]
493    fn test_fraction_get_ceil() {
494        // exact integers are returned unchanged
495        let f = Fraction::new(6, 2, false);
496        assert_eq!(f.get_ceil(), Some(3));
497
498        let f = Fraction::new(4, 2, true);
499        assert_eq!(f.get_ceil(), None);
500
501        // fractional parts are rounded up
502        let f = Fraction::new(5, 2, false);
503        assert_eq!(f.get_ceil(), Some(3));
504
505        let f = Fraction::new(1, 3, false);
506        assert_eq!(f.get_ceil(), Some(1));
507
508        let f = Fraction::new(7, 3, false);
509        assert_eq!(f.get_ceil(), Some(3));
510
511        let f = Fraction::new(2, 3, false);
512        assert_eq!(f.get_ceil(), Some(1));
513
514        // zero
515        let f = Fraction::new(0, 1, false);
516        assert_eq!(f.get_ceil(), Some(0));
517
518        // negative fractions
519        let f = Fraction::new(1, 2, true);
520        assert_eq!(f.get_ceil(), None);
521    }
522
523    #[test]
524    fn test_fraction_get_floor() {
525        // exact integers are returned unchanged
526        let f = Fraction::new(6, 2, false);
527        assert_eq!(f.get_floor(), Some(3));
528
529        let f = Fraction::new(4, 2, true);
530        assert_eq!(f.get_floor(), None);
531
532        // fractional parts are rounded down
533        let f = Fraction::new(5, 2, false);
534        assert_eq!(f.get_floor(), Some(2));
535
536        let f = Fraction::new(1, 3, false);
537        assert_eq!(f.get_floor(), Some(0));
538
539        let f = Fraction::new(7, 3, false);
540        assert_eq!(f.get_floor(), Some(2));
541
542        // zero
543        let f = Fraction::new(0, 1, false);
544        assert_eq!(f.get_floor(), Some(0));
545
546        // negative fractions
547        let f = Fraction::new(1, 2, true);
548        assert_eq!(f.get_floor(), None);
549
550        // floor and ceil coincide for integers
551        let f = Fraction::new(9, 3, false);
552        assert_eq!(f.get_floor(), f.get_ceil());
553    }
554
555    #[test]
556    fn test_fraction_is_zero() {
557        // zero values
558        let f = Fraction::new(0, 1, false);
559        assert!(f.is_zero());
560
561        let f = Fraction::new(0, 1, true);
562        assert!(f.is_zero());
563
564        let f = Fraction::new(0, 5, false);
565        assert!(f.is_zero());
566
567        // non-zero values
568        let f = Fraction::new(1, 2, false);
569        assert!(!f.is_zero());
570
571        let f = Fraction::new(42, 7, false);
572        assert!(!f.is_zero());
573
574        let f = Fraction::new(1, 2, true);
575        assert!(!f.is_zero());
576
577        // canonicalize maps zero to 0/1 with negated = false
578        let f = Fraction::new(0, 5, true);
579        assert!(f.is_zero());
580        assert_eq!(f.numerator(), 0);
581        assert_eq!(f.denominator(), 1);
582        assert!(!f.is_negative());
583    }
584
585    #[test]
586    fn test_fraction_is_integer() {
587        let f1 = Fraction::new(4, 2, false);
588        assert!(f1.is_integer());
589        let f2 = Fraction::new(5, 2, false);
590        assert!(!f2.is_integer());
591        let f3 = Fraction::new(0, 1, false);
592        assert!(f3.is_integer());
593        let f4 = Fraction::new(0, 0, false);
594        assert!(f4.is_integer()); // 0 is considered an integer
595    }
596
597    #[test]
598    fn test_simplification_for_div_by_zero() {
599        let f1 = Fraction::new(42, 0, true);
600        let f2 = Fraction::new(1, 0, false);
601        assert_eq!(f1, f2);
602    }
603
604    #[test]
605    fn test_fraction_addition() {
606        let f1 = Fraction::new(1, 2, false);
607        let f2 = Fraction::new(1, 3, false);
608        let result = f1 + f2;
609        assert_eq!(result.numerator, 5);
610        assert_eq!(result.denominator, 6);
611        assert!(!result.negated);
612
613        let f1 = Fraction::new(5, 10, false);
614        let f2 = Fraction::new(10, 30, true);
615        let result = f1 + f2;
616        assert_eq!(result.numerator, 1);
617        assert_eq!(result.denominator, 6);
618        assert!(!result.negated);
619
620        let f1 = Fraction::new(3, 6, true);
621        let f2 = Fraction::new(1, 3, false);
622        let result = f1 + f2;
623        assert_eq!(result.numerator, 1);
624        assert_eq!(result.denominator, 6);
625        assert!(result.negated);
626
627        let mut f1 = Fraction::new(3, 6, true);
628        let f2 = Fraction::new(2, 8, true);
629        f1 += f2;
630        assert_eq!(f1.numerator, 3);
631        assert_eq!(f1.denominator, 4);
632        assert!(f1.negated);
633    }
634
635    #[test]
636    fn test_fraction_subtraction() {
637        let f1 = Fraction::new(100, 200, false);
638        let f2 = Fraction::new(10, 30, false);
639        let result = f1 - f2;
640        assert_eq!(result.numerator, 1);
641        assert_eq!(result.denominator, 6);
642        assert!(!result.negated);
643
644        let f1 = Fraction::new(9, 18, false);
645        let f2 = Fraction::new(1, 3, true);
646        let result = f1 - f2;
647        assert_eq!(result.numerator, 5);
648        assert_eq!(result.denominator, 6);
649        assert!(!result.negated);
650
651        let mut f1 = Fraction::new(3, 6, true);
652        let f2 = Fraction::new(1, 3, false);
653        f1 -= f2;
654        assert_eq!(f1.numerator, 5);
655        assert_eq!(f1.denominator, 6);
656        assert!(f1.negated);
657    }
658
659    #[test]
660    fn test_fraction_multiplication() {
661        let f1 = Fraction::new(1, 2, false);
662        let f2 = Fraction::new(1, 3, false);
663        let result = f1 * f2;
664        assert_eq!(result.numerator, 1);
665        assert_eq!(result.denominator, 6);
666        assert!(!result.negated);
667
668        let f1 = Fraction::new(1, 2, false);
669        let f2 = Fraction::new(5, 15, true);
670        let result = f1 * f2;
671        assert_eq!(result.numerator, 1);
672        assert_eq!(result.denominator, 6);
673        assert!(result.negated);
674
675        let f1 = Fraction::new(1, 2, true);
676        let f2 = Fraction::new(1, 3, true);
677        let result = f1 * f2;
678        assert_eq!(result.numerator, 1);
679        assert_eq!(result.denominator, 6);
680        assert!(!result.negated);
681
682        let mut f1 = Fraction::new(1, 2, false);
683        let f2 = Fraction::new(1, 3, true);
684        f1 *= f2;
685        assert_eq!(f1.numerator, 1);
686        assert_eq!(f1.denominator, 6);
687        assert!(f1.negated);
688    }
689
690    #[test]
691    fn test_fraction_division() {
692        let f1 = Fraction::new(1, 2, false);
693        let f2 = Fraction::new(1, 3, false);
694        let result = f1 / f2;
695        assert_eq!(result.numerator, 3);
696        assert_eq!(result.denominator, 2);
697        assert!(!result.negated);
698
699        let f1 = Fraction::new(5, 10, false);
700        let f2 = Fraction::new(20, 40, false);
701        let result = f1 / f2;
702        assert_eq!(result.numerator, 1);
703        assert_eq!(result.denominator, 1);
704
705        let mut f1 = Fraction::new(1, 2, false);
706        let f2 = Fraction::new(1, 3, true);
707        f1 /= f2;
708        assert_eq!(f1.numerator, 3);
709        assert_eq!(f1.denominator, 2);
710        assert!(f1.negated);
711    }
712
713    #[test]
714    fn test_fraction_negation() {
715        let f1 = Fraction::new(1, 2, false);
716        let result = -f1;
717        assert_eq!(result.numerator, 1);
718        assert_eq!(result.denominator, 2);
719        assert!(result.negated);
720    }
721
722    #[test]
723    fn test_fraction_addition_with_negation() {
724        let f1 = Fraction::new(1, 2, false);
725        let f2 = Fraction::new(1, 3, true);
726        let result = f1 + f2;
727        assert_eq!(result.numerator, 1);
728        assert_eq!(result.denominator, 6);
729        assert!(!result.negated);
730    }
731
732    #[test]
733    fn test_fraction_subtraction_with_negation() {
734        let f1 = Fraction::new(1, 2, false);
735        let f2 = Fraction::new(1, 3, true);
736        let result = f1 - f2;
737        assert_eq!(result.numerator, 5);
738        assert_eq!(result.denominator, 6);
739        assert!(!result.negated);
740    }
741
742    #[test]
743    fn test_fraction_multiplication_with_negation() {
744        let f1 = Fraction::new(1, 2, false);
745        let f2 = Fraction::new(1, 3, true);
746        let result = f1 * f2;
747        assert_eq!(result.numerator, 1);
748        assert_eq!(result.denominator, 6);
749        assert!(result.negated);
750    }
751
752    #[test]
753    fn test_fraction_division_with_negation() {
754        let f1 = Fraction::new(1, 2, false);
755        let f2 = Fraction::new(1, 3, true);
756        let result = f1 / f2;
757        assert_eq!(result.numerator, 3);
758        assert_eq!(result.denominator, 2);
759        assert!(result.negated);
760    }
761
762    #[test]
763    fn test_fraction_simplification() {
764        let f = Fraction::new(4, 8, false).canonicalize();
765        assert_eq!(f.numerator, 1);
766        assert_eq!(f.denominator, 2);
767        assert!(!f.negated);
768    }
769
770    #[test]
771    fn test_fraction_eq() {
772        let f1 = Fraction::new(1, 2, false);
773        let f2 = Fraction::new(1, 2, false);
774        assert_eq!(f1, f2);
775
776        let f1 = Fraction::new(1, 2, false);
777        let f2 = Fraction::new(1, 2, true);
778        assert_ne!(f1, f2);
779
780        let f1 = Fraction::new(1, 2, false);
781        let f2 = Fraction::new(2, 4, false);
782        assert_eq!(f1, f2);
783
784        let f1 = Fraction::new(1, 2, false);
785        let f2 = Fraction::new(2, 4, true);
786        assert_ne!(f1, f2);
787
788        let f1 = Fraction::new(0, 2, true);
789        let f2 = Fraction::new(0, 2, false);
790        assert_eq!(f1, f2);
791
792        let f1 = Fraction::new(0, 2, true);
793        let f2 = Fraction::new(0, 3, true);
794        assert_eq!(f1, f2);
795    }
796
797    #[test]
798    fn test_fraction_simplification_with_negation() {
799        let f = Fraction::new(4, 8, true).canonicalize();
800        assert_eq!(f.numerator, 1);
801        assert_eq!(f.denominator, 2);
802        assert!(f.negated);
803    }
804
805    #[test]
806    fn test_fraction_from_u32() {
807        let f: Fraction = 5u32.into();
808        assert_eq!(f.numerator, 5);
809        assert_eq!(f.denominator, 1);
810        assert!(!f.negated);
811    }
812
813    #[test]
814    fn test_fraction_try_into_i64_success() {
815        let f = Fraction::new(4, 2, false);
816        let result: Result<i64, ()> = f.try_into();
817        assert_eq!(result.unwrap(), 2);
818    }
819
820    #[test]
821    fn test_fraction_try_into_i64_failure() {
822        let f = Fraction::new(5, 2, false);
823        let result: Result<i64, ()> = f.try_into();
824        assert!(result.is_err());
825    }
826
827    #[test]
828    fn test_fraction_try_into_i64_negated() {
829        let f = Fraction::new(4, 2, true);
830        let result: Result<i64, ()> = f.try_into();
831        assert_eq!(result.unwrap(), -2);
832    }
833
834    #[test]
835    fn test_fraction_display() {
836        let f = Fraction::new(1, 2, false);
837        assert_eq!(f.to_string(), "1/2");
838
839        let f = Fraction::new(1, 2, true);
840        assert_eq!(f.to_string(), "-1/2");
841
842        let f = Fraction::new(1, 1, false);
843        assert_eq!(f.to_string(), "1");
844    }
845
846    #[test]
847    fn test_fraction_into_integer_expression() {
848        let f = Fraction::new(1, 2, false);
849        let expr: IntegerExpression<Variable> = f.into();
850        assert_eq!(
851            expr,
852            IntegerExpression::Const(1) / IntegerExpression::Const(2)
853        );
854
855        let f = Fraction::new(1, 2, true);
856        let expr: IntegerExpression<Variable> = f.into();
857        assert_eq!(
858            expr,
859            -(IntegerExpression::Const(1) / IntegerExpression::Const(2))
860        );
861
862        let f = Fraction::new(1, 1, false);
863        let expr: IntegerExpression<Variable> = f.into();
864        assert_eq!(expr, IntegerExpression::Const(1));
865
866        let f = Fraction::new(1, 1, true);
867        let expr: IntegerExpression<Variable> = f.into();
868        assert_eq!(expr, -IntegerExpression::Const(1));
869    }
870
871    #[test]
872    fn test_inverse() {
873        let f = Fraction::new(1, 2, true);
874        let expected = Fraction::new(2, 1, true);
875        assert_eq!(f.inverse(), expected);
876
877        let f = Fraction::new(1, 2, false);
878        let expected = Fraction::new(2, 1, false);
879        assert_eq!(f.inverse(), expected);
880    }
881
882    #[test]
883    #[should_panic]
884    fn test_inverse_panics_on_0_numerator() {
885        let f = Fraction::new(0, 2, true);
886        let _ = f.inverse();
887    }
888
889    #[test]
890    fn test_fraction_ord() {
891        let f1 = Fraction::new(1, 2, false);
892        let f2 = Fraction::new(1, 2, false);
893        assert_eq!(f1.cmp(&f2), Ordering::Equal);
894        assert_eq!(f2.cmp(&f1), Ordering::Equal);
895
896        let f1 = Fraction::new(1, 2, true);
897        let f2 = Fraction::new(1, 2, true);
898        assert_eq!(f1.cmp(&f2), Ordering::Equal);
899        assert_eq!(f2.cmp(&f1), Ordering::Equal);
900
901        let f1 = Fraction::new(1, 2, true);
902        let f2 = Fraction::new(1, 2, false);
903        assert_eq!(f1.cmp(&f2), Ordering::Less);
904        assert_eq!(f2.cmp(&f1), Ordering::Greater);
905
906        let f1 = Fraction::new(1, 2, false);
907        let f2 = Fraction::new(2, 2, false);
908        assert_eq!(f1.cmp(&f2), Ordering::Less);
909        assert_eq!(f2.cmp(&f1), Ordering::Greater);
910
911        let f1 = Fraction::new(1, 4, false);
912        let f2 = Fraction::new(1, 2, false);
913        assert_eq!(f1.cmp(&f2), Ordering::Less);
914        assert_eq!(f2.cmp(&f1), Ordering::Greater);
915    }
916}