arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/math/src/dft/ntt.rs
math/src/dft/ntt.rsRaw
  1use crate::dft::DFT;
  2use crate::modulus::barrett::Barrett;
  3use crate::modulus::montgomery::Montgomery;
  4use crate::modulus::prime::Prime;
  5use crate::modulus::ReduceOnce;
  6use crate::modulus::WordOps;
  7use crate::modulus::{BARRETT, NONE, ONCE};
  8use itertools::izip;
  9
 10#[allow(dead_code)]
 11pub struct Table<O> {
 12    prime: Prime<O>,
 13    psi: O,
 14    psi_forward_rev: Vec<Barrett<u64>>,
 15    psi_backward_rev: Vec<Barrett<u64>>,
 16    q: O,
 17    two_q: O,
 18    four_q: O,
 19}
 20
 21impl Table<u64> {
 22    pub fn new(prime: Prime<u64>, nth_root: u64) -> Table<u64> {
 23        assert!(
 24            nth_root & (nth_root - 1) == 0,
 25            "invalid argument: nth_root = {} is not a power of two",
 26            nth_root
 27        );
 28
 29        let psi: u64 = prime.primitive_nth_root(nth_root);
 30
 31        let psi_mont: Montgomery<u64> = prime.montgomery.prepare::<ONCE>(psi);
 32        let psi_inv_mont: Montgomery<u64> = prime.montgomery.pow(psi_mont, prime.phi - 1);
 33
 34        let mut psi_forward_rev: Vec<Barrett<u64>> = vec![Barrett(0, 0); (nth_root >> 1) as usize];
 35        let mut psi_backward_rev: Vec<Barrett<u64>> = vec![Barrett(0, 0); (nth_root >> 1) as usize];
 36
 37        psi_forward_rev[0] = prime.barrett.prepare(1);
 38        psi_backward_rev[0] = prime.barrett.prepare(1);
 39
 40        let log_nth_root_half: u32 = (nth_root >> 1).log2() as _;
 41
 42        let mut powers_forward: u64 = 1u64;
 43        let mut powers_backward: u64 = 1u64;
 44
 45        for i in 1..(nth_root >> 1) as usize {
 46            let i_rev: usize = i.reverse_bits_msb(log_nth_root_half);
 47
 48            prime
 49                .montgomery
 50                .mul_external_assign::<ONCE>(psi_mont, &mut powers_forward);
 51            prime
 52                .montgomery
 53                .mul_external_assign::<ONCE>(psi_inv_mont, &mut powers_backward);
 54
 55            psi_forward_rev[i_rev] = prime.barrett.prepare(powers_forward);
 56            psi_backward_rev[i_rev] = prime.barrett.prepare(powers_backward);
 57        }
 58
 59        let q: u64 = prime.q();
 60
 61        Self {
 62            prime: prime,
 63            psi: psi,
 64            psi_forward_rev: psi_forward_rev,
 65            psi_backward_rev: psi_backward_rev,
 66            q: q,
 67            two_q: q << 1,
 68            four_q: q << 2,
 69        }
 70    }
 71}
 72
 73impl DFT<u64> for Table<u64> {
 74    fn forward_inplace(&self, a: &mut [u64]) {
 75        self.forward_inplace::<false>(a)
 76    }
 77
 78    fn forward_inplace_lazy(&self, a: &mut [u64]) {
 79        self.forward_inplace::<true>(a)
 80    }
 81
 82    fn backward_inplace(&self, a: &mut [u64]) {
 83        self.backward_inplace::<false>(a)
 84    }
 85
 86    fn backward_inplace_lazy(&self, a: &mut [u64]) {
 87        self.backward_inplace::<true>(a)
 88    }
 89}
 90
 91impl Table<u64> {
 92    pub fn forward_inplace<const LAZY: bool>(&self, a: &mut [u64]) {
 93        self.forward_inplace_core::<LAZY, 0, 0>(a);
 94    }
 95
 96    pub fn forward_inplace_core<const LAZY: bool, const SKIPSTART: u8, const SKIPEND: u8>(
 97        &self,
 98        a: &mut [u64],
 99    ) {
100        let n: usize = a.len();
101        assert!(
102            n & n - 1 == 0,
103            "invalid x.len()= {} must be a power of two",
104            n
105        );
106        let log_n: u32 = usize::BITS - ((n as usize) - 1).leading_zeros();
107
108        let start: u32 = SKIPSTART as u32;
109        let end: u32 = log_n - (SKIPEND as u32);
110
111        for layer in start..end {
112            let (m, size) = (1 << layer, 1 << (log_n - layer - 1));
113            let t: usize = 2 * size;
114            if layer == log_n - 1 {
115                if LAZY {
116                    izip!(a.chunks_exact_mut(t), &self.psi_forward_rev[m..]).for_each(
117                        |(a, psi)| {
118                            let (a, b) = a.split_at_mut(size);
119                            self.dit_inplace::<false>(&mut a[0], &mut b[0], *psi);
120                            debug_assert!(
121                                a[0] < self.two_q,
122                                "forward_inplace_core::<LAZY=true> output {} > {} (2q-1)",
123                                a[0],
124                                self.two_q - 1
125                            );
126                            debug_assert!(
127                                b[0] < self.two_q,
128                                "forward_inplace_core::<LAZY=true> output {} > {} (2q-1)",
129                                b[0],
130                                self.two_q - 1
131                            );
132                        },
133                    );
134                } else {
135                    izip!(a.chunks_exact_mut(t), &self.psi_forward_rev[m..]).for_each(
136                        |(a, psi)| {
137                            let (a, b) = a.split_at_mut(size);
138                            self.dit_inplace::<true>(&mut a[0], &mut b[0], *psi);
139                            self.prime.barrett.reduce_assign::<BARRETT>(&mut a[0]);
140                            self.prime.barrett.reduce_assign::<BARRETT>(&mut b[0]);
141                            debug_assert!(
142                                a[0] < self.q,
143                                "forward_inplace_core::<LAZY=false> output {} > {} (q-1)",
144                                a[0],
145                                self.q - 1
146                            );
147                            debug_assert!(
148                                b[0] < self.q,
149                                "forward_inplace_core::<LAZY=false> output {} > {} (q-1)",
150                                b[0],
151                                self.q - 1
152                            );
153                        },
154                    );
155                }
156            } else if t >= 16 {
157                izip!(a.chunks_exact_mut(t), &self.psi_forward_rev[m..]).for_each(|(a, psi)| {
158                    let (a, b) = a.split_at_mut(size);
159                    izip!(a.chunks_exact_mut(8), b.chunks_exact_mut(8)).for_each(|(a, b)| {
160                        self.dit_inplace::<true>(&mut a[0], &mut b[0], *psi);
161                        self.dit_inplace::<true>(&mut a[1], &mut b[1], *psi);
162                        self.dit_inplace::<true>(&mut a[2], &mut b[2], *psi);
163                        self.dit_inplace::<true>(&mut a[3], &mut b[3], *psi);
164                        self.dit_inplace::<true>(&mut a[4], &mut b[4], *psi);
165                        self.dit_inplace::<true>(&mut a[5], &mut b[5], *psi);
166                        self.dit_inplace::<true>(&mut a[6], &mut b[6], *psi);
167                        self.dit_inplace::<true>(&mut a[7], &mut b[7], *psi);
168                    });
169                });
170            } else {
171                izip!(a.chunks_exact_mut(t), &self.psi_forward_rev[m..]).for_each(|(a, psi)| {
172                    let (a, b) = a.split_at_mut(size);
173                    izip!(a, b).for_each(|(a, b)| self.dit_inplace::<true>(a, b, *psi));
174                });
175            }
176        }
177    }
178
179    #[inline(always)]
180    fn dit_inplace<const LAZY: bool>(&self, a: &mut u64, b: &mut u64, t: Barrett<u64>) {
181        debug_assert!(*a < self.four_q, "a:{} q:{}", a, self.four_q);
182        debug_assert!(*b < self.four_q, "b:{} q:{}", b, self.four_q);
183        a.reduce_once_assign(self.two_q);
184        let bt: u64 = self.prime.barrett.mul_external::<NONE>(t, *b);
185        *b = *a + self.two_q - bt;
186        *a += bt;
187        if !LAZY {
188            a.reduce_once_assign(self.two_q);
189            b.reduce_once_assign(self.two_q);
190        }
191    }
192
193    pub fn backward_inplace<const LAZY: bool>(&self, a: &mut [u64]) {
194        self.backward_inplace_core::<LAZY, 0, 0>(a);
195    }
196
197    pub fn backward_inplace_core<const LAZY: bool, const SKIPSTART: u8, const SKIPEND: u8>(
198        &self,
199        a: &mut [u64],
200    ) {
201        let n: usize = a.len();
202        assert!(
203            n & n - 1 == 0,
204            "invalid x.len()= {} must be a power of two",
205            n
206        );
207        let log_n = usize::BITS - ((n as usize) - 1).leading_zeros();
208
209        let start: u32 = SKIPEND as u32;
210        let end: u32 = log_n - (SKIPSTART as u32);
211
212        for layer in (start..end).rev() {
213            let (m, size) = (1 << layer, 1 << (log_n - layer - 1));
214            let t: usize = 2 * size;
215            if layer == 0 {
216                let n_inv: Barrett<u64> = self.prime.barrett.prepare(self.prime.inv(n as u64));
217                let psi: Barrett<u64> = self.prime.barrett.prepare(
218                    self.prime
219                        .barrett
220                        .mul_external::<ONCE>(n_inv, self.psi_backward_rev[1].0),
221                );
222
223                izip!(a.chunks_exact_mut(2 * size)).for_each(|a| {
224                    let (a, b) = a.split_at_mut(size);
225                    izip!(a.chunks_exact_mut(8), b.chunks_exact_mut(8)).for_each(|(a, b)| {
226                        self.dif_last_inplace::<LAZY>(&mut a[0], &mut b[0], psi, n_inv);
227                        self.dif_last_inplace::<LAZY>(&mut a[1], &mut b[1], psi, n_inv);
228                        self.dif_last_inplace::<LAZY>(&mut a[2], &mut b[2], psi, n_inv);
229                        self.dif_last_inplace::<LAZY>(&mut a[3], &mut b[3], psi, n_inv);
230                        self.dif_last_inplace::<LAZY>(&mut a[4], &mut b[4], psi, n_inv);
231                        self.dif_last_inplace::<LAZY>(&mut a[5], &mut b[5], psi, n_inv);
232                        self.dif_last_inplace::<LAZY>(&mut a[6], &mut b[6], psi, n_inv);
233                        self.dif_last_inplace::<LAZY>(&mut a[7], &mut b[7], psi, n_inv);
234                    });
235                });
236            } else if t >= 16 {
237                izip!(a.chunks_exact_mut(t), &self.psi_backward_rev[m..]).for_each(|(a, psi)| {
238                    let (a, b) = a.split_at_mut(size);
239                    izip!(a.chunks_exact_mut(8), b.chunks_exact_mut(8)).for_each(|(a, b)| {
240                        self.dif_inplace::<true>(&mut a[0], &mut b[0], *psi);
241                        self.dif_inplace::<true>(&mut a[1], &mut b[1], *psi);
242                        self.dif_inplace::<true>(&mut a[2], &mut b[2], *psi);
243                        self.dif_inplace::<true>(&mut a[3], &mut b[3], *psi);
244                        self.dif_inplace::<true>(&mut a[4], &mut b[4], *psi);
245                        self.dif_inplace::<true>(&mut a[5], &mut b[5], *psi);
246                        self.dif_inplace::<true>(&mut a[6], &mut b[6], *psi);
247                        self.dif_inplace::<true>(&mut a[7], &mut b[7], *psi);
248                    });
249                });
250            } else {
251                izip!(a.chunks_exact_mut(2 * size), &self.psi_backward_rev[m..]).for_each(
252                    |(a, psi)| {
253                        let (a, b) = a.split_at_mut(size);
254                        izip!(a, b).for_each(|(a, b)| self.dif_inplace::<true>(a, b, *psi));
255                    },
256                );
257            }
258        }
259    }
260
261    #[inline(always)]
262    fn dif_inplace<const LAZY: bool>(&self, a: &mut u64, b: &mut u64, t: Barrett<u64>) {
263        debug_assert!(*a < self.two_q, "a:{} q:{}", a, self.two_q);
264        debug_assert!(*b < self.two_q, "b:{} q:{}", b, self.two_q);
265        let d: u64 = self
266            .prime
267            .barrett
268            .mul_external::<NONE>(t, *a + self.two_q - *b);
269        *a = *a + *b;
270        a.reduce_once_assign(self.two_q);
271        *b = d;
272        if !LAZY {
273            a.reduce_once_assign(self.q);
274            b.reduce_once_assign(self.q);
275        }
276    }
277
278    fn dif_last_inplace<const LAZY: bool>(
279        &self,
280        a: &mut u64,
281        b: &mut u64,
282        psi: Barrett<u64>,
283        n_inv: Barrett<u64>,
284    ) {
285        debug_assert!(*a < self.two_q);
286        debug_assert!(*b < self.two_q);
287        if LAZY {
288            let d: u64 = self
289                .prime
290                .barrett
291                .mul_external::<NONE>(psi, *a + self.two_q - *b);
292            *a = self.prime.barrett.mul_external::<NONE>(n_inv, *a + *b);
293            *b = d;
294        } else {
295            let d: u64 = self
296                .prime
297                .barrett
298                .mul_external::<ONCE>(psi, *a + self.two_q - *b);
299            *a = self.prime.barrett.mul_external::<ONCE>(n_inv, *a + *b);
300            *b = d;
301        }
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn test_ntt() {
311        let q_base: u64 = 0x800000000004001;
312        let q_power: usize = 1;
313        let prime_instance: Prime<u64> = Prime::<u64>::new(q_base, q_power);
314        let n: u64 = 32;
315        let two_nth_root: u64 = n << 1;
316        let ntt_table: Table<u64> = Table::<u64>::new(prime_instance, two_nth_root);
317        let mut a: Vec<u64> = vec![0; n as usize];
318        for i in 0..a.len() {
319            a[i] = i as u64;
320        }
321
322        let b: Vec<u64> = a.clone();
323        ntt_table.forward_inplace::<false>(&mut a);
324        ntt_table.backward_inplace::<false>(&mut a);
325        assert!(a == b);
326    }
327}