axiolid_exact/
arith.rs

1//! The arithmetic both evaluation tiers share.
2
3use axiolid_guarantees::Sign;
4
5use crate::dyadic::Dyadic;
6
7/// A number system an expression can be evaluated in.
8///
9/// Implemented by [`crate::Interval`] (fast, may be undecided) and
10/// [`crate::Dyadic`] (exact, always decided). Writing an expression once,
11/// generically, is what guarantees the filter and the exact fallback
12/// evaluate the same polynomial.
13pub trait Arith: Clone {
14    /// The value of a finite `f64`.
15    ///
16    /// Callers must pass finite values; check with [`crate::require_finite`].
17    /// Non-finite input is never silently accepted: the interval tier widens
18    /// it to the whole line and the exact tier panics.
19    fn from_f64(value: f64) -> Self;
20
21    /// The value of an exact dyadic number: itself in the exact tier, a
22    /// sound enclosure in the interval tier ([`Dyadic::enclosure`]).
23    ///
24    /// Lets exact intermediate data (a constructed point, a normalised
25    /// coefficient) enter an expression that is then evaluated in both
26    /// tiers, so the filter still runs first.
27    fn from_dyadic(value: &Dyadic) -> Self;
28
29    /// `self + other`.
30    #[must_use]
31    fn add(&self, other: &Self) -> Self;
32
33    /// `self - other`.
34    #[must_use]
35    fn sub(&self, other: &Self) -> Self;
36
37    /// `self * other`.
38    #[must_use]
39    fn mul(&self, other: &Self) -> Self;
40
41    /// `-self`.
42    #[must_use]
43    fn neg(&self) -> Self;
44
45    /// The sign, or `None` when this arithmetic cannot decide it.
46    ///
47    /// Exact arithmetics always return `Some`.
48    fn sign(&self) -> Option<Sign>;
49
50    /// An enclosure of `sqrt(self)`, for approximate arithmetics only.
51    ///
52    /// The interval tier uses it to evaluate nested radicals numerically,
53    /// which decides far more signs than case analysis on coefficients
54    /// (a coefficient that is exactly zero becomes an interval straddling
55    /// zero, and case analysis stops there). Exact arithmetics return
56    /// `None`: they decide by case analysis instead, never by a rounded
57    /// root. Also `None` whenever `self` might be negative, so a filter
58    /// never assigns a sign to a value that is not real.
59    fn sqrt_enclosure(&self) -> Option<Self> {
60        None
61    }
62
63    /// `self * self`.
64    #[must_use]
65    fn square(&self) -> Self {
66        self.mul(self)
67    }
68}
69
70/// Product of two signs.
71#[must_use]
72pub(crate) fn sign_product(left: Sign, right: Sign) -> Sign {
73    match right {
74        Sign::Positive => left,
75        Sign::Negative => left.flip(),
76        // Zero, and any future variant: a product with zero is zero, and an
77        // unknown variant must not be read as decisive.
78        _ => Sign::Zero,
79    }
80}