Garner’s algorithm (ModularArithmetic/garner.cpp)
- category: ModularArithmetic
-
View this file on GitHub
- Last commit date: 2020-04-06 04:52:14+09:00
Required by
- 階乗の高速計算 (ModularArithmetic/factorial.cpp)
- 補間多項式 (ModularArithmetic/interpolation.cpp)
- 多項式 (ModularArithmetic/polynomial.cpp)
Verified with
- test/yc_502.test.cpp
- test/yj_convolution_mod.test.cpp
- test/yj_convolution_mod_1000000007.test.cpp
- test/yj_inv_of_formal_power_series.test.cpp
- test/yj_log_of_formal_power_series.test.cpp
- test/yj_multipoint_evaluation.test.cpp
- test/yj_polynomial_interpolation.test.cpp
Code
#ifndef H_garner
#define H_garner
/**
* @brief Garner's algorithm
* @author えびちゃん
*/
#include <algorithm>
#include <tuple>
#include <utility>
class simultaneous_congruences_garner {
public:
using value_type = intmax_t;
using size_type = size_t;
private:
value_type M_mod = 1;
value_type M_sol = 0;
std::vector<value_type> M_c, M_m;
static auto S_gcd_bezout(value_type a, value_type b) {
value_type x{1}, y{0};
for (value_type u{y}, v{x}; b != 0;) {
value_type q{a/b};
std::swap(x -= q*u, u);
std::swap(y -= q*v, v);
std::swap(a -= q*b, b);
}
return std::make_tuple(a, x, y);
}
public:
simultaneous_congruences_garner() = default;
void push(value_type a, value_type m) {
if (M_c.empty()) {
M_c.push_back(a);
M_m.push_back(m);
return;
}
value_type c = M_c.back();
value_type mi = M_m.back();
for (size_type i = M_c.size()-1; i--;) {
c = (c * M_m[i] + M_c[i]) % m;
(mi *= M_m[i]) %= m;
}
c = (a-c) * std::get<1>(S_gcd_bezout(mi, m)) % m;
if (c < 0) c += m;
M_c.push_back(c);
M_m.push_back(m);
}
auto get(value_type m) const {
value_type x = M_c.back() % m;
for (size_type i = M_c.size()-1; i--;) {
x = (x * M_m[i] + M_c[i]) % m;
}
return x;
}
};
#endif /* !defined(H_garner) */
#line 1 "ModularArithmetic/garner.cpp"
/**
* @brief Garner's algorithm
* @author えびちゃん
*/
#include <algorithm>
#include <tuple>
#include <utility>
class simultaneous_congruences_garner {
public:
using value_type = intmax_t;
using size_type = size_t;
private:
value_type M_mod = 1;
value_type M_sol = 0;
std::vector<value_type> M_c, M_m;
static auto S_gcd_bezout(value_type a, value_type b) {
value_type x{1}, y{0};
for (value_type u{y}, v{x}; b != 0;) {
value_type q{a/b};
std::swap(x -= q*u, u);
std::swap(y -= q*v, v);
std::swap(a -= q*b, b);
}
return std::make_tuple(a, x, y);
}
public:
simultaneous_congruences_garner() = default;
void push(value_type a, value_type m) {
if (M_c.empty()) {
M_c.push_back(a);
M_m.push_back(m);
return;
}
value_type c = M_c.back();
value_type mi = M_m.back();
for (size_type i = M_c.size()-1; i--;) {
c = (c * M_m[i] + M_c[i]) % m;
(mi *= M_m[i]) %= m;
}
c = (a-c) * std::get<1>(S_gcd_bezout(mi, m)) % m;
if (c < 0) c += m;
M_c.push_back(c);
M_m.push_back(m);
}
auto get(value_type m) const {
value_type x = M_c.back() % m;
for (size_type i = M_c.size()-1; i--;) {
x = (x * M_m[i] + M_c[i]) % m;
}
return x;
}
};