Skip to main content

nekolib/math/
equiv_mod.rs

1//! Chinese remaindering
2
3use super::gcd_recip;
4
5use gcd_recip::GcdRecip;
6
7/// Chinese remaindering。
8///
9/// $\\gdef\\lcm{\\operatorname{lcm}}$
10/// $(r\_0, m\_0)$ と $(r\_1, m\_1)$ に対し、以下を満たす $0\\le x\\lt\\lcm(m\_0, m\_1)$ を求める。
11/// - $x\\bmod m\_0 = r\_0$
12/// - $x\\bmod m\_1 = r\_1$
13///
14/// 中国剰余定理から、$\\gcd(m\_0, m\_1)=1$ であれば常に存在する。
15/// そうでない場合、高々一つ存在する。
16///
17/// なお、計算の利便性のため、$\\lcm(m\_0, m\_1)$ も返す。以下の例も参照。
18///
19/// # Idea
20/// $$ \\begin{aligned}
21/// (y\\cdot m\_0+r\_0)\\bmod m\_1 &= r\_1 \\\\
22/// (y\\cdot m\_0)\\bmod m\_1 &= (r\_1-r\_0)\\bmod m\_1 \\\\
23/// \\end{aligned} $$
24/// $g = \\gcd(m\_0, m\_1)$ として $(m\_0, m\_1) = (u\_0g, u\_1g)$ とすると、
25/// $$ (y\\cdot u\_0g)\\bmod u\_1g = (r\_1-r\_0)\\bmod u\_1g. $$
26/// 左辺は $g$ の倍数なので、右辺も $g$ の倍数となる必要がある。
27/// よって、$r\_1\\not\\equiv r\_0\\pmod{g}$ であれば解なしとなる。
28///
29/// 以下、$r\_1\\equiv r\_0\\pmod{g}$ とする。このとき、$r\_1-r\_0$ は $g$ で割り切れ、
30/// $$ \\begin{aligned}
31/// (y\\cdot u\_0)\\bmod u\_1 &= \\left(\\frac{r\_1-r\_0}{g}\\right)\\bmod u\_1 \\\\
32/// y &\\equiv \\left(\\frac{r\_1-r\_0}{g}\\right)\\cdot u\_0^{-1} \\pmod{u\_1}
33/// \\end{aligned} $$
34/// となる。$\\gcd(u\_0, u\_1)=1$ なので $u\_0^{-1}$ は存在する。
35///
36/// これを満たす $0\\le y\\lt u\_1$ は一意に定まり、$x = y\\cdot m\_0+r\_0$ を計算すればよい。
37/// また、$u\_1 = m\_1/g$ より、$\\lcm(m\_0, m\_1) = m\_0\\cdot u\_1$ となる。
38///
39/// # Examples
40/// ```
41/// use nekolib::math::EquivMod;
42///
43/// assert_eq!((0_i64, 2).equiv_mod((1, 3)), Some((4, 6)));
44/// assert_eq!((0_i64, 2).equiv_mod((1, 4)), None);
45/// ```
46///
47/// イテレータと組み合わせてもよい。
48/// 条件 $x\\bmod 1 = 0$ が単位元となることに注意。
49/// ```
50/// use nekolib::math::{EquivMod, Lcm};
51///
52/// let x = (2_i64..=20)
53///     .map(|m| (m - 1, m))
54///     .try_fold((0, 1), |x, y| x.equiv_mod(y));
55///
56/// let lcm = (2_i64..=20).fold(1, |x, y| x.lcm(y));
57/// assert_eq!(x, Some((lcm - 1, lcm)));
58/// ```
59///
60/// 簡略版もある。
61/// ```
62/// use nekolib::math::{EquivModIter, Lcm};
63///
64/// let x = (2_i64..=20).map(|m| (m - 1, m)).equiv_mod();
65/// let lcm = (2_i64..=20).fold(1, |x, y| x.lcm(y));
66/// assert_eq!(x, Some((lcm - 1, lcm)));
67///
68/// assert_eq!(std::iter::empty().equiv_mod(), Some((0_i32, 1)));
69/// ```
70///
71///
72/// # Reference
73/// - <https://github.com/atcoder/ac-library/blob/master/atcoder/math.hpp>
74///
75/// ## See also
76/// - <https://rsk0315.hatenablog.com/entry/2021/01/18/065720#crt>
77pub trait EquivMod: Sized {
78    fn equiv_mod(self, other: Self) -> Option<Self>;
79}
80
81macro_rules! impl_uint {
82    ($t:ty) => {
83        impl EquivMod for ($t, $t) {
84            fn equiv_mod(self, other: Self) -> Option<Self> {
85                let ((r0, m0), (r1, m1))
86                    = if self.1 >= other.1 { (self, other) } else { (other, self) };
87                if m0 % m1 == 0 {
88                    return if r0 % m1 == r1 { Some((r0, m0)) } else { None }
89                }
90                let (g, re) = m0.gcd_recip(m1);
91                let u1 = m1 / g;
92                let (dr, neg) = if r1 >= r0 { (r1 - r0, false) } else { (r0 - r1, true) };
93                if dr % g != 0 {
94                    return None
95                }
96                let x = (if neg { u1 - dr / g % u1 } else { dr / g % u1 }) * re % u1;
97                Some((r0 + x * m0, m0 * u1))
98            }
99        }
100    };
101    ( $($t:ty)* ) => { $(impl_uint!($t);)* };
102}
103
104macro_rules! impl_int {
105    ($t:ty) => {
106        impl EquivMod for ($t, $t) {
107            fn equiv_mod(self, other: Self) -> Option<Self> {
108                let ((r0, m0), (r1, m1))
109                    = if self.1 >= other.1 { (self, other) } else { (other, self) };
110                if m0 % m1 == 0 {
111                    return if r0 % m1 == r1 { Some((r0, m0)) } else { None }
112                }
113                let (g, re) = m0.gcd_recip(m1);
114                let u1 = m1 / g;
115                if (r1 - r0) % g != 0 {
116                    return None
117                }
118                let x = ((r1 - r0) / g).rem_euclid(u1) * re % u1;
119                Some((r0 + x * m0, m0 * u1))
120            }
121        }
122    };
123    ( $($t:ty)* ) => { $(impl_int!($t);)* };
124}
125
126impl_uint!(u8 u16 u32 u64 u128 usize);
127impl_int!(i8 i16 i32 i64 i128 isize);
128
129#[test]
130fn test_uint() {
131    let m_max = 150_usize;
132    for m0 in 1..=m_max {
133        for m1 in 1..=m_max {
134            let naive = {
135                let mut res = vec![vec![None; m1]; m0];
136                for x in (0..m0 * m1).rev() {
137                    let r0 = x % m0;
138                    let r1 = x % m1;
139                    res[r0][r1] = Some(x);
140                }
141                res
142            };
143            for r0 in 0..m0 {
144                for r1 in 0..m1 {
145                    let x = (r0, m0).equiv_mod((r1, m1)).map(|x| x.0);
146                    assert_eq!(x, naive[r0][r1]);
147                }
148            }
149        }
150    }
151}
152
153#[test]
154fn test_int() {
155    let m_max = 150_isize;
156    for m0 in 1..=m_max {
157        for m1 in 1..=m_max {
158            let naive = {
159                let mut res = vec![vec![None; m1 as usize]; m0 as usize];
160                for x in (0..m0 * m1).rev() {
161                    let r0 = (x % m0) as usize;
162                    let r1 = (x % m1) as usize;
163                    res[r0][r1] = Some(x);
164                }
165                res
166            };
167            for r0 in 0..m0 {
168                for r1 in 0..m1 {
169                    let x = (r0, m0).equiv_mod((r1, m1)).map(|x| x.0);
170                    assert_eq!(x, naive[r0 as usize][r1 as usize]);
171                }
172            }
173        }
174    }
175}
176
177#[test]
178fn test_iter() {
179    let x = (2_i64..=20)
180        .map(|m| (m - 1, m))
181        .try_fold((0, 1), |x, y| x.equiv_mod(y));
182    let y = 232792560;
183
184    assert_eq!(x, Some((y - 1, y)));
185}
186
187/// Chinese remaindering。
188///
189/// [`EquivMod`] のイテレータ版。
190pub trait EquivModIter<I: Sized> {
191    fn equiv_mod(self) -> Option<(I, I)>;
192}
193
194macro_rules! impl_iter {
195    ($t:ty) => {
196        impl<I: Iterator<Item = ($t, $t)>> EquivModIter<$t> for I {
197            fn equiv_mod(mut self) -> Option<($t, $t)> {
198                self.try_fold((0, 1), |x, y| x.equiv_mod(y))
199            }
200        }
201    };
202    ( $($t:ty)* ) => { $(impl_iter!($t);)* };
203}
204
205impl_iter!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
206
207#[test]
208fn test_iter2() {
209    let x = (2_i64..=20).map(|m| (m - 1, m)).equiv_mod();
210    let y = 232792560;
211
212    assert_eq!(x, Some((y - 1, y)));
213}