p3_field/
coset.rs

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