rateslib/dual/dual_ops/
convert.rs

1use crate::dual::dual::{Dual, Dual2};
2use crate::dual::enums::{ADOrder, Number};
3use std::convert::From;
4
5/// Convert a `Number` of one `ADOrder` to another and consume the value.
6///
7/// **Why use this method?**
8///
9/// `From` is implemented to convert into all the types `f64`, `Dual` and `Dual2` from each other,
10/// however, when doing so there is no variable information attached for an `f64`. For example,
11///
12/// ```rust
13/// # use rateslib::dual::Dual;
14/// let d = Dual::from(2.5_f64);
15/// assert_eq!(d, Dual::new(2.5_f64, vec![]));
16/// ```
17///
18/// On the other hand using a `Number` enum can convert any value to a dual data type tagged
19/// with specific variables. `vars` are only used in this instance when converting an `f64` to a
20/// dual type.
21///
22/// ```rust
23/// # use rateslib::dual::{Number, set_order, ADOrder, Dual};
24/// let f_ = Number::F64(2.5_f64);
25/// let d_ = set_order(f_, ADOrder::One, vec!["x".to_string()]);
26/// let d = Dual::from(d_);
27/// assert_eq!(d, Dual::new(2.5_f64, vec!["x".to_string()]));
28/// ```
29///
30pub fn set_order(value: Number, order: ADOrder, vars: Vec<String>) -> Number {
31    match (value, order) {
32        (Number::F64(f), ADOrder::Zero) => Number::F64(f),
33        (Number::Dual(d), ADOrder::Zero) => Number::F64(d.real),
34        (Number::Dual2(d), ADOrder::Zero) => Number::F64(d.real),
35        (Number::F64(f), ADOrder::One) => Number::Dual(Dual::new(f, vars)),
36        (Number::Dual(d), ADOrder::One) => Number::Dual(d),
37        (Number::Dual2(d), ADOrder::One) => Number::Dual(Dual::from(d)),
38        (Number::F64(f), ADOrder::Two) => Number::Dual2(Dual2::new(f, vars)),
39        (Number::Dual(d), ADOrder::Two) => Number::Dual2(Dual2::from(d)),
40        (Number::Dual2(d), ADOrder::Two) => Number::Dual2(d),
41    }
42}
43
44/// Convert a `Number` of one `ADOrder` to another.
45///
46/// Similar to `set_order` except the value is not consumed during conversion.
47pub fn set_order_clone(value: &Number, order: ADOrder, vars: Vec<String>) -> Number {
48    match (value, order) {
49        (Number::F64(f), ADOrder::Zero) => Number::F64(*f),
50        (Number::Dual(d), ADOrder::Zero) => Number::F64(d.real),
51        (Number::Dual2(d), ADOrder::Zero) => Number::F64(d.real),
52        (Number::F64(f), ADOrder::One) => Number::Dual(Dual::new(*f, vars)),
53        (Number::Dual(d), ADOrder::One) => Number::Dual(d.clone()),
54        (Number::Dual2(d), ADOrder::One) => Number::Dual(Dual::from(d)),
55        (Number::F64(f), ADOrder::Two) => Number::Dual2(Dual2::new(*f, vars)),
56        (Number::Dual(d), ADOrder::Two) => Number::Dual2(Dual2::from(d)),
57        (Number::Dual2(d), ADOrder::Two) => Number::Dual2(d.clone()),
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_set_order_with_conversion() {
67        let f = 2.5_f64;
68        let d = set_order(Number::F64(f), ADOrder::One, vec!["var1".to_string()]);
69        assert_eq!(d, Number::Dual(Dual::new(2.5, vec!["var1".to_string()])));
70
71        let d2 = set_order(d, ADOrder::Two, vec![]);
72        assert_eq!(d2, Number::Dual2(Dual2::new(2.5, vec!["var1".to_string()])));
73
74        let f = set_order(d2, ADOrder::Zero, vec![]);
75        assert_eq!(f, Number::F64(2.5_f64));
76    }
77
78    #[test]
79    fn test_docstring() {
80        let f_ = Number::F64(2.5_f64);
81        let d_ = set_order(f_, ADOrder::One, vec!["x".to_string()]);
82        let d = Dual::from(d_);
83        assert_eq!(d, Dual::new(2.5_f64, vec!["x".to_string()])); // true
84    }
85}