#pragma GCC optimize ("O3")
// #pragma GCC target ("avx")
#include<stdio.h>
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
#include<string.h>
#ifdef LOCAL
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define NDEBUG
#define eprintf(...) do {} while (0)
#endif
#include<cassert>
#include<random>
using namespace std;
typedef long long LL;
typedef vector<int> VI;
using ULL = unsigned long long;
#define REP(i,n) for(int i=0, i##_len=(n); i<i##_len; ++i)
#define EACH(i,c) for(__typeof((c).begin()) i=(c).begin(),i##_end=(c).end();i!=i##_end;++i)
template<class T> inline void amin(T &x, const T &y) { if (y<x) x=y; }
template<class T> inline void amax(T &x, const T &y) { if (x<y) x=y; }
template<class Iter> void rprintf(const char *fmt, Iter begin, Iter end) {
for (bool sp=0; begin!=end; ++begin) { if (sp) putchar(' '); else sp = true; printf(fmt, *begin); }
putchar('\n');
}
struct MontgomeryReduction {
ULL MOD;
// MOD * NEG_INV % (2^32) == (2^32) - 1;
ULL NEG_INV;
// R2 == (2^64) % MOD;
ULL R2;
MontgomeryReduction(ULL MOD_): MOD(MOD_) {
NEG_INV = 0;
ULL s = 1, t = 0;
REP (i, 32) {
if (~t & 1) {
t += MOD;
NEG_INV += s;
}
t /= 2;
s *= 2;
}
R2 = (1ULL<<32) % MOD;
R2 = R2 * R2 % MOD;
}
// return x * R % MOD;
ULL generate(ULL x) const {
assert(x < MOD);
return reduce(x * R2);
}
// return x / R % MOD;
ULL reduce(ULL x) const {
assert(x < MOD * MOD);
x = (x + ((unsigned)x * (unsigned)NEG_INV) * MOD) >> 32;
return x < MOD? x: x-MOD;
}
};
struct BarrettReduction {
ULL MOD;
__int128 m;
BarrettReduction(ULL MOD_): MOD(MOD_) {
m = (__int128(1)<<64) / MOD;
}
ULL reduce(ULL x) const {
ULL q = (x*m)>>64;
x -= q * MOD;
return x < MOD? x: x-MOD;
}
};
struct BarrettReduction64 {
ULL MOD;
ULL mh, ml;
BarrettReduction64(ULL MOD_): MOD(MOD_) {
ULL m = ~0ULL / MOD;
if (m*MOD+MOD == 0ULL) m++;
mh = m>>32;
ml = m & ~0u;
}
ULL reduce(ULL x) const {
ULL z = (x & ~0u) * ml;
z = (x & ~0u) * mh + (x>>32) * ml + (z>>32);
z = (x>>32) * mh + (z>>32);
x -= z * MOD;
return x < MOD? x: x-MOD;
}
};
const int SIZE = 1e8;
void test1(ULL MOD) {
ULL p = 1;
for (int i=1; i<SIZE; i++) {
p = p * i % MOD;
}
printf("%llu\n", p);
}
void test2(ULL MOD) {
MontgomeryReduction MR(MOD);
ULL p = MR.generate(1);
for (int i=1; i<SIZE; i++) {
p = MR.reduce(p * MR.generate(i));
}
p = MR.reduce(p);
printf("%llu\n", p);
}
void test3(ULL MOD) {
ULL p = 1;
BarrettReduction BR(MOD);
for (int i=1; i<SIZE; i++) {
p = BR.reduce(p * i);
}
printf("%llu\n", p);
}
void test4(ULL MOD) {
ULL p = 1;
BarrettReduction64 BR(MOD);
for (int i=1; i<SIZE; i++) {
p = BR.reduce(p * i);
}
printf("%llu\n", p);
}
int prime[] = {
998244353,
1000000007,
1000000009,
1000000021,
1000000033,
1000000087,
1000000093,
1000000097,
1000000103,
1000000123,
};
void MAIN() {
REP (i, 10) {
test3(prime[i]);
}
}
int main() {
int TC = 1;
// scanf("%d", &TC);
REP (tc, TC) MAIN();
return 0;
}
I3ByYWdtYSBHQ0Mgb3B0aW1pemUgKCJPMyIpCi8vICNwcmFnbWEgR0NDIHRhcmdldCAoImF2eCIpCiNpbmNsdWRlPHN0ZGlvLmg+CiNpbmNsdWRlPGlvc3RyZWFtPgojaW5jbHVkZTx2ZWN0b3I+CiNpbmNsdWRlPGFsZ29yaXRobT4KI2luY2x1ZGU8c3RyaW5nPgojaW5jbHVkZTxzdHJpbmcuaD4KCiNpZmRlZiBMT0NBTAojZGVmaW5lIGVwcmludGYoLi4uKSBmcHJpbnRmKHN0ZGVyciwgX19WQV9BUkdTX18pCiNlbHNlCiNkZWZpbmUgTkRFQlVHCiNkZWZpbmUgZXByaW50ZiguLi4pIGRvIHt9IHdoaWxlICgwKQojZW5kaWYKI2luY2x1ZGU8Y2Fzc2VydD4KI2luY2x1ZGU8cmFuZG9tPgoKdXNpbmcgbmFtZXNwYWNlIHN0ZDsKCnR5cGVkZWYgbG9uZyBsb25nIExMOwp0eXBlZGVmIHZlY3RvcjxpbnQ+IFZJOwp1c2luZyBVTEwgPSB1bnNpZ25lZCBsb25nIGxvbmc7CgojZGVmaW5lIFJFUChpLG4pIGZvcihpbnQgaT0wLCBpIyNfbGVuPShuKTsgaTxpIyNfbGVuOyArK2kpCiNkZWZpbmUgRUFDSChpLGMpIGZvcihfX3R5cGVvZigoYykuYmVnaW4oKSkgaT0oYykuYmVnaW4oKSxpIyNfZW5kPShjKS5lbmQoKTtpIT1pIyNfZW5kOysraSkKCnRlbXBsYXRlPGNsYXNzIFQ+IGlubGluZSB2b2lkIGFtaW4oVCAmeCwgY29uc3QgVCAmeSkgeyBpZiAoeTx4KSB4PXk7IH0KdGVtcGxhdGU8Y2xhc3MgVD4gaW5saW5lIHZvaWQgYW1heChUICZ4LCBjb25zdCBUICZ5KSB7IGlmICh4PHkpIHg9eTsgfQp0ZW1wbGF0ZTxjbGFzcyBJdGVyPiB2b2lkIHJwcmludGYoY29uc3QgY2hhciAqZm10LCBJdGVyIGJlZ2luLCBJdGVyIGVuZCkgewogICAgZm9yIChib29sIHNwPTA7IGJlZ2luIT1lbmQ7ICsrYmVnaW4pIHsgaWYgKHNwKSBwdXRjaGFyKCcgJyk7IGVsc2Ugc3AgPSB0cnVlOyBwcmludGYoZm10LCAqYmVnaW4pOyB9CiAgICBwdXRjaGFyKCdcbicpOwp9CgpzdHJ1Y3QgTW9udGdvbWVyeVJlZHVjdGlvbiB7CiAgICBVTEwgTU9EOwogICAgLy8gTU9EICogTkVHX0lOViAlICgyXjMyKSA9PSAoMl4zMikgLSAxOwogICAgVUxMIE5FR19JTlY7CiAgICAvLyBSMiA9PSAoMl42NCkgJSBNT0Q7CiAgICBVTEwgUjI7CiAgICBNb250Z29tZXJ5UmVkdWN0aW9uKFVMTCBNT0RfKTogTU9EKE1PRF8pIHsKCU5FR19JTlYgPSAwOwoJVUxMIHMgPSAxLCB0ID0gMDsKCVJFUCAoaSwgMzIpIHsKCSAgICBpZiAofnQgJiAxKSB7CgkJdCArPSBNT0Q7CgkJTkVHX0lOViArPSBzOwoJICAgIH0KCSAgICB0IC89IDI7CgkgICAgcyAqPSAyOwoJfQoKCVIyID0gKDFVTEw8PDMyKSAlIE1PRDsKCVIyID0gUjIgKiBSMiAlIE1PRDsKICAgIH0KCiAgICAvLyByZXR1cm4geCAqIFIgJSBNT0Q7CiAgICBVTEwgZ2VuZXJhdGUoVUxMIHgpIGNvbnN0IHsKCWFzc2VydCh4IDwgTU9EKTsKCXJldHVybiByZWR1Y2UoeCAqIFIyKTsKICAgIH0KCiAgICAvLyByZXR1cm4geCAvIFIgJSBNT0Q7CiAgICBVTEwgcmVkdWNlKFVMTCB4KSBjb25zdCB7Cglhc3NlcnQoeCA8IE1PRCAqIE1PRCk7Cgl4ID0gKHggKyAoKHVuc2lnbmVkKXggKiAodW5zaWduZWQpTkVHX0lOVikgKiBNT0QpID4+IDMyOwoJcmV0dXJuIHggPCBNT0Q/IHg6IHgtTU9EOwogICAgfQp9OwoKCnN0cnVjdCBCYXJyZXR0UmVkdWN0aW9uIHsKICAgIFVMTCBNT0Q7CiAgICBfX2ludDEyOCBtOwoKICAgIEJhcnJldHRSZWR1Y3Rpb24oVUxMIE1PRF8pOiBNT0QoTU9EXykgewoJbSA9IChfX2ludDEyOCgxKTw8NjQpIC8gTU9EOwogICAgfQoKICAgIFVMTCByZWR1Y2UoVUxMIHgpIGNvbnN0IHsKCVVMTCBxID0gKHgqbSk+PjY0OwoJeCAtPSBxICogTU9EOwoJcmV0dXJuIHggPCBNT0Q/IHg6IHgtTU9EOwogICAgfQp9OwoKc3RydWN0IEJhcnJldHRSZWR1Y3Rpb242NCB7CiAgICBVTEwgTU9EOwogICAgVUxMIG1oLCBtbDsKCiAgICBCYXJyZXR0UmVkdWN0aW9uNjQoVUxMIE1PRF8pOiBNT0QoTU9EXykgewoJVUxMIG0gPSB+MFVMTCAvIE1PRDsKCWlmIChtKk1PRCtNT0QgPT0gMFVMTCkgbSsrOwoJbWggPSBtPj4zMjsKCW1sID0gbSAmIH4wdTsKICAgIH0KCiAgICBVTEwgcmVkdWNlKFVMTCB4KSBjb25zdCB7CglVTEwgeiA9ICh4ICYgfjB1KSAqIG1sOwoJeiA9ICh4ICYgfjB1KSAqIG1oICsgKHg+PjMyKSAqIG1sICsgKHo+PjMyKTsKCXogPSAoeD4+MzIpICogbWggKyAoej4+MzIpOwoJeCAtPSB6ICogTU9EOwoJcmV0dXJuIHggPCBNT0Q/IHg6IHgtTU9EOwogICAgfQp9OwoKY29uc3QgaW50IFNJWkUgPSAxZTg7Cgp2b2lkIHRlc3QxKFVMTCBNT0QpIHsKICAgIFVMTCBwID0gMTsKICAgIGZvciAoaW50IGk9MTsgaTxTSVpFOyBpKyspIHsKCXAgPSBwICogaSAlIE1PRDsKICAgIH0KICAgIHByaW50ZigiJWxsdVxuIiwgcCk7Cn0KCnZvaWQgdGVzdDIoVUxMIE1PRCkgewogICAgTW9udGdvbWVyeVJlZHVjdGlvbiBNUihNT0QpOwogICAgVUxMIHAgPSBNUi5nZW5lcmF0ZSgxKTsKICAgIGZvciAoaW50IGk9MTsgaTxTSVpFOyBpKyspIHsKCXAgPSBNUi5yZWR1Y2UocCAqIE1SLmdlbmVyYXRlKGkpKTsKICAgIH0KICAgIHAgPSBNUi5yZWR1Y2UocCk7CiAgICBwcmludGYoIiVsbHVcbiIsIHApOwp9Cgp2b2lkIHRlc3QzKFVMTCBNT0QpIHsKICAgIFVMTCBwID0gMTsKICAgIEJhcnJldHRSZWR1Y3Rpb24gQlIoTU9EKTsKICAgIGZvciAoaW50IGk9MTsgaTxTSVpFOyBpKyspIHsKCXAgPSBCUi5yZWR1Y2UocCAqIGkpOwogICAgfQogICAgcHJpbnRmKCIlbGx1XG4iLCBwKTsKfQoKdm9pZCB0ZXN0NChVTEwgTU9EKSB7CiAgICBVTEwgcCA9IDE7CiAgICBCYXJyZXR0UmVkdWN0aW9uNjQgQlIoTU9EKTsKICAgIGZvciAoaW50IGk9MTsgaTxTSVpFOyBpKyspIHsKCXAgPSBCUi5yZWR1Y2UocCAqIGkpOwogICAgfQogICAgcHJpbnRmKCIlbGx1XG4iLCBwKTsKfQoKaW50IHByaW1lW10gPSB7CiAgICAgOTk4MjQ0MzUzLAogICAgMTAwMDAwMDAwNywKICAgIDEwMDAwMDAwMDksCiAgICAxMDAwMDAwMDIxLAogICAgMTAwMDAwMDAzMywKICAgIDEwMDAwMDAwODcsCiAgICAxMDAwMDAwMDkzLAogICAgMTAwMDAwMDA5NywKICAgIDEwMDAwMDAxMDMsCiAgICAxMDAwMDAwMTIzLAp9OwoKdm9pZCBNQUlOKCkgewogICAgUkVQIChpLCAxMCkgewoJdGVzdDMocHJpbWVbaV0pOwogICAgfQp9CgppbnQgbWFpbigpIHsKICAgIGludCBUQyA9IDE7Ci8vICAgIHNjYW5mKCIlZCIsICZUQyk7CiAgICBSRVAgKHRjLCBUQykgTUFJTigpOwogICAgcmV0dXJuIDA7Cn0K