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 % 2 == 0 {
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,
147 R::Additive: Invertible,
148{
149 fn offset(x: i64, y: i64) -> FloorSumData<R, X, Y> {
150 FloorSumData {
151 dp: array![array![R::zero(); Y]; X],
152 dx: R::Additive::signed_pow(R::one(), x),
153 dy: R::Additive::signed_pow(R::one(), y),
154 _marker: PhantomData,
155 }
156 }
157}
158
159impl<R, const X: usize, const Y: usize> Magma for FloorSum<R, X, Y>
160where
161 R: SemiRing,
162{
163 type T = FloorSumData<R, X, Y>;
164
165 fn operate(a: &Self::T, b: &Self::T) -> Self::T {
166 let mut a = a.clone();
167 let mut b = b.clone();
168 let mut pow_x = array![R::zero(); X];
169 let mut pow_y = array![R::zero(); Y];
170 pow_x[0] = R::one();
171 pow_y[0] = R::one();
172 for i in 1..X {
173 pow_x[i] = R::mul(&pow_x[i - 1], &a.dx);
174 }
175 for j in 1..Y {
176 pow_y[j] = R::mul(&pow_y[j - 1], &a.dy);
177 }
178 macro_rules! go {
179 ($N:ident) => {
180 let mut comb = array![array![R::zero(); $N]; $N];
181 comb[0][0] = R::one();
182 let mut i = 0;
183 while i + 1 < $N {
184 let mut j = 0;
185 while j <= i {
186 let x = comb[i][j].clone();
187 R::add_assign(&mut comb[i + 1][j], &x);
188 R::add_assign(&mut comb[i + 1][j + 1], &x);
189 j += 1;
190 }
191 i += 1;
192 }
193 for i in 0..X {
194 for j in (0..Y).rev() {
195 for k in j + 1..Y {
196 let mut x = b.dp[i][j].clone();
197 R::mul_assign(&mut x, &comb[k][j]);
198 R::mul_assign(&mut x, &pow_y[k - j]);
199 R::add_assign(&mut b.dp[i][k], &x);
200 }
201 }
202 }
203 for j in 0..Y {
204 for i in (0..X).rev() {
205 for k in i..X {
206 let mut x = b.dp[i][j].clone();
207 R::mul_assign(&mut x, &comb[k][i]);
208 R::mul_assign(&mut x, &pow_x[k - i]);
209 R::add_assign(&mut a.dp[k][j], &x);
210 }
211 }
212 }
213 };
214 }
215 if X <= Y {
216 go!(Y);
217 } else {
218 go!(X);
219 }
220 R::add_assign(&mut a.dx, &b.dx);
221 R::add_assign(&mut a.dy, &b.dy);
222 a
223 }
224}
225
226impl<R, const X: usize, const Y: usize> Unital for FloorSum<R, X, Y>
227where
228 R: SemiRing,
229{
230 fn unit() -> Self::T {
231 FloorSumData {
232 dp: array![array![R::zero(); Y]; X],
233 dx: R::zero(),
234 dy: R::zero(),
235 _marker: PhantomData,
236 }
237 }
238}
239
240impl<R, const X: usize, const Y: usize> Associative for FloorSum<R, X, Y> where R: SemiRing {}
241
242fn floor_monoid_product<M>(
243 mut x: M::T,
244 mut y: M::T,
245 mut n: u64,
246 mut a: u64,
247 mut b: u64,
248 mut m: u64,
249) -> M::T
250where
251 M: Monoid,
252{
253 let mut c = (a * n + b) / m;
254 let mut pre = M::unit();
255 let mut suf = M::unit();
256 loop {
257 let (p, q) = (a / m, b / m);
258 a %= m;
259 b %= m;
260 x = M::operate(&x, &M::pow(y.clone(), p));
261 pre = M::operate(&pre, &M::pow(y.clone(), q));
262 c -= p * n + q;
263 if c == 0 {
264 break;
265 }
266 let d = (m * c - b - 1) / a + 1;
267 suf = M::operate(&y, &M::operate(&M::pow(x.clone(), n - d), &suf));
268 b = m - b - 1 + a;
269 n = c - 1;
270 c = d;
271 swap(&mut m, &mut a);
272 swap(&mut x, &mut y);
273 }
274 x = M::pow(x.clone(), n);
275 M::operate(&M::operate(&pre, &x), &suf)
276}
277
278pub fn floor_sum_polynomial<T, const X: usize, const Y: usize>(
280 n: u64,
281 a: u64,
282 b: u64,
283 m: u64,
284) -> [[T; Y]; X]
285where
286 T: Clone + Zero + One + Add<Output = T> + Mul<Output = T>,
287{
288 debug_assert!(a == 0 || n < (u64::MAX - b) / a);
289 floor_monoid_product::<FloorSum<AddMulOperation<T>, X, Y>>(
290 FloorSum::<AddMulOperation<T>, X, Y>::to_x(),
291 FloorSum::<AddMulOperation<T>, X, Y>::to_y(),
292 n,
293 a,
294 b,
295 m,
296 )
297 .dp
298}
299
300pub fn floor_sum_polynomial_i64<T, const X: usize, const Y: usize>(
302 l: i64,
303 r: i64,
304 a: i64,
305 b: i64,
306 m: u64,
307) -> [[T; Y]; X]
308where
309 T: Clone + Zero + One + Add<Output = T> + Mul<Output = T>,
310 <AddMulOperation<T> as SemiRing>::Additive: Invertible,
311{
312 assert!(l <= r);
313 assert!(m > 0);
314
315 if a < 0 {
316 let mut ans = floor_sum_polynomial_i64::<T, X, Y>(-r + 1, -l + 1, -a, b, m);
317 for i in (1..X).step_by(2) {
318 for j in 0..Y {
319 ans[i][j] = AddMulOperation::<T>::neg(&ans[i][j]);
320 }
321 }
322 return ans;
323 }
324
325 let add_x = l;
326 let n = (r - l) as u64;
327 let b = a * add_x + b;
328
329 let add_y = b.div_euclid(m as i64);
330 let b = b.rem_euclid(m as i64);
331 assert!(a >= 0);
332 assert!(b >= 0);
333 let data = floor_monoid_product::<FloorSum<AddMulOperation<T>, X, Y>>(
334 FloorSum::<AddMulOperation<T>, X, Y>::to_x(),
335 FloorSum::<AddMulOperation<T>, X, Y>::to_y(),
336 n,
337 a as u64,
338 b as u64,
339 m,
340 );
341
342 let offset = FloorSum::<AddMulOperation<T>, X, Y>::offset(add_x, add_y);
343 FloorSum::<AddMulOperation<T>, X, Y>::operate(&offset, &data).dp
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use crate::tools::Xorshift;
350
351 #[test]
352 fn test_floor_sum() {
353 const A: u64 = 1_000;
354 const B: i64 = 1_000;
355 const Q: usize = 1_000;
356 let mut rng = Xorshift::new();
357 for _ in 0..Q {
358 let (n, a, b, m) = rng.random((..A, ..A, ..A, 1..A));
359 let expected: u64 = (0..n).map(|i| (a * i + b) / m).sum();
360 let result = floor_sum(n, a, b, m);
361 assert_eq!(expected, result);
362
363 let (mut l, mut r, a, b) = rng.random((-B..B, -B..B, -B..B, -B..B));
364 if l > r {
365 swap(&mut l, &mut r);
366 }
367 let expected: i64 = (l..r).map(|i| (a * i + b).div_euclid(m as i64)).sum();
368 let result = floor_sum_i64(l, r, a, b, m);
369 assert_eq!(expected, result);
370
371 let (mut lv, mut rv) = rng.random((0..m as i64, 0..m as i64));
372 if lv > rv {
373 swap(&mut lv, &mut rv);
374 }
375 let range = lv..rv + 1;
376 let expected = (l..r)
377 .filter(|&i| range.contains(&(a * i + b).rem_euclid(m as i64)))
378 .count() as i64;
379 let result = floor_sum_range_freq(l, r, a, b, m, range);
380 assert_eq!(expected, result);
381 }
382 }
383
384 #[test]
385 fn test_floor_sum_polynomial() {
386 const P: usize = 3;
387 const A: u64 = 100;
388 const B: i64 = 100;
389 const Q: usize = 1_000;
390 let mut rng = Xorshift::new();
391 for _ in 0..Q {
392 let (n, a, b, m) = rng.random((..A, ..A, ..A, 1..A));
393 let mut expected: [[u64; P]; P] = [[0; P]; P];
394 for (x, expected) in expected.iter_mut().enumerate() {
395 for (y, expected) in expected.iter_mut().enumerate() {
396 *expected = (0..n)
397 .map(|i| i.pow(x as u32) * ((a * i + b) / m).pow(y as u32))
398 .sum();
399 }
400 }
401 let result = floor_sum_polynomial::<u64, P, P>(n, a, b, m);
402 assert_eq!(expected, result);
403
404 let (mut l, mut r, a, b) = rng.random((-B..B, -B..B, -B..B, -B..B));
405 if l > r {
406 swap(&mut l, &mut r);
407 }
408 let mut expected: [[i64; P]; P] = [[0; P]; P];
409 for (x, expected) in expected.iter_mut().enumerate() {
410 for (y, expected) in expected.iter_mut().enumerate() {
411 *expected = (l..r)
412 .map(|i| i.pow(x as u32) * (a * i + b).div_euclid(m as i64).pow(y as u32))
413 .sum();
414 }
415 }
416 let result = floor_sum_polynomial_i64::<i64, P, P>(l, r, a, b, m);
417 assert_eq!(expected, result);
418 }
419 }
420}