pub(crate) const CANTOR_BASIS_128: [u128; 128] = [
0x00000000000000000000000000000001, 0x00000000000000000000000000000002, 0x0000000000000000000000000000000c, 0x00000000000000000000000000000006, 0x00000000000000000000000000000066, 0x000000000000000000000000000000be, 0x00000000000000000000000000000036, 0x000000000000000000000000000000ac, 0x00000000000000000000000000001444, 0x000000000000000000000000000028f6, 0x0000000000000000000000000000c70e, 0x000000000000000000000000000061b0, 0x000000000000000000000000000067e6, 0x0000000000000000000000000000e4e8, 0x000000000000000000000000000062f4, 0x0000000000000000000000000000cd68, 0x0000000000000000000000000110a454, 0x0000000000000000000000000220108c, 0x0000000000000000000000000cc04ae6, 0x00000000000000000000000006608fe0, 0x00000000000000000000000066764360, 0x000000000000000000000000becba7e2, 0x00000000000000000000000036a31b50, 0x000000000000000000000000acaabff8, 0x00000000000000000000000044c0ca14, 0x000000000000000000000000f6f7191e, 0x0000000000000000000000000e278cfa, 0x000000000000000000000000b04a5fbe, 0x000000000000000000000000e6f91854, 0x000000000000000000000000e89aa24c, 0x000000000000000000000000f49dac5c, 0x000000000000000000000000685bb70c, 0x0000000000000000000101003bc34582, 0x000000000000000000020200396bb03a, 0x0000000000000000000c0c00d0b0e8b8, 0x0000000000000000000606004e9ad958, 0x00000000000000000066660044f0b874, 0x000000000000000000bebe00aec9fbde, 0x000000000000000000363600635f3dec, 0x000000000000000000acac00e7fcaec8, 0x000000000000000014444514b62553ae, 0x000000000000000028f6f4280229b55a, 0x0000000000000000c70e02c700c6a3ac, 0x000000000000000061b0b66163f636ca, 0x000000000000000067e68067519e8e0c, 0x0000000000000000e4e856e4fe5a106c, 0x000000000000000062f4c26257122bf8, 0x0000000000000000cd68c4cd741f31fc, 0x0000000000000000a4547fb4e2455764, 0x0000000000000000108ccf306a375460, 0x00000000000000004ae6de8a2a81316e, 0x00000000000000008fe0feefcd4bc340, 0x00000000000000004360a235b3dc7fe0, 0x0000000000000000a7e2466cf2223352, 0x00000000000000001b5097b817c26446, 0x0000000000000000bff88f155221e120, 0x0000000000000000ca148c0a63a8ee6a, 0x0000000000000000191e39ee81f99646, 0x00000000000000008cfadcabc3c3aeee, 0x00000000000000005fbe2b152121db4a, 0x0000000000000000185473e1430fdc24, 0x0000000000000000a24cee38da4d8828, 0x0000000000000000ac5c0231925591ac, 0x0000000000000000b70c3fecc1c7c4cc, 0x000000010001000051ec1382cf362d58, 0x0000000200020000988d9fd3a2bef392, 0x0000000c000c00002f822c5f61945b52, 0x0000000600060000b8f0134234d14098, 0x0000006600660000df36d959956bdac0, 0x000000be00be00001f2c8d1d900e9b18, 0x00000036003600005fe999a3debb9ea6, 0x000000ac00ac0000637b1635bdb090a6, 0x0000144414440000e3487406d92c5496, 0x000028f628f600008db07c530db5036a, 0x0000c70ec70e00002e5834cfbf6bb772, 0x000061b061b00000d8a7c9649277163c, 0x000067e667e60000aaef92cdbf75be70, 0x0000e4e8e4e800005336c5936a5e98b8, 0x000062f462f40000520751dd3dae2c88, 0x0000cd68cd68000043ec54073b43381c, 0x0110a454a4550110393fb5bfb75f91c0, 0x0220108c108e02205d095f01c84ac1de, 0x0cc04ae64aea0cc0f72c19c6d2dc3176, 0x06608fe08fe60660139b69c2aa148fc0, 0x667643604306667624635608ad1d04a8, 0xbecba7e2a75cbecb36d02fddc0ce697c, 0x36a31b501b6636a3d3dea74e02b99a0c, 0xacaabff8bf54acaae95d8634b11339fe, 0x44c0ca14de5044c05d11c1d0ef2f5148, 0xf6f7191e31e8f6f72b9f2efbb8c15e7c, 0x0e278cfa4bf40e27448196416b37c3e2, 0xb04a5fbe3e0eb04a8ec28859c3484036, 0xe6f918547fb2e6f9a23a62337d1227ac, 0xe89aa24c46a4e89a4d60005a92307dc8, 0xf49dac5ccea8f49dd1917996f644f246, 0x685bb70c7a64685b5685858ec16125ce, 0x3bc34582f5b83ac34f7d352c22a85848, 0x396bb03a88013b6bfa8e5b530962b4ec, 0xd0b0e8b86564dcb09c4967490a0eaf20, 0x4e9ad9583710489a5782d8adc3ed609c, 0x44f0b8749c5622f097ff66566c81a23a, 0xaec9fbdeb8ce10c9560d64b18f1ea1e4, 0x635f3dec44b9555f0d3718d7b4efd9f4, 0xe7fcaec8dc834bfc232edf18ad02d7cc, 0xb62553ae295cf3312d288469082f3e6c, 0x0229b55a94aef601f511844e1cdd037c, 0x00c6a3aca2a20201e213bf6d8bd2a1cc, 0x63f636ca8719d597dc7886c0904bf926, 0x519e8e0cb2bbd1f9fa3035797f5d6f52, 0xfe5a106cf17aa8be13062f9ad892a87c, 0x57122bf8fe5b9570162c56709eceba34, 0x741f31fcf4e0b0d25386f18c7587e27c, 0xe245576468d29df19413bb1e4cc9c15a, 0x6a375460c586a507598b9ef6cf61f8a4, 0x2a81316ed8a2f40b70ae9ec84dfeb48c, 0xcd4bc340ab6d33a4566d03b99de7c522, 0xb3dc7fe0fb3311e93163772c0d4592da, 0xf22233522942b44e1bc51d5f4e30af34, 0x17c264468c01807a614eeccd14f35232, 0x5221e1208a8add3417755bb752221124, 0x63a8ee6aaa1defa259bbf63fde0852da, 0x81f996468ed9b817c15dddc5c448b84a, 0xc3c3aeeeadd71f687b7440d8015c79a8, 0x2121db4a37d50a345c04f31a67e0ea34, 0x430fdc246f3330eef0a8ff5700db4fec, 0xda4d8828fabe34758244f65913a656c0, 0x925591ace16290644c9c0c5214ba545c, 0xc1c7c4ccd801fe2b6b65f1fbd173938c, ];
#[cfg(test)]
mod tests {
extern crate std;
use std::println;
use p3_field::PrimeCharacteristicRing;
use crate::tower::TowerLevel;
use crate::{
BinaryField2, BinaryField4, BinaryField8, BinaryField16, BinaryField32, BinaryField64,
BinaryField128, Gf2, tables,
};
fn check_recurrence<F: TowerLevel>(count: usize) {
assert_eq!(F::cantor_basis(0), F::ONE);
for i in 1..count {
let v = F::cantor_basis(i);
assert_eq!(v.square() + v, F::cantor_basis(i - 1), "level bit {i}");
}
}
fn check_nesting<F: TowerLevel>(count: usize)
where
F::Repr: Into<u128>,
{
for i in 0..count {
let width = (i + 1).next_power_of_two();
let repr: u128 = F::cantor_basis(i).to_repr().into();
assert!(
width >= 128 || repr < 1u128 << width,
"v_{i} escapes GF(2^{width})"
);
assert_eq!(
repr & 1,
u128::from(i == 0),
"v_{i} is not the even representation"
);
}
}
fn check_independent<F: TowerLevel>(count: usize)
where
F::Repr: Into<u128>,
{
let mut pivots = [0u128; 128];
for i in 0..count {
let mut v: u128 = F::cantor_basis(i).to_repr().into();
while v != 0 {
let high = 127 - v.leading_zeros() as usize;
if pivots[high] == 0 {
pivots[high] = v;
break;
}
v ^= pivots[high];
}
assert_ne!(v, 0, "v_{i} depends on the earlier vectors");
}
}
#[test]
fn recurrence_holds_at_every_level() {
check_recurrence::<Gf2>(1);
check_recurrence::<BinaryField2>(2);
check_recurrence::<BinaryField4>(4);
check_recurrence::<BinaryField8>(8);
check_recurrence::<BinaryField16>(16);
check_recurrence::<BinaryField32>(32);
check_recurrence::<BinaryField64>(64);
check_recurrence::<BinaryField128>(128);
}
#[test]
fn basis_is_nested_and_independent() {
check_nesting::<BinaryField2>(2);
check_nesting::<BinaryField4>(4);
check_nesting::<BinaryField8>(8);
check_nesting::<BinaryField16>(16);
check_nesting::<BinaryField32>(32);
check_nesting::<BinaryField64>(64);
check_nesting::<BinaryField128>(128);
check_independent::<BinaryField2>(2);
check_independent::<BinaryField4>(4);
check_independent::<BinaryField8>(8);
check_independent::<BinaryField16>(16);
check_independent::<BinaryField32>(32);
check_independent::<BinaryField64>(64);
check_independent::<BinaryField128>(128);
}
#[test]
fn smallest_vectors_are_pinned() {
assert_eq!(BinaryField128::cantor_basis(0).to_repr(), 1);
assert_eq!(BinaryField128::cantor_basis(1).to_repr(), 2);
let v2 = u8::try_from(BinaryField128::cantor_basis(2).to_repr()).unwrap();
let v3 = u8::try_from(BinaryField128::cantor_basis(3).to_repr()).unwrap();
assert_eq!(tables::square(v2) ^ v2, 2, "v_2^2 + v_2 = v_1");
assert_eq!(tables::square(v3) ^ v3, v2, "v_3^2 + v_3 = v_2");
assert_eq!(v2 & 1, 0, "v_2 is not the even representation");
assert_eq!(v3 & 1, 0, "v_3 is not the even representation");
}
#[test]
#[should_panic(expected = "index out of range")]
fn out_of_range_index_panics() {
let _ = BinaryField8::cantor_basis(8);
}
#[test]
#[ignore]
fn regenerate_cantor_basis() {
let columns: [u128; 128] = core::array::from_fn(|b| {
let e = BinaryField128::from_repr(1u128 << b);
(e.square() + e).to_repr()
});
let mut basis = [0u128; 128];
basis[0] = 1;
for i in 1..128 {
basis[i] = solve_artin_schreier(&columns, basis[i - 1]);
let v = BinaryField128::from_repr(basis[i]);
assert_eq!(v.square() + v, BinaryField128::from_repr(basis[i - 1]));
assert_eq!(basis[i] & 1, 0, "v_{i} is not the even representation");
}
for (i, v) in basis.iter().enumerate() {
println!(" 0x{v:032x}, // v_{i}");
}
}
fn solve_artin_schreier(columns: &[u128; 128], target: u128) -> u128 {
let mut image = *columns;
let mut preimage: [u128; 128] = core::array::from_fn(|c| 1u128 << c);
let mut rhs = target;
let mut solution = 0u128;
let mut consumed = 0u128;
for bit in 0..128 {
let Some(pivot) =
(0..128).find(|&c| consumed >> c & 1 == 0 && image[c] >> bit & 1 == 1)
else {
continue;
};
consumed |= 1 << pivot;
for c in 0..128 {
if c != pivot && image[c] >> bit & 1 == 1 {
image[c] ^= image[pivot];
preimage[c] ^= preimage[pivot];
}
}
if rhs >> bit & 1 == 1 {
rhs ^= image[pivot];
solution ^= preimage[pivot];
}
}
assert_eq!(rhs, 0, "target is outside the image of x -> x^2 + x");
if solution & 1 == 1 {
solution ^ 1
} else {
solution
}
}
}