p3_field/
coset.rs

1use crate::{BoundedPowers, TwoAdicField};
2
3/// Coset of a subgroup of the group of units of a finite field of order equal
4/// to a power of two.
5///
6/// # Examples
7///
8/// ```
9/// # use p3_field::{
10///     TwoAdicField,
11///     PrimeCharacteristicRing,
12///     coset::TwoAdicMultiplicativeCoset
13/// };
14/// # use itertools::Itertools;
15/// # use p3_baby_bear::BabyBear;
16/// #
17/// type F = BabyBear;
18/// let log_size = 3;
19/// let shift = F::from_u64(7);
20/// let mut coset = TwoAdicMultiplicativeCoset::new(shift, log_size).unwrap();
21/// let generator = coset.subgroup_generator();
22///
23/// // Coset elements can be queried by index
24/// assert_eq!(coset.element(4), shift * generator.exp_u64(4));
25///
26/// // Coset elements can be iterated over in the canonical order
27/// assert_eq!(
28///     coset.iter().collect_vec(),
29///     (0..1 << log_size).map(|i| shift * generator.exp_u64(i)).collect_vec()
30/// );
31///
32/// // Cosets can be (element-wise) raised to a power of 2, either maintaining
33/// // the shift and raising only the subgroup, or raising both.
34/// let coset_shrunk_subgroup = coset.shrink_coset(2).unwrap();
35/// assert_eq!(
36///     coset_shrunk_subgroup.subgroup_generator(),
37///     coset.subgroup_generator().exp_power_of_2(2),
38/// );
39/// assert_eq!(
40///     coset_shrunk_subgroup.shift(),
41///     coset.shift()
42/// );
43///
44/// let coset_power = coset.exp_power_of_2(2).unwrap();
45/// assert_eq!(
46///     coset_power.subgroup_generator(),
47///     coset.subgroup_generator().exp_power_of_2(2),
48/// );
49/// assert_eq!(
50///     coset_power.shift(),
51///     coset.shift().exp_power_of_2(2),
52/// );
53/// ```
54#[derive(Clone, Copy, Debug)]
55pub struct TwoAdicMultiplicativeCoset<F: TwoAdicField> {
56    // Letting s = shift, and g = generator (of order 2^log_size), the coset in
57    // question is
58    //     s * <g> = {s, s * g, shift * g^2, ..., s * g^(2^log_size - 1)]
59    shift: F,
60    shift_inverse: F,
61    log_size: usize,
62}
63
64impl<F: TwoAdicField> TwoAdicMultiplicativeCoset<F> {
65    /// Returns the coset `shift * <generator>`, where `generator` is a
66    /// canonical (i. e. fixed in the implementation of `F: TwoAdicField`)
67    /// generator of the unique subgroup of the units of `F` of order `2 ^
68    /// log_size`. Returns `None` if `log_size > F::TWO_ADICITY` or if `shift` is zero.
69    ///
70    /// # Arguments
71    ///
72    ///  - `shift`: the value by which the subgroup is (multiplicatively)
73    ///    shifted
74    ///  - `log_size`: the size of the subgroup (and hence of the coset) is `2 ^
75    ///    log_size`. This determines the subgroup uniquely.
76    #[must_use]
77    pub fn new(shift: F, log_size: usize) -> Option<Self> {
78        (shift != F::ZERO && log_size <= F::TWO_ADICITY).then(|| {
79            let shift_inverse = shift.inverse();
80            Self {
81                shift,
82                shift_inverse,
83                log_size,
84            }
85        })
86    }
87
88    /// Returns the generator of the subgroup of order `self.size()`.
89    #[inline]
90    #[must_use]
91    pub fn subgroup_generator(&self) -> F {
92        F::two_adic_generator(self.log_size)
93    }
94
95    /// Returns the shift of the coset.
96    #[inline]
97    #[must_use]
98    pub const fn shift(&self) -> F {
99        self.shift
100    }
101
102    /// Returns the inverse of the coset shift.
103    #[inline]
104    #[must_use]
105    pub const fn shift_inverse(&self) -> F {
106        self.shift_inverse
107    }
108
109    /// Returns the log2 of the size of the coset.
110    #[inline]
111    #[must_use]
112    pub const fn log_size(&self) -> usize {
113        self.log_size
114    }
115
116    /// Returns the size of the coset.
117    #[inline]
118    #[must_use]
119    pub const fn size(&self) -> usize {
120        1 << self.log_size
121    }
122
123    /// Returns a new coset with its subgroup reduced by a factor of
124    /// `2^log_scale_factor` in size (i. e. with generator equal to the
125    /// `2^log_scale_factor`-th power of that of the original coset), leaving
126    /// the shift untouched. Note that new coset is contained in the original one.
127    /// Returns `None` if `log_scale_factor` is greater than `self.log_size()`.
128    #[inline]
129    #[must_use]
130    pub fn shrink_coset(&self, log_scale_factor: usize) -> Option<Self> {
131        self.log_size
132            .checked_sub(log_scale_factor)
133            .map(|new_log_size| Self {
134                shift: self.shift,
135                shift_inverse: self.shift_inverse,
136                log_size: new_log_size,
137            })
138    }
139
140    /// Returns the coset `self^(2^log_scale_factor)` (i. e. with shift and
141    /// subgroup generator equal to the `2^log_scale_factor`-th power of the
142    /// original ones). Returns `None` if `log_scale_factor` is greater than `self.log_size()`.
143    #[inline]
144    #[must_use]
145    pub fn exp_power_of_2(&self, log_scale_factor: usize) -> Option<Self> {
146        self.shrink_coset(log_scale_factor).map(|mut coset| {
147            coset.shift = self.shift.exp_power_of_2(log_scale_factor);
148            coset.shift_inverse = self.shift_inverse.exp_power_of_2(log_scale_factor);
149            coset
150        })
151    }
152
153    /// Returns a new coset of the same size whose shift is equal to `scale * self.shift`.
154    #[inline]
155    #[must_use]
156    pub fn shift_by(&self, scale: F) -> Self {
157        let shift = self.shift * scale;
158        Self {
159            shift,
160            shift_inverse: shift.inverse(),
161            log_size: self.log_size,
162        }
163    }
164
165    /// Returns a new coset where the shift has been set to `shift`
166    #[inline]
167    #[must_use]
168    pub fn set_shift(&self, shift: F) -> Self {
169        Self {
170            shift,
171            shift_inverse: shift.inverse(),
172            log_size: self.log_size,
173        }
174    }
175
176    /// Checks if the given field element is in the coset
177    #[inline]
178    #[must_use]
179    pub fn contains(&self, element: F) -> bool {
180        // Note that, in a finite field F (this is not true of a general finite
181        // commutative ring), there is exactly one subgroup of |F^*| of order n
182        // for each divisor n of |F| - 1, and its elements e are uniquely
183        // characterised by the condition e^n = 1.
184
185        // We check (shift^{-1} * element)^(2^log_size) = 1, which is equivalent
186        // to checking shift^(2^log_size) = element^(2^log_size) - this avoids
187        // inversion at the cost of a few squarings. The loop terminates early
188        // if possible.
189        let (mut shift, mut element) = (self.shift, element);
190
191        for _ in 0..self.log_size {
192            if element == shift {
193                return true;
194            }
195            element = element.square();
196            shift = shift.square();
197        }
198
199        element == shift
200    }
201
202    /// Returns the element `shift * generator^index`, which is the `index %
203    /// self.size()`-th element of `self` (and, in particular, the `index`-th
204    /// element of `self` whenever `index` < self.size()).
205    #[inline]
206    #[must_use]
207    pub fn element(&mut self, index: usize) -> F {
208        self.shift * self.generator_exp(index)
209    }
210
211    // Internal function which computes `generator^exp`. It uses the
212    // square-and-multiply algorithm with the caveat that squares of the
213    // generator are queried from the field (which typically should have them
214    // stored), i. e. rather "fetch-and-multiply".
215    #[inline]
216    #[must_use]
217    fn generator_exp(&self, exp: usize) -> F {
218        let mut gen_power = F::ONE;
219        // As `generator` satisfies `generator^{self.size()} == 1` we can replace `exp` by `exp mod self.size()`.
220        // As `self.size()` is a power of `2` this can be done with an `&` instead of a `%`.
221        let mut exp = exp & (self.size() - 1);
222        let mut i = self.log_size();
223
224        while exp > 0 {
225            if exp & 1 != 0 {
226                gen_power *= F::two_adic_generator(i);
227            }
228            exp >>= 1;
229
230            i -= 1;
231        }
232
233        gen_power
234    }
235
236    /// Returns an iterator over the elements of the coset in the canonical order
237    /// `shift * generator^0, shift * generator^1, ...,
238    /// shift * generator^(2^log_size - 1)`.
239    #[inline]
240    #[must_use]
241    pub fn iter(&self) -> BoundedPowers<F> {
242        self.subgroup_generator()
243            .shifted_powers(self.shift)
244            .take(self.size())
245    }
246}
247
248impl<F: TwoAdicField> IntoIterator for TwoAdicMultiplicativeCoset<F> {
249    type Item = F;
250    type IntoIter = BoundedPowers<F>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        self.iter()
254    }
255}
256
257impl<F: TwoAdicField> IntoIterator for &TwoAdicMultiplicativeCoset<F> {
258    type Item = F;
259    type IntoIter = BoundedPowers<F>;
260
261    fn into_iter(self) -> Self::IntoIter {
262        self.iter()
263    }
264}