| // Copyright 2021 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // "Schoolbook" division. This is loosely based on Go's implementation |
| // found at https://golang.org/src/math/big/nat.go, licensed as follows: |
| // |
| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file [1]. |
| // |
| // [1] https://golang.org/LICENSE |
| |
| #include <limits> |
| |
| #include "src/bigint/bigint-internal.h" |
| #include "src/bigint/digit-arithmetic.h" |
| #include "src/bigint/div-helpers.h" |
| #include "src/bigint/util.h" |
| #include "src/bigint/vector-arithmetic.h" |
| |
| namespace v8 { |
| namespace bigint { |
| |
| // Computes Q(uotient) and remainder for A/b, such that |
| // Q = (A - remainder) / b, with 0 <= remainder < b. |
| // If Q.len == 0, only the remainder will be returned. |
| // Q may be the same as A for an in-place division. |
| void ProcessorImpl::DivideSingle(RWDigits Q, digit_t* remainder, Digits A, |
| digit_t b) { |
| DCHECK(b != 0); |
| DCHECK(A.len() > 0); |
| *remainder = 0; |
| int length = A.len(); |
| if (Q.len() != 0) { |
| if (A[length - 1] >= b) { |
| DCHECK(Q.len() >= A.len()); |
| for (int i = length - 1; i >= 0; i--) { |
| Q[i] = digit_div(*remainder, A[i], b, remainder); |
| } |
| for (int i = length; i < Q.len(); i++) Q[i] = 0; |
| } else { |
| DCHECK(Q.len() >= A.len() - 1); |
| *remainder = A[length - 1]; |
| for (int i = length - 2; i >= 0; i--) { |
| Q[i] = digit_div(*remainder, A[i], b, remainder); |
| } |
| for (int i = length - 1; i < Q.len(); i++) Q[i] = 0; |
| } |
| } else { |
| for (int i = length - 1; i >= 0; i--) { |
| digit_div(*remainder, A[i], b, remainder); |
| } |
| } |
| } |
| |
| // Z += X. Returns the "carry" (0 or 1) after adding all of X's digits. |
| inline digit_t InplaceAdd(RWDigits Z, Digits X) { |
| return AddAndReturnCarry(Z, Z, X); |
| } |
| |
| // Z -= X. Returns the "borrow" (0 or 1) after subtracting all of X's digits. |
| inline digit_t InplaceSub(RWDigits Z, Digits X) { |
| return SubtractAndReturnBorrow(Z, Z, X); |
| } |
| |
| // Returns whether (factor1 * factor2) > (high << kDigitBits) + low. |
| bool ProductGreaterThan(digit_t factor1, digit_t factor2, digit_t high, |
| digit_t low) { |
| digit_t result_high; |
| digit_t result_low = digit_mul(factor1, factor2, &result_high); |
| return result_high > high || (result_high == high && result_low > low); |
| } |
| |
| #if DEBUG |
| bool QLengthOK(Digits Q, Digits A, Digits B) { |
| // If A's top B.len digits are greater than or equal to B, then the division |
| // result will be greater than A.len - B.len, otherwise it will be that |
| // difference. Intuitively: 100/10 has 2 digits, 100/11 has 1. |
| if (GreaterThanOrEqual(Digits(A, A.len() - B.len(), B.len()), B)) { |
| return Q.len() >= A.len() - B.len() + 1; |
| } |
| return Q.len() >= A.len() - B.len(); |
| } |
| #endif |
| |
| // Computes Q(uotient) and R(emainder) for A/B, such that |
| // Q = (A - R) / B, with 0 <= R < B. |
| // Both Q and R are optional: callers that are only interested in one of them |
| // can pass the other with len == 0. |
| // If Q is present, its length must be at least A.len - B.len + 1. |
| // If R is present, its length must be at least B.len. |
| // See Knuth, Volume 2, section 4.3.1, Algorithm D. |
| void ProcessorImpl::DivideSchoolbook(RWDigits Q, RWDigits R, Digits A, |
| Digits B) { |
| DCHECK(B.len() >= 2); // Use DivideSingle otherwise. |
| DCHECK(A.len() >= B.len()); // No-op otherwise. |
| DCHECK(Q.len() == 0 || QLengthOK(Q, A, B)); |
| DCHECK(R.len() == 0 || R.len() >= B.len()); |
| // The unusual variable names inside this function are consistent with |
| // Knuth's book, as well as with Go's implementation of this algorithm. |
| // Maintaining this consistency is probably more useful than trying to |
| // come up with more descriptive names for them. |
| const int n = B.len(); |
| const int m = A.len() - n; |
| |
| // In each iteration, {qhatv} holds {divisor} * {current quotient digit}. |
| // "v" is the book's name for {divisor}, "qhat" the current quotient digit. |
| ScratchDigits qhatv(n + 1); |
| |
| // D1. |
| // Left-shift inputs so that the divisor's MSB is set. This is necessary |
| // to prevent the digit-wise divisions (see digit_div call below) from |
| // overflowing (they take a two digits wide input, and return a one digit |
| // result). |
| ShiftedDigits b_normalized(B); |
| B = b_normalized; |
| // U holds the (continuously updated) remaining part of the dividend, which |
| // eventually becomes the remainder. |
| ScratchDigits U(A.len() + 1); |
| LeftShift(U, A, b_normalized.shift()); |
| |
| // D2. |
| // Iterate over the dividend's digits (like the "grad school" algorithm). |
| // {vn1} is the divisor's most significant digit. |
| digit_t vn1 = B[n - 1]; |
| for (int j = m; j >= 0; j--) { |
| // D3. |
| // Estimate the current iteration's quotient digit (see Knuth for details). |
| // {qhat} is the current quotient digit. |
| digit_t qhat = std::numeric_limits<digit_t>::max(); |
| // {ujn} is the dividend's most significant remaining digit. |
| digit_t ujn = U[j + n]; |
| if (ujn != vn1) { |
| // {rhat} is the current iteration's remainder. |
| digit_t rhat = 0; |
| // Estimate the current quotient digit by dividing the most significant |
| // digits of dividend and divisor. The result will not be too small, |
| // but could be a bit too large. |
| qhat = digit_div(ujn, U[j + n - 1], vn1, &rhat); |
| |
| // Decrement the quotient estimate as needed by looking at the next |
| // digit, i.e. by testing whether |
| // qhat * v_{n-2} > (rhat << kDigitBits) + u_{j+n-2}. |
| digit_t vn2 = B[n - 2]; |
| digit_t ujn2 = U[j + n - 2]; |
| while (ProductGreaterThan(qhat, vn2, rhat, ujn2)) { |
| qhat--; |
| digit_t prev_rhat = rhat; |
| rhat += vn1; |
| // v[n-1] >= 0, so this tests for overflow. |
| if (rhat < prev_rhat) break; |
| } |
| } |
| |
| // D4. |
| // Multiply the divisor with the current quotient digit, and subtract |
| // it from the dividend. If there was "borrow", then the quotient digit |
| // was one too high, so we must correct it and undo one subtraction of |
| // the (shifted) divisor. |
| if (qhat == 0) { |
| qhatv.Clear(); |
| } else { |
| MultiplySingle(qhatv, B, qhat); |
| } |
| digit_t c = InplaceSub(U + j, qhatv); |
| if (c != 0) { |
| c = InplaceAdd(U + j, B); |
| U[j + n] = U[j + n] + c; |
| qhat--; |
| } |
| |
| if (Q.len() != 0) { |
| if (j >= Q.len()) { |
| DCHECK(qhat == 0); |
| } else { |
| Q[j] = qhat; |
| } |
| } |
| } |
| if (R.len() != 0) { |
| RightShift(R, U, b_normalized.shift()); |
| } |
| // If Q has extra storage, clear it. |
| for (int i = m + 1; i < Q.len(); i++) Q[i] = 0; |
| } |
| |
| } // namespace bigint |
| } // namespace v8 |