competitive/math/
lcm_convolve.rs1use super::{ConvolveSteps, Group, Invertible, Monoid, Ring, with_prime_list};
2use std::marker::PhantomData;
3
4pub struct LcmConvolve<M> {
5 _marker: PhantomData<fn() -> M>,
6}
7
8impl<M> LcmConvolve<M>
9where
10 M: Monoid,
11{
12 pub fn zeta_transform(f: &mut [M::T]) {
14 let n = f.len().saturating_sub(1) as u64;
15 with_prime_list(n, |pl| {
16 for &p in pl.primes_lte(n).iter() {
17 for (i, j) in (0..f.len()).step_by(p as _).enumerate() {
18 f[j] = M::operate(&f[j], &f[i]);
19 }
20 }
21 })
22 }
23}
24
25impl<G> LcmConvolve<G>
26where
27 G: Group,
28{
29 pub fn mobius_transform(f: &mut [G::T]) {
31 let n = f.len().saturating_sub(1) as u64;
32 with_prime_list(n, |pl| {
33 for &p in pl.primes_lte(n).iter() {
34 for (i, j) in (0..f.len()).step_by(p as _).enumerate().rev() {
35 f[j] = G::rinv_operate(&f[j], &f[i]);
36 }
37 }
38 })
39 }
40}
41
42impl<R> ConvolveSteps for LcmConvolve<R>
43where
44 R: Ring,
45 R::Additive: Invertible,
46{
47 type T = Vec<R::T>;
48 type F = Vec<R::T>;
49
50 fn length(t: &Self::T) -> usize {
51 t.len()
52 }
53
54 fn transform(mut t: Self::T, _len: usize) -> Self::F {
55 LcmConvolve::<R::Additive>::zeta_transform(&mut t);
56 t
57 }
58
59 fn inverse_transform(mut f: Self::F, _len: usize) -> Self::T {
60 LcmConvolve::<R::Additive>::mobius_transform(&mut f);
61 f
62 }
63
64 fn multiply(f: &mut Self::F, g: &Self::F) {
65 for (f, g) in f.iter_mut().zip(g) {
66 *f = R::mul(f, g);
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::{
75 algebra::{AddMulOperation, AdditiveOperation},
76 math::lcm,
77 rand,
78 tools::Xorshift,
79 };
80
81 const A: i64 = 100_000;
82
83 #[test]
84 fn test_lcm_convolve() {
85 let mut rng = Xorshift::new();
86
87 for m in 1..=300 {
88 rand!(rng, mut f: [-A..A; m]);
89 f[0] = 0;
90 let mut g = vec![0i64; m];
91 let h = f.clone();
92 for (s, f) in f.iter().enumerate().skip(1) {
93 for (t, g) in g.iter_mut().enumerate().skip(1) {
94 if t % s == 0 {
95 *g += f;
96 }
97 }
98 }
99 LcmConvolve::<AdditiveOperation<i64>>::zeta_transform(&mut f);
100 assert_eq!(&f[1..], &g[1..]);
101 LcmConvolve::<AdditiveOperation<i64>>::mobius_transform(&mut f);
102 assert_eq!(&f[1..], &h[1..]);
103
104 rand!(rng, mut f: [-A..A; m], mut g: [-A..A; m]);
105 f[0] = 0;
106 g[0] = 0;
107 let mut h = vec![0i64; m];
108 for (i, f) in f.iter().enumerate().skip(1) {
109 for (j, g) in g.iter().enumerate().skip(1) {
110 let k = lcm(i as _, j as _) as usize;
111 if k < m {
112 h[k] += f * g;
113 }
114 }
115 }
116 let i = LcmConvolve::<AddMulOperation<i64>>::convolve(f, g);
117 assert_eq!(&h[1..], &i[1..]);
118 }
119 }
120}