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}