Skip to content

Commit 8bad0b4

Browse files
committed
Faster divmod for tiny inputs
1 parent 3454603 commit 8bad0b4

File tree

1 file changed

+36
-0
lines changed

1 file changed

+36
-0
lines changed

cp-algo/math/decimal.hpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,47 @@ namespace cp_algo::math {
112112
}
113113
};
114114

115+
template<base_v base>
116+
auto divmod_fast(bigint<base> const& a, int64_t b) {
117+
// Optimized divmod for small divisors that fit in int64_t
118+
if (b == 0) {
119+
assert(false && "Division by zero");
120+
}
121+
bool neg_a = a.negative;
122+
bool neg_b = b < 0;
123+
b = std::abs(b);
124+
125+
bigint<base> quotient;
126+
uint64_t remainder = 0;
127+
128+
auto n = ssize(a.digits);
129+
for (auto i = n - 1; i >= 0; i--) {
130+
__uint128_t val = (__uint128_t)remainder * bigint<base>::Base + a.digits[i];
131+
uint64_t q = uint64_t(val / b);
132+
remainder = uint64_t(val % b);
133+
quotient.digits.push_back(q);
134+
}
135+
std::ranges::reverse(quotient.digits);
136+
quotient.negative = (neg_a ^ neg_b);
137+
quotient.normalize();
138+
139+
bigint<base> rem{int64_t(remainder)};
140+
rem.negative = neg_a;
141+
142+
return std::pair{quotient, rem};
143+
}
144+
115145
template<base_v base>
116146
auto divmod(bigint<base> const& a, bigint<base> const& b) {
117147
if (a < b) {
118148
return std::pair{bigint<base>(0), a};
119149
}
150+
// Use fast path if b fits in int64_t
151+
if (size(b.digits) == 1) {
152+
int64_t b_val = b.negative ? -int64_t(b.digits[0]) : int64_t(b.digits[0]);
153+
return divmod_fast(a, b_val);
154+
}
155+
// General case using decimal arithmetic
120156
auto A = decimal<base>(a);
121157
auto B = decimal<base>(b);
122158
auto d = (A * B.inv(A.magnitude())).trunc();

0 commit comments

Comments
 (0)