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}