1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! Typed refusals for the function-field API.
//!
//! Every variant carries a stable `E-FFLD-NNN` code registered in
//! [`crate::errors::codes::REGISTRY`]. The split between a **refusal** (the
//! implementation cannot answer) and a **verdict** (the answer is "no") is
//! deliberate and is documented per variant: [`FunctionFieldError::NonTorsion`]
//! is a verdict, everything else is a refusal.
use std::fmt;
/// Why a function-field operation refused.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FunctionFieldError {
/// The defining polynomial is not a curve model this module implements.
///
/// Only `c₂·y² + c₁(x)·y + c₀(x)` with `c₂` a non-zero **rational constant**
/// is accepted; it is normalised to `y² = a(x)` with `a` squarefree of
/// degree ≥ 1. Anything else — `deg_y f ≠ 2`, a non-constant leading
/// coefficient, a discriminant that is zero or a non-zero constant — is
/// outside the modelled class.
UnsupportedModel {
/// What specifically failed.
reason: String,
},
/// The curve is a **real** (even-degree) hyperelliptic model, and this
/// operation is implemented only for the **imaginary** (odd-degree) one.
///
/// `y² = a(x)` with `deg a = 2g+2` has **two** places above `x = ∞`. The
/// Mumford/Cantor representation reused here (`integrate::algebraic`) fixes
/// a single rational place at infinity as the base point of every class, so
/// it does not apply. The **genus** is still reported: it does not depend
/// on the model.
RealModel {
/// `deg a`, always even here.
degree: usize,
/// The genus, which *is* known.
genus: usize,
/// The operation that refused.
operation: &'static str,
},
/// The computation needs every place in play to be ℚ-**rational**, and one
/// is not — or could not be shown to be.
///
/// [`super::Place`] represents a degree-1 place `(α, β)` with `α, β ∈ ℚ`,
/// plus the place at infinity. A conjugate pair `(α, ±√c)` with `c` a
/// non-square, or an `x`-coordinate that is not rational at all, is a place
/// of degree 2 or more and has no representation here. Silently dropping
/// it would return a divisor of the wrong degree, so it is a refusal.
NonRationalSupport {
/// What produced the non-rational place.
context: String,
},
/// A place `(α, β)` was supplied with `β² ≠ a(α)`: it is not on the curve.
PlaceNotOnCurve {
/// Rendered `x`-coordinate.
x: String,
/// Rendered `y`-coordinate.
y: String,
},
/// A divisor-class operation was handed a divisor whose degree is not zero.
///
/// The group modelled here is `Pic⁰`, the classes of **degree-zero**
/// divisors. A divisor of degree `n ≠ 0` has no class in it; subtract
/// `n·∞` first if that is what you meant.
NotDegreeZero {
/// The degree that was found.
degree: String,
},
/// The order of a torsion class could not be pinned down within scope.
///
/// The reduction-modulo-good-primes test needs at least two good primes
/// below its search ceiling, and the reconstructed candidate must fit in a
/// machine word. Neither a torsion certificate nor a non-torsion one.
UndecidedOrder {
/// What ran out.
reason: String,
},
/// The class is **not** torsion: it has infinite order.
///
/// **A verdict, not a refusal.** Reduction modulo good primes is injective
/// on prime-to-`p` torsion, so orders that disagree across two good primes
/// prove the class is of infinite order; so does an exact ℚ-Cantor test
/// that rejects the only candidate order the reconstruction allows.
/// Retrying with a bigger budget will not change this answer.
NonTorsion,
/// `div(0)` was requested. The zero function has no divisor.
ZeroFunction,
/// Two objects built over different function fields were combined.
CurveMismatch {
/// Rendered left curve.
left: String,
/// Rendered right curve.
right: String,
},
/// A multiplicity, degree or order is too large for this implementation.
///
/// Cantor arithmetic here folds a place in by repeated addition bounded by
/// a machine word, and the Riemann–Roch solver builds a dense matrix.
TooLarge {
/// What exceeded what.
reason: String,
},
/// A result was **computed and then withheld** because it failed its own
/// consistency check.
///
/// Two checks can fire. `divisor_of_function` cross-checks the pole order
/// at infinity obtained from degree balance against the one read off the
/// degrees of `p` and `q`; `riemann_roch` checks the dimension it found
/// against Riemann's inequality `dim ≥ deg D + 1 − g` and, above the
/// canonical degree, against the Riemann–Roch equality itself. A
/// disagreement means this module has a bug, and returning the answer
/// anyway is the silent-wrong-answer failure mode the crate refuses by
/// policy.
SelfCheckFailed {
/// Which check, and what disagreed.
detail: String,
},
}
impl fmt::Display for FunctionFieldError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionFieldError::UnsupportedModel { reason } => write!(
f,
"not a supported curve model: {reason}. This module implements \
ℚ(x)[y]/(y² − a(x)) with a squarefree of degree ≥ 1"
),
FunctionFieldError::RealModel {
degree,
genus,
operation,
} => write!(
f,
"`{operation}` is implemented only for the imaginary (odd-degree) \
hyperelliptic model; `deg a = {degree}` is even, so the curve has two \
places above x = ∞ and no single rational base point (the genus, {genus}, \
is still available)"
),
FunctionFieldError::NonRationalSupport { context } => write!(
f,
"a place of degree > 1 appears ({context}); only ℚ-rational places \
(α, β) and the place at infinity can be represented here"
),
FunctionFieldError::PlaceNotOnCurve { x, y } => {
write!(f, "({x}, {y}) is not on the curve: y² ≠ a(x)")
}
FunctionFieldError::NotDegreeZero { degree } => write!(
f,
"divisor has degree {degree}, but the class group modelled here is Pic⁰; \
subtract deg(D)·∞ to land in it"
),
FunctionFieldError::UndecidedOrder { reason } => write!(
f,
"could not determine the order of the class: {reason}. This is neither a \
torsion nor a non-torsion certificate"
),
FunctionFieldError::NonTorsion => write!(
f,
"the divisor class is not torsion — it has infinite order, so no multiple \
of the divisor is principal"
),
FunctionFieldError::ZeroFunction => {
write!(f, "the zero function has no divisor")
}
FunctionFieldError::CurveMismatch { left, right } => write!(
f,
"these objects live on different curves ({left} and {right}); divisors and \
classes can only be combined within one function field"
),
FunctionFieldError::TooLarge { reason } => {
write!(f, "too large for this implementation: {reason}")
}
FunctionFieldError::SelfCheckFailed { detail } => write!(
f,
"a result was computed and then withheld because it failed its own \
consistency check: {detail}"
),
}
}
}
impl std::error::Error for FunctionFieldError {}
impl crate::errors::AlkahestError for FunctionFieldError {
fn code(&self) -> &'static str {
match self {
FunctionFieldError::UnsupportedModel { .. } => "E-FFLD-001",
FunctionFieldError::RealModel { .. } => "E-FFLD-002",
FunctionFieldError::NonRationalSupport { .. } => "E-FFLD-003",
FunctionFieldError::PlaceNotOnCurve { .. } => "E-FFLD-004",
FunctionFieldError::NotDegreeZero { .. } => "E-FFLD-005",
FunctionFieldError::UndecidedOrder { .. } => "E-FFLD-006",
FunctionFieldError::NonTorsion => "E-FFLD-007",
FunctionFieldError::ZeroFunction => "E-FFLD-008",
FunctionFieldError::CurveMismatch { .. } => "E-FFLD-009",
FunctionFieldError::TooLarge { .. } => "E-FFLD-010",
FunctionFieldError::SelfCheckFailed { .. } => "E-FFLD-011",
}
}
fn remediation(&self) -> Option<&'static str> {
match self {
FunctionFieldError::UnsupportedModel { .. } => Some(
"supply c₂·y² + c₁(x)·y + c₀(x) with c₂ a non-zero rational constant, whose discriminant c₁² − 4c₂c₀ is non-constant; higher-degree plane curves are not modelled",
),
FunctionFieldError::RealModel { .. } => Some(
"move the model to odd degree by sending a rational root of a(x) to infinity, or use genus() alone, which is model-independent",
),
FunctionFieldError::NonRationalSupport { .. } => Some(
"restrict to divisors supported on rational places, or work with the divisor class (Mumford form), which represents conjugate places implicitly",
),
FunctionFieldError::PlaceNotOnCurve { .. } => Some(
"check the sign and the model: the place must satisfy y² = a(x) in the *normalised* coordinates reported by FunctionField::curve()",
),
FunctionFieldError::NotDegreeZero { .. } => {
Some("replace D by D − deg(D)·∞ before asking for its class")
}
FunctionFieldError::UndecidedOrder { .. } => Some(
"record the result as undecided — never as non-torsion; a larger prime search or a different model may settle it",
),
FunctionFieldError::NonTorsion => Some(
"this is a verdict: no multiple of the divisor is principal, so stop looking for one",
),
FunctionFieldError::ZeroFunction => {
Some("pass a non-zero function; div(0) is not defined")
}
FunctionFieldError::CurveMismatch { .. } => {
Some("rebuild both operands over the same FunctionField")
}
FunctionFieldError::TooLarge { .. } => Some(
"reduce the multiplicities or the degree of the divisor; the Cantor and linear-algebra steps here are bounded by a machine word",
),
FunctionFieldError::SelfCheckFailed { .. } => Some(
"report this as a bug with the curve and divisor that produced it; the answer was withheld rather than returned wrong",
),
}
}
}