-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
mul_mod for 2^31 < m < 2^32 #111
Milestone
Comments
The following add overflow checks and subtruct borrow checks should also be considered. For example, use Lines 793 to 811 in b09e6b5
|
Example of subtraction borrow check using built-in instruction (GCC/MSVC): https://godbolt.org/z/P8749355T #ifdef _MSC_VER
#include <intrin.h>
#endif
// @param a `0 <= a < m`
// @param b `0 <= b < m`
// @return `a * b % m`
unsigned int barrett_mul_before(unsigned int a, unsigned int b, unsigned int _m, unsigned long long im) {
// [1] m = 1
// a = b = im = 0, so okay
// [2] m >= 2
// im = ceil(2^64 / m)
// -> im * m = 2^64 + r (0 <= r < m)
// let z = a*b = c*m + d (0 <= c, d < m)
// a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
// c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
// ((ab * im) >> 64) == c or c + 1
unsigned long long z = a;
z *= b;
#ifdef _MSC_VER
unsigned long long x;
_umul128(z, im, &x);
#else
unsigned long long x =
(unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
unsigned int v = (unsigned int)(z - x * _m);
if (_m <= v) v += _m;
return v;
}
// @param a `0 <= a < m`
// @param b `0 <= b < m`
// @return `a * b % m`
unsigned int barrett_mul_after(unsigned int a, unsigned int b, unsigned int _m, unsigned long long im) {
// [1] m = 1
// a = b = im = 0, so okay
// [2] m >= 2
// im = ceil(2^64 / m)
// -> im * m = 2^64 + r (0 <= r < m)
// let z = a*b = c*m + d (0 <= c, d < m)
// a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
// c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
// ((ab * im) >> 64) == c or c + 1
unsigned long long z = a;
z *= b;
#ifdef _MSC_VER
unsigned long long x;
_umul128(z, im, &x);
#else
unsigned long long x =
(unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
unsigned long long y = x * _m;
#ifdef __GNUC__
// https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html
unsigned long long v;
unsigned int w = __builtin_usubll_overflow(z, y, &v) ? _m : 0;
return (unsigned int)(v + w);
#elif defined(_MSC_VER) && defined(_M_AMD64)
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_subborrow_u64&ig_expand=7252
unsigned long long v;
unsigned int w = _subborrow_u64(0, z, y, &v) ? _m : 0;
return (unsigned int)(v + w);
#else
return (unsigned int)((z - y) + (z < y ? _m : 0));
#endif
} |
Open
Open
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
mul_mod
seems easily be adapted to the case 2^31 < m < 2^32 by simply improving the last subtraction borrow check.(current code: ac-library)
https://github.com/atcoder/ac-library/blob/6c88a70c8f95fef575af354900d107fbd0db1a12/atcoder/internal_math.hpp#L22-L62
(current code: ac-library-rs)
ac-library-rs/src/internal_math.rs
Lines 19 to 84 in b473a61
(proof)
(C++:$1\le m\lt 2^{32}$ draft code)
https://godbolt.org/z/9Gz1oGrTa
(Rust:$1\le m\lt 2^{32}$ draft code)
https://rust.godbolt.org/z/7P5rjahMn
The text was updated successfully, but these errors were encountered: