p3_field/subfield.rs
1//! Small subfields that a kernel can compute inside before lifting its result.
2
3use crate::{Algebra, Field};
4
5/// A field containing a copy of the small field `S`.
6///
7/// A kernel whose inputs all lie in `S` can do its arithmetic there and lift only the result.
8/// The [`Algebra`] supertrait embeds `S` and lets its elements act on this field.
9/// This trait answers whether an element lies in that copy of `S`.
10///
11/// # Contract
12///
13/// For every `x: Self`, `s: S` and `values: &[Self]`:
14///
15/// - `Self::from` is an injective ring homomorphism: it sends `S::ONE` to `Self::ONE`, it
16/// commutes with addition and multiplication, and distinct elements of `S` have distinct images.
17/// - `x + s`, `x - s` and `x * s` equal `x + Self::from(s)`, `x - Self::from(s)` and
18/// `x * Self::from(s)`, and so do the assigning forms.
19/// - `x.as_subfield() == Some(s)` holds exactly when `x == Self::from(s)`.
20/// So it returns `None` exactly when `x` is the image of no element of `S`.
21/// - `Self::all_in_subfield(values)` holds exactly when every `v.as_subfield()` is `Some`.
22/// In particular it holds on an empty slice.
23///
24/// Callers narrow values into `S` on the strength of these equalities, so they must hold exactly.
25/// A `Some` for an element outside the subfield would silently compute with a different value.
26pub trait HasSubfield<S: Field>: Field + Algebra<S> {
27 /// The element of `S` whose image this is, or `None` if it lies outside the subfield.
28 #[must_use]
29 fn as_subfield(&self) -> Option<S>;
30
31 /// Whether every element of `values` lies in the subfield.
32 ///
33 /// Equal to `values.iter().all(|v| v.as_subfield().is_some())`, which is what the default
34 /// computes. Override it where a whole slice can be tested without narrowing each element in
35 /// turn.
36 #[must_use]
37 #[inline]
38 fn all_in_subfield(values: &[Self]) -> bool {
39 values.iter().all(|v| v.as_subfield().is_some())
40 }
41}