1use alloc::vec;
2use alloc::vec::Vec;
3use core::iter::Sum;
4use core::mem::MaybeUninit;
5use core::ops::{Add, Mul};
6
7use num_bigint::BigUint;
8use p3_maybe_rayon::prelude::*;
9
10use crate::field::Field;
11use crate::{PackedValue, PrimeCharacteristicRing, PrimeField, PrimeField32};
12
13pub fn cyclic_subgroup_known_order<F: Field>(
15 generator: F,
16 order: usize,
17) -> impl Iterator<Item = F> + Clone {
18 generator.powers().take(order)
19}
20
21pub fn cyclic_subgroup_coset_known_order<F: Field>(
23 generator: F,
24 shift: F,
25 order: usize,
26) -> impl Iterator<Item = F> + Clone {
27 generator.shifted_powers(shift).take(order)
28}
29
30pub fn scale_slice_in_place_single_core<F: Field>(slice: &mut [F], s: F) {
35 let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
36 let packed_s: F::Packing = s.into();
37 packed.iter_mut().for_each(|x| *x *= packed_s);
38 sfx.iter_mut().for_each(|x| *x *= s);
39}
40
41#[inline]
47pub fn par_scale_slice_in_place<F: Field>(slice: &mut [F], s: F) {
48 let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
49 let packed_s: F::Packing = s.into();
50 packed.par_iter_mut().for_each(|x| *x *= packed_s);
51 sfx.iter_mut().for_each(|x| *x *= s);
52}
53
54pub fn add_scaled_slice_in_place<F: Field>(slice: &mut [F], other: &[F], s: F) {
59 debug_assert_eq!(slice.len(), other.len(), "slices must have equal length");
60 let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
61 let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other);
62 let packed_s: F::Packing = s.into();
63 slice_packed
64 .iter_mut()
65 .zip(other_packed)
66 .for_each(|(x, y)| *x += *y * packed_s);
67 slice_sfx
68 .iter_mut()
69 .zip(other_sfx)
70 .for_each(|(x, y)| *x += *y * s);
71}
72
73pub fn par_add_scaled_slice_in_place<F: Field>(slice: &mut [F], other: &[F], s: F) {
79 debug_assert_eq!(slice.len(), other.len(), "slices must have equal length");
80 let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
81 let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other);
82 let packed_s: F::Packing = s.into();
83 slice_packed
84 .par_iter_mut()
85 .zip(other_packed.par_iter())
86 .for_each(|(x, y)| *x += *y * packed_s);
87 slice_sfx
88 .iter_mut()
89 .zip(other_sfx)
90 .for_each(|(x, y)| *x += *y * s);
91}
92
93#[inline]
96#[must_use]
97pub const fn field_to_array<R: PrimeCharacteristicRing, const D: usize>(x: R) -> [R; D] {
98 let mut arr = [const { MaybeUninit::uninit() }; D];
99 arr[0] = MaybeUninit::new(x);
100 let mut i = 1;
101 while i < D {
102 arr[i] = MaybeUninit::new(R::ZERO);
103 i += 1;
104 }
105 unsafe { core::mem::transmute_copy::<_, [R; D]>(&arr) }
108}
109
110#[inline]
112#[must_use]
113pub const fn halve_u32<const P: u32>(x: u32) -> u32 {
114 let shift = (P + 1) >> 1;
115 let half = x >> 1;
116 if x & 1 == 0 { half } else { half + shift }
117}
118
119#[inline]
121#[must_use]
122pub const fn halve_u64<const P: u64>(x: u64) -> u64 {
123 let shift = (P + 1) >> 1;
124 let half = x >> 1;
125 if x & 1 == 0 { half } else { half + shift }
126}
127
128#[must_use]
140pub fn reduce_32<SF: PrimeField32, TF: PrimeField>(vals: &[SF]) -> TF {
141 reduce_packed(vals, 32)
142}
143
144#[must_use]
149pub fn reduce_packed_shifted<SF: PrimeField32, TF: PrimeField>(vals: &[SF], radix_bits: u32) -> TF {
150 debug_assert!((radix_bits < 64) && ((SF::ORDER_U32 as u64) < (1u64 << radix_bits)));
151 let base = TF::from_int(1u64 << radix_bits);
152 vals.iter()
153 .map(|val| TF::from_int(val.as_canonical_u32() as u64 + 1))
154 .horner(base)
155}
156
157#[inline]
163#[must_use]
164pub const fn absorb_radix_bits<F: PrimeField32>() -> u32 {
165 u32::BITS - (F::ORDER_U32 - 1).leading_zeros()
166}
167
168#[must_use]
173pub fn reduce_packed<SF: PrimeField32, TF: PrimeField>(vals: &[SF], radix_bits: u32) -> TF {
174 debug_assert!((absorb_radix_bits::<SF>() <= radix_bits) && (radix_bits < 64));
175 let base = TF::from_int(1u64 << radix_bits);
176 vals.iter()
177 .map(|val| TF::from_int(val.as_canonical_u32()))
178 .horner(base)
179}
180
181#[inline]
185#[must_use]
186pub const fn injective_pack_bits<F: PrimeField32>() -> u32 {
187 (F::ORDER_U32 - 1).ilog2()
188}
189
190#[must_use]
197pub fn max_packed_injective_limbs<F: PrimeField32, PF: PrimeField>(radix_bits: u32) -> usize {
198 max_packed_injective_limbs_with_max_digit::<PF>(radix_bits, F::ORDER_U32 - 1)
199}
200
201fn max_packed_injective_limbs_with_max_digit<PF: PrimeField>(
202 radix_bits: u32,
203 max_digit: u32,
204) -> usize {
205 debug_assert!((0 < radix_bits) && (radix_bits < 64));
206 let max_digit = BigUint::from(max_digit);
207 let base = BigUint::from(1u32) << (radix_bits as usize);
208 let pf_order = PF::order();
209 let mut k = 0usize;
210 let mut max_val = BigUint::ZERO;
211 let mut power = BigUint::from(1u32);
212 loop {
213 let new_max = &max_val + &max_digit * &power;
214 if new_max >= pf_order {
215 break k;
216 }
217 max_val = new_max;
218 power *= &base;
219 k += 1;
220 }
221}
222
223#[must_use]
229pub fn max_shifted_packed_injective_limbs<F: PrimeField32, PF: PrimeField>(
230 radix_bits: u32,
231) -> usize {
232 max_packed_injective_limbs_with_max_digit::<PF>(radix_bits, F::ORDER_U32)
233}
234
235#[must_use]
238pub fn max_absorb_injective_limbs<F: PrimeField32, PF: PrimeField>() -> usize {
239 max_packed_injective_limbs::<F, PF>(absorb_radix_bits::<F>())
240}
241
242#[must_use]
245pub fn max_shifted_absorb_injective_limbs<F: PrimeField32, PF: PrimeField>() -> usize {
246 max_shifted_packed_injective_limbs::<F, PF>(absorb_radix_bits::<F>())
247}
248
249#[must_use]
252pub fn pf_packed_limbs_cover_order<SF: PrimeField>(num_limbs: usize, radix_bits: u32) -> bool {
253 let Some(total_bits) = num_limbs.checked_mul(radix_bits as usize) else {
254 return false;
255 };
256 (BigUint::from(1u32) << total_bits) >= SF::order()
257}
258
259#[must_use]
274pub fn split_pf_to_packed_limbs<SF: PrimeField, TF: PrimeField32>(
275 val: SF,
276 num_limbs: usize,
277 radix_bits: u32,
278) -> Vec<TF> {
279 debug_assert!((0 < radix_bits) && (radix_bits < 64));
280 debug_assert!(
281 radix_bits <= injective_pack_bits::<TF>(),
282 "radix_bits must be ≤ injective_pack_bits::<TF>() for injective limb embedding"
283 );
284
285 let mask_u32: u32 = (1u32 << radix_bits) - 1;
287 let mut rem = val.as_canonical_biguint();
288 let mut out = vec![TF::ZERO; num_limbs];
289
290 for item in out.iter_mut() {
291 let limb = rem.iter_u32_digits().next().unwrap_or(0) & mask_u32;
293 *item = TF::from_int(limb);
294
295 rem >>= radix_bits;
297 }
298
299 out
300}
301
302#[must_use]
319pub fn squeeze_field_order_num_limbs<PF: PrimeField, TF: PrimeField32>() -> usize {
320 let p = BigUint::from(TF::ORDER_U32);
321 let n = PF::order();
322 let mut count = 0usize;
323 let mut power = BigUint::from(1u32);
324 while &power * &p < n {
325 power *= &p;
326 count += 1;
327 }
328 count.saturating_sub(1)
329}
330
331#[must_use]
340pub fn split_pf_to_field_order_limbs<SF: PrimeField, TF: PrimeField32>(
341 val: SF,
342 num_limbs: usize,
343) -> Vec<TF> {
344 let p_u32 = TF::ORDER_U32;
345 let mut rem = val.as_canonical_biguint();
346 let mut out = Vec::with_capacity(num_limbs);
347
348 for _ in 0..num_limbs {
349 let limb = (&rem % p_u32).to_u32_digits().first().copied().unwrap_or(0);
351 out.push(TF::from_int(limb));
352
353 rem /= p_u32;
355 }
356 out
357}
358
359#[must_use]
370pub fn split_32<SF: PrimeField, TF: PrimeField32>(val: SF, n: usize) -> Vec<TF> {
371 let mut result: Vec<TF> = val
372 .as_canonical_biguint()
373 .to_u64_digits()
374 .iter()
375 .take(n)
376 .map(|d| TF::from_u64(*d))
377 .collect();
378
379 result.resize_with(n, || TF::ZERO);
381 result
382}
383
384#[must_use]
386pub fn dot_product<S, LI, RI>(li: LI, ri: RI) -> S
387where
388 LI: Iterator,
389 RI: Iterator,
390 LI::Item: Mul<RI::Item>,
391 S: Sum<<LI::Item as Mul<RI::Item>>::Output>,
392{
393 li.zip(ri).map(|(l, r)| l * r).sum()
394}
395
396pub trait HornerIter: DoubleEndedIterator + Sized {
418 #[inline]
421 fn horner_acc<Acc, X>(self, acc: Acc, x: X) -> Acc
422 where
423 Acc: Mul<X, Output = Acc> + Add<Self::Item, Output = Acc>,
424 X: Clone,
425 {
426 self.rfold(acc, |a, v| a * x.clone() + v)
427 }
428
429 #[inline]
432 fn horner<Acc, X>(self, x: X) -> Acc
433 where
434 Acc: Default + Mul<X, Output = Acc> + Add<Self::Item, Output = Acc>,
435 X: Clone,
436 {
437 self.horner_acc(Acc::default(), x)
438 }
439}
440
441impl<I: DoubleEndedIterator> HornerIter for I {}