Skip to main content

dashu_base/
approx.rs

1//! Trait definitions for approximated values
2
3/// Represent an calculation result with a possible error.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Approximation<T, E> {
6    /// The result is exact, contains the result value
7    Exact(T),
8
9    /// The result is inexact, contains the result value and error
10    Inexact(T, E),
11}
12
13impl<T, E> Approximation<T, E> {
14    /// Get the value of the calculation regardless of error
15    #[inline]
16    pub fn value(self) -> T {
17        match self {
18            Self::Exact(v) => v,
19            Self::Inexact(v, _) => v,
20        }
21    }
22
23    /// Get a reference to the calculation result
24    #[inline]
25    pub const fn value_ref(&self) -> &T {
26        match self {
27            Self::Exact(v) => v,
28            Self::Inexact(v, _) => v,
29        }
30    }
31
32    /// The value together with whether the computation was exact.
33    ///
34    /// The boolean is `true` for an exact result and `false` for an inexact one; the error `E` is
35    /// discarded (use [`error`](Self::error) when the error magnitude matters). Handy for the
36    /// "value + exactness flag" pattern (e.g. MPFR's `exact` flag, which a Ziv closure needs to
37    /// report a zero radius for an exactly-representable result).
38    #[inline]
39    pub fn value_with_exact(self) -> (T, bool) {
40        match self {
41            Self::Exact(v) => (v, true),
42            Self::Inexact(v, _) => (v, false),
43        }
44    }
45
46    /// Return the value if the result is exact, panic otherwise.
47    #[inline]
48    pub fn unwrap(self) -> T {
49        match self {
50            Self::Exact(val) => val,
51            Self::Inexact(_, _) => panic!("called `Approximation::unwrap()` on a `Inexact` value"),
52        }
53    }
54
55    /// Return the error if the result is inexact, [`None`] if it is exact.
56    #[inline]
57    pub fn error(self) -> Option<E> {
58        match self {
59            Self::Exact(_) => None,
60            Self::Inexact(_, e) => Some(e),
61        }
62    }
63
64    /// Borrow the error if the result is inexact, [`None`] if it is exact.
65    #[inline]
66    pub const fn error_ref(&self) -> Option<&E> {
67        match self {
68            Self::Exact(_) => None,
69            Self::Inexact(_, e) => Some(e),
70        }
71    }
72
73    /// Map the result value to a new type, preserving the error (if any).
74    #[inline]
75    pub fn map<U, F>(self, f: F) -> Approximation<U, E>
76    where
77        F: FnOnce(T) -> U,
78    {
79        match self {
80            Self::Exact(v) => Approximation::Exact(f(v)),
81            Self::Inexact(v, e) => Approximation::Inexact(f(v), e),
82        }
83    }
84
85    /// Chain a fallible mapping that itself returns an [`Approximation`], combining the
86    /// errors: an inexact input or an inexact result both yield an inexact result.
87    #[inline]
88    pub fn and_then<U, F>(self, f: F) -> Approximation<U, E>
89    where
90        F: FnOnce(T) -> Approximation<U, E>,
91    {
92        match self {
93            Self::Exact(v) => match f(v) {
94                Approximation::Exact(v2) => Approximation::Exact(v2),
95                Approximation::Inexact(v2, e) => Approximation::Inexact(v2, e),
96            },
97            Self::Inexact(v, e) => match f(v) {
98                Approximation::Exact(v2) => Approximation::Inexact(v2, e),
99                Approximation::Inexact(v2, e2) => Approximation::Inexact(v2, e2),
100            },
101        }
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::Approximation;
108
109    #[test]
110    fn value_with_exact() {
111        assert_eq!(Approximation::<i32, ()>::Exact(3).value_with_exact(), (3, true));
112        assert_eq!(Approximation::<i32, ()>::Inexact(3, ()).value_with_exact(), (3, false));
113        // `value()` and `error()` are consistent with the split
114        let (v, is_exact) = Approximation::<i32, &str>::Inexact(7, "err").value_with_exact();
115        assert_eq!(v, 7);
116        assert!(!is_exact);
117    }
118}