use super::super::extension::arithmetic;
use super::super::extension::descriptor::{COORDINATES, Node};
use super::super::{Elem, Field, FieldElem, PrimeIdentity};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum ConjugationCoefficient {
Zero,
One,
General,
}
pub(super) const fn conjugation_shape<T: Field>() -> Option<(u8, ConjugationCoefficient)> {
let descriptor = T::DESCRIPTOR;
let root = descriptor.root_index();
let node = descriptor.node(root);
let (base, base_degree, base_words, coefficients, characteristic) = match node {
Node::Extension {
base,
relative_degree: 2,
base_degree,
base_words,
coefficients,
characteristic,
..
} => (
*base,
*base_degree,
*base_words,
*coefficients,
*characteristic,
),
_ => return None,
};
#[allow(clippy::cast_possible_truncation)]
let base_degree = base_degree as u8;
if characteristic == 2 {
if coefficients[1] == arithmetic::word_one(descriptor, base) {
return Some((base_degree, ConjugationCoefficient::One));
}
return Some((base_degree, ConjugationCoefficient::General));
}
let mut linear = arithmetic::zero_element();
let mut k = 0;
while k < base_words {
linear[k] = coefficients[base_words + k];
k += 1;
}
if arithmetic::element_is_zero(descriptor, base, &linear) {
return Some((base_degree, ConjugationCoefficient::Zero));
}
Some((base_degree, ConjugationCoefficient::General))
}
pub(super) const fn routes<S: Field, T: Field>() -> bool {
if T::DEGREE != 2 * S::DEGREE {
return false;
}
let target = T::DESCRIPTOR;
let root = target.root_index();
let (base_degree,) = match target.node(root) {
Node::Extension {
relative_degree: 2,
base_degree,
..
} => (*base_degree,),
_ => return false,
};
if base_degree != S::DEGREE {
return false;
}
target.base_descriptor().same_structure(S::DESCRIPTOR)
}
pub(super) const fn is_tiny_gf4<S: Field, T: Field>() -> bool {
S::DEGREE == 1
&& T::DEGREE == 2
&& <S::Characteristic as PrimeIdentity>::CHARACTERISTIC == 2
&& <T::Characteristic as PrimeIdentity>::CHARACTERISTIC == 2
}
fn linear_coefficient<T: Field>() -> arithmetic::Element {
let descriptor = T::DESCRIPTOR;
let root = descriptor.root_index();
let Node::Extension {
base_words,
coefficients,
..
} = descriptor.node(root)
else {
debug_assert!(false, "stored coefficient on a non-relation root");
return arithmetic::zero_element();
};
let characteristic = arithmetic::characteristic_of(descriptor, root);
let mut coefficient = arithmetic::zero_element();
if characteristic == 2 {
coefficient[0] = coefficients[1];
return coefficient;
}
let mut k = 0;
while k < *base_words {
coefficient[k] = coefficients[*base_words + k];
k += 1;
}
coefficient
}
fn mask_of(bits: u32) -> u64 {
if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
}
}
pub(super) fn apply_conjugation<T: Field>(
value: Elem<T>,
base_degree: u8,
coefficient: ConjugationCoefficient,
) -> Elem<T> {
let descriptor = T::DESCRIPTOR;
let root = descriptor.root_index();
let Node::Extension { base, .. } = descriptor.node(root) else {
debug_assert!(false, "conjugation on a non-relation root");
return value;
};
let base = *base;
let characteristic = arithmetic::characteristic_of(descriptor, root);
if characteristic == 2 {
let word = T::coordinate_word(value.raw);
let half = u32::from(base_degree);
let mask = mask_of(half);
let (lo, hi) = (word & mask, (word >> half) & mask);
let c1 = linear_coefficient::<T>();
let sheared = match coefficient {
ConjugationCoefficient::One => lo ^ hi,
ConjugationCoefficient::General => {
lo ^ arithmetic::word_mul(descriptor, base, c1[0], hi)
}
ConjugationCoefficient::Zero => {
debug_assert!(false, "vanishing coefficient is inseparable here");
lo
}
};
return Elem {
raw: T::from_coordinate_word(sheared | (hi << half)),
};
}
let degree = T::DEGREE as usize;
let half = usize::from(base_degree);
debug_assert!(degree == 2 * half, "conjugation spans the whole root");
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..degree], value.raw);
let mut lower = arithmetic::zero_element();
let mut upper = arithmetic::zero_element();
let mut k = 0;
while k < half {
lower[k] = coordinates[k];
upper[k] = coordinates[half + k];
k += 1;
}
let image_upper = arithmetic::element_neg(descriptor, base, &upper);
let image_lower = match coefficient {
ConjugationCoefficient::Zero => lower,
ConjugationCoefficient::General => {
let product =
arithmetic::element_mul(descriptor, base, &linear_coefficient::<T>(), &upper);
arithmetic::element_sub(descriptor, base, &lower, &product)
}
ConjugationCoefficient::One => {
debug_assert!(false, "unit coefficient is inseparable here");
lower
}
};
let mut image = [0u64; COORDINATES];
let mut k = 0;
while k < half {
image[k] = image_lower[k];
image[half + k] = image_upper[k];
k += 1;
}
Elem {
raw: T::from_prime_coordinates(&image[..degree]),
}
}
pub(super) fn apply_trace<S: Field, T: Field>(
value: Elem<T>,
base_degree: u8,
coefficient: ConjugationCoefficient,
) -> Elem<S> {
let _ = coefficient;
let _ = base_degree;
let conjugate = apply_conjugation::<T>(value, base_degree, coefficient);
let sum = Elem::<T> {
raw: add_raw::<T>(value.raw, conjugate.raw),
};
restrict_quad::<S, T>(sum)
}
pub(super) fn apply_norm<S: Field, T: Field>(
value: Elem<T>,
base_degree: u8,
coefficient: ConjugationCoefficient,
) -> Elem<S> {
let conjugate = apply_conjugation::<T>(value, base_degree, coefficient);
let product = Elem::<T> {
raw: mul_raw::<T>(value.raw, conjugate.raw),
};
restrict_quad::<S, T>(product)
}
fn add_raw<T: Field>(left: T::Raw, right: T::Raw) -> T::Raw {
T::add_raw(left, right)
}
fn mul_raw<T: Field>(left: T::Raw, right: T::Raw) -> T::Raw {
T::mul_raw(left, right)
}
pub(super) fn restrict_quad<S: Field, T: Field>(value: Elem<T>) -> Elem<S> {
let degree = T::DEGREE as usize;
let span = S::DEGREE as usize;
debug_assert!(degree == 2 * span, "quadratic restriction spans the row");
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..degree], value.raw);
Elem::<S>::from_raw(S::from_prime_coordinates(&coordinates[..span]))
}