#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use super::super::extension::descriptor::COORDINATES;
#[cfg(feature = "alloc")]
use super::super::private::RowAccess;
#[cfg(feature = "alloc")]
use super::super::{Elem, Field, FieldElem, PrimeIdentity, ResidueRow, Validate};
#[cfg(feature = "alloc")]
use super::frobenius::Frobenius;
#[cfg(feature = "alloc")]
use super::frobenius::{accumulate_residue_columns, basis_vector, fold_binary_columns};
use super::{Embedding, Isomorphism};
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
enum PreparedArm<T: Field> {
Injection {
inputs: u8,
},
BinaryColumns {
inputs: u8,
outputs: u8,
forward: Box<[u64]>,
inverse: Box<[u64]>,
},
ResidueColumns {
inputs: u8,
outputs: u8,
forward: Box<[Elem<ResidueRow<T>>]>,
inverse: Box<[Elem<ResidueRow<T>>]>,
},
}
#[cfg(feature = "alloc")]
pub struct PreparedEmbedding<S: Field, T: Field<Characteristic = S::Characteristic>> {
embedding: Embedding<S, T>,
relative_degree: u32,
arm: PreparedArm<T>,
frobenius_map: Frobenius<T>,
trace: TraceColumns<T>,
bit_words: Option<(u64, u64)>,
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> Clone for PreparedEmbedding<S, T> {
fn clone(&self) -> Self {
Self {
embedding: self.embedding,
relative_degree: self.relative_degree,
arm: self.arm.clone(),
frobenius_map: self.frobenius_map.clone(),
trace: self.trace.clone(),
bit_words: self.bit_words,
}
}
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> core::fmt::Debug
for PreparedEmbedding<S, T>
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "PreparedEmbedding<{}, {}>", S::NAME, T::NAME)
}
}
#[cfg(feature = "alloc")]
pub struct PreparedIsomorphism<S: Field, T: Field<Characteristic = S::Characteristic>> {
isomorphism: Isomorphism<S, T>,
forward: PreparedIsoArm<T>,
reverse: PreparedIsoArm<S>,
}
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
enum PreparedIsoArm<T: Field> {
Injection {
degree: u8,
},
BinaryColumns {
degree: u8,
entries: Box<[u64]>,
},
ResidueColumns {
degree: u8,
entries: Box<[Elem<ResidueRow<T>>]>,
},
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> Clone for PreparedIsomorphism<S, T> {
fn clone(&self) -> Self {
Self {
isomorphism: self.isomorphism,
forward: self.forward.clone(),
reverse: self.reverse.clone(),
}
}
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> core::fmt::Debug
for PreparedIsomorphism<S, T>
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "PreparedIsomorphism<{}, {}>", S::NAME, T::NAME)
}
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> PreparedEmbedding<S, T> {
#[must_use]
pub fn new(embedding: &Embedding<S, T>) -> Self {
let () = Validate::<S>::OK;
let () = Validate::<T>::OK;
let relative_degree = T::DEGREE / S::DEGREE;
debug_assert!(
relative_degree > 1,
"a proper inclusion has degree above one"
);
let arm = compile::<S, T>(embedding);
let trace = compile_trace::<S, T>(embedding);
let bit_words = compile_bit_words::<S, T>(embedding, &trace);
Self {
embedding: *embedding,
relative_degree,
arm,
frobenius_map: Frobenius::<T>::new(S::DEGREE),
trace,
bit_words,
}
}
#[must_use]
pub fn embedding(&self) -> Embedding<S, T> {
self.embedding
}
#[must_use]
pub(crate) fn bit_map_words(&self) -> Option<(u64, u64)> {
self.bit_words
}
#[must_use]
pub fn relative_degree(&self) -> u32 {
self.relative_degree
}
#[must_use]
pub fn embed(&self, value: Elem<S>) -> Elem<T> {
apply_forward::<S, T>(&self.arm, value)
}
#[must_use]
pub fn contains(&self, value: Elem<T>) -> bool {
self.frobenius(value) == value
}
#[must_use]
pub fn restrict(&self, value: Elem<T>) -> Option<Elem<S>> {
if !self.contains(value) {
return None;
}
Some(apply_inverse::<S, T>(&self.arm, value))
}
#[must_use]
pub fn frobenius(&self, value: Elem<T>) -> Elem<T> {
value.frobenius_with(&self.frobenius_map)
}
#[must_use]
pub fn trace(&self, value: Elem<T>) -> Elem<S> {
apply_trace::<S, T>(&self.trace, value)
}
#[must_use]
pub fn norm(&self, value: Elem<T>) -> Elem<S> {
let mut product = value;
let mut power = value;
let mut k = 1;
while k < self.relative_degree {
power = self.frobenius(power);
product *= power;
k += 1;
}
assert!(
self.contains(product),
"a conjugate product lies in the embedding image"
);
apply_inverse::<S, T>(&self.arm, product)
}
}
#[cfg(feature = "alloc")]
impl<S: Field, T: Field<Characteristic = S::Characteristic>> PreparedIsomorphism<S, T> {
#[must_use]
pub fn new(isomorphism: &Isomorphism<S, T>) -> Self {
let () = Validate::<S>::OK;
let () = Validate::<T>::OK;
debug_assert_eq!(S::DEGREE, T::DEGREE, "an isomorphism has equal degrees");
let compact = *isomorphism;
let forward = compile_iso::<S, T>(|value| compact.apply(value));
let reverse = compile_iso::<T, S>(|value| compact.unapply(value));
Self {
isomorphism: compact,
forward,
reverse,
}
}
#[must_use]
pub fn isomorphism(&self) -> Isomorphism<S, T> {
self.isomorphism
}
#[must_use]
pub fn apply(&self, value: Elem<S>) -> Elem<T> {
apply_iso::<S, T>(&self.forward, value)
}
#[must_use]
pub fn unapply(&self, value: Elem<T>) -> Elem<S> {
apply_iso::<T, S>(&self.reverse, value)
}
}
#[cfg(feature = "alloc")]
fn compile<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
) -> PreparedArm<T> {
let characteristic = <S::Characteristic as PrimeIdentity>::CHARACTERISTIC;
let inputs = S::DEGREE as usize;
let outputs = T::DEGREE as usize;
#[allow(clippy::cast_possible_truncation)]
let (tag_in, tag_out) = (inputs as u8, outputs as u8);
if characteristic == 2 {
let mut forward = Vec::with_capacity(inputs);
for j in 0..inputs {
forward.push(T::coordinate_word(
embedding.embed(basis_vector::<S>(j)).raw,
));
}
if is_identity_prefix(&forward, inputs) {
return PreparedArm::Injection { inputs: tag_in };
}
let inverse = compile_inverse_binary::<S, T>(embedding, &forward, inputs, outputs);
PreparedArm::BinaryColumns {
inputs: tag_in,
outputs: tag_out,
forward: forward.into_boxed_slice(),
inverse: inverse.into_boxed_slice(),
}
} else {
let mut forward = Vec::with_capacity(inputs * outputs);
for j in 0..inputs {
let image = embedding.embed(basis_vector::<S>(j));
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..outputs], image.raw);
for coordinate in &coordinates[..outputs] {
forward.push(Elem::<ResidueRow<T>>::from_raw(
ResidueRow::<T>::from_prime(*coordinate),
));
}
}
if is_identity_prefix_residue::<T>(&forward, inputs, outputs) {
return PreparedArm::Injection { inputs: tag_in };
}
let inverse = compile_inverse_residue::<S, T>(embedding, inputs, outputs);
PreparedArm::ResidueColumns {
inputs: tag_in,
outputs: tag_out,
forward: forward.into_boxed_slice(),
inverse: inverse.into_boxed_slice(),
}
}
}
#[cfg(feature = "alloc")]
fn is_identity_prefix(columns: &[u64], inputs: usize) -> bool {
if columns.len() != inputs {
return false;
}
for (j, column) in columns.iter().enumerate() {
if *column != 1u64 << j {
return false;
}
}
true
}
#[cfg(feature = "alloc")]
fn is_identity_prefix_residue<T: Field>(
columns: &[Elem<ResidueRow<T>>],
inputs: usize,
outputs: usize,
) -> bool {
if columns.len() != inputs * outputs {
return false;
}
let one = Elem::<ResidueRow<T>>::ONE;
let zero = Elem::<ResidueRow<T>>::ZERO;
for j in 0..inputs {
for o in 0..outputs {
if columns[j * outputs + o] != if o == j { one } else { zero } {
return false;
}
}
}
true
}
#[cfg(feature = "alloc")]
fn compile_inverse_binary<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
forward: &[u64],
inputs: usize,
outputs: usize,
) -> Vec<u64> {
let normalizer = normalizer::<S, T>(embedding);
let mut inverse = Vec::with_capacity(outputs);
for i in 0..outputs {
let probe = basis_vector::<T>(i);
let traced = embedding.trace(normalizer * probe);
inverse.push(S::coordinate_word(traced.raw));
}
debug_assert_inverse::<S, T>(embedding, forward, &inverse, inputs, outputs);
inverse
}
#[cfg(feature = "alloc")]
fn compile_inverse_residue<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
inputs: usize,
outputs: usize,
) -> Vec<Elem<ResidueRow<T>>> {
let normalizer = normalizer::<S, T>(embedding);
let mut inverse = Vec::with_capacity(outputs * inputs);
for i in 0..outputs {
let probe = basis_vector::<T>(i);
let traced = embedding.trace(normalizer * probe);
let mut coordinates = [0u64; COORDINATES];
S::prime_coordinates_into(&mut coordinates[..inputs], traced.raw);
for coordinate in &coordinates[..inputs] {
inverse.push(Elem::<ResidueRow<T>>::from_raw(
ResidueRow::<T>::from_prime(*coordinate),
));
}
}
inverse
}
#[cfg(feature = "alloc")]
fn normalizer<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
) -> Elem<T> {
let outputs = T::DEGREE as usize;
(0..outputs)
.map(|i| scaled_probe(embedding, basis_vector::<T>(i)))
.find_map(|scaled| scaled)
.expect("some target basis vector carries nonzero trace")
}
#[cfg(feature = "alloc")]
fn scaled_probe<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
probe: Elem<T>,
) -> Option<Elem<T>> {
let traced = embedding.trace(probe);
if traced.is_zero() {
return None;
}
Some(probe * embedding.embed(traced.inv()))
}
#[cfg(feature = "alloc")]
fn debug_assert_inverse<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
forward: &[u64],
inverse: &[u64],
inputs: usize,
outputs: usize,
) {
debug_assert_eq!(forward.len(), inputs, "forward column count");
debug_assert_eq!(inverse.len(), outputs, "inverse row count");
for (j, column) in forward.iter().enumerate().take(inputs) {
let basis = basis_vector::<S>(j);
let embedded = T::coordinate_word(embedding.embed(basis).raw);
debug_assert_eq!(embedded, *column, "forward column reproduces the map");
let image = fold_binary_columns(inverse, embedded);
let mask = mask_prefix(inputs);
debug_assert_eq!(
S::from_coordinate_word(image & mask),
S::from_coordinate_word(1u64 << j & mask),
"the left inverse inverts the forward map"
);
}
}
#[cfg(feature = "alloc")]
fn mask_prefix(inputs: usize) -> u64 {
if inputs >= 64 {
u64::MAX
} else {
(1u64 << inputs) - 1
}
}
#[cfg(feature = "alloc")]
fn inject_prefix<S: Field, T: Field<Characteristic = S::Characteristic>>(
value: Elem<S>,
) -> Elem<T> {
let mut coordinates = [0u64; COORDINATES];
S::prime_coordinates_into(&mut coordinates[..S::DEGREE as usize], value.raw);
Elem::<T>::from_raw(T::from_prime_coordinates(
&coordinates[..T::DEGREE as usize],
))
}
#[cfg(feature = "alloc")]
fn project_prefix<S: Field, T: Field<Characteristic = S::Characteristic>>(
value: Elem<T>,
) -> Elem<S> {
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..T::DEGREE as usize], value.raw);
Elem::<S>::from_raw(S::from_prime_coordinates(
&coordinates[..S::DEGREE as usize],
))
}
#[cfg(feature = "alloc")]
fn apply_forward<S: Field, T: Field<Characteristic = S::Characteristic>>(
arm: &PreparedArm<T>,
value: Elem<S>,
) -> Elem<T> {
match arm {
PreparedArm::Injection { inputs, .. } => {
debug_assert_eq!(usize::from(*inputs), S::DEGREE as usize, "prefix width");
inject_prefix::<S, T>(value)
}
PreparedArm::BinaryColumns {
inputs,
outputs,
forward,
..
} => {
debug_assert_eq!(usize::from(*inputs), S::DEGREE as usize, "column count");
debug_assert_eq!(usize::from(*outputs), T::DEGREE as usize, "row count");
let image = fold_binary_columns(forward, S::coordinate_word(value.raw));
Elem {
raw: T::from_coordinate_word(image),
}
}
PreparedArm::ResidueColumns {
inputs,
outputs,
forward,
..
} => {
let inputs = usize::from(*inputs);
let outputs = usize::from(*outputs);
debug_assert_eq!(inputs, S::DEGREE as usize, "column count");
debug_assert_eq!(outputs, T::DEGREE as usize, "row count");
let mut coordinates = [0u64; COORDINATES];
S::prime_coordinates_into(&mut coordinates[..inputs], value.raw);
let mut image = [Elem::<ResidueRow<T>>::ZERO; COORDINATES];
accumulate_residue_columns::<T>(forward, &coordinates[..inputs], outputs, &mut image);
let mut residues = [0u64; COORDINATES];
for (o, slot) in image[..outputs].iter().enumerate() {
residues[o] = slot
.to_residue()
.expect("degree-one readout covers the row");
}
Elem {
raw: T::from_prime_coordinates(&residues[..outputs]),
}
}
}
}
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
enum TraceColumns<T: Field> {
Binary {
inputs: u8,
entries: Box<[u64]>,
},
Residue {
inputs: u8,
outputs: u8,
entries: Box<[Elem<ResidueRow<T>>]>,
},
}
#[cfg(feature = "alloc")]
fn compile_trace<S: Field, T: Field<Characteristic = S::Characteristic>>(
map: &Embedding<S, T>,
) -> TraceColumns<T> {
let outputs = T::DEGREE as usize;
let inputs = S::DEGREE as usize;
if <S::Characteristic as PrimeIdentity>::CHARACTERISTIC == 2 {
let mut entries = alloc::vec![0u64; outputs];
for (i, slot) in entries.iter_mut().enumerate() {
let mut source = [0u64; COORDINATES];
S::prime_coordinates_into(&mut source[..inputs], map.trace(basis_vector::<T>(i)).raw);
*slot = S::coordinate_word(
Elem::<S>::from_raw(S::from_prime_coordinates(&source[..inputs])).raw,
);
}
return TraceColumns::Binary {
inputs: u8::try_from(outputs).expect("a degree fits its byte"),
entries: entries.into_boxed_slice(),
};
}
let mut entries = alloc::vec![Elem::<ResidueRow<T>>::ZERO; outputs * inputs];
for i in 0..outputs {
let traced = map.trace(basis_vector::<T>(i));
let mut source = [0u64; COORDINATES];
S::prime_coordinates_into(&mut source[..inputs], traced.raw);
for k in 0..inputs {
entries[i * inputs + k] =
Elem::<ResidueRow<T>>::from_raw(ResidueRow::<T>::from_prime(source[k]));
}
}
TraceColumns::Residue {
inputs: u8::try_from(inputs).expect("a degree fits its byte"),
outputs: u8::try_from(outputs).expect("a degree fits its byte"),
entries: entries.into_boxed_slice(),
}
}
#[cfg(feature = "alloc")]
fn compile_bit_words<S: Field, T: Field<Characteristic = S::Characteristic>>(
embedding: &Embedding<S, T>,
trace: &TraceColumns<T>,
) -> Option<(u64, u64)> {
if <S::Characteristic as PrimeIdentity>::CHARACTERISTIC != 2 || S::DEGREE != 1 {
return None;
}
let one_w = T::coordinate_word(embedding.embed(Elem::<S>::ONE).raw);
let TraceColumns::Binary { entries, .. } = trace else {
return None;
};
let mut trace_word = 0u64;
for (i, entry) in entries.iter().enumerate() {
trace_word |= (entry & 1) << i;
}
Some((one_w, trace_word))
}
#[cfg(feature = "alloc")]
fn apply_trace<S: Field, T: Field<Characteristic = S::Characteristic>>(
columns: &TraceColumns<T>,
value: Elem<T>,
) -> Elem<S> {
match columns {
TraceColumns::Binary { inputs, entries } => {
let inputs = usize::from(*inputs);
debug_assert_eq!(inputs, T::DEGREE as usize, "input bit count");
let image = fold_binary_columns(entries, T::coordinate_word(value.raw));
Elem {
raw: S::from_coordinate_word(image),
}
}
TraceColumns::Residue {
inputs,
outputs,
entries,
} => {
let inputs = usize::from(*inputs);
let outputs = usize::from(*outputs);
debug_assert_eq!(outputs, T::DEGREE as usize, "column count");
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..outputs], value.raw);
let mut image = [Elem::<ResidueRow<T>>::ZERO; COORDINATES];
accumulate_residue_columns::<T>(entries, &coordinates[..outputs], inputs, &mut image);
let mut residues = [0u64; COORDINATES];
for (k, slot) in image[..inputs].iter().enumerate() {
residues[k] = slot
.to_residue()
.expect("degree-one readout covers the row");
}
Elem {
raw: S::from_prime_coordinates(&residues[..inputs]),
}
}
}
}
fn apply_inverse<S: Field, T: Field<Characteristic = S::Characteristic>>(
arm: &PreparedArm<T>,
value: Elem<T>,
) -> Elem<S> {
match arm {
PreparedArm::Injection { inputs, .. } => {
debug_assert_eq!(usize::from(*inputs), S::DEGREE as usize, "prefix width");
project_prefix::<S, T>(value)
}
PreparedArm::BinaryColumns {
outputs, inverse, ..
} => {
debug_assert_eq!(usize::from(*outputs), T::DEGREE as usize, "row count");
let image = fold_binary_columns(inverse, T::coordinate_word(value.raw));
Elem {
raw: S::from_coordinate_word(image),
}
}
PreparedArm::ResidueColumns {
inputs,
outputs,
inverse,
..
} => {
let inputs = usize::from(*inputs);
let outputs = usize::from(*outputs);
debug_assert_eq!(outputs, T::DEGREE as usize, "row count");
debug_assert_eq!(inputs, S::DEGREE as usize, "column count");
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..outputs], value.raw);
let mut image = [Elem::<ResidueRow<T>>::ZERO; COORDINATES];
accumulate_residue_columns::<T>(inverse, &coordinates[..outputs], inputs, &mut image);
let mut residues = [0u64; COORDINATES];
for (k, slot) in image[..inputs].iter().enumerate() {
residues[k] = slot
.to_residue()
.expect("degree-one readout covers the row");
}
Elem {
raw: S::from_prime_coordinates(&residues[..inputs]),
}
}
}
}
#[cfg(feature = "alloc")]
fn compile_iso<S: Field, T: Field<Characteristic = S::Characteristic>>(
apply: impl Fn(Elem<S>) -> Elem<T>,
) -> PreparedIsoArm<T> {
let characteristic = <S::Characteristic as PrimeIdentity>::CHARACTERISTIC;
let degree = S::DEGREE as usize;
debug_assert_eq!(
degree,
T::DEGREE as usize,
"an isomorphism has equal degrees"
);
#[allow(clippy::cast_possible_truncation)]
let tag = degree as u8;
if characteristic == 2 {
let mut entries = Vec::with_capacity(degree);
for j in 0..degree {
entries.push(T::coordinate_word(apply(basis_vector::<S>(j)).raw));
}
if is_identity_prefix(&entries, degree) {
return PreparedIsoArm::Injection { degree: tag };
}
PreparedIsoArm::BinaryColumns {
degree: tag,
entries: entries.into_boxed_slice(),
}
} else {
let mut entries = Vec::with_capacity(degree * degree);
for j in 0..degree {
let image = apply(basis_vector::<S>(j));
let mut coordinates = [0u64; COORDINATES];
T::prime_coordinates_into(&mut coordinates[..degree], image.raw);
for coordinate in &coordinates[..degree] {
entries.push(Elem::<ResidueRow<T>>::from_raw(
ResidueRow::<T>::from_prime(*coordinate),
));
}
}
if is_identity_prefix_residue::<T>(&entries, degree, degree) {
return PreparedIsoArm::Injection { degree: tag };
}
PreparedIsoArm::ResidueColumns {
degree: tag,
entries: entries.into_boxed_slice(),
}
}
}
#[cfg(feature = "alloc")]
fn apply_iso<S: Field, T: Field<Characteristic = S::Characteristic>>(
arm: &PreparedIsoArm<T>,
value: Elem<S>,
) -> Elem<T> {
match arm {
PreparedIsoArm::Injection { degree } => {
debug_assert_eq!(usize::from(*degree), S::DEGREE as usize, "prefix width");
inject_prefix::<S, T>(value)
}
PreparedIsoArm::BinaryColumns { degree, entries } => {
debug_assert_eq!(usize::from(*degree), S::DEGREE as usize, "column count");
let image = fold_binary_columns(entries, S::coordinate_word(value.raw));
Elem {
raw: T::from_coordinate_word(image),
}
}
PreparedIsoArm::ResidueColumns { degree, entries } => {
let degree = usize::from(*degree);
debug_assert_eq!(degree, S::DEGREE as usize, "column count");
let mut coordinates = [0u64; COORDINATES];
S::prime_coordinates_into(&mut coordinates[..degree], value.raw);
let mut image = [Elem::<ResidueRow<T>>::ZERO; COORDINATES];
accumulate_residue_columns::<T>(entries, &coordinates[..degree], degree, &mut image);
let mut residues = [0u64; COORDINATES];
for (o, slot) in image[..degree].iter().enumerate() {
residues[o] = slot
.to_residue()
.expect("degree-one readout covers the row");
}
Elem {
raw: T::from_prime_coordinates(&residues[..degree]),
}
}
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::super::super::extension::Extension;
use super::super::super::finite_field::FiniteField;
use super::super::super::{Elem, FieldElem, Normal};
use super::super::{Embedding, Frobenius, Isomorphism};
use super::PreparedEmbedding;
#[test]
fn injection_arm_round_trips_the_full_word() {
use super::super::super::{Rijndael32, Rijndael64};
type Gf32 = FiniteField<2, 32, Extension<Rijndael32, 2>>;
type Gf64 = FiniteField<2, 64, Extension<Rijndael64, 2>>;
let compact = Embedding::<Gf32, Gf64>::new().unwrap();
let prepared = PreparedEmbedding::<Gf32, Gf64>::new(&compact);
let probe = Elem::<Gf32>::from_raw(0xDEAD_BEEF);
assert_eq!(prepared.restrict(prepared.embed(probe)), Some(probe));
let wide = Elem::<Gf64>::from_raw(0xFFFF_FFFF_FFFF_FFFF);
assert_eq!(compact.restrict(wide), prepared.restrict(wide));
}
#[test]
fn permuted_unit_columns_take_the_column_arm() {
type Normal8 = FiniteField<2, 8, Normal<0x11B, 0x20>>;
type Gf16 = FiniteField<2, 16, Extension<super::super::super::Rijndael16, 2>>;
let power_one = Frobenius::<Normal8>::new(1);
let generator = Embedding::<Normal8, Normal8>::generator(0);
let image = generator.frobenius_with(&power_one);
assert_ne!(image, generator, "the power-one map moves a generator");
let auto = Isomorphism::<Normal8, Normal8>::from_images(&[image]).unwrap();
let inclusion = Embedding::<Normal8, Gf16>::new().unwrap();
let composite = auto.then_embedding(&inclusion);
let prepared = PreparedEmbedding::<Normal8, Gf16>::new(&composite);
for word in 0..256u32 {
let s = Elem::<Normal8>::from_index(u128::from(word)).unwrap();
assert_eq!(prepared.embed(s), composite.embed(s), "embed at {word}");
}
}
#[test]
fn normalizer_reconstructs_the_identity_on_the_image() {
use super::super::super::{Mersenne31, QuadMersenne31};
let compact = Embedding::<Mersenne31, QuadMersenne31>::new().unwrap();
let prepared = PreparedEmbedding::<Mersenne31, QuadMersenne31>::new(&compact);
let probe = Elem::<QuadMersenne31>::ONE;
assert_eq!(
prepared.restrict(prepared.embed(Elem::<Mersenne31>::ONE)),
Some(Elem::<Mersenne31>::ONE)
);
assert_eq!(prepared.trace(probe), compact.trace(probe));
assert_eq!(
prepared.restrict(Elem::<QuadMersenne31>::ZERO),
Some(Elem::<Mersenne31>::ZERO)
);
}
}