1use super::BinaryRepr;
54use super::description::{BinaryDescription, ByteLogExp};
55use crate::field::FiniteFieldRepr;
56
57pub const AES: u128 = 0x11B;
63
64pub const RS: u128 = 0x11D;
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
80pub struct Polynomial<const P: u128>;
81
82impl<const P: u128> crate::field::private::Sealed for Polynomial<P> {}
83
84impl<const P: u128> Polynomial<P> {
85 pub const POLY: u128 = P;
87
88 #[allow(dead_code)]
93 pub(crate) const REDUCTION_LOW: u8 = (P & 0xFF) as u8;
94
95 const NAME_BYTES8: [u8; 13] = name_bytes8(P);
97 const NAME_BYTES4: [u8; 12] = name_bytes4(P);
99 const NAME_BYTES2: [u8; 11] = name_bytes2(P);
101
102 pub(crate) const fn smallest_generator(description: &BinaryDescription) -> u64 {
109 let limit = 1u64 << description.degree();
110 let mut candidate = 2u64;
111 loop {
112 assert!(candidate < limit, "Polynomial P has no primitive element");
113 if description.is_generator(candidate) {
114 break candidate;
115 }
116 candidate += 1;
117 }
118 }
119}
120
121const fn name_bytes8(poly: u128) -> [u8; 13] {
123 const HEX: &[u8; 16] = b"0123456789ABCDEF";
124 let mut name = *b"GF(2^8)/0x000";
125 name[10] = HEX[((poly >> 8) & 0xf) as usize];
126 name[11] = HEX[((poly >> 4) & 0xf) as usize];
127 name[12] = HEX[(poly & 0xf) as usize];
128 name
129}
130
131const fn name_bytes4(poly: u128) -> [u8; 12] {
133 const HEX: &[u8; 16] = b"0123456789ABCDEF";
134 let mut name = *b"GF(2^4)/0x00";
135 name[10] = HEX[((poly >> 4) & 0xf) as usize];
136 name[11] = HEX[(poly & 0xf) as usize];
137 name
138}
139
140const fn name_bytes2(poly: u128) -> [u8; 11] {
142 const HEX: &[u8; 16] = b"0123456789ABCDEF";
143 let mut name = *b"GF(2^2)/0x0";
144 name[10] = HEX[(poly & 0xf) as usize];
145 name
146}
147
148macro_rules! poly_degree8 {
149 () => {
150 impl<const P: u128> FiniteFieldRepr<2, 8> for Polynomial<P> {
151 const NAME: &'static str = match core::str::from_utf8(&Polynomial::<P>::NAME_BYTES8) {
152 Ok(name) => name,
153 Err(_) => panic!("Polynomial field names are ASCII"),
154 };
155 const VALID: () = {
156 assert!(
157 super::description::poly_degree(P) == 8,
158 "polynomial degree does not match field degree"
159 );
160 <Self as BinaryRepr<8>>::DESCRIPTION.validate();
161 assert!(
162 <Self as BinaryRepr<8>>::DESCRIPTION.is_generator(
163 Polynomial::<P>::smallest_generator(<Self as BinaryRepr<8>>::DESCRIPTION)
164 ),
165 "byte representation generator does not have full order"
166 );
167 };
168 }
169 impl<const P: u128> BinaryRepr<8> for Polynomial<P> {
170 const DESCRIPTION: &'static BinaryDescription = &BinaryDescription::polynomial(8, P);
171 const LOG_EXP: Option<&'static ByteLogExp> = Some(&ByteLogExp::build(
172 &BinaryDescription::polynomial(8, P),
173 Polynomial::<P>::smallest_generator(&BinaryDescription::polynomial(8, P)),
174 ));
175 }
176 };
177}
178
179poly_degree8!();
180
181impl<const P: u128> FiniteFieldRepr<2, 4> for Polynomial<P> {
182 const NAME: &'static str = match core::str::from_utf8(&Polynomial::<P>::NAME_BYTES4) {
183 Ok(name) => name,
184 Err(_) => panic!("Polynomial field names are ASCII"),
185 };
186 const VALID: () = {
187 assert!(
188 super::description::poly_degree(P) == 4,
189 "polynomial degree does not match field degree"
190 );
191 <Self as BinaryRepr<4>>::DESCRIPTION.validate();
192 };
193}
194
195impl<const P: u128> BinaryRepr<4> for Polynomial<P> {
196 const DESCRIPTION: &'static BinaryDescription = &BinaryDescription::polynomial(4, P);
197 const LOG_EXP: Option<&'static ByteLogExp> = None;
198}
199
200impl<const P: u128> FiniteFieldRepr<2, 2> for Polynomial<P> {
201 const NAME: &'static str = match core::str::from_utf8(&Polynomial::<P>::NAME_BYTES2) {
202 Ok(name) => name,
203 Err(_) => panic!("Polynomial field names are ASCII"),
204 };
205 const VALID: () = {
206 assert!(
207 super::description::poly_degree(P) == 2,
208 "polynomial degree does not match field degree"
209 );
210 <Self as BinaryRepr<2>>::DESCRIPTION.validate();
211 };
212}
213
214impl<const P: u128> BinaryRepr<2> for Polynomial<P> {
215 const DESCRIPTION: &'static BinaryDescription = &BinaryDescription::polynomial(2, P);
216 const LOG_EXP: Option<&'static ByteLogExp> = None;
217}
218
219impl<const P: u128> FiniteFieldRepr<2, 1> for Polynomial<P> {
220 const NAME: &'static str = "GF(2)";
221 const VALID: () = {
222 assert!(P == 3, "Polynomial<3> is the only degree-one form");
223 <Self as BinaryRepr<1>>::DESCRIPTION.validate();
224 };
225}
226
227impl<const P: u128> BinaryRepr<1> for Polynomial<P> {
228 const DESCRIPTION: &'static BinaryDescription = &BinaryDescription::polynomial(1, P);
229 const LOG_EXP: Option<&'static ByteLogExp> = None;
230}
231
232use super::super::HasGenerator;
233use crate::field::FiniteField;
234
235impl<const P: u128> HasGenerator for FiniteField<2, 8, Polynomial<P>> {
236 const GENERATOR_RAW: u8 = {
237 let g = Polynomial::<P>::smallest_generator(<Polynomial<P> as BinaryRepr<8>>::DESCRIPTION);
238 assert!(
239 <Polynomial<P> as BinaryRepr<8>>::DESCRIPTION.is_generator(g),
240 "byte representation generator does not have full order"
241 );
242 #[allow(clippy::cast_possible_truncation)]
243 let generator = g as u8;
244 generator
245 };
246}
247
248impl<const P: u128> HasGenerator for FiniteField<2, 4, Polynomial<P>> {
249 const GENERATOR_RAW: u8 = {
250 let g = Polynomial::<P>::smallest_generator(<Polynomial<P> as BinaryRepr<4>>::DESCRIPTION);
251 assert!(
252 <Polynomial<P> as BinaryRepr<4>>::DESCRIPTION.is_generator(g),
253 "representation generator does not have full order"
254 );
255 #[allow(clippy::cast_possible_truncation)]
256 let generator = g as u8;
257 generator
258 };
259}
260
261impl<const P: u128> HasGenerator for FiniteField<2, 2, Polynomial<P>> {
262 const GENERATOR_RAW: u8 = {
263 let g = Polynomial::<P>::smallest_generator(<Polynomial<P> as BinaryRepr<2>>::DESCRIPTION);
264 assert!(
265 <Polynomial<P> as BinaryRepr<2>>::DESCRIPTION.is_generator(g),
266 "representation generator does not have full order"
267 );
268 #[allow(clippy::cast_possible_truncation)]
269 let generator = g as u8;
270 generator
271 };
272}
273
274impl<const P: u128> HasGenerator for FiniteField<2, 1, Polynomial<P>> {
275 const GENERATOR_RAW: u8 = 1;
276}
277
278#[cfg(test)]
279pub(super) mod tests {
280 extern crate std;
281
282 use core::hint::black_box;
283
284 use super::super::description::poly_irreducible;
285 use super::*;
286
287 pub(in crate::field::binary) fn oracle_mul(poly: u128, n: u32, x: u64, y: u64) -> u64 {
289 let mut acc: u128 = 0;
290 for k in 0..n {
291 if (y >> k) & 1 == 1 {
292 acc ^= u128::from(x) << k;
293 }
294 }
295 for k in (n..2 * n).rev() {
296 if (acc >> k) & 1 == 1 {
297 acc ^= poly << (k - n);
298 }
299 }
300 #[allow(clippy::cast_possible_truncation)]
301 let product = acc as u64;
302 product
303 }
304
305 pub(in crate::field::binary) fn oracle_smallest_generator(poly: u128, n: u32) -> u64 {
308 let order = (1u64 << n) - 1;
309 (2..=order)
310 .find(|&candidate| {
311 let mut power = candidate;
312 let mut steps = 1;
313 while power != 1 && steps <= order {
314 power = oracle_mul(poly, n, power, candidate);
315 steps += 1;
316 }
317 steps == order
318 })
319 .expect("a field has a primitive element")
320 }
321
322 pub(in crate::field::binary) fn apply_columns(columns: [u8; 8], word: u64) -> u64 {
324 (0..8)
325 .filter(|&i| (word >> i) & 1 == 1)
326 .fold(0, |image, i| image ^ u64::from(columns[i]))
327 }
328
329 fn irreducibles(n: u32) -> impl Iterator<Item = u128> {
331 (1u128 << n..2u128 << n).filter(move |&p| poly_irreducible(p, n))
332 }
333
334 #[test]
338 fn smallest_generator_matches_brute_force_order() {
339 for n in [2u8, 4, 8] {
340 for poly in irreducibles(u32::from(n)) {
341 let description = BinaryDescription::polynomial(black_box(n), black_box(poly));
342 assert_eq!(
343 Polynomial::<AES>::smallest_generator(&description),
344 oracle_smallest_generator(poly, u32::from(n)),
345 "degree {n}, polynomial {poly:#x}"
346 );
347 }
348 }
349 }
350
351 #[test]
354 fn field_names_round_trip_their_polynomial() {
355 fn parsed(name: &[u8]) -> u128 {
356 let name = core::str::from_utf8(name).expect("ASCII name");
357 let (_, digits) = name.rsplit_once("0x").expect("hexadecimal suffix");
358 u128::from_str_radix(digits, 16).expect("hexadecimal digits")
359 }
360 for poly in irreducibles(8) {
361 assert_eq!(parsed(&name_bytes8(black_box(poly))), poly);
362 }
363 for poly in irreducibles(4) {
364 assert_eq!(parsed(&name_bytes4(black_box(poly))), poly);
365 }
366 for poly in irreducibles(2) {
367 assert_eq!(parsed(&name_bytes2(black_box(poly))), poly);
368 }
369 }
370
371 #[test]
374 fn irreducibility_check_admits_exactly_the_thirty_fields() {
375 let count = (0x100..=0x1FFu128)
376 .filter(|&p| super::super::description::poly_irreducible(p, 8))
377 .count();
378 assert_eq!(count, 30);
379 assert!(super::super::description::poly_irreducible(AES, 8));
380 assert!(super::super::description::poly_irreducible(RS, 8));
381 assert!(super::super::description::poly_irreducible(0x12D, 8));
382 assert!(super::super::description::poly_irreducible(0x187, 8));
383 assert!(!super::super::description::poly_irreducible(0x101, 8));
384 assert!(!super::super::description::poly_irreducible(0x0FF, 8));
385 assert!(!super::super::description::poly_irreducible(0x200, 8));
386 }
387}