-
Notifications
You must be signed in to change notification settings - Fork 237
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 #149
Comments
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
} |
thanks |
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)
ac-library/atcoder/internal_math.hpp
Lines 22 to 62 in 6c88a70
(current code: ac-library-rs)
https://github.com/rust-lang-ja/ac-library-rs/blob/b473a615a7d7cfe4d8b26e369808cb2aa2d2f5a0/src/internal_math.rs#L19-L84
(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: