Skip to content

Migrating from 2.x

Zaak edited this page Oct 7, 2026 · 1 revision

Migrating from 2.x to 3.0

fgf 3.0.0 replaces field-specific element wrappers with Elem<F> and makes scalar algebra, byte encoding, and generator selection separate capabilities. Migration changes both type names and the handling of noncanonical prime lanes.

The 3.0.0 changelog is the authoritative release record. This page groups the changes by caller task. Historical 2.x fragments are fenced as text because they do not compile against 3.0.0; all Rust-fenced replacements target 3.0.0.

Replace field and element spellings together

Released 2.x field 3.0 field
Gf8B Binary<8, Polynomial<AES>>
Gf8D Binary<8, Polynomial<RS>>
Gf16 Binary<16, Tower<Rijndael16>>
Gf32 Binary<32, Tower<Rijndael32>>
Gf64 Binary<64, Tower<Rijndael64>>
FanPaarN field marker Binary<N, Tower<FanPaarN>>, where FanPaarN is a specification marker
Gf2 Binary<1, Polynomial<3>>
Mersenne31, Goldilocks, QuadMersenne31 Unchanged field marker names

Every element type becomes Elem<the_field>. For example, gf16::Elem becomes Elem<Binary<16, Tower<Rijndael16>>>, and mersenne31::Elem becomes Elem<Mersenne31>. The crate supplies no compatibility field aliases. Applications can define their own aliases to keep signatures readable.

A historical 2.x scalar computation:

use fgf::gf16;
const A: gf16::Elem = gf16::Elem::from_raw(0x1234);
const B: gf16::Elem = gf16::Elem::from_raw(0x0108);
const PRODUCT: gf16::Elem = A.mul(B);

Its 3.0 replacement:

use fgf::{Binary, Elem, Rijndael16, Tower};

type F = Binary<16, Tower<Rijndael16>>;
const A: Elem<F> = Elem::<F>::from_raw(0x1234);
const B: Elem<F> = Elem::<F>::from_raw(0x0108);
const PRODUCT: Elem<F> = A.mul(B);
assert_eq!(PRODUCT.div(B), A);

Import polynomial constants before using them in the const-generic position, such as Polynomial<AES>. The full polynomial type is Polynomial<P> with P: u128. RS names the unchanged 0x11D convention. Intermediate names from development branches are not compatibility aliases; the changelog also maps those spellings.

Update generic trait bounds

Removed associated item or trait name Replacement
F::Elem Elem<F>
Field::BITS FieldBuffer::STORAGE_BITS for lane width; Field::DEGREE for extension degree
Field::CHARACTERISTIC <F::Characteristic as PrimeIdentity>::CHARACTERISTIC
Field::GENERATOR Elem::<F>::GENERATOR with F: HasGenerator
Field::BYTES, encode, decode FieldBuffer capability
field::Elem trait FieldElem; import the trait separately from the Elem<F> wrapper
Field::elem_count(bytes) Validate whole lanes, then divide by F::BYTES under FieldBuffer

A scalar-only generic algorithm uses F: Field. Encoding uses F: FieldBuffer; packed arithmetic uses F: FieldKernels, which implies FieldBuffer. Generator selection is optional rather than a universal field property.

Historical 2.x coefficient signature:

fn scale<F: fgf::FieldKernels>(dst: &mut [u8], c: F::Elem, src: &[u8]) {
    fgf::ops::mul_into::<F>(dst, c, src);
}

The replacement keeps the operation but changes the value type:

use fgf::{AES, Binary, Elem, FieldKernels, Polynomial, ops};

fn scale<F: FieldKernels>(dst: &mut [u8], c: Elem<F>, src: &[u8]) {
    ops::mul_into::<F>(dst, c, src);
}

type F = Binary<8, Polynomial<AES>>;
let mut destination = [0xa5u8; 3];
scale::<F>(&mut destination, Elem::<F>::from_raw(2), &[1, 2, 3]);
assert_eq!(destination, [2, 4, 6]);

pow takes u128 on both the scalar trait and inherent shipped-family methods. Existing u64 variables need an explicit conversion such as u128::from(exponent). The separate QuadMersenne31::pow_u128 method is replaced by pow.

Retain prepared-operation geometry

Coeff<F>, CoeffVec<F>, and CoeffMatrix<F> remain typed by the field; their constructors take Elem<F> coefficients. Matrix construction remains source-major, and application still borrows ordinary byte buffers. A field rename does not transpose coefficient axes or change row lengths.

use fgf::{AES, Binary, Elem, Polynomial, ops};

type F = Binary<8, Polynomial<AES>>;
let coefficient = ops::Coeff::<F>::new(Elem::<F>::from_raw(3));
let mut destination = [0xa5u8; 3];
ops::mul_into_with::<F>(&mut destination, &coefficient, &[1, 2, 3]);
assert_eq!(destination, [3, 6, 5]);

Prepared values are not a serialized compatibility surface. Rebuild them from scalar values in the new process. Prepared coefficients explains owned collections and borrowed views.

Remove explicit post-construction canonicalization

In 2.x, prime construction and decoding could retain raw lanes, and canonical() reduced the element explicitly. In 3.0, construction and decoding store canonical coordinates, so canonical() is removed and to_raw() returns the reduced storage.

use fgf::{Elem, FieldBuffer, Mersenne31, mersenne31};

let input = mersenne31::MODULUS + 7;
let value = Elem::<Mersenne31>::from_raw(input);
assert_eq!(value.to_raw(), 7);
assert_eq!(Mersenne31::decode(&input.to_le_bytes()), value);
assert_eq!(value.to_bytes(), 7u32.to_le_bytes());

Applications that relied on retaining unreduced bits must retain the original bytes separately. Published canonical encodings and mathematical values remain unchanged, but decoding a noncanonical alias and encoding it again does not preserve its original bytes.

Packed prime operations accept raw lane patterns supported by decoding. Manual input canonicalization is not a precondition for arithmetic. Zero-coefficient accumulation, unit in-place scaling, and unit overwrite copying can preserve raw bytes; those operations must not be treated as normalization requests. Prime fields gives an explicit decode–encode boundary and executable shortcut examples.

Replace constants and tower paths

The gf16, gf32, gf64, and concrete Fan–Paar element modules are removed. The binary presentations live under field::binary; root re-exports provide the usual imports.

Module generator constants become Elem::<F>::GENERATOR under HasGenerator. Tower relation constants come from the specification's LINEAR_COEFFICIENT and CONSTANT_COEFFICIENT; the tower indeterminate is not necessarily its multiplicative generator.

TowerSpec declares a supported half-degree binary base and relation coefficients in that base's raw coordinates. TowerGeneratorSpec separately selects a generator. Custom specifications using intermediate A/B names replace them with the descriptive associated constants. Quadratic towers explains the relation and validity checks.

Keep GF(2) on its bit-packed surface

bits::dot_product returns Elem<Binary<1, Polynomial<3>>>. Scalar GF(2) is a Field, but its vectors remain bit-packed and do not implement the FieldBuffer contract.

use fgf::{Binary, Elem, Polynomial, bits};

type F = Binary<1, Polynomial<3>>;
let parity: Elem<F> = bits::dot_product(&[0b101], &[0b001], 3);
assert_eq!(parity, Elem::<F>::ONE);

Logical bit length, LSB-first ordering, and caller-owned padding remain part of the buffer convention.

Check the application boundary

Migration checks compare canonical wire fixtures, generator-dependent schedules, rectangular coefficient geometry, and prime inputs near the modulus. A passing type check alone does not establish that a raw-coordinate reinterpretation is correct.

From a crate checkout, just examples exercises complete workflows and just doc builds the API reference. The type model is the entry point for rewriting generic library code; deployment covers feature-specific verification.

Clone this wiki locally