p3_binary_field/
subfield.rs1use core::ops::{Add, Mul, Sub};
12
13use p3_field::op_assign_macros::{impl_add_base_field, impl_sub_base_field};
14use p3_field::{Algebra, HasSubfield};
15
16use crate::tower::TowerLevel;
17use crate::{BinaryField2, BinaryField128};
18
19const LOW_BITS: u128 = 0x5555_5555_5555_5555_5555_5555_5555_5555;
21
22const HIGH_BITS: u128 = !LOW_BITS;
24
25#[inline]
29const fn mul_x0(bits: u128) -> u128 {
30 let b1 = bits & HIGH_BITS;
31 (b1 >> 1) ^ ((bits & LOW_BITS) << 1) ^ b1
32}
33
34impl From<BinaryField2> for BinaryField128 {
35 #[inline]
37 fn from(x: BinaryField2) -> Self {
38 Self::from_repr(u128::from(x.to_repr()))
39 }
40}
41
42impl_add_base_field!(BinaryField128, BinaryField2);
43impl_sub_base_field!(BinaryField128, BinaryField2);
44
45impl Mul<BinaryField2> for BinaryField128 {
46 type Output = Self;
47
48 #[inline]
53 fn mul(self, rhs: BinaryField2) -> Self {
54 let bits = self.to_repr();
55 let c = rhs.to_repr();
56 let c0 = u128::from(c & 1).wrapping_neg();
57 let c1 = u128::from((c >> 1) & 1).wrapping_neg();
58 Self::from_repr((c0 & bits) ^ (c1 & mul_x0(bits)))
59 }
60}
61
62impl Mul<BinaryField128> for BinaryField2 {
63 type Output = BinaryField128;
64
65 #[inline]
66 fn mul(self, rhs: BinaryField128) -> BinaryField128 {
67 rhs * self
68 }
69}
70
71impl Algebra<BinaryField2> for BinaryField128 {}
72
73impl HasSubfield<BinaryField2> for BinaryField128 {
74 #[inline]
76 fn as_subfield(&self) -> Option<BinaryField2> {
77 let bits = self.to_repr();
78 (bits & !3 == 0).then(|| BinaryField2::from_repr(bits as u8))
79 }
80
81 #[inline]
86 fn all_in_subfield(values: &[Self]) -> bool {
87 values.iter().fold(0, |acc, v| acc | v.to_repr()) & !3 == 0
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use alloc::vec::Vec;
94
95 use p3_field::{HasSubfield, PrimeCharacteristicRing};
96 use p3_field_testing::test_has_subfield;
97 use proptest::prelude::*;
98
99 use crate::tower::TowerLevel;
100 use crate::{BinaryField2, BinaryField8, BinaryField128};
101
102 fn gf4() -> impl Iterator<Item = BinaryField2> {
104 (0..4).map(BinaryField2::from_repr)
105 }
106
107 #[test]
108 fn the_embedding_is_a_ring_homomorphism_on_every_pair() {
109 assert_eq!(
110 BinaryField128::from(BinaryField2::ZERO),
111 BinaryField128::ZERO
112 );
113 assert_eq!(BinaryField128::from(BinaryField2::ONE), BinaryField128::ONE);
114
115 for a in gf4() {
116 for b in gf4() {
117 let (x, y) = (BinaryField128::from(a), BinaryField128::from(b));
118 assert_eq!(BinaryField128::from(a + b), x + y, "{a} + {b}");
119 assert_eq!(BinaryField128::from(a * b), x * y, "{a} * {b}");
120
121 assert_eq!(BinaryField128::from(a * b), x.reference_mul(y), "{a} * {b}");
124 }
125 }
126 }
127
128 #[test]
131 fn the_embedding_factors_through_the_byte_level() {
132 let byte = |a: BinaryField2| BinaryField8::from_repr(a.to_repr());
133
134 for a in gf4() {
135 assert_eq!(BinaryField128::from(a), BinaryField128::from(byte(a)));
136
137 for b in gf4() {
138 assert_eq!(byte(a * b), byte(a) * byte(b), "{a} * {b}");
139 }
140 }
141 }
142
143 #[test]
144 fn every_subfield_element_narrows_back_to_itself() {
145 for a in gf4() {
146 assert_eq!(BinaryField128::from(a).as_subfield(), Some(a));
147 }
148 }
149
150 #[test]
151 fn the_has_subfield_contract_holds() {
152 test_has_subfield::<BinaryField128, BinaryField2>();
153 }
154
155 #[test]
159 fn every_bit_above_the_lowest_two_lies_outside_the_subfield() {
160 let members: Vec<BinaryField128> = gf4().map(BinaryField128::from).collect();
161 for s in 0..4u128 {
162 for k in 2..128 {
163 let x = BinaryField128::from_repr(s | (1u128 << k));
164 assert_eq!(x.as_subfield(), None, "{s} with bit {k} set");
165
166 let mut values = members.clone();
167 values.insert(k % (members.len() + 1), x);
168 assert!(
169 !BinaryField128::all_in_subfield(&values),
170 "{s} with bit {k} set among the subfield elements"
171 );
172 }
173 }
174 }
175
176 proptest! {
177 #![proptest_config(ProptestConfig::with_cases(1000))]
178
179 #[test]
180 fn the_masked_product_agrees_with_the_tower_product(bits: u128) {
181 let x = BinaryField128::from_repr(bits);
182 for s in gf4() {
183 let image = BinaryField128::from(s);
184 prop_assert_eq!(x * s, x * image, "scalar {}", s);
185 prop_assert_eq!(s * x, image * x, "scalar {}", s);
186 }
187 }
188
189 #[test]
191 fn narrowing_accepts_exactly_the_low_two_bits(bits: u128, shift in 0u32..128) {
192 let bits = bits >> shift;
193 let x = BinaryField128::from_repr(bits);
194 match x.as_subfield() {
195 Some(s) => {
196 prop_assert!(bits < 4);
197 prop_assert_eq!(BinaryField128::from(s), x);
198 }
199 None => prop_assert!(bits >= 4),
200 }
201 }
202
203 #[test]
206 fn all_in_subfield_agrees_with_narrowing_each_element(
207 bits in prop::collection::vec(
208 prop_oneof![
209 3 => 0u128..4,
210 1 => (any::<u128>(), 0u32..128).prop_map(|(bits, shift)| bits >> shift),
211 ],
212 0..12,
213 ),
214 ) {
215 let values: Vec<BinaryField128> =
216 bits.into_iter().map(BinaryField128::from_repr).collect();
217 prop_assert_eq!(
218 BinaryField128::all_in_subfield(&values),
219 values.iter().all(|v| v.as_subfield().is_some())
220 );
221 }
222 }
223}