binius_math/
binary_subspace.rs1use binius_field::BinaryField;
4use binius_utils::{bail, iter::IterExtensions};
5
6use super::error::Error;
7
8#[derive(Debug, Clone)]
13pub struct BinarySubspace<F: BinaryField> {
14 basis: Vec<F>,
15}
16
17impl<F: BinaryField> BinarySubspace<F> {
18 pub const fn new_unchecked(basis: Vec<F>) -> Self {
22 Self { basis }
23 }
24
25 pub fn with_dim(dim: usize) -> Result<Self, Error> {
34 let basis = (0..dim)
35 .map(|i| F::basis(i).map_err(|_| Error::DomainSizeTooLarge))
36 .collect::<Result<_, _>>()?;
37 Ok(Self { basis })
38 }
39
40 pub fn reduce_dim(&self, dim: usize) -> Result<Self, Error> {
48 if dim > self.dim() {
49 bail!(Error::DomainSizeTooLarge);
50 }
51 Ok(Self {
52 basis: self.basis[..dim].to_vec(),
53 })
54 }
55
56 pub fn isomorphic<FIso>(&self) -> BinarySubspace<FIso>
58 where
59 FIso: BinaryField + From<F>,
60 {
61 BinarySubspace {
62 basis: self.basis.iter().copied().map(Into::into).collect(),
63 }
64 }
65
66 pub fn dim(&self) -> usize {
68 self.basis.len()
69 }
70
71 pub fn basis(&self) -> &[F] {
73 &self.basis
74 }
75
76 pub fn get(&self, index: usize) -> F {
77 self.basis
78 .iter()
79 .take(usize::BITS as usize)
80 .enumerate()
81 .fold(F::ZERO, |acc, (i, basis_i)| {
82 if (index >> i) & 1 != 0 {
83 acc + basis_i
84 } else {
85 acc
86 }
87 })
88 }
89
90 pub fn get_checked(&self, index: usize) -> Result<F, Error> {
91 if index >= 1 << self.basis.len() {
92 bail!(Error::ArgumentRangeError {
93 arg: "index".to_string(),
94 range: 0..1 << self.basis.len(),
95 });
96 }
97 Ok(self.get(index))
98 }
99
100 pub fn iter(&self) -> impl Iterator<Item = F> + '_ {
104 let last = if self.basis.len() < usize::BITS as usize {
105 (1 << self.basis.len()) - 1
106 } else {
107 usize::MAX
108 };
109 (0..=last).map_skippable(|i| self.get(i))
110 }
111}
112
113impl<F: BinaryField> Default for BinarySubspace<F> {
114 fn default() -> Self {
115 let basis = (0..F::DEGREE)
116 .map(|i| F::basis(i).expect("index is in range"))
117 .collect();
118 Self { basis }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use assert_matches::assert_matches;
125 use binius_field::{BinaryField128b, BinaryField8b};
126
127 use super::*;
128
129 #[test]
130 fn test_default_binary_subspace_iterates_elements() {
131 let subspace = BinarySubspace::<BinaryField8b>::default();
132 for i in 0..=255 {
133 assert_eq!(subspace.get(i), BinaryField8b::new(i as u8));
134 }
135 }
136
137 #[test]
138 fn test_binary_subspace_range_error() {
139 let subspace = BinarySubspace::<BinaryField8b>::default();
140 assert_matches!(subspace.get_checked(256), Err(Error::ArgumentRangeError { .. }));
141 }
142
143 #[test]
144 fn test_default_large_binary_subspace_iterates_elements() {
145 let subspace = BinarySubspace::<BinaryField128b>::default();
146 for i in 0..=255 {
147 assert_eq!(subspace.get(i), BinaryField128b::new(i as u128));
148 }
149 }
150
151 #[test]
152 fn test_default_binary_subspace() {
153 let subspace = BinarySubspace::<BinaryField8b>::default();
154 assert_eq!(subspace.dim(), 8);
155 assert_eq!(subspace.basis().len(), 8);
156
157 assert_eq!(
158 subspace.basis(),
159 [
160 BinaryField8b::new(0b00000001),
161 BinaryField8b::new(0b00000010),
162 BinaryField8b::new(0b00000100),
163 BinaryField8b::new(0b00001000),
164 BinaryField8b::new(0b00010000),
165 BinaryField8b::new(0b00100000),
166 BinaryField8b::new(0b01000000),
167 BinaryField8b::new(0b10000000)
168 ]
169 );
170
171 let expected_elements: [u8; 256] = (0..=255).collect::<Vec<_>>().try_into().unwrap();
172
173 for (i, &expected) in expected_elements.iter().enumerate() {
174 assert_eq!(subspace.get(i), BinaryField8b::new(expected));
175 }
176 }
177
178 #[test]
179 fn test_with_dim_valid() {
180 let subspace = BinarySubspace::<BinaryField8b>::with_dim(3).unwrap();
181 assert_eq!(subspace.dim(), 3);
182 assert_eq!(subspace.basis().len(), 3);
183
184 assert_eq!(
185 subspace.basis(),
186 [
187 BinaryField8b::new(0b001),
188 BinaryField8b::new(0b010),
189 BinaryField8b::new(0b100)
190 ]
191 );
192
193 let expected_elements: [u8; 8] = [0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111];
194
195 for (i, &expected) in expected_elements.iter().enumerate() {
196 assert_eq!(subspace.get(i), BinaryField8b::new(expected));
197 }
198 }
199
200 #[test]
201 fn test_with_dim_invalid() {
202 let result = BinarySubspace::<BinaryField8b>::with_dim(10);
203 assert_matches!(result, Err(Error::DomainSizeTooLarge));
204 }
205
206 #[test]
207 fn test_reduce_dim_valid() {
208 let subspace = BinarySubspace::<BinaryField8b>::with_dim(6).unwrap();
209 let reduced = subspace.reduce_dim(4).unwrap();
210 assert_eq!(reduced.dim(), 4);
211 assert_eq!(reduced.basis().len(), 4);
212
213 assert_eq!(
214 reduced.basis(),
215 [
216 BinaryField8b::new(0b0001),
217 BinaryField8b::new(0b0010),
218 BinaryField8b::new(0b0100),
219 BinaryField8b::new(0b1000)
220 ]
221 );
222
223 let expected_elements: [u8; 16] = (0..16).collect::<Vec<_>>().try_into().unwrap();
224
225 for (i, &expected) in expected_elements.iter().enumerate() {
226 assert_eq!(reduced.get(i), BinaryField8b::new(expected));
227 }
228 }
229
230 #[test]
231 fn test_reduce_dim_invalid() {
232 let subspace = BinarySubspace::<BinaryField8b>::with_dim(4).unwrap();
233 let result = subspace.reduce_dim(6);
234 assert_matches!(result, Err(Error::DomainSizeTooLarge));
235 }
236
237 #[test]
238 fn test_isomorphic_conversion() {
239 let subspace = BinarySubspace::<BinaryField8b>::with_dim(3).unwrap();
240 let iso_subspace: BinarySubspace<BinaryField128b> = subspace.isomorphic();
241 assert_eq!(iso_subspace.dim(), 3);
242 assert_eq!(iso_subspace.basis().len(), 3);
243
244 assert_eq!(
245 iso_subspace.basis(),
246 [
247 BinaryField128b::from(BinaryField8b::new(0b001)),
248 BinaryField128b::from(BinaryField8b::new(0b010)),
249 BinaryField128b::from(BinaryField8b::new(0b100))
250 ]
251 );
252 }
253
254 #[test]
255 fn test_get_checked_valid() {
256 let subspace = BinarySubspace::<BinaryField8b>::default();
257 for i in 0..256 {
258 assert!(subspace.get_checked(i).is_ok());
259 }
260 }
261
262 #[test]
263 fn test_get_checked_invalid() {
264 let subspace = BinarySubspace::<BinaryField8b>::default();
265 assert_matches!(subspace.get_checked(256), Err(Error::ArgumentRangeError { .. }));
266 }
267
268 #[test]
269 fn test_iterate_subspace() {
270 let subspace = BinarySubspace::<BinaryField8b>::with_dim(3).unwrap();
271 let elements: Vec<_> = subspace.iter().collect();
272 assert_eq!(elements.len(), 8);
273
274 let expected_elements: [u8; 8] = [0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111];
275
276 for (i, &expected) in expected_elements.iter().enumerate() {
277 assert_eq!(elements[i], BinaryField8b::new(expected));
278 }
279 }
280}