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}