#![forbid(unsafe_code)]
use fgf::poly::AES;
use fgf::{
Backend, Components, Elem, Extension, ExtensionField, ExtensionSpec, Field, FieldBuffer,
FieldElem, FiniteField, Packed, PackedPadded, Polynomial, Relation, Rijndael16, backend_for,
ops,
};
type Gf7 = FiniteField<7, 1>;
type Gf5 = FiniteField<5, 1>;
type Base8 = FiniteField<2, 8, Polynomial<AES>>;
type Gf16 = FiniteField<2, 16, Extension<Rijndael16, 2>>;
struct Gf49Spec;
impl ExtensionSpec<2> for Gf49Spec {
type Base = Gf7;
type Storage = Components;
const NAME: &'static str = "GF(49)";
const RELATION: Relation = Relation::from_coordinates(&[4, 0]);
}
type Gf49 = FiniteField<7, 2, Extension<Gf49Spec, 2>>;
struct Gf125Spec;
impl ExtensionSpec<3> for Gf125Spec {
type Base = Gf5;
type Storage = Components;
const NAME: &'static str = "GF(125)";
const RELATION: Relation = Relation::from_coordinates(&[1, 1, 0]);
}
type Gf125 = FiniteField<5, 3, Extension<Gf125Spec, 3>>;
struct Packed16Spec;
impl ExtensionSpec<2> for Packed16Spec {
type Base = Base8;
type Storage = Packed<u16>;
const NAME: &'static str = "custom GF(2^16), packed";
const RELATION: Relation = Relation::from_binary_words::<Base8>(&[0x20, 0x01]);
}
type Packed16 = FiniteField<2, 16, Extension<Packed16Spec, 2>>;
struct Padded16Spec;
impl ExtensionSpec<2> for Padded16Spec {
type Base = Base8;
type Storage = PackedPadded<u32>;
const NAME: &'static str = "custom GF(2^16), padded";
const RELATION: Relation = Relation::from_binary_words::<Base8>(&[0x20, 0x01]);
}
type Padded16 = FiniteField<2, 16, Extension<Padded16Spec, 2>>;
struct Component16Spec;
impl ExtensionSpec<2> for Component16Spec {
type Base = Base8;
type Storage = Components;
const NAME: &'static str = "custom GF(2^16), components";
const RELATION: Relation = Relation::from_binary_words::<Base8>(&[0x20, 0x01]);
}
type Component16 = FiniteField<2, 16, Extension<Component16Spec, 2>>;
const LANES: usize = 9;
fn main() {
gf49();
gf125();
storages();
}
fn gf49() {
const ONE: Elem<Gf49> = Elem::<Gf49>::ONE;
let t = <Gf49 as ExtensionField<2>>::from_components([Elem::<Gf7>::ZERO, Elem::<Gf7>::ONE]);
let square = t * t;
let [constant, linear] = <Gf49 as ExtensionField<2>>::to_components(square);
assert_eq!((constant.to_raw(), linear.to_raw()), (3, 0));
let (p, q, r) = (
<Gf49 as ExtensionField<2>>::from_components([
Elem::<Gf7>::from_raw(2),
Elem::<Gf7>::from_raw(5),
]),
<Gf49 as ExtensionField<2>>::from_components([
Elem::<Gf7>::from_raw(6),
Elem::<Gf7>::from_raw(1),
]),
<Gf49 as ExtensionField<2>>::from_components([
Elem::<Gf7>::from_raw(3),
Elem::<Gf7>::from_raw(4),
]),
);
assert_eq!((p + q) * r, p * r + q * r);
assert_eq!(Elem::<Gf49>::ZERO.inv(), Elem::<Gf49>::ZERO);
let w = t + ONE;
assert_eq!(w * w.inv(), ONE);
assert_eq!(t.pow(48), ONE);
let mut bytes = [0u8; <Gf49 as FieldBuffer>::BYTES];
<Gf49 as FieldBuffer>::encode(&mut bytes, w);
assert_eq!(bytes, [1, 1]);
assert_eq!(<Gf49 as FieldBuffer>::decode(&bytes), w);
println!(
"{}: t^2 = 3, (t + 1) has bytes {bytes:?}, inversion total",
Gf49::NAME
);
}
fn gf125() {
let t = <Gf125 as ExtensionField<3>>::from_components([
Elem::<Gf5>::ZERO,
Elem::<Gf5>::ONE,
Elem::<Gf5>::ZERO,
]);
let cube = t * t * t;
let [constant, linear, quadratic] = <Gf125 as ExtensionField<3>>::to_components(cube);
assert_eq!(
(constant.to_raw(), linear.to_raw(), quadratic.to_raw()),
(4, 4, 0)
);
assert_eq!(Elem::<Gf125>::ZERO.inv(), Elem::<Gf125>::ZERO);
let v = <Gf125 as ExtensionField<3>>::from_components([
Elem::<Gf5>::from_raw(1),
Elem::<Gf5>::from_raw(3),
Elem::<Gf5>::from_raw(2),
]);
assert_eq!(v * v.inv(), Elem::<Gf125>::ONE);
assert_eq!(t.pow(124), Elem::<Gf125>::ONE);
println!(
"{}: t^3 + t + 1 folds the cube to {cube}, inversion total",
Gf125::NAME
);
}
fn storages() {
let (a, b) = (0x53C7u16, 0x1D2Eu16);
let packed_product = Elem::<Packed16>::from_raw(a) * Elem::<Packed16>::from_raw(b);
let shipped_product = Elem::<Gf16>::from_raw(a) * Elem::<Gf16>::from_raw(b);
assert_eq!(packed_product.to_raw(), shipped_product.to_raw());
assert_eq!(backend_for::<Packed16>(), backend_for::<Gf16>());
let padded_product = Elem::<Padded16>::from_raw(0xBEEF_0000 | u32::from(a))
* Elem::<Padded16>::from_raw(u32::from(b));
assert_eq!(padded_product.to_raw(), u32::from(packed_product.to_raw()));
assert_eq!(
Elem::<Padded16>::from_raw(0xBEEF_0000 | u32::from(a)).to_raw(),
u32::from(a)
);
let component = |lo: u8, hi: u8| {
<Component16 as ExtensionField<2>>::from_components([
Elem::<Base8>::from_raw(lo),
Elem::<Base8>::from_raw(hi),
])
};
let component_product = component(a as u8, (a >> 8) as u8) * component(b as u8, (b >> 8) as u8);
let [low, high] = <Component16 as ExtensionField<2>>::to_components(component_product);
let product_word = packed_product.to_raw();
assert_eq!(
(low.to_raw(), high.to_raw()),
(product_word as u8, (product_word >> 8) as u8)
);
assert_eq!(backend_for::<Component16>(), Backend::Scalar);
let xs: [Elem<Component16>; LANES] =
std::array::from_fn(|i| component((i * 13 + 5) as u8, (i * 29 + 3) as u8));
let ys: [Elem<Component16>; LANES] =
std::array::from_fn(|i| component((i * 7 + 11) as u8, (i * 19 + 1) as u8));
let k = component(0x7B, 0x2A);
let prepared = ops::Coeff::<Component16>::new(k);
const WIDTH: usize = LANES * <Component16 as FieldBuffer>::BYTES;
let mut xs_packed = [0u8; WIDTH];
ops::pack::<Component16>(&mut xs_packed, &xs);
let mut ys_packed = [0u8; WIDTH];
ops::pack::<Component16>(&mut ys_packed, &ys);
let mut acc = [0u8; WIDTH];
ops::mul_add_with::<Component16>(&mut acc, &prepared, &xs_packed);
ops::mul_add_with::<Component16>(&mut acc, &prepared, &ys_packed);
ops::add_assign::<Component16>(&mut acc, &xs_packed);
ops::sub_assign::<Component16>(&mut acc, &xs_packed);
let mut lanes = [Elem::<Component16>::ZERO; LANES];
ops::unpack::<Component16>(&mut lanes, &acc);
for (lane, (&x, &y)) in lanes.iter().zip(xs.iter().zip(&ys)) {
assert_eq!(*lane, k * x + k * y, "lane agrees with element arithmetic");
}
println!(
"{} ({:?}) = {} = {}: one relation, three storages, one arithmetic",
Component16::NAME,
backend_for::<Component16>(),
Packed16::NAME,
Padded16::NAME
);
}