LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
modular_arithmetic.hpp
浏览该文件的文档.
1/**
2 * @file modular_arithmetic.hpp
3 * @brief 模运算工具:ModInt 类、扩展欧几里得、CRT、有理重构。
4 */
5#pragma once
6
7#include <cstdint>
8#include <vector>
9#include <utility>
10#include <cmath>
11#include <stdexcept>
12#include <algorithm>
13#include <limits>
14#include <string>
16#include <lmmp.h>
17#include <numth.h>
18#include "result.hpp"
19
20namespace LMCAS {
21
24
25namespace modular_detail {
26
27inline uint64_t abs_to_u64(int64_t value) {
28 if (value >= 0) return static_cast<uint64_t>(value);
29 return static_cast<uint64_t>(-(value + 1)) + 1;
30}
31
32inline bool checked_positive_product(int64_t a, int64_t b, int64_t& out) {
33 if (a <= 0 || b <= 0) return false;
34 if (static_cast<uint64_t>(a) >
35 static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) / static_cast<uint64_t>(b)) {
36 return false;
37 }
38 out = a * b;
39 return true;
40}
41
42} // namespace modular_detail
43
44/**
45 * @brief 用扩展欧几里得算法求 gcd(a,b) 及 Bezout 系数。
46 * @param a 第一个有符号机器整数
47 * @param b 第二个有符号机器整数
48 * @param s 输出 Bezout 系数 s,满足 a*s + b*t = gcd(a,b)
49 * @param t 输出 Bezout 系数 t
50 * @return 非负的 gcd(a, b)
51 * @throws std::overflow_error 任一输入为 INT64_MIN,其绝对值无法由返回类型表示
52 *
53 * 输出参数仅在计算成功后写入。
54 */
55inline int64_t extended_gcd(int64_t a, int64_t b, int64_t& s, int64_t& t) {
56 const uint64_t magnitude_a = modular_detail::abs_to_u64(a);
57 const uint64_t magnitude_b = modular_detail::abs_to_u64(b);
58 const auto maximum =
59 static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
60 if (magnitude_a > maximum || magnitude_b > maximum) {
61 throw std::overflow_error(
62 "modular extended_gcd does not support INT64_MIN");
63 }
64
65 /** @brief 余数及交替 Bezout 系数以 max(|a|,|b|) 为界,包括余数归零的最后一步。 */
66 int64_t old_r = static_cast<int64_t>(magnitude_a);
67 int64_t r = static_cast<int64_t>(magnitude_b);
68 int64_t old_s = 1;
69 int64_t next_s = 0;
70 int64_t old_t = 0;
71 int64_t next_t = 1;
72
73 while (r != 0) {
74 const int64_t quotient = old_r / r;
75 int64_t temporary = r;
76 r = old_r - quotient * r;
77 old_r = temporary;
78
79 temporary = next_s;
80 next_s = old_s - quotient * next_s;
81 old_s = temporary;
82
83 temporary = next_t;
84 next_t = old_t - quotient * next_t;
85 old_t = temporary;
86 }
87
88 const int64_t result_s = a < 0 ? -old_s : old_s;
89 const int64_t result_t = b < 0 ? -old_t : old_t;
90 s = result_s;
91 t = result_t;
92 return old_r;
93}
94
95/**
96 * @brief 计算两个无符号整数的最大公约数(调用 LMMP)
97 * @param a 第一个无符号整数
98 * @param b 第二个无符号整数
99 * @return gcd(a, b)
100 */
101inline uint64_t lammp_gcd(uint64_t a, uint64_t b) {
102 if (a == 0) return b;
103 if (b == 0) return a;
104 return lmmp_gcd_11_(a, b);
105}
106
107/**
108 * @brief 判断无符号整数是否为素数
109 * @param n 待判断的整数
110 * @return 是素数返回 true
111 */
112inline bool lammp_is_prime(uint64_t n) {
113 return lmmp_is_prime_ulong_(n);
114}
115
116/**
117 * @brief 求大于 n 的下一个素数
118 * @param n 起始值
119 * @return 大于 n 的最小素数
120 */
121inline uint64_t lammp_next_prime(uint64_t n) {
122 return lmmp_next_prime_ulong_(n);
123}
124
125/** @brief 模整数类,封装模 p 下的算术运算 */
126class ModInt {
127 int64_t val_;
128 int64_t mod_;
129
130 void require_same_modulus(const ModInt& other) const {
131 if (mod_ != other.mod_) {
132 throw std::domain_error("modular operands must have the same modulus");
133 }
134 }
135
136public:
137 /** @brief 默认构造,值为 0,模为 2 */
138 ModInt() : val_(0), mod_(2) {}
139
140 /**
141 * @brief 构造模整数
142 * @param v 整数值(自动取模归约到 [0, p))
143 * @param p 模数
144 */
145 ModInt(int64_t v, int64_t p) : mod_(p) {
146 if (p <= 0) {
147 throw std::domain_error("modulus must be positive");
148 }
149 val_ = v % p;
150 if (val_ < 0) val_ += p;
151 }
152
153 /** @brief 获取当前值 */
154 int64_t value() const { return val_; }
155
156 /** @brief 获取模数 */
157 int64_t modulus() const { return mod_; }
158
159 /** @brief 模加法 */
160 ModInt operator+(const ModInt& other) const {
162 const int64_t distance = mod_ - other.val_;
163 return ModInt(val_ >= distance ? val_ - distance : val_ + other.val_, mod_);
164 }
165
166 /** @brief 模减法 */
167 ModInt operator-(const ModInt& other) const {
169 return ModInt(val_ >= other.val_ ? val_ - other.val_
170 : mod_ - (other.val_ - val_), mod_);
171 }
172
173 /** @brief 模乘法 */
174 ModInt operator*(const ModInt& other) const {
176
177 uint64_t q;
178 uint64_t result = lmmp_mulmod_ulong_(
179 static_cast<uint64_t>(val_),
180 static_cast<uint64_t>(other.val_),
181 static_cast<uint64_t>(mod_), &q);
182 return ModInt(static_cast<int64_t>(result), mod_);
183 }
184
185 /** @brief 模除法(乘以逆元) */
186 ModInt operator/(const ModInt& other) const {
187 return *this * other.inverse();
188 }
189
190 /** @brief 取负 */
192 return ModInt(val_ == 0 ? 0 : mod_ - val_, mod_);
193 }
194
195 /** @brief 判等 */
196 bool operator==(const ModInt& other) const {
197 return val_ == other.val_ && mod_ == other.mod_;
198 }
199
200 /** @brief 判不等 */
201 bool operator!=(const ModInt& other) const {
202 return !(*this == other);
203 }
204
205 /** @brief 小于比较(先比模数,再比值) */
206 bool operator<(const ModInt& other) const {
207 if (mod_ != other.mod_) return mod_ < other.mod_;
208 return val_ < other.val_;
209 }
210
211 /** @brief 大于比较 */
212 bool operator>(const ModInt& other) const {
213 return other < *this;
214 }
215
216 /** @brief 判断是否为零 */
217 bool is_zero() const { return val_ == 0; }
218
219 /**
220 * @brief 求模逆元
221 * @return 当前值在模 mod_ 下的乘法逆元
222 * @throw std::domain_error 若值为 0 或不可逆
223 */
224 ModInt inverse() const {
225 if (val_ == 0) {
226 throw std::domain_error("ModInt::inverse(): zero is not invertible");
227 }
228 int64_t s, t;
229 int64_t g = extended_gcd(val_, mod_, s, t);
230 if (g != 1) {
231 throw std::domain_error(
232 "ModInt::inverse(): element is not invertible (gcd != 1)");
233 }
234 return ModInt(s, mod_);
235 }
236
237 /**
238 * @brief 模幂运算
239 * @param base 底数
240 * @param exp 指数(可为负,负指数先求逆)
241 * @return base^exp mod p
242 */
243 static ModInt pow(ModInt base, int64_t exp) {
244 uint64_t power = 0;
245 if (exp < 0) {
246 base = base.inverse();
247 power = static_cast<uint64_t>(-(exp + 1)) + 1;
248 } else {
249 power = static_cast<uint64_t>(exp);
250 }
251 ModInt result(1, base.mod_);
252 while (power != 0) {
253 if ((power & 1U) != 0) result = result * base;
254 power >>= 1U;
255 if (power != 0) base = base * base;
256 }
257 return result;
258 }
259};
260
261
262/**
263 * @brief Checked Chinese remainder theorem for two positive coprime moduli.
264 */
265inline CrtResult crt_checked(int64_t r1, int64_t m1,
266 int64_t r2, int64_t m2,
268 constexpr const char* operation = "crt";
269 auto step = context.consume_steps(1, operation);
270 if (!step) return CrtResult::failure(step.error());
271 if (m1 <= 0 || m2 <= 0) {
273 "CRT moduli must be positive", operation);
274 }
275
276 int64_t product = 0;
277 if (!modular_detail::checked_positive_product(m1, m2, product)) {
279 "CRT modulus product exceeds int64 range", operation);
280 }
281
282 int64_t s = 0;
283 int64_t t = 0;
284 int64_t g = extended_gcd(m1, m2, s, t);
285 if (g != 1) {
287 "CRT moduli must be coprime", operation);
288 }
289
290 const auto factor = ((ModInt(r2, m2) - ModInt(r1, m2)) * ModInt(s, m2)).value();
291 /** @brief factor < m2 且 m1*m2 <= INT64_MAX,保证 m1*factor 在机器整数范围内。 */
292 const auto merged = ModInt(r1, product) + ModInt(m1 * factor, product);
293 return CrtResult::success({merged.value(), product});
294}
295
296inline CrtResult crt_checked(int64_t r1, int64_t m1, int64_t r2, int64_t m2) {
298 return crt_checked(r1, m1, r2, m2, context);
299}
300/**
301 * @brief 中国剩余定理(两模数),返回规范余数与模数乘积。
302 * @throw std::domain_error 模数非正或不互素。
303 * @throw std::overflow_error 合并模数超出机器内核范围。
304 */
305inline std::pair<int64_t, int64_t> crt(
306 int64_t r1, int64_t m1, int64_t r2, int64_t m2) {
307 auto merged = crt_checked(r1, m1, r2, m2);
308 if (merged) return merged.value();
309 if (merged.error().code == CasErrc::ResourceLimit) {
310 throw std::overflow_error(merged.error().message);
311 }
312 throw std::domain_error(merged.error().message);
313}
314
315
316/**
317 * @brief 多模数中国剩余定理
318 * @param residues 余数列表
319 * @param primes 对应的模数列表(两两互素)
320 * @return pair(合并余数, 合并模数)
321 * @throw std::invalid_argument 若输入为空或大小不匹配
322 */
323inline std::pair<int64_t, int64_t> multi_crt(const std::vector<int64_t>& residues,
324 const std::vector<int64_t>& primes) {
325 if (residues.empty() || residues.size() != primes.size()) {
326 throw std::invalid_argument("multi_crt(): residues and primes must be non-empty and same size");
327 }
328
329 int64_t modulus = primes[0];
330 int64_t combined = ModInt(residues[0], modulus).value();
331
332 for (size_t i = 1; i < residues.size(); ++i) {
333 auto [x, m] = crt(combined, modulus, residues[i], primes[i]);
334 combined = x;
335 modulus = m;
336 }
337
338 return {combined, modulus};
339}
340
341/**
342 * @brief Checked CRT for a non-empty list of pairwise coprime positive moduli.
343 */
344inline CrtResult multi_crt_checked(const std::vector<int64_t>& residues,
345 const std::vector<int64_t>& primes,
347 constexpr const char* operation = "multi_crt";
348 auto step = context.consume_steps(residues.size() + 1, operation);
349 if (!step) return CrtResult::failure(step.error());
350 if (residues.empty() || residues.size() != primes.size()) {
351 return CrtResult::failure(
353 "multi_crt residues and moduli must be non-empty and the same size",
354 operation);
355 }
356 for (int64_t modulus : primes) {
357 if (modulus <= 0) {
359 "multi_crt moduli must be positive", operation);
360 }
361 }
362
363 int64_t modulus = primes[0];
364 int64_t combined = ModInt(residues[0], modulus).value();
365 for (size_t i = 1; i < residues.size(); ++i) {
366 auto merged = crt_checked(combined, modulus, residues[i], primes[i], context);
367 if (!merged) return merged;
368 combined = merged.value().first;
369 modulus = merged.value().second;
370 }
371
372 return CrtResult::success({combined, modulus});
373}
374
375inline CrtResult multi_crt_checked(const std::vector<int64_t>& residues,
376 const std::vector<int64_t>& primes) {
378 return multi_crt_checked(residues, primes, context);
379}
380
381
382/**
383 * @brief Checked rational reconstruction. Returns Inconclusive when no
384 * supported coprime numerator/denominator can be reconstructed.
385 */
387 int64_t x,
388 int64_t m,
390 constexpr const char* operation = "rational_reconstruction";
391 auto step = context.consume_steps(1, operation);
392 if (!step) {
393 return RationalReconstructionResult::failure(step.error());
394 }
395 if (m <= 0) {
398 "rational reconstruction modulus must be positive",
399 operation);
400 }
401
402 x %= m;
403 if (x < 0) {
404 x += m;
405 }
406 int64_t bound = static_cast<int64_t>(
407 std::floor(std::sqrt(static_cast<double>(m) / 2.0)));
408 if (bound == 0) {
409 bound = 1;
410 }
411 int64_t r0 = m;
412 int64_t r1 = x;
413 int64_t s0 = 0;
414 int64_t s1 = 1;
415 while (r1 > bound) {
416 step = context.consume_steps(1, operation);
417 if (!step) {
418 return RationalReconstructionResult::failure(step.error());
419 }
420 const int64_t quotient = r0 / r1;
421 const int64_t next_r = r0 - quotient * r1;
422 const int64_t next_s = s0 - quotient * s1;
423 r0 = r1;
424 r1 = next_r;
425 s0 = s1;
426 s1 = next_s;
427 }
428 int64_t numerator = r1;
429 int64_t denominator = s1;
430 if (denominator < 0) {
431 numerator = -numerator;
433 }
434 int64_t unused_s = 0;
435 int64_t unused_t = 0;
436 const bool outside_bound =
437 std::abs(numerator) >= bound || denominator == 0 ||
438 denominator >= bound;
439 const bool not_coprime = !outside_bound &&
440 extended_gcd(std::abs(numerator), denominator,
441 unused_s, unused_t) != 1;
442 if (outside_bound || not_coprime) {
445 "no rational reconstruction exists in the supported bound",
446 operation);
447 }
449 {numerator, denominator});
450}
451
456
457/** @brief 预定义的大素数表,用于模运算多素数方案 */
458constexpr int64_t MODULAR_PRIMES[] = {
459 1000000007, 1000000009, 1000000021, 1000000033,
460 1000000087, 1000000093, 1000000097, 1000000103,
461 1000000123, 1000000181, 1000000207, 1000000223,
462 1000000231, 1000000271, 1000000289, 1000000297
463};
464/** @brief 预定义素数表的长度 */
465constexpr size_t NUM_MODULAR_PRIMES = sizeof(MODULAR_PRIMES) / sizeof(MODULAR_PRIMES[0]);
466
467/**
468 * @brief 判断素数 p 是否为"好素数"(不整除任何首项系数)
469 * @param p 待检测素数
470 * @param leading_coeffs 首项系数列表
471 * @return 若 p 不整除任何系数则返回 true
472 */
473inline bool is_good_prime(int64_t p, const std::vector<BigInt>& leading_coeffs) {
474 if (p < 2) {
475 throw std::domain_error("prime modulus must be at least two");
476 }
477 const BigInt modulus(p);
478 return std::none_of(leading_coeffs.begin(), leading_coeffs.end(),
479 [&modulus](const BigInt& coefficient) {
480 return (coefficient % modulus).is_zero();
481 });
482}
483
484/**
485 * @brief 生成指定数量的好素数
486 * @param count 需要的素数个数
487 * @param leading_coeffs 首项系数列表(用于过滤)
488 * @param start_from 搜索起始值
489 * @return 好素数列表
490 */
491inline std::vector<int64_t> generate_good_primes(
492 int count, const std::vector<BigInt>& leading_coeffs,
493 uint64_t start_from = UINT64_C(1000000000)) {
494 if (count < 0) {
495 throw std::invalid_argument("prime count must be nonnegative");
496 }
497 const auto requested = static_cast<std::size_t>(count);
498 const auto maximum = static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
499 std::vector<int64_t> result;
500 result.reserve(requested);
501 uint64_t candidate = start_from;
502 while (result.size() < requested) {
503 if (candidate >= maximum) {
504 throw std::overflow_error("prime search exceeds the signed residue range");
505 }
506 const uint64_t next = lmmp_next_prime_ulong_(candidate);
507 if (next <= candidate || next > maximum) {
508 throw std::overflow_error("prime search exhausted the signed residue range");
509 }
510 candidate = next;
511 const auto prime = static_cast<int64_t>(candidate);
512 if (is_good_prime(prime, leading_coeffs)) {
513 result.push_back(prime);
514 }
515 }
516 return result;
517}
518
519}
ModInt()
默认构造,值为 0,模为 2
bool operator!=(const ModInt &other) const
判不等
void require_same_modulus(const ModInt &other) const
bool operator>(const ModInt &other) const
大于比较
bool is_zero() const
判断是否为零
ModInt operator+(const ModInt &other) const
模加法
int64_t modulus() const
获取模数
ModInt inverse() const
求模逆元
ModInt operator-() const
取负
ModInt operator-(const ModInt &other) const
模减法
ModInt operator/(const ModInt &other) const
模除法(乘以逆元)
bool operator==(const ModInt &other) const
判等
ModInt(int64_t v, int64_t p)
构造模整数
int64_t value() const
获取当前值
bool operator<(const ModInt &other) const
小于比较(先比模数,再比值)
static ModInt pow(ModInt base, int64_t exp)
模幂运算
ModInt operator*(const ModInt &other) const
模乘法
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(std::pair< int64_t, int64_t > value)
bool checked_positive_product(int64_t a, int64_t b, int64_t &out)
uint64_t abs_to_u64(int64_t value)
bool lammp_is_prime(uint64_t n)
判断无符号整数是否为素数
uint64_t lammp_next_prime(uint64_t n)
求大于 n 的下一个素数
ExprResult exp(const ExprPtr &expression, ComputationContext &context)
__attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_exp_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexSymbolicResult complex_trig_form_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_nth_root_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std __attribute__((visibility("default"))) ComplexRootsResult solve_complex_quadratic_checked(std std::shared_ptr< SymbolicExpr > r
__attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult surface_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::string & v
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
uint64_t lammp_gcd(uint64_t a, uint64_t b)
计算两个无符号整数的最大公约数(调用 LMMP)
CrtResult multi_crt_checked(const std::vector< int64_t > &residues, const std::vector< int64_t > &primes, ComputationContext &context)
Checked CRT for a non-empty list of pairwise coprime positive moduli.
const MultiPoly & b
Result< std::pair< int64_t, int64_t > > CrtResult
std::vector< int64_t > generate_good_primes(int count, const std::vector< BigInt > &leading_coeffs, uint64_t start_from=UINT64_C(1000000000))
生成指定数量的好素数
CrtResult crt_checked(int64_t r1, int64_t m1, int64_t r2, int64_t m2, ComputationContext &context)
Checked Chinese remainder theorem for two positive coprime moduli.
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
Result< std::pair< int64_t, int64_t > > RationalReconstructionResult
RationalReconstructionResult rational_reconstruction_checked(int64_t x, int64_t m, ComputationContext &context)
Checked rational reconstruction.
bool is_good_prime(int64_t p, const std::vector< BigInt > &leading_coeffs)
判断素数 p 是否为"好素数"(不整除任何首项系数)
constexpr size_t NUM_MODULAR_PRIMES
预定义素数表的长度
std::pair< int64_t, int64_t > multi_crt(const std::vector< int64_t > &residues, const std::vector< int64_t > &primes)
多模数中国剩余定理
constexpr int64_t MODULAR_PRIMES[]
预定义的大素数表,用于模运算多素数方案
std::pair< int64_t, int64_t > crt(int64_t r1, int64_t m1, int64_t r2, int64_t m2)
中国剩余定理(两模数),返回规范余数与模数乘积。
const std::string const ExprPtr & value
const ExprPtr & denominator
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string & t
int64_t extended_gcd(int64_t a, int64_t b, int64_t &s, int64_t &t)
用扩展欧几里得算法求 gcd(a,b) 及 Bezout 系数。