Skip to main content

alkahest_cas/poly/
error.rs

1use std::fmt;
2
3/// Reason an expression could not be converted to a polynomial representation.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum ConversionError {
6    /// A symbol other than the target variable(s) appeared as a free term.
7    UnexpectedSymbol(String),
8    /// A rational or float coefficient was encountered; only integers are allowed.
9    NonIntegerCoefficient,
10    /// An exponent was a negative integer (produces a rational function, not a poly).
11    NegativeExponent,
12    /// An exponent was a non-negative integer too large to fit in u32.
13    ExponentTooLarge,
14    /// An exponent was not a constant integer (e.g. symbolic or float).
15    NonConstantExponent,
16    /// A function call appeared and may contain the variable.
17    NonPolynomialFunction(String),
18    /// The denominator of a RationalFunction would be zero.
19    ZeroDenominator,
20}
21
22impl fmt::Display for ConversionError {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        match self {
25            ConversionError::UnexpectedSymbol(s) => {
26                write!(f, "unexpected free symbol '{s}' in polynomial expression")
27            }
28            ConversionError::NonIntegerCoefficient => {
29                write!(
30                    f,
31                    "non-integer coefficient (rational or float) in polynomial"
32                )
33            }
34            ConversionError::NegativeExponent => {
35                write!(
36                    f,
37                    "negative exponent yields a rational function, not a polynomial"
38                )
39            }
40            ConversionError::ExponentTooLarge => {
41                write!(f, "exponent exceeds u32::MAX")
42            }
43            ConversionError::NonConstantExponent => {
44                write!(f, "exponent is not a constant integer")
45            }
46            ConversionError::NonPolynomialFunction(name) => {
47                write!(f, "function '{name}' cannot appear in a polynomial")
48            }
49            ConversionError::ZeroDenominator => {
50                write!(f, "denominator is zero")
51            }
52        }
53    }
54}
55
56impl std::error::Error for ConversionError {}
57
58impl crate::errors::AlkahestError for ConversionError {
59    fn code(&self) -> &'static str {
60        match self {
61            ConversionError::UnexpectedSymbol(_) => "E-POLY-001",
62            ConversionError::NonIntegerCoefficient => "E-POLY-002",
63            ConversionError::NegativeExponent => "E-POLY-003",
64            ConversionError::ExponentTooLarge => "E-POLY-004",
65            ConversionError::NonConstantExponent => "E-POLY-005",
66            ConversionError::NonPolynomialFunction(_) => "E-POLY-006",
67            ConversionError::ZeroDenominator => "E-POLY-007",
68        }
69    }
70
71    fn remediation(&self) -> Option<&'static str> {
72        ConversionError::remediation(self)
73    }
74}
75
76impl ConversionError {
77    /// A human-readable remediation hint for the user.
78    pub fn remediation(&self) -> Option<&'static str> {
79        match self {
80            ConversionError::NonPolynomialFunction(_) => Some(
81                "not a polynomial; wrap in the function only after rational reduction",
82            ),
83            ConversionError::NegativeExponent => Some(
84                "negative exponent yields a rational function; use RationalFunction::from_symbolic instead",
85            ),
86            ConversionError::NonConstantExponent => Some(
87                "symbolic exponents are not supported; substitute concrete values first",
88            ),
89            ConversionError::NonIntegerCoefficient => Some(
90                "rational/float coefficients not allowed; multiply through by the denominator",
91            ),
92            ConversionError::UnexpectedSymbol(_) => Some(
93                "pass all free variables in the `vars` argument to poly_normal",
94            ),
95            ConversionError::ExponentTooLarge => Some(
96                "exponent exceeds u32::MAX; consider working with rational functions",
97            ),
98            ConversionError::ZeroDenominator => None,
99        }
100    }
101
102    /// Optional source span `(start_byte, end_byte)` within the input text.
103    /// `None` until the parser is integrated.
104    pub fn span(&self) -> Option<(usize, usize)> {
105        None
106    }
107}
108
109/// Failure modes for polynomial factorization (V2-7).
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum FactorError {
112    /// The zero polynomial has no multiplicative factorization.
113    ZeroPolynomial,
114    /// Modulus must be an integer ≥ 2 for 𝔽_p factoring.
115    InvalidModulus,
116    /// FLINT returned an error (rare for well-formed input).
117    FlintFailure,
118}
119
120impl fmt::Display for FactorError {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self {
123            FactorError::ZeroPolynomial => write!(f, "cannot factor the zero polynomial"),
124            FactorError::InvalidModulus => write!(f, "modulus must be at least 2"),
125            FactorError::FlintFailure => {
126                write!(f, "polynomial factorization failed internally (FLINT)")
127            }
128        }
129    }
130}
131
132impl std::error::Error for FactorError {}
133
134impl crate::errors::AlkahestError for FactorError {
135    fn code(&self) -> &'static str {
136        match self {
137            FactorError::ZeroPolynomial => "E-POLY-008",
138            FactorError::InvalidModulus => "E-POLY-009",
139            FactorError::FlintFailure => "E-POLY-010",
140        }
141    }
142
143    fn remediation(&self) -> Option<&'static str> {
144        Some(match self {
145            FactorError::ZeroPolynomial => "factorization is only defined for non-zero polynomials",
146            FactorError::InvalidModulus => {
147                "use a modulus ≥ 2 that fits in a machine word (FLINT `nmod`)"
148            }
149            FactorError::FlintFailure => "report the polynomial as a minimal failing example",
150        })
151    }
152}