#![forbid(unsafe_code)]
use fgf::poly::{AES, RS};
use fgf::{Elem, Embedding, EmbeddingError, FiniteField, Isomorphism, Polynomial};
type Gf1 = fgf::FiniteField<2, 1, fgf::Polynomial<3>>;
type Gf16 = fgf::FiniteField<2, 16, fgf::Extension<fgf::Rijndael16, 2>>;
type Gf64 = fgf::FiniteField<2, 64, fgf::Extension<fgf::Rijndael64, 2>>;
type FanPaar16Field = fgf::FiniteField<2, 16, fgf::Extension<fgf::FanPaar16, 2>>;
type Aes = FiniteField<2, 8, Polynomial<AES>>;
type Nibble = FiniteField<2, 4, Polynomial<0x13>>;
fn main() {
let small = Embedding::<Nibble, Aes>::new().unwrap();
let x = Elem::<Nibble>::from_raw(2);
let image = small.embed(x);
assert_eq!(image, Elem::<Aes>::from_raw(0x5C));
assert!(small.contains(image));
assert_eq!(small.restrict(image), Some(x));
assert_eq!(small.restrict(Elem::<Aes>::from_raw(2)), None);
println!("GF(16) x embeds into AES as {image}; the literal byte 2 is outside the image.");
let involution =
Isomorphism::<FiniteField<2, 8, Polynomial<AES>>, FiniteField<2, 8, Polynomial<RS>>>::new()
.unwrap();
let (a, b) = (Elem::<Aes>::from_raw(0x53), Elem::<Aes>::from_raw(0xca));
assert_eq!(
involution.apply(Elem::<Aes>::ONE),
Elem::<FiniteField<2, 8, Polynomial<RS>>>::ONE
);
assert_eq!(
involution.apply(a * b),
involution.apply(a) * involution.apply(b)
);
assert_eq!(involution.unapply(involution.apply(a)), a);
println!(
"AES {a} * {b} = {} maps to {} in the Reed-Solomon basis.",
a * b,
involution.apply(a * b)
);
let tower = Embedding::<Aes, Gf16>::new().unwrap();
let (lo, hi) = (Elem::<Aes>::from_raw(0x53), Elem::<Aes>::from_raw(0xca));
let z = Elem::<Gf16>::from_components([lo, hi]);
assert_eq!(
tower.embed(lo),
Elem::<Gf16>::from_components([lo, Elem::<Aes>::ZERO])
);
assert_eq!(
tower.frobenius(z),
Elem::<Gf16>::from_components([lo + hi, hi])
);
assert_eq!(tower.trace(z), hi);
assert_eq!(
tower.norm(z),
lo.square() + lo * hi + Elem::<Aes>::from_raw(0x20) * hi.square()
);
let indeterminate = Elem::<Gf16>::from_raw(0x0100);
assert!(!tower.contains(indeterminate));
assert_eq!(tower.restrict(indeterminate), None);
println!(
"Gf16 z={z} has relative trace {hi} and norm {}.",
tower.norm(z)
);
let cross = Isomorphism::<FanPaar16Field, Gf16>::new().unwrap();
let (p, q) = (
Elem::<FanPaar16Field>::from_raw(0x35C2),
Elem::<FanPaar16Field>::from_raw(0x1234),
);
assert_eq!(cross.unapply(cross.apply(p * q)), p * q);
assert_ne!(cross.apply(p).to_raw(), p.to_raw());
println!(
"Fan-Paar {p} maps to {} in the Rijndael basis.",
cross.apply(p)
);
let wide = Embedding::<Gf16, Gf64>::new().unwrap();
let lifted = wide.embed(z);
assert!(wide.contains(lifted));
assert_eq!(wide.restrict(lifted), Some(z));
assert_eq!(
wide.embed(tower.embed(lo)),
Embedding::<Aes, Gf64>::new().unwrap().embed(lo)
);
let mut frobenius = lifted;
for _ in 0..4 {
frobenius = wide.frobenius(frobenius);
}
assert_eq!(frobenius, lifted);
println!("Gf16 z={z} lifts into GF(2^64) as {lifted}; four Frobenius steps return it.");
let rebuilt = Embedding::<Aes, Gf16>::from_images(tower.to_images()).unwrap();
assert_eq!(rebuilt.embed(lo), tower.embed(lo));
let ladder = tower.then(&wide);
assert_eq!(ladder.embed(lo), wide.embed(tower.embed(lo)));
assert_eq!(
ladder.embed(lo),
Embedding::<Aes, Gf64>::new().unwrap().embed(lo)
);
println!("tower.then(&wide) composes into one AES-into-GF(2^64) inclusion.");
#[cfg(feature = "alloc")]
{
let absolute_frobenius = fgf::Frobenius::<Gf64>::new(1);
let prepared_first = lifted.frobenius_with(&absolute_frobenius);
assert_eq!(prepared_first, lifted.square());
let prepared = fgf::PreparedEmbedding::new(&tower);
let mut lane = [0u8; 1];
<Aes as fgf::FieldBuffer>::encode(&mut lane, lo);
let mut image = [0u8; 2];
fgf::ops::embed_into_with(&mut image, &prepared, &lane);
assert_eq!(<Gf16 as fgf::FieldBuffer>::decode(&image), tower.embed(lo));
}
let absolute = Embedding::<Gf1, Gf16>::new().unwrap();
let mut conjugate_sum = Elem::<Gf16>::ZERO;
let mut power = z;
for _ in 0..16 {
conjugate_sum += power;
power = power.square();
}
assert_eq!(absolute.restrict(conjugate_sum), Some(absolute.trace(z)));
println!(
"Absolute trace of z is {}.",
u8::from(absolute.trace(z).is_one())
);
assert_eq!(
Embedding::<Gf16, Aes>::new().unwrap_err(),
EmbeddingError::IncompatibleDegree {
source: 16,
target: 8
}
);
println!("GF(2^16) into GF(2^8) is rejected: source degree does not divide target degree.");
}