1use std::iter;
4
5use crate::{Field, PackedField, UnderlierType, field::FieldOps};
6
7pub trait FieldFn<F: Field> {
14 fn call<E: FieldOps<Scalar = F> + From<F>>(&self, inputs: &[E]) -> E;
19
20 fn call_native(&self, inputs: &[F]) -> F {
27 self.call::<F>(inputs)
28 }
29}
30
31pub fn powers<F: FieldOps>(val: F) -> impl Iterator<Item = F> {
33 iter::successors(Some(F::one()), move |power| Some(power.clone() * val.clone()))
34}
35
36pub fn expand_subset_sums_array<P: PackedField, const N: usize, const N_EXP2: usize>(
71 elems: [P; N],
72) -> [P; N_EXP2] {
73 assert_eq!(N_EXP2, 1 << N);
74
75 let mut expanded = [P::zero(); N_EXP2];
76 for (i, elem_i) in elems.into_iter().enumerate() {
77 let span = &mut expanded[..1 << (i + 1)];
78 let (lo_half, hi_half) = span.split_at_mut(1 << i);
79 for (lo_half_i, hi_half_i) in iter::zip(lo_half, hi_half) {
80 *hi_half_i = *lo_half_i + elem_i;
81 }
82 }
83 expanded
84}
85
86pub fn expand_subset_xors<U: UnderlierType, const N: usize, const N_EXP2: usize>(
96 elems: [U; N],
97) -> [U; N_EXP2] {
98 assert_eq!(N_EXP2, 1 << N);
99
100 let mut expanded = [U::ZERO; N_EXP2];
101 for (i, elem_i) in elems.into_iter().enumerate() {
102 let span = &mut expanded[..1 << (i + 1)];
103 let (lo_half, hi_half) = span.split_at_mut(1 << i);
104 for (lo_half_i, hi_half_i) in iter::zip(lo_half, hi_half) {
105 *hi_half_i = *lo_half_i ^ elem_i;
106 }
107 }
108 expanded
109}
110
111#[cfg(test)]
112mod tests {
113 use std::array;
114
115 use proptest::prelude::*;
116 use rand::{SeedableRng, rngs::StdRng};
117
118 use super::*;
119 use crate::{BinaryField128bGhash, Random};
120
121 #[test]
122 fn test_powers_against_pow() {
123 let generator = BinaryField128bGhash::MULTIPLICATIVE_GENERATOR;
124 let power_values: Vec<_> = powers(generator).take(10).collect();
125
126 for i in 0..10 {
127 assert_eq!(power_values[i], generator.pow(i as u64));
128 }
129 }
130
131 type F = BinaryField128bGhash;
132
133 fn check_subset_sums<const N: usize, const N_EXP2: usize>(seed: u64, index: usize) {
136 let mut rng = StdRng::seed_from_u64(seed);
137 let elems: [F; N] = array::from_fn(|_| F::random(&mut rng));
138
139 let result = expand_subset_sums_array::<_, N, N_EXP2>(elems);
140 assert_eq!(result.len(), N_EXP2);
141
142 let index = index % N_EXP2;
144 let mut expected = F::ZERO;
145 for (bit_pos, &elem) in elems.iter().enumerate() {
146 if (index >> bit_pos) & 1 == 1 {
147 expected += elem;
148 }
149 }
150
151 assert_eq!(
152 result[index], expected,
153 "index {index} should hold the subset sum for its binary representation"
154 );
155 }
156
157 proptest! {
158 #[test]
159 fn test_expand_subset_sums_array_correctness(
160 n in 0usize..=8, index in 0usize..256, ) {
163 match n {
166 0 => check_subset_sums::<0, 1>(n as u64, index),
167 1 => check_subset_sums::<1, 2>(n as u64, index),
168 2 => check_subset_sums::<2, 4>(n as u64, index),
169 3 => check_subset_sums::<3, 8>(n as u64, index),
170 4 => check_subset_sums::<4, 16>(n as u64, index),
171 5 => check_subset_sums::<5, 32>(n as u64, index),
172 6 => check_subset_sums::<6, 64>(n as u64, index),
173 7 => check_subset_sums::<7, 128>(n as u64, index),
174 8 => check_subset_sums::<8, 256>(n as u64, index),
175 _ => unreachable!("n is constrained to 0..=8"),
176 }
177 }
178 }
179}