1use super::{
2 AddMulOperation, Associative, BarrettReduction, Group, Invertible, Magma, Monoid, One, Ring,
3 SemiRing, Unital, Wrapping, Zero, array,
4};
5use std::{
6 marker::PhantomData,
7 mem::swap,
8 ops::{Add, Mul, Range},
9};
10
11fn choose2(n: Wrapping<u64>) -> Wrapping<u64> {
12 if n.0.is_multiple_of(2) {
13 n / 2 * (n - 1)
14 } else {
15 (n - 1) / 2 * n
16 }
17}
18
19pub fn floor_sum(n: u64, a: u64, b: u64, m: u64) -> u64 {
23 let mut ans = Wrapping(0u64);
24 let (mut n, mut m, mut a, mut b) = (Wrapping(n), m, a, b);
25 loop {
26 let br = BarrettReduction::<u64>::new(m);
27 if a >= m {
28 let (q, r) = br.div_rem(a);
29 ans += choose2(n) * q;
30 a = r;
31 }
32 if b >= m {
33 let (q, r) = br.div_rem(b);
34 ans += n * q;
35 b = r;
36 }
37 let y_max = (n * a + b).0;
38 if y_max < m {
39 break;
40 }
41 let (q, r) = br.div_rem(y_max);
42 n = Wrapping(q);
43 b = r;
44 swap(&mut m, &mut a);
45 }
46 ans.0
47}
48
49pub fn floor_sum_i64(l: i64, r: i64, a: i64, b: i64, m: u64) -> i64 {
53 let mut ans = Wrapping(0i64);
54 let (n, m, a, b) = (
55 Wrapping((r - l) as u64),
56 m as i64,
57 a,
58 (Wrapping(l) * a + b).0,
59 );
60 let a = if a < 0 {
61 let r = a.rem_euclid(m);
62 let nc2 = choose2(n);
63 ans -= Wrapping(nc2.0 as _) * ((Wrapping(r) - a) / m);
64 r
65 } else {
66 a
67 };
68 let b = if b < 0 {
69 let r = b.rem_euclid(m);
70 ans -= Wrapping(n.0 as _) * ((Wrapping(r) - b) / m);
71 r
72 } else {
73 b
74 };
75 (ans + floor_sum(n.0, a as u64, b as u64, m as u64) as i64).0
76}
77
78pub fn floor_sum_range_freq(l: i64, r: i64, a: i64, b: i64, m: u64, range: Range<i64>) -> i64 {
79 if range.start >= range.end {
80 return 0;
81 }
82 assert!(0 <= range.start && range.end <= m as i64);
83 let x1 = floor_sum_i64(l, r, a, b - range.start, m);
84 let x2 = floor_sum_i64(l, r, a, b - range.end, m);
85 x1 - x2
86}
87
88struct FloorSum<R, const X: usize, const Y: usize>
89where
90 R: SemiRing,
91{
92 _marker: PhantomData<fn() -> R>,
93}
94
95#[derive(Debug)]
96struct FloorSumData<R, const X: usize, const Y: usize>
97where
98 R: SemiRing,
99{
100 dp: [[R::T; Y]; X],
101 dx: R::T,
102 dy: R::T,
103 _marker: PhantomData<fn() -> R>,
104}
105
106impl<R, const X: usize, const Y: usize> Clone for FloorSumData<R, X, Y>
107where
108 R: SemiRing,
109{
110 fn clone(&self) -> Self {
111 Self {
112 dp: self.dp.clone(),
113 dx: self.dx.clone(),
114 dy: self.dy.clone(),
115 _marker: self._marker,
116 }
117 }
118}
119
120impl<R, const X: usize, const Y: usize> FloorSum<R, X, Y>
121where
122 R: SemiRing,
123{
124 fn to_x() -> FloorSumData<R, X, Y> {
125 let mut dp = array![array![R::zero(); Y]; X];
126 dp[0][0] = R::one();
127 FloorSumData {
128 dp,
129 dx: R::one(),
130 dy: R::zero(),
131 _marker: PhantomData,
132 }
133 }
134 fn to_y() -> FloorSumData<R, X, Y> {
135 FloorSumData {
136 dp: array![array![R::zero(); Y]; X],
137 dx: R::zero(),
138 dy: R::one(),
139 _marker: PhantomData,
140 }
141 }
142}
143
144impl<R, const X: usize, const Y: usize> FloorSum<R, X, Y>
145where
146 R: Ring<Additive: Invertible>,
147{
148 fn offset(x: i64, y: i64) -> FloorSumData<R, X, Y> {
149 FloorSumData {
150 dp: array![array![R::zero(); Y]; X],
151 dx: R::Additive::signed_pow(R::one(), x),
152 dy: R::Additive::signed_pow(R::one(), y),
153 _marker: PhantomData,
154 }
155 }
156}
157
158impl<R, const X: usize, const Y: usize> Magma for FloorSum<R, X, Y>
159where
160 R: SemiRing,
161{
162 type T = FloorSumData<R, X, Y>;
163
164 fn operate(a: &Self::T, b: &Self::T) -> Self::T {
165 let mut a = a.clone();
166 let mut b = b.clone();
167 let mut pow_x = array![R::zero(); X];
168 let mut pow_y = array![R::zero(); Y];
169 pow_x[0] = R::one();
170 pow_y[0] = R::one();
171 for i in 1..X {
172 pow_x[i] = R::mul(&pow_x[i - 1], &a.dx);
173 }
174 for j in 1..Y {
175 pow_y[j] = R::mul(&pow_y[j - 1], &a.dy);
176 }
177 macro_rules! go {
178 ($N:ident) => {
179 let mut comb = array![array![R::zero(); $N]; $N];
180 comb[0][0] = R::one();
181 let mut i = 0;
182 while i + 1 < $N {
183 let mut j = 0;
184 while j <= i {
185 let x = comb[i][j].clone();
186 R::add_assign(&mut comb[i + 1][j], &x);
187 R::add_assign(&mut comb[i + 1][j + 1], &x);
188 j += 1;
189 }
190 i += 1;
191 }
192 for i in 0..X {
193 for j in (0..Y).rev() {
194 for k in j + 1..Y {
195 let mut x = b.dp[i][j].clone();
196 R::mul_assign(&mut x, &comb[k][j]);
197 R::mul_assign(&mut x, &pow_y[k - j]);
198 R::add_assign(&mut b.dp[i][k], &x);
199 }
200 }
201 }
202 for j in 0..Y {
203 for i in (0..X).rev() {
204 for k in i..X {
205 let mut x = b.dp[i][j].clone();
206 R::mul_assign(&mut x, &comb[k][i]);
207 R::mul_assign(&mut x, &pow_x[k - i]);
208 R::add_assign(&mut a.dp[k][j], &x);
209 }
210 }
211 }
212 };
213 }
214 if X <= Y {
215 go!(Y);
216 } else {
217 go!(X);
218 }
219 R::add_assign(&mut a.dx, &b.dx);
220 R::add_assign(&mut a.dy, &b.dy);
221 a
222 }
223}
224
225impl<R, const X: usize, const Y: usize> Unital for FloorSum<R, X, Y>
226where
227 R: SemiRing,
228{
229 fn unit() -> Self::T {
230 FloorSumData {
231 dp: array![array![R::zero(); Y]; X],
232 dx: R::zero(),
233 dy: R::zero(),
234 _marker: PhantomData,
235 }
236 }
237}
238
239impl<R, const X: usize, const Y: usize> Associative for FloorSum<R, X, Y> where R: SemiRing {}
240
241fn floor_monoid_product<M>(
242 mut x: M::T,
243 mut y: M::T,
244 mut n: u64,
245 mut a: u64,
246 mut b: u64,
247 mut m: u64,
248) -> M::T
249where
250 M: Monoid,
251{
252 let mut c = (a * n + b) / m;
253 let mut pre = M::unit();
254 let mut suf = M::unit();
255 loop {
256 let (p, q) = (a / m, b / m);
257 a %= m;
258 b %= m;
259 x = M::operate(&x, &M::pow(y.clone(), p));
260 pre = M::operate(&pre, &M::pow(y.clone(), q));
261 c -= p * n + q;
262 if c == 0 {
263 break;
264 }
265 let d = (m * c - b - 1) / a + 1;
266 suf = M::operate(&y, &M::operate(&M::pow(x.clone(), n - d), &suf));
267 b = m - b - 1 + a;
268 n = c - 1;
269 c = d;
270 swap(&mut m, &mut a);
271 swap(&mut x, &mut y);
272 }
273 x = M::pow(x.clone(), n);
274 M::operate(&M::operate(&pre, &x), &suf)
275}
276
277pub fn floor_sum_polynomial<T, const X: usize, const Y: usize>(
279 n: u64,
280 a: u64,
281 b: u64,
282 m: u64,
283) -> [[T; Y]; X]
284where
285 T: Clone + Zero + One + Add<Output = T> + Mul<Output = T>,
286{
287 debug_assert!(a == 0 || n < (u64::MAX - b) / a);
288 floor_monoid_product::<FloorSum<AddMulOperation<T>, X, Y>>(
289 FloorSum::<AddMulOperation<T>, X, Y>::to_x(),
290 FloorSum::<AddMulOperation<T>, X, Y>::to_y(),
291 n,
292 a,
293 b,
294 m,
295 )
296 .dp
297}
298
299pub fn floor_sum_polynomial_i64<T, const X: usize, const Y: usize>(
301 l: i64,
302 r: i64,
303 a: i64,
304 b: i64,
305 m: u64,
306) -> [[T; Y]; X]
307where
308 T: Clone + Zero + One + Add<Output = T> + Mul<Output = T>,
309 AddMulOperation<T>: SemiRing<T = T, Additive: Invertible>,
310{
311 assert!(l <= r);
312 assert!(m > 0);
313
314 if a < 0 {
315 let mut ans = floor_sum_polynomial_i64::<T, X, Y>(-r + 1, -l + 1, -a, b, m);
316 for ans in ans.iter_mut().skip(1).step_by(2) {
317 for ans in ans.iter_mut() {
318 *ans = AddMulOperation::<T>::neg(ans);
319 }
320 }
321 return ans;
322 }
323
324 let add_x = l;
325 let n = (r - l) as u64;
326 let b = a * add_x + b;
327
328 let add_y = b.div_euclid(m as i64);
329 let b = b.rem_euclid(m as i64);
330 assert!(a >= 0);
331 assert!(b >= 0);
332 let data = floor_monoid_product::<FloorSum<AddMulOperation<T>, X, Y>>(
333 FloorSum::<AddMulOperation<T>, X, Y>::to_x(),
334 FloorSum::<AddMulOperation<T>, X, Y>::to_y(),
335 n,
336 a as u64,
337 b as u64,
338 m,
339 );
340
341 let offset = FloorSum::<AddMulOperation<T>, X, Y>::offset(add_x, add_y);
342 FloorSum::<AddMulOperation<T>, X, Y>::operate(&offset, &data).dp
343}
344
345#[derive(Debug)]
346struct FloorPowerSum<R>
347where
348 R: SemiRing,
349{
350 x: R::T,
351 sum: R::T,
352}
353
354impl<R> Clone for FloorPowerSum<R>
355where
356 R: SemiRing,
357{
358 fn clone(&self) -> Self {
359 Self {
360 x: self.x.clone(),
361 sum: self.sum.clone(),
362 }
363 }
364}
365
366impl<R> FloorPowerSum<R>
367where
368 R: SemiRing,
369{
370 fn to_x(x: R::T) -> Self {
371 Self { x, sum: R::one() }
372 }
373 fn to_y(y: R::T) -> Self {
374 Self {
375 x: y,
376 sum: R::zero(),
377 }
378 }
379}
380
381impl<R> Magma for FloorPowerSum<R>
382where
383 R: SemiRing,
384{
385 type T = Self;
386
387 fn operate(a: &Self::T, b: &Self::T) -> Self::T {
388 Self {
389 x: R::mul(&a.x, &b.x),
390 sum: R::add(&a.sum, &R::mul(&a.x, &b.sum)),
391 }
392 }
393}
394
395impl<R> Unital for FloorPowerSum<R>
396where
397 R: SemiRing,
398{
399 fn unit() -> Self::T {
400 Self {
401 x: R::one(),
402 sum: R::zero(),
403 }
404 }
405}
406
407impl<R> Associative for FloorPowerSum<R> where R: SemiRing {}
408
409pub fn floor_power_sum<R>(x: R::T, y: R::T, n: u64, a: u64, b: u64, m: u64) -> R::T
411where
412 R: SemiRing,
413{
414 floor_monoid_product::<FloorPowerSum<R>>(
415 FloorPowerSum::<R>::to_x(x),
416 FloorPowerSum::<R>::to_y(y),
417 n,
418 a,
419 b,
420 m,
421 )
422 .sum
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use crate::{num::mint_basic::MInt998244353, tools::Xorshift};
429
430 #[test]
431 fn test_floor_sum() {
432 const A: u64 = 1_000;
433 const B: i64 = 1_000;
434 const Q: usize = 1_000;
435 let mut rng = Xorshift::default();
436 for _ in 0..Q {
437 let (n, a, b, m) = rng.random((..A, ..A, ..A, 1..A));
438 let expected: u64 = (0..n).map(|i| (a * i + b) / m).sum();
439 let result = floor_sum(n, a, b, m);
440 assert_eq!(expected, result);
441
442 let (mut l, mut r, a, b) = rng.random((-B..B, -B..B, -B..B, -B..B));
443 if l > r {
444 swap(&mut l, &mut r);
445 }
446 let expected: i64 = (l..r).map(|i| (a * i + b).div_euclid(m as i64)).sum();
447 let result = floor_sum_i64(l, r, a, b, m);
448 assert_eq!(expected, result);
449
450 let (mut lv, mut rv) = rng.random((0..m as i64, 0..m as i64));
451 if lv > rv {
452 swap(&mut lv, &mut rv);
453 }
454 let range = lv..rv + 1;
455 let expected = (l..r)
456 .filter(|&i| range.contains(&(a * i + b).rem_euclid(m as i64)))
457 .count() as i64;
458 let result = floor_sum_range_freq(l, r, a, b, m, range);
459 assert_eq!(expected, result);
460 }
461 }
462
463 #[test]
464 fn test_floor_sum_polynomial() {
465 const P: usize = 3;
466 const A: u64 = 100;
467 const B: i64 = 100;
468 const Q: usize = 1_000;
469 let mut rng = Xorshift::default();
470 for _ in 0..Q {
471 let (n, a, b, m) = rng.random((..A, ..A, ..A, 1..A));
472 let mut expected: [[u64; P]; P] = [[0; P]; P];
473 for (x, expected) in expected.iter_mut().enumerate() {
474 for (y, expected) in expected.iter_mut().enumerate() {
475 *expected = (0..n)
476 .map(|i| i.pow(x as u32) * ((a * i + b) / m).pow(y as u32))
477 .sum();
478 }
479 }
480 let result = floor_sum_polynomial::<u64, P, P>(n, a, b, m);
481 assert_eq!(expected, result);
482
483 let (mut l, mut r, a, b) = rng.random((-B..B, -B..B, -B..B, -B..B));
484 if l > r {
485 swap(&mut l, &mut r);
486 }
487 let mut expected: [[i64; P]; P] = [[0; P]; P];
488 for (x, expected) in expected.iter_mut().enumerate() {
489 for (y, expected) in expected.iter_mut().enumerate() {
490 *expected = (l..r)
491 .map(|i| i.pow(x as u32) * (a * i + b).div_euclid(m as i64).pow(y as u32))
492 .sum();
493 }
494 }
495 let result = floor_sum_polynomial_i64::<i64, P, P>(l, r, a, b, m);
496 assert_eq!(expected, result);
497 }
498 }
499
500 #[test]
501 fn test_floor_power_sum() {
502 const A: u64 = 1_000;
503 const Q: usize = 1_000;
504 let mut rng = Xorshift::default();
505 for _ in 0..Q {
506 let (n, a, b, m) = rng.random((..A, ..A, ..A, 1..A));
507 let x: MInt998244353 = rng.random(..);
508 let y: MInt998244353 = rng.random(..);
509 let expected: MInt998244353 = (0..n)
510 .map(|i| {
511 let floor = (a * i + b) / m;
512 x.pow(i as _) * y.pow(floor as _)
513 })
514 .sum();
515 let result = floor_power_sum::<AddMulOperation<MInt998244353>>(x, y, n, a, b, m);
516 assert_eq!(expected, result);
517 }
518 }
519}