p3_field/
helpers.rs

1use alloc::vec::Vec;
2use core::iter::Sum;
3use core::mem::MaybeUninit;
4use core::ops::Mul;
5
6use p3_maybe_rayon::prelude::*;
7
8use crate::field::Field;
9use crate::{PackedValue, PrimeCharacteristicRing, PrimeField, PrimeField32};
10
11/// Computes a multiplicative subgroup whose order is known in advance.
12pub fn cyclic_subgroup_known_order<F: Field>(
13    generator: F,
14    order: usize,
15) -> impl Iterator<Item = F> + Clone {
16    generator.powers().take(order)
17}
18
19/// Computes a coset of a multiplicative subgroup whose order is known in advance.
20pub fn cyclic_subgroup_coset_known_order<F: Field>(
21    generator: F,
22    shift: F,
23    order: usize,
24) -> impl Iterator<Item = F> + Clone {
25    generator.shifted_powers(shift).take(order)
26}
27
28/// Scales each element of the slice by `s` using packing.
29///
30/// # Performance
31/// For large slices, use [`par_scale_slice_in_place`].
32///
33/// # Deprecation
34/// This function will be replaced with [`scale_slice_in_place`] whose semantics and arguments
35/// will be the same as this one.
36pub fn scale_slice_in_place_single_core<F: Field>(slice: &mut [F], s: F) {
37    let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
38    let packed_s: F::Packing = s.into();
39    packed.iter_mut().for_each(|x| *x *= packed_s);
40    sfx.iter_mut().for_each(|x| *x *= s);
41}
42
43/// Scales each element of the slice by `s` using packing and parallelization.
44///
45/// # Performance
46/// For small slices, use [`scale_slice_in_place_single_core`].
47/// Requires the `parallel` feature.
48#[inline]
49pub fn par_scale_slice_in_place<F: Field>(slice: &mut [F], s: F) {
50    let (packed, sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
51    let packed_s: F::Packing = s.into();
52    packed.par_iter_mut().for_each(|x| *x *= packed_s);
53    sfx.iter_mut().for_each(|x| *x *= s);
54}
55
56/// This function is deprecated. It is currently a wrapper for [`par_scale_slice_in_place`], which
57/// it should be replaced with if parallelization is required.
58#[deprecated(
59    note = "use `par_scale_slice_in_place` instead which will replace this method in the future"
60)]
61pub fn scale_slice_in_place<F: Field>(s: F, slice: &mut [F]) {
62    par_scale_slice_in_place(slice, s)
63}
64
65/// Adds `other`, scaled by `s`, to the mutable `slice` using packing, or `slice += other * s`.
66///
67/// # Performance
68/// For large slices, use [`par_add_scaled_slice_in_place`].
69pub fn add_scaled_slice_in_place<F: Field>(slice: &mut [F], other: &[F], s: F) {
70    debug_assert_eq!(slice.len(), other.len(), "slices must have equal length");
71    let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
72    let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other);
73    let packed_s: F::Packing = s.into();
74    slice_packed
75        .iter_mut()
76        .zip(other_packed)
77        .for_each(|(x, y)| *x += *y * packed_s);
78    slice_sfx
79        .iter_mut()
80        .zip(other_sfx)
81        .for_each(|(x, y)| *x += *y * s);
82}
83
84/// Adds `other`, scaled by `s`, to the mutable `slice` using packing, or `slice += other * s`.
85///
86/// # Performance
87/// For small slices, use [`add_scaled_slice_in_place`].
88/// Requires the `parallel` feature.
89pub fn par_add_scaled_slice_in_place<F: Field>(slice: &mut [F], other: &[F], s: F) {
90    debug_assert_eq!(slice.len(), other.len(), "slices must have equal length");
91    let (slice_packed, slice_sfx) = F::Packing::pack_slice_with_suffix_mut(slice);
92    let (other_packed, other_sfx) = F::Packing::pack_slice_with_suffix(other);
93    let packed_s: F::Packing = s.into();
94    slice_packed
95        .par_iter_mut()
96        .zip(other_packed.par_iter())
97        .for_each(|(x, y)| *x += *y * packed_s);
98    slice_sfx
99        .iter_mut()
100        .zip(other_sfx)
101        .for_each(|(x, y)| *x += *y * s);
102}
103
104/// Extend a ring `R` element `x` to an array of length `D`
105/// by filling zeros.
106#[inline]
107pub const fn field_to_array<R: PrimeCharacteristicRing, const D: usize>(x: R) -> [R; D] {
108    let mut arr = [const { MaybeUninit::uninit() }; D];
109    arr[0] = MaybeUninit::new(x);
110    let mut i = 1;
111    while i < D {
112        arr[i] = MaybeUninit::new(R::ZERO);
113        i += 1;
114    }
115    unsafe { core::mem::transmute_copy::<_, [R; D]>(&arr) }
116}
117
118/// Given an element x from a 32 bit field F_P compute x/2.
119#[inline]
120pub const fn halve_u32<const P: u32>(x: u32) -> u32 {
121    let shift = (P + 1) >> 1;
122    let half = x >> 1;
123    if x & 1 == 0 { half } else { half + shift }
124}
125
126/// Given an element x from a 64 bit field F_P compute x/2.
127#[inline]
128pub const fn halve_u64<const P: u64>(x: u64) -> u64 {
129    let shift = (P + 1) >> 1;
130    let half = x >> 1;
131    if x & 1 == 0 { half } else { half + shift }
132}
133
134/// Reduce a slice of 32-bit field elements into a single element of a larger field.
135///
136/// Uses base-$2^{32}$ decomposition:
137///
138/// ```math
139/// \begin{equation}
140///     \text{reduce\_32}(vals) = \sum_{i=0}^{n-1} a_i \cdot 2^{32i}
141/// \end{equation}
142/// ```
143pub fn reduce_32<SF: PrimeField32, TF: PrimeField>(vals: &[SF]) -> TF {
144    // If the characteristic of TF is > 2^64, from_int and from_canonical_unchecked act identically
145    let base = TF::from_int(1u64 << 32);
146    vals.iter().rev().fold(TF::ZERO, |acc, val| {
147        acc * base + TF::from_int(val.as_canonical_u32())
148    })
149}
150
151/// Split a large field element into `n` base-$2^{64}$ chunks and map each into a 32-bit field.
152///
153/// Converts:
154/// ```math
155/// \begin{equation}
156///     x = \sum_{i=0}^{n-1} d_i \cdot 2^{64i}
157/// \end{equation}
158/// ```
159///
160/// Pads with zeros if needed.
161pub fn split_32<SF: PrimeField, TF: PrimeField32>(val: SF, n: usize) -> Vec<TF> {
162    let mut result: Vec<TF> = val
163        .as_canonical_biguint()
164        .to_u64_digits()
165        .iter()
166        .take(n)
167        .map(|d| TF::from_u64(*d))
168        .collect();
169
170    // Pad with zeros if needed
171    result.resize_with(n, || TF::ZERO);
172    result
173}
174
175/// Maximally generic dot product.
176pub fn dot_product<S, LI, RI>(li: LI, ri: RI) -> S
177where
178    LI: Iterator,
179    RI: Iterator,
180    LI::Item: Mul<RI::Item>,
181    S: Sum<<LI::Item as Mul<RI::Item>>::Output>,
182{
183    li.zip(ri).map(|(l, r)| l * r).sum()
184}