rateslib/dual/dual_ops/
ord.rs

1use crate::dual::dual::{Dual, Dual2};
2use crate::dual::enums::Number;
3use std::cmp::Ordering;
4
5/// Compares `Dual` by `real` component only.
6impl PartialOrd<Dual> for Dual {
7    fn partial_cmp(&self, other: &Dual) -> Option<Ordering> {
8        self.real.partial_cmp(&other.real)
9    }
10}
11
12impl PartialOrd<f64> for Dual {
13    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
14        self.real.partial_cmp(other)
15    }
16}
17
18impl PartialOrd<f64> for Dual2 {
19    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
20        self.real.partial_cmp(other)
21    }
22}
23
24impl PartialOrd<Dual2> for Dual2 {
25    fn partial_cmp(&self, other: &Dual2) -> Option<Ordering> {
26        self.real.partial_cmp(&other.real)
27    }
28}
29
30impl PartialOrd<Dual> for f64 {
31    fn partial_cmp(&self, other: &Dual) -> Option<Ordering> {
32        self.partial_cmp(&other.real)
33    }
34}
35
36impl PartialOrd<Dual2> for f64 {
37    fn partial_cmp(&self, other: &Dual2) -> Option<Ordering> {
38        self.partial_cmp(&other.real)
39    }
40}
41
42impl PartialOrd<Number> for Number {
43    fn partial_cmp(&self, other: &Number) -> Option<Ordering> {
44        match (self, other) {
45            (Number::F64(f), Number::F64(f2)) => f.partial_cmp(f2),
46            (Number::F64(f), Number::Dual(d2)) => f.partial_cmp(d2),
47            (Number::F64(f), Number::Dual2(d2)) => f.partial_cmp(d2),
48            (Number::Dual(d), Number::F64(f2)) => d.partial_cmp(f2),
49            (Number::Dual(d), Number::Dual(d2)) => d.partial_cmp(d2),
50            (Number::Dual(_), Number::Dual2(_)) => {
51                panic!("Cannot mix dual types: Dual compare Dual2")
52            }
53            (Number::Dual2(d), Number::F64(f2)) => d.partial_cmp(f2),
54            (Number::Dual2(_), Number::Dual(_)) => {
55                panic!("Cannot mix dual types: Dual2 compare Dual")
56            }
57            (Number::Dual2(d), Number::Dual2(d2)) => d.partial_cmp(d2),
58        }
59    }
60}
61
62impl PartialOrd<f64> for Number {
63    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
64        match self {
65            Number::F64(f) => f.partial_cmp(other),
66            Number::Dual(d) => d.partial_cmp(other),
67            Number::Dual2(d) => d.partial_cmp(other),
68        }
69    }
70}
71
72impl PartialOrd<Number> for f64 {
73    fn partial_cmp(&self, other: &Number) -> Option<Ordering> {
74        match other {
75            Number::F64(f) => self.partial_cmp(f),
76            Number::Dual(d) => self.partial_cmp(d),
77            Number::Dual2(d) => self.partial_cmp(d),
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn ord() {
88        let d1 = Dual::try_new(
89            1.0,
90            vec!["v0".to_string(), "v1".to_string()],
91            vec![1.0, 2.0],
92        )
93        .unwrap();
94        assert!(d1 < 2.0);
95        assert!(d1 > 0.5);
96        assert!(d1 <= 1.0);
97        assert!(d1 >= 1.0);
98        assert!(1.0 <= d1);
99        assert!(1.0 >= d1);
100        assert!(2.0 > d1);
101        assert!(0.5 < d1);
102        let d2 = Dual::try_new(
103            2.0,
104            vec!["v0".to_string(), "v2".to_string()],
105            vec![1.0, 2.0],
106        )
107        .unwrap();
108        assert!(d2 > d1);
109        assert!(d1 < d2);
110        let d3 = Dual::try_new(1.0, vec!["v3".to_string()], vec![10.0]).unwrap();
111        assert!(d1 >= d3);
112        assert!(d1 <= d3);
113    }
114
115    #[test]
116    fn ord2() {
117        let d1 = Dual2::try_new(
118            1.0,
119            vec!["v0".to_string(), "v1".to_string()],
120            vec![1.0, 2.0],
121            Vec::new(),
122        )
123        .unwrap();
124        assert!(d1 < 2.0);
125        assert!(d1 > 0.5);
126        assert!(d1 <= 1.0);
127        assert!(d1 >= 1.0);
128        assert!(1.0 <= d1);
129        assert!(1.0 >= d1);
130        assert!(2.0 > d1);
131        assert!(0.5 < d1);
132        let d2 = Dual2::try_new(
133            2.0,
134            vec!["v0".to_string(), "v2".to_string()],
135            vec![1.0, 2.0],
136            Vec::new(),
137        )
138        .unwrap();
139        assert!(d2 > d1);
140        assert!(d1 < d2);
141        let d3 = Dual2::try_new(1.0, vec!["v3".to_string()], vec![10.0], Vec::new()).unwrap();
142        assert!(d1 >= d3);
143        assert!(d1 <= d3);
144    }
145
146    #[test]
147    fn test_enum() {
148        let d = Number::Dual(Dual::new(2.0, vec!["x".to_string()]));
149        let d2 = Number::Dual(Dual::new(3.0, vec!["x".to_string()]));
150        assert!(d <= d2)
151    }
152
153    #[test]
154    fn test_cross_enum_eq() {
155        let f = Number::F64(2.5_f64);
156        let d = Number::Dual(Dual::new(3.5_f64, vec![]));
157        assert!(f <= d);
158    }
159
160    #[test]
161    #[should_panic]
162    fn test_cross_enum_eq_error() {
163        let d2 = Number::Dual2(Dual2::new(2.5_f64, vec![]));
164        let d = Number::Dual(Dual::new(2.5_f64, vec![]));
165        assert!(d <= d2);
166    }
167
168    #[test]
169    fn test_cross_enum_f64() {
170        let d2 = Number::Dual2(Dual2::new(2.5_f64, vec![]));
171        assert!(d2 <= 3.0_f64);
172    }
173}