1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use crate::fraction::{GenericFraction, Sign};
use crate::{CheckedDiv, CheckedMul, Integer, Ratio, Zero};

impl<T> CheckedDiv for GenericFraction<T>
where
    T: Clone + Integer + CheckedDiv + CheckedMul,
{
    fn checked_div(&self, other: &Self) -> Option<GenericFraction<T>> {
        match *self {
            GenericFraction::NaN => Some(self.clone()),
            GenericFraction::Infinity(sign) => match *other {
                GenericFraction::NaN => Some(other.clone()),
                GenericFraction::Infinity(_) => Some(GenericFraction::NaN),
                GenericFraction::Rational(osign, _) => {
                    Some(GenericFraction::Infinity(if sign == osign {
                        Sign::Plus
                    } else {
                        Sign::Minus
                    }))
                }
            },
            GenericFraction::Rational(sign, ref l) => match *other {
                GenericFraction::NaN => Some(other.clone()),
                GenericFraction::Infinity(_) => {
                    Some(GenericFraction::Rational(Sign::Plus, Ratio::zero()))
                }
                GenericFraction::Rational(osign, ref r) => {
                    if l.is_zero() && r.is_zero() {
                        Some(GenericFraction::NaN)
                    } else if r.is_zero() {
                        Some(GenericFraction::Infinity(sign))
                    } else if l.is_zero() {
                        Some(GenericFraction::Rational(Sign::Plus, l.clone()))
                    } else {
                        l.checked_div(r).map(|value| {
                            GenericFraction::Rational(
                                if sign == osign {
                                    Sign::Plus
                                } else {
                                    Sign::Minus
                                },
                                value,
                            )
                        })
                    }
                }
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{CheckedDiv, GenericFraction};
    use crate::{One, Zero};

    type F = GenericFraction<u8>;

    #[test]
    fn checked_div() {
        assert_eq!(Some(F::nan()), F::nan().checked_div(&F::nan()));

        assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::nan()));
        assert_eq!(Some(F::nan()), F::infinity().checked_div(&F::infinity()));
        assert_eq!(
            Some(F::nan()),
            F::infinity().checked_div(&F::neg_infinity())
        );
        assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::one()));
        assert_eq!(
            Some(F::neg_infinity()),
            F::infinity().checked_div(&F::new_neg(1, 1))
        );

        assert_eq!(Some(F::infinity()), F::infinity().checked_div(&F::zero()));
        assert_eq!(Some(F::zero()), F::zero().checked_div(&F::infinity()));

        assert_eq!(
            Some(F::neg_infinity()),
            F::infinity().checked_div(&F::new_neg(1, 1))
        );

        assert_eq!(Some(F::nan()), F::one().checked_div(&F::nan()));
        assert_eq!(Some(F::zero()), F::one().checked_div(&F::infinity()));
        assert_eq!(Some(F::zero()), F::one().checked_div(&F::neg_infinity()));

        assert_eq!(Some(F::one()), F::one().checked_div(&F::one()));
        assert_eq!(
            Some(F::new_neg(1, 1)),
            F::one().checked_div(&F::new_neg(1, 1))
        );
        assert_eq!(
            Some(F::new_neg(1, 1)),
            F::new_neg(1, 1).checked_div(&F::one())
        );
        assert_eq!(
            Some(F::one()),
            F::new_neg(1, 1).checked_div(&F::new_neg(1, 1))
        );

        assert_eq!(Some(F::new(1, 2)), F::one().checked_div(&F::new(2, 1)));
        assert_eq!(
            Some(F::new(1, 2)),
            F::new_neg(1, 1).checked_div(&F::new_neg(2, 1))
        );

        assert_eq!(Some(F::infinity()), F::one().checked_div(&F::zero()));
        assert_eq!(
            Some(F::neg_infinity()),
            F::new_neg(1, 1).checked_div(&F::zero())
        );
        assert_eq!(Some(F::zero()), F::zero().checked_div(&F::one()));
        assert_eq!(Some(F::zero()), F::zero().checked_div(&F::new_neg(1, 1)));

        assert_eq!(Some(F::nan()), F::zero().checked_div(&F::zero()));
    }
}