Index: vm/bigint_operations.cc |
=================================================================== |
--- vm/bigint_operations.cc (revision 4707) |
+++ vm/bigint_operations.cc (working copy) |
@@ -1,67 +1,46 @@ |
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
+// Copyright 2012 Google Inc. All Rights Reserved. |
#include "vm/bigint_operations.h" |
-#include <openssl/crypto.h> |
+#include "platform/utils.h" |
-#include "platform/utils.h" |
-#include "vm/bigint_store.h" |
#include "vm/double_internals.h" |
#include "vm/exceptions.h" |
#include "vm/zone.h" |
namespace dart { |
-bool Bigint::IsZero() const { return BN_is_zero(BNAddr()); } |
-bool Bigint::IsNegative() const { return !!BNAddr()->neg; } |
- |
- |
-void Bigint::SetSign(bool is_negative) const { |
- BIGNUM* bn = MutableBNAddr(); |
- // Danger Will Robinson! Use of OpenSSL internals! |
- // FIXME(benl): can be changed to use BN_set_negative() on more |
- // recent OpenSSL releases (> 1.0.0). |
- if (!is_negative || BN_is_zero(bn)) { |
- bn->neg = 0; |
- } else { |
- bn->neg = 1; |
- } |
-} |
- |
- |
-BIGNUM* BigintOperations::TmpBN() { |
- BigintStore* store = BigintStore::Get(); |
- if (store->bn_ == NULL) { |
- store->bn_ = BN_new(); |
- } |
- return store->bn_; |
-} |
- |
- |
-BN_CTX* BigintOperations::TmpBNCtx() { |
- BigintStore* store = BigintStore::Get(); |
- if (store->bn_ctx_ == NULL) { |
- store->bn_ctx_ = BN_CTX_new(); |
- } |
- return store->bn_ctx_; |
-} |
- |
- |
RawBigint* BigintOperations::NewFromSmi(const Smi& smi, Heap::Space space) { |
intptr_t value = smi.Value(); |
- bool is_negative = value < 0; |
+ if (value == 0) { |
+ return Zero(); |
+ } |
+ bool is_negative = (value < 0); |
if (is_negative) { |
value = -value; |
} |
+ // Assert that there are no overflows. Smis reserve a bit for themselves, but |
+ // protect against future changes. |
+ ASSERT(-Smi::kMinValue > 0); |
- BN_set_word(TmpBN(), value); |
+ // A single digit of a Bigint might not be sufficient to store a Smi. |
+ // Count number of needed Digits. |
+ intptr_t digit_count = 0; |
+ intptr_t count_value = value; |
+ while (count_value > 0) { |
+ digit_count++; |
+ count_value >>= kDigitBitSize; |
+ } |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); |
+ // Allocate a bigint of the correct size and copy the bits. |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(digit_count, space)); |
+ for (int i = 0; i < digit_count; i++) { |
+ result.SetChunkAt(i, static_cast<Chunk>(value & kDigitMask)); |
+ value >>= kDigitBitSize; |
+ } |
result.SetSign(is_negative); |
- |
+ ASSERT(IsClamped(result)); |
return result.raw(); |
} |
@@ -81,15 +60,27 @@ |
RawBigint* BigintOperations::NewFromUint64(uint64_t value, Heap::Space space) { |
- const int kNumBytes = sizeof(value); |
- unsigned char pch[kNumBytes]; |
- for (int i = kNumBytes - 1; i >= 0; i--) { |
- unsigned char c = value & 0xFF; |
- value >>=8; |
- pch[i] = c; |
+ if (value == 0) { |
+ return Zero(); |
} |
- BN_bin2bn(pch, kNumBytes, TmpBN()); |
- return Bigint::New(TmpBN(), space); |
+ // A single digit of a Bigint might not be sufficient to store the value. |
+ // Count number of needed Digits. |
+ intptr_t digit_count = 0; |
+ uint64_t count_value = value; |
+ while (count_value > 0) { |
+ digit_count++; |
+ count_value >>= kDigitBitSize; |
+ } |
+ |
+ // Allocate a bigint of the correct size and copy the bits. |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(digit_count, space)); |
+ for (int i = 0; i < digit_count; i++) { |
+ result.SetChunkAt(i, static_cast<Chunk>(value & kDigitMask)); |
+ value >>= kDigitBitSize; |
+ } |
+ result.SetSign(false); |
+ ASSERT(IsClamped(result)); |
+ return result.raw(); |
} |
@@ -97,7 +88,7 @@ |
Heap::Space space) { |
ASSERT(str != NULL); |
if (str[0] == '\0') { |
- return NewFromInt64(0, space); |
+ return Zero(); |
} |
// If the string starts with '-' recursively restart the whole operation |
@@ -108,11 +99,9 @@ |
// We don't catch leading '-'s for zero. Ex: "--0", or "---". |
if (str[0] == '-') { |
const Bigint& result = Bigint::Handle(NewFromCString(&str[1], space)); |
- if (!result.IsNull()) { |
- result.ToggleSign(); |
- // FIXME(benl): this will fail if there is more than one leading '-'. |
- ASSERT(result.IsZero() || result.IsNegative()); |
- } |
+ result.ToggleSign(); |
+ ASSERT(result.IsZero() || result.IsNegative()); |
+ ASSERT(IsClamped(result)); |
return result.raw(); |
} |
@@ -121,13 +110,111 @@ |
(str[0] == '0') && |
((str[1] == 'x') || (str[1] == 'X'))) { |
const Bigint& result = Bigint::Handle(FromHexCString(&str[2], space)); |
+ ASSERT(IsClamped(result)); |
return result.raw(); |
} else { |
- return FromDecimalCString(str, space); |
+ return FromDecimalCString(str); |
} |
} |
+RawBigint* BigintOperations::FromHexCString(const char* hex_string, |
+ Heap::Space space) { |
+ // If the string starts with '-' recursively restart the whole operation |
+ // without the character and then toggle the sign. |
+ // This allows multiple leading '-' (which will cancel each other out), but |
+ // we have added an assert, to make sure that the returned result of the |
+ // recursive call is not negative. |
+ // We don't catch leading '-'s for zero. Ex: "--0", or "---". |
+ if (hex_string[0] == '-') { |
+ const Bigint& value = Bigint::Handle(FromHexCString(&hex_string[1], space)); |
+ value.ToggleSign(); |
+ ASSERT(value.IsZero() || value.IsNegative()); |
+ ASSERT(IsClamped(value)); |
+ return value.raw(); |
+ } |
+ |
+ ASSERT(kDigitBitSize % 4 == 0); |
+ const int kHexCharsPerDigit = kDigitBitSize / 4; |
+ |
+ intptr_t hex_length = strlen(hex_string); |
+ // Round up. |
+ intptr_t bigint_length = ((hex_length - 1) / kHexCharsPerDigit) + 1; |
+ const Bigint& result = |
+ Bigint::Handle(Bigint::Allocate(bigint_length, space)); |
+ // The bigint's least significant digit (lsd) is at position 0, whereas the |
+ // given string has it's lsd at the last position. |
+ // The hex_i index, pointing into the string, starts therefore at the end, |
+ // whereas the bigint-index (i) starts at 0. |
+ intptr_t hex_i = hex_length - 1; |
+ for (intptr_t i = 0; i < bigint_length; i++) { |
+ Chunk digit = 0; |
+ int shift = 0; |
+ for (int j = 0; j < kHexCharsPerDigit; j++) { |
+ // Reads a block of hexadecimal digits and stores it in 'digit'. |
+ // Ex: "0123456" with kHexCharsPerDigit == 3, hex_i == 6, reads "456". |
+ if (hex_i < 0) { |
+ break; |
+ } |
+ ASSERT(hex_i >= 0); |
+ char c = hex_string[hex_i--]; |
+ ASSERT(Utils::IsHexDigit(c)); |
+ digit += static_cast<Chunk>(Utils::HexDigitToInt(c)) << shift; |
+ shift += 4; |
+ } |
+ result.SetChunkAt(i, digit); |
+ } |
+ ASSERT(hex_i == -1); |
+ Clamp(result); |
+ return result.raw(); |
+} |
+ |
+ |
+RawBigint* BigintOperations::FromDecimalCString(const char* str, |
+ Heap::Space space) { |
+ // Read 8 digits a time. 10^8 < 2^27. |
+ const int kDigitsPerIteration = 8; |
+ const Chunk kTenMultiplier = 100000000; |
+ ASSERT(kDigitBitSize >= 27); |
+ |
+ intptr_t str_length = strlen(str); |
+ intptr_t str_pos = 0; |
+ |
+ // Read first digit separately. This avoids a multiplication and addition. |
+ // The first digit might also not have kDigitsPerIteration decimal digits. |
+ int first_digit_decimal_digits = str_length % kDigitsPerIteration; |
+ Chunk digit = 0; |
+ for (intptr_t i = 0; i < first_digit_decimal_digits; i++) { |
+ char c = str[str_pos++]; |
+ ASSERT(('0' <= c) && (c <= '9')); |
+ digit = digit * 10 + c - '0'; |
+ } |
+ Bigint& result = Bigint::Handle(Bigint::Allocate(1, space)); |
+ result.SetChunkAt(0, digit); |
+ Clamp(result); // Multiplication requires the inputs to be clamped. |
+ |
+ // Read kDigitsPerIteration at a time, and store it in 'increment'. |
+ // Then multiply the temporary result by 10^kDigitsPerIteration and add |
+ // 'increment' to the new result. |
+ const Bigint& increment = Bigint::Handle(Bigint::Allocate(1, space)); |
+ while (str_pos < str_length - 1) { |
+ Chunk digit = 0; |
+ for (intptr_t i = 0; i < kDigitsPerIteration; i++) { |
+ char c = str[str_pos++]; |
+ ASSERT(('0' <= c) && (c <= '9')); |
+ digit = digit * 10 + c - '0'; |
+ } |
+ result ^= MultiplyWithDigit(result, kTenMultiplier); |
+ if (digit != 0) { |
+ increment.SetChunkAt(0, digit); |
+ result ^= Add(result, increment); |
+ } |
+ } |
+ Clamp(result); |
+ return result.raw(); |
+} |
+ |
+ |
RawBigint* BigintOperations::NewFromDouble(double d, Heap::Space space) { |
if ((-1.0 < d) && (d < 1.0)) { |
// Shortcut for small numbers. Also makes the right-shift below |
@@ -167,112 +254,141 @@ |
} |
-RawBigint* BigintOperations::FromHexCString(const char* hex_string, |
- Heap::Space space) { |
- BIGNUM *bn = TmpBN(); |
- BN_hex2bn(&bn, hex_string); |
- ASSERT(bn == TmpBN()); |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); |
- return result.raw(); |
-} |
+const char* BigintOperations::ToHexCString(intptr_t length, |
+ bool is_negative, |
+ void* data, |
+ uword (*allocator)(intptr_t size)) { |
+ NoGCScope no_gc; |
+ ASSERT(kDigitBitSize % 4 == 0); |
+ const int kHexCharsPerDigit = kDigitBitSize / 4; |
-RawBigint* BigintOperations::FromDecimalCString(const char* str, |
- Heap::Space space) { |
- BIGNUM *bn = TmpBN(); |
- int len = BN_dec2bn(&bn, str); |
- if (len == 0) { |
- return Bigint::null(); |
+ intptr_t chunk_length = length; |
+ Chunk* chunk_data = reinterpret_cast<Chunk*>(data); |
+ if (length == 0) { |
+ const char* zero = "0x0"; |
+ const int kLength = strlen(zero); |
+ char* result = reinterpret_cast<char*>(allocator(kLength + 1)); |
+ ASSERT(result != NULL); |
+ memmove(result, zero, kLength); |
+ result[kLength] = '\0'; |
+ return result; |
} |
- ASSERT(bn == TmpBN()); |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN(), space)); |
- return result.raw(); |
-} |
+ ASSERT(chunk_data != NULL); |
- |
-const char* BigintOperations::ToHexCString(const BIGNUM *bn, |
- uword (*allocator)(intptr_t size)) { |
- char* str = BN_bn2hex(bn); |
- char* to_free = str; |
- intptr_t neg = 0; |
- if (str[0] == '-') { |
- ++str; |
- neg = 1; |
+ // Compute the number of hex-digits that are needed to represent the |
+ // leading bigint-digit. All other digits need exactly kHexCharsPerDigit |
+ // characters. |
+ int leading_hex_digits = 0; |
+ Chunk leading_digit = chunk_data[chunk_length - 1]; |
+ while (leading_digit != 0) { |
+ leading_hex_digits++; |
+ leading_digit >>= 4; |
} |
- if (str[0] == '0' && str[1] != '\0') { |
- ++str; |
+ // Sum up the space that is needed for the string-representation. |
+ intptr_t required_size = 0; |
+ if (is_negative) { |
+ required_size++; // For the leading "-". |
} |
- intptr_t length = strlen(str) + 3 + neg; |
- char *result = reinterpret_cast<char*>(allocator(length)); |
- if (neg) { |
- result[0] = '-'; |
- result[1] = '0'; |
- result[2] = 'x'; |
- // memcpy would suffice |
- memmove(result + 3, str, length - 3); |
- } else { |
- result[0] = '0'; |
- result[1] = 'x'; |
- // memcpy would suffice |
- memmove(result + 2, str, length - 2); |
+ required_size += 2; // For the "0x". |
+ required_size += leading_hex_digits; |
+ required_size += (chunk_length - 1) * kHexCharsPerDigit; |
+ required_size++; // For the trailing '\0'. |
+ char* result = reinterpret_cast<char*>(allocator(required_size)); |
+ // Print the number into the string. |
+ // Start from the last position. |
+ intptr_t pos = required_size - 1; |
+ result[pos--] = '\0'; |
+ for (intptr_t i = 0; i < (chunk_length - 1); i++) { |
+ // Print all non-leading characters (which are printed with |
+ // kHexCharsPerDigit characters. |
+ Chunk digit = chunk_data[i]; |
+ for (int j = 0; j < kHexCharsPerDigit; j++) { |
+ result[pos--] = Utils::IntToHexDigit(static_cast<int>(digit & 0xF)); |
+ digit >>= 4; |
+ } |
} |
- OPENSSL_free(to_free); |
+ // Print the leading digit. |
+ leading_digit = chunk_data[chunk_length - 1]; |
+ while (leading_digit != 0) { |
+ result[pos--] = Utils::IntToHexDigit(static_cast<int>(leading_digit & 0xF)); |
+ leading_digit >>= 4; |
+ } |
+ result[pos--] = 'x'; |
+ result[pos--] = '0'; |
+ if (is_negative) { |
+ result[pos--] = '-'; |
+ } |
+ ASSERT(pos == -1); |
return result; |
} |
-const char* BigintOperations::ToDecCString(const Bigint& bigint, |
+const char* BigintOperations::ToHexCString(const Bigint& bigint, |
uword (*allocator)(intptr_t size)) { |
- return ToDecCString(bigint.BNAddr(), allocator); |
-} |
+ NoGCScope no_gc; |
- |
-const char* BigintOperations::ToDecCString(const BIGNUM *bn, |
- uword (*allocator)(intptr_t size)) { |
- char* str = BN_bn2dec(bn); |
- intptr_t length = strlen(str) + 1; // '\0'-terminated. |
- char* result = reinterpret_cast<char*>(allocator(length)); |
- memmove(result, str, length); |
- OPENSSL_free(str); |
- return result; |
+ intptr_t length = bigint.Length(); |
+ return ToHexCString(length, |
+ bigint.IsNegative(), |
+ length ? bigint.ChunkAddr(0) : NULL, |
+ allocator); |
} |
-const char* BigintOperations::ToHexCString(const Bigint& bigint, |
- uword (*allocator)(intptr_t size)) { |
- return ToHexCString(bigint.BNAddr(), allocator); |
-} |
+bool BigintOperations::FitsIntoSmi(const Bigint& bigint) { |
+ intptr_t bigint_length = bigint.Length(); |
+ if (bigint_length == 0) { |
+ return true; |
+ } |
+ if ((bigint_length == 1) && |
+ (static_cast<size_t>(kDigitBitSize) < |
+ (sizeof(intptr_t) * kBitsPerByte))) { |
+ return true; |
+ } |
+ uintptr_t limit; |
+ if (bigint.IsNegative()) { |
+ limit = static_cast<uintptr_t>(-Smi::kMinValue); |
+ } else { |
+ limit = static_cast<uintptr_t>(Smi::kMaxValue); |
+ } |
+ bool bigint_is_greater = false; |
+ // Consume the least-significant digits of the bigint. |
+ // If bigint_is_greater is set, then the processed sub-part of the bigint is |
+ // greater than the corresponding part of the limit. |
+ for (int i = 0; i < bigint_length - 1; i++) { |
+ Chunk limit_digit = static_cast<Chunk>(limit & kDigitMask); |
+ Chunk bigint_digit = bigint.GetChunkAt(i); |
+ if (limit_digit < bigint_digit) { |
+ bigint_is_greater = true; |
+ } else if (limit_digit > bigint_digit) { |
+ bigint_is_greater = false; |
+ } // else don't change the boolean. |
+ limit >>= kDigitBitSize; |
-bool BigintOperations::FitsIntoSmi(const Bigint& bigint) { |
- ASSERT(!bigint.IsNull()); |
- const BIGNUM *bn = bigint.BNAddr(); |
- int bits = BN_num_bits(bn); |
- // Special case for kMinValue as the absolute value is 1 bit longer |
- // than anything else |
- if (bits == Smi::kBits + 1 && BN_abs_is_word(bn, -Smi::kMinValue) |
- && bigint.IsNegative()) { |
+ // Bail out if the bigint is definitely too big. |
+ if (limit == 0) { |
+ return false; |
+ } |
+ } |
+ Chunk most_significant_digit = bigint.GetChunkAt(bigint_length - 1); |
+ if (limit > most_significant_digit) { |
return true; |
} |
- // All other cases must have no more bits than the size of an Smi |
- if (bits > Smi::kBits) { |
+ if (limit < most_significant_digit) { |
return false; |
} |
- return true; |
+ return !bigint_is_greater; |
} |
RawSmi* BigintOperations::ToSmi(const Bigint& bigint) { |
ASSERT(FitsIntoSmi(bigint)); |
- unsigned char bytes[kBitsPerWord / kBitsPerByte]; |
- ASSERT(BN_num_bytes(bigint.BNAddr()) <= static_cast<int>(sizeof bytes)); |
- int n = BN_bn2bin(bigint.BNAddr(), bytes); |
- ASSERT(n >= 0); |
intptr_t value = 0; |
- ASSERT(n <= static_cast<int>(sizeof value)); |
- for (int i = 0; i < n; ++i) { |
- value <<= 8; |
- value |= bytes[i]; |
+ for (int i = bigint.Length() - 1; i >= 0; i--) { |
+ value <<= kDigitBitSize; |
+ value += static_cast<intptr_t>(bigint.GetChunkAt(i)); |
} |
if (bigint.IsNegative()) { |
value = -value; |
@@ -284,10 +400,11 @@ |
RawDouble* BigintOperations::ToDouble(const Bigint& bigint) { |
// TODO(floitsch/benl): This is a quick and dirty implementation to unblock |
// other areas of the code. It does not handle all bit-twiddling correctly. |
+ const double shift_value = (1 << kDigitBitSize); |
double value = 0.0; |
- for (int i = bigint.NumberOfBits() - 1; i >= 0; --i) { |
- value *= 2; |
- value += static_cast<double>(bigint.Bit(i)); |
+ for (int i = bigint.Length() - 1; i >= 0; i--) { |
+ value *= shift_value; |
+ value += static_cast<double>(bigint.GetChunkAt(i)); |
} |
if (bigint.IsNegative()) { |
value = -value; |
@@ -296,40 +413,65 @@ |
} |
-bool BigintOperations::FitsIntoInt64(const Bigint& bigint) { |
- const BIGNUM *bn = bigint.BNAddr(); |
- int bits = BN_num_bits(bn); |
- if (bits <= 63) return true; |
- if (bits > 64) return false; |
- if (!bigint.IsNegative()) return false; |
- // Special case for negative values, since Int64 representation may lose |
- // one bit. |
- ASSERT(bigint.Bit(63) != 0); |
- for (int i = 0; i < 63; i++) { |
- // Verify that all 63 least significant bits are 0. |
- if (bigint.Bit(i) != 0) return false; |
+bool BigintOperations::FitsIntoMint(const Bigint& bigint) { |
+ intptr_t bigint_length = bigint.Length(); |
+ if (bigint_length == 0) { |
+ return true; |
} |
- return true; |
+ if ((bigint_length < 3) && |
+ (static_cast<size_t>(kDigitBitSize) < |
+ (sizeof(intptr_t) * kBitsPerByte))) { |
+ return true; |
+ } |
+ |
+ uint64_t limit; |
+ if (bigint.IsNegative()) { |
+ limit = static_cast<uint64_t>(Mint::kMinValue); |
+ } else { |
+ limit = static_cast<uint64_t>(Mint::kMaxValue); |
+ } |
+ bool bigint_is_greater = false; |
+ // Consume the least-significant digits of the bigint. |
+ // If bigint_is_greater is set, then the processed sub-part of the bigint is |
+ // greater than the corresponding part of the limit. |
+ for (int i = 0; i < bigint_length - 1; i++) { |
+ Chunk limit_digit = static_cast<Chunk>(limit & kDigitMask); |
+ Chunk bigint_digit = bigint.GetChunkAt(i); |
+ if (limit_digit < bigint_digit) { |
+ bigint_is_greater = true; |
+ } else if (limit_digit > bigint_digit) { |
+ bigint_is_greater = false; |
+ } // else don't change the boolean. |
+ limit >>= kDigitBitSize; |
+ |
+ // Bail out if the bigint is definitely too big. |
+ if (limit == 0) { |
+ return false; |
+ } |
+ } |
+ Chunk most_significant_digit = bigint.GetChunkAt(bigint_length - 1); |
+ if (limit > most_significant_digit) { |
+ return true; |
+ } |
+ if (limit < most_significant_digit) { |
+ return false; |
+ } |
+ return !bigint_is_greater; |
} |
uint64_t BigintOperations::AbsToUint64(const Bigint& bigint) { |
- unsigned char bytes[8]; |
- ASSERT(BN_num_bytes(bigint.BNAddr()) <= static_cast<int>(sizeof bytes)); |
- int n = BN_bn2bin(bigint.BNAddr(), bytes); |
- ASSERT(n >= 0); |
uint64_t value = 0; |
- ASSERT(n <= static_cast<int>(sizeof value)); |
- for (int i = 0; i < n; ++i) { |
- value <<= 8; |
- value |= bytes[i]; |
+ for (int i = bigint.Length() - 1; i >= 0; i--) { |
+ value <<= kDigitBitSize; |
+ value += static_cast<intptr_t>(bigint.GetChunkAt(i)); |
} |
return value; |
} |
-int64_t BigintOperations::ToInt64(const Bigint& bigint) { |
- ASSERT(FitsIntoInt64(bigint)); |
+int64_t BigintOperations::ToMint(const Bigint& bigint) { |
+ ASSERT(FitsIntoMint(bigint)); |
int64_t value = AbsToUint64(bigint); |
if (bigint.IsNegative()) { |
value = -value; |
@@ -339,10 +481,11 @@ |
bool BigintOperations::FitsIntoUint64(const Bigint& bigint) { |
- const BIGNUM *bn = bigint.BNAddr(); |
if (bigint.IsNegative()) return false; |
- int bits = BN_num_bits(bn); |
- if (bits > 64) return false; |
+ intptr_t b_length = bigint.Length(); |
+ int num_bits = CountBits(bigint.GetChunkAt(b_length - 1)); |
+ num_bits += (kDigitBitSize * (b_length - 1)); |
+ if (num_bits > 64) return false; |
return true; |
} |
@@ -353,285 +496,929 @@ |
} |
-RawBigint* BigintOperations::Add(const Bigint& a, const Bigint& b) { |
- int status = BN_add(TmpBN(), a.BNAddr(), b.BNAddr()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Add"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
- } |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
- return result.raw(); |
-} |
+RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ intptr_t result_length = a_length + b_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
-RawBigint* BigintOperations::Subtract(const Bigint& a, const Bigint& b) { |
- int status = BN_sub(TmpBN(), a.BNAddr(), b.BNAddr()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Subtract"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
+ if (a.IsNegative() != b.IsNegative()) { |
+ result.ToggleSign(); |
} |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
- return result.raw(); |
-} |
+ // Comba multiplication: compute each column separately. |
+ // Example: r = a2a1a0 * b2b1b0. |
+ // r = 1 * a0b0 + |
+ // 10 * (a1b0 + a0b1) + |
+ // 100 * (a2b0 + a1b1 + a0b2) + |
+ // 1000 * (a2b1 + a1b2) + |
+ // 10000 * a2b2 |
+ // |
+ // Each column will be accumulated in an integer of type DoubleChunk. We |
+ // must guarantee that the column-sum will not overflow. |
+ // |
+ // In the worst case we have to accumulate k = Min(a.length, b.length) |
+ // products plus the carry from the previous round. |
+ // Each bigint-digit is smaller than beta = 2^kDigitBitSize. |
+ // Each product is at most (beta - 1)^2. |
+ // If we want to use Comba multiplication the following condition must hold: |
+ // k * (beta - 1)^2 + (2^(kDoubleChunkBitSize - kDigitBitSize) - 1) < |
+ // 2^kDoubleChunkBitSize. |
+ const DoubleChunk square = |
+ static_cast<DoubleChunk>(kDigitMaxValue) * kDigitMaxValue; |
+ const DoubleChunk kDoubleChunkMaxValue = static_cast<DoubleChunk>(-1); |
+ const DoubleChunk left_over_carry = kDoubleChunkMaxValue >> kDigitBitSize; |
+ const intptr_t kMaxDigits = (kDoubleChunkMaxValue - left_over_carry) / square; |
+ if (Utils::Minimum(a_length, b_length) > kMaxDigits) { |
+ UNIMPLEMENTED(); |
+ } |
-RawBigint* BigintOperations::Multiply(const Bigint& a, const Bigint& b) { |
- int status = BN_mul(TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Multiply"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
+ DoubleChunk accumulator = 0; // Accumulates the result of one column. |
+ for (intptr_t i = 0; i < result_length; i++) { |
+ // Example: r = a2a1a0 * b2b1b0. |
+ // For i == 0, compute a0b0. |
+ // i == 1, a1b0 + a0b1 + overflow from i == 0. |
+ // i == 2, a2b0 + a1b1 + a0b2 + overflow from i == 1. |
+ // ... |
+ // The indices into a and b are such that their sum equals i. |
+ intptr_t a_index = Utils::Minimum(a_length - 1, i); |
+ intptr_t b_index = i - a_index; |
+ ASSERT(a_index + b_index == i); |
+ |
+ // Instead of testing for a_index >= 0 && b_index < b_length we compute the |
+ // number of iterations first. |
+ intptr_t iterations = Utils::Minimum(b_length - b_index, a_index + 1); |
+ for (intptr_t j = 0; j < iterations; j++) { |
+ DoubleChunk chunk_a = a.GetChunkAt(a_index); |
+ DoubleChunk chunk_b = b.GetChunkAt(b_index); |
+ accumulator += chunk_a * chunk_b; |
+ a_index--; |
+ b_index++; |
+ } |
+ result.SetChunkAt(i, static_cast<Chunk>(accumulator & kDigitMask)); |
+ accumulator >>= kDigitBitSize; |
} |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
+ ASSERT(accumulator == 0); |
+ |
+ Clamp(result); |
return result.raw(); |
} |
RawBigint* BigintOperations::Divide(const Bigint& a, const Bigint& b) { |
- int status = BN_div(TmpBN(), NULL, a.BNAddr(), b.BNAddr(), TmpBNCtx()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Divide"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
- } |
- const Bigint& quotient = Bigint::Handle(Bigint::New(TmpBN())); |
+ Bigint& quotient = Bigint::Handle(); |
+ Bigint& remainder = Bigint::Handle(); |
+ DivideRemainder(a, b, "ient, &remainder); |
return quotient.raw(); |
} |
RawBigint* BigintOperations::Modulo(const Bigint& a, const Bigint& b) { |
- int status = BN_nnmod(TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Modulo"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
- } |
- const Bigint& modulo = Bigint::Handle(Bigint::New(TmpBN())); |
+ Bigint& quotient = Bigint::Handle(); |
+ Bigint& modulo = Bigint::Handle(); |
+ DivideRemainder(a, b, "ient, &modulo); |
return modulo.raw(); |
} |
RawBigint* BigintOperations::Remainder(const Bigint& a, const Bigint& b) { |
- int status = BN_div(NULL, TmpBN(), a.BNAddr(), b.BNAddr(), TmpBNCtx()); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::Remainder"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
- } |
- const Bigint& remainder = Bigint::Handle(Bigint::New(TmpBN())); |
+ Bigint& quotient = Bigint::Handle(); |
+ Bigint& remainder = Bigint::Handle(); |
+ DivideRemainder(a, b, "ient, &remainder); |
return remainder.raw(); |
} |
RawBigint* BigintOperations::ShiftLeft(const Bigint& bigint, intptr_t amount) { |
- int status = BN_lshift(TmpBN(), bigint.BNAddr(), amount); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::ShiftLeft"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
+ ASSERT(IsClamped(bigint)); |
+ ASSERT(amount >= 0); |
+ intptr_t bigint_length = bigint.Length(); |
+ if (bigint.IsZero()) { |
+ return Zero(); |
} |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
- return result.raw(); |
+ // TODO(floitsch): can we reuse the input? |
+ if (amount == 0) { |
+ return Copy(bigint); |
+ } |
+ intptr_t digit_shift = amount / kDigitBitSize; |
+ intptr_t bit_shift = amount % kDigitBitSize; |
+ if (bit_shift == 0) { |
+ const Bigint& result = |
+ Bigint::Handle(Bigint::Allocate(bigint_length + digit_shift)); |
+ for (intptr_t i = 0; i < digit_shift; i++) { |
+ result.SetChunkAt(i, 0); |
+ } |
+ for (intptr_t i = 0; i < bigint_length; i++) { |
+ result.SetChunkAt(i + digit_shift, bigint.GetChunkAt(i)); |
+ } |
+ if (bigint.IsNegative()) { |
+ result.ToggleSign(); |
+ } |
+ return result.raw(); |
+ } else { |
+ const Bigint& result = |
+ Bigint::Handle(Bigint::Allocate(bigint_length + digit_shift + 1)); |
+ for (intptr_t i = 0; i < digit_shift; i++) { |
+ result.SetChunkAt(i, 0); |
+ } |
+ Chunk carry = 0; |
+ for (intptr_t i = 0; i < bigint_length; i++) { |
+ Chunk digit = bigint.GetChunkAt(i); |
+ Chunk shifted_digit = ((digit << bit_shift) & kDigitMask) + carry; |
+ result.SetChunkAt(i + digit_shift, shifted_digit); |
+ carry = digit >> (kDigitBitSize - bit_shift); |
+ } |
+ result.SetChunkAt(bigint_length + digit_shift, carry); |
+ if (bigint.IsNegative()) { |
+ result.ToggleSign(); |
+ } |
+ Clamp(result); |
+ return result.raw(); |
+ } |
} |
RawBigint* BigintOperations::ShiftRight(const Bigint& bigint, intptr_t amount) { |
+ ASSERT(IsClamped(bigint)); |
ASSERT(amount >= 0); |
- int status = BN_rshift(TmpBN(), bigint.BNAddr(), amount); |
- if (status == 0) { |
- GrowableArray<const Object*> exception_arguments; |
- exception_arguments.Add( |
- &Object::ZoneHandle(String::New("BigintOperations::ShiftRight"))); |
- Exceptions::ThrowByType(Exceptions::kInternalError, exception_arguments); |
+ intptr_t bigint_length = bigint.Length(); |
+ if (bigint.IsZero()) { |
+ return Zero(); |
} |
- // OpenSSL doesn't take account of sign when shifting - this fixes it. |
+ // TODO(floitsch): can we reuse the input? |
+ if (amount == 0) { |
+ return Copy(bigint); |
+ } |
+ intptr_t digit_shift = amount / kDigitBitSize; |
+ intptr_t bit_shift = amount % kDigitBitSize; |
+ if (digit_shift >= bigint_length) { |
+ return bigint.IsNegative() ? MinusOne() : Zero(); |
+ } |
+ |
+ const Bigint& result = |
+ Bigint::Handle(Bigint::Allocate(bigint_length - digit_shift)); |
+ if (bit_shift == 0) { |
+ for (intptr_t i = 0; i < bigint_length - digit_shift; i++) { |
+ result.SetChunkAt(i, bigint.GetChunkAt(i + digit_shift)); |
+ } |
+ } else { |
+ Chunk carry = 0; |
+ for (intptr_t i = bigint_length - 1; i >= digit_shift; i--) { |
+ Chunk digit = bigint.GetChunkAt(i); |
+ Chunk shifted_digit = (digit >> bit_shift) + carry; |
+ result.SetChunkAt(i - digit_shift, shifted_digit); |
+ carry = (digit << (kDigitBitSize - bit_shift)) & kDigitMask; |
+ } |
+ Clamp(result); |
+ } |
+ |
if (bigint.IsNegative()) { |
- for (intptr_t i = 0; i < amount; ++i) { |
- if (bigint.IsBitSet(i)) { |
- int status = BN_sub_word(TmpBN(), 1); |
- ASSERT(status == 1); |
+ result.ToggleSign(); |
+ // If the input is negative then the result needs to be rounded down. |
+ // Example: -5 >> 2 => -2 |
+ bool needs_rounding = false; |
+ for (intptr_t i = 0; i < digit_shift; i++) { |
+ if (bigint.GetChunkAt(i) != 0) { |
+ needs_rounding = true; |
break; |
} |
} |
+ if (!needs_rounding && (bit_shift > 0)) { |
+ Chunk digit = bigint.GetChunkAt(digit_shift); |
+ needs_rounding = (digit << (kChunkBitSize - bit_shift)) != 0; |
+ } |
+ if (needs_rounding) { |
+ Bigint& one = Bigint::Handle(One()); |
+ return Subtract(result, one); |
+ } |
} |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
+ |
return result.raw(); |
} |
-/* Bit operations are complicated by negatives: BIGNUMs don't use 2's |
- * complement, but instead store the absolute value and a sign |
- * indicator. However bit operations are defined on 2's complement |
- * representations. This function handles the necessary |
- * invert-and-carry operations to deal with the fact that -x = ~x + 1 |
- * both on the operands and result. The actual operation performed is |
- * specified by the truth table |tt|, which is in the order of input |
- * bits (00, 01, 10, 11). |
- */ |
-RawBigint* BigintOperations::BitTT(const Bigint& a, const Bigint& b, |
- bool tt[4]) { |
- BN_zero(TmpBN()); |
- int n; |
- int rflip = 0; |
- int rborrow = 0; |
- // There's probably a clever way to figure out why these are what |
- // they are, but I confess I worked them out pragmatically. |
- if (a.IsNegative() && b.IsNegative()) { |
- if (tt[3]) { |
- rflip = -1; |
- rborrow = -1; |
+ |
+RawBigint* BigintOperations::BitAnd(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ |
+ if (a.IsZero() || b.IsZero()) { |
+ return Zero(); |
+ } |
+ if (a.IsNegative() && !b.IsNegative()) { |
+ return BitAnd(b, a); |
+ } |
+ if ((a.IsNegative() == b.IsNegative()) && (a.Length() < b.Length())) { |
+ return BitAnd(b, a); |
+ } |
+ |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ intptr_t min_length = Utils::Minimum(a_length, b_length); |
+ intptr_t max_length = Utils::Maximum(a_length, b_length); |
+ if (!b.IsNegative()) { |
+ ASSERT(!a.IsNegative()); |
+ intptr_t result_length = min_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ |
+ for (intptr_t i = 0; i < min_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i) & b.GetChunkAt(i)); |
} |
- if (tt[2] != tt[3]) { |
- n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); |
- } else { |
- n = Utils::Minimum(a.NumberOfBits(), b.NumberOfBits()); |
+ Clamp(result); |
+ return result.raw(); |
+ } |
+ |
+ // Bigints encode negative values by storing the absolute value and the sign |
+ // separately. To do bit operations we need to simulate numbers that are |
+ // implemented as two's complement. |
+ // The negation of a positive number x would be encoded as follows in |
+ // two's complement: n = ~(x - 1). |
+ // The inverse transformation is hence (~n) + 1. |
+ |
+ if (!a.IsNegative()) { |
+ ASSERT(b.IsNegative()); |
+ // The result will be positive. |
+ intptr_t result_length = a_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ Chunk borrow = 1; |
+ for (intptr_t i = 0; i < min_length; i++) { |
+ Chunk b_digit = b.GetChunkAt(i) - borrow; |
+ result.SetChunkAt(i, a.GetChunkAt(i) & (~b_digit) & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
} |
- } else if (a.IsNegative() || b.IsNegative()) { |
- if (tt[2]) { |
- rflip = rborrow = -1; |
+ for (intptr_t i = min_length; i < a_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i) & (kDigitMaxValue - borrow)); |
+ borrow = 0; |
} |
- n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); |
- } else { |
- if (tt[2]) { |
- n = Utils::Maximum(a.NumberOfBits(), b.NumberOfBits()); |
- } else { |
- n = Utils::Minimum(a.NumberOfBits(), b.NumberOfBits()); |
+ Clamp(result); |
+ return result.raw(); |
+ } |
+ |
+ ASSERT(a.IsNegative()); |
+ ASSERT(b.IsNegative()); |
+ // The result will be negative. |
+ // We need to convert a and b to two's complement. Do the bit-operation there, |
+ // and transform the resulting bits from two's complement back to separated |
+ // magnitude and sign. |
+ // a & b is therefore computed as ~((~(a - 1)) & (~(b - 1))) + 1 which is |
+ // equal to ((a-1) | (b-1)) + 1. |
+ intptr_t result_length = max_length + 1; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ result.ToggleSign(); |
+ Chunk a_borrow = 1; |
+ Chunk b_borrow = 1; |
+ Chunk result_carry = 1; |
+ ASSERT(a_length >= b_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i) - a_borrow; |
+ Chunk b_digit = b.GetChunkAt(i) - b_borrow; |
+ Chunk result_chunk = ((a_digit | b_digit) & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_chunk & kDigitMask); |
+ a_borrow = a_digit >> (kChunkBitSize - 1); |
+ b_borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_chunk >> kDigitBitSize; |
+ } |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i) - a_borrow; |
+ Chunk b_digit = -b_borrow; |
+ Chunk result_chunk = ((a_digit | b_digit) & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_chunk & kDigitMask); |
+ a_borrow = a_digit >> (kChunkBitSize - 1); |
+ b_borrow = 0; |
+ result_carry = result_chunk >> kDigitBitSize; |
+ } |
+ Chunk a_digit = -a_borrow; |
+ Chunk b_digit = -b_borrow; |
+ Chunk result_chunk = ((a_digit | b_digit) & kDigitMask) + result_carry; |
+ result.SetChunkAt(a_length, result_chunk & kDigitMask); |
+ Clamp(result); |
+ return result.raw(); |
+} |
+ |
+ |
+RawBigint* BigintOperations::BitOr(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ |
+ if (a.IsNegative() && !b.IsNegative()) { |
+ return BitOr(b, a); |
+ } |
+ if ((a.IsNegative() == b.IsNegative()) && (a.Length() < b.Length())) { |
+ return BitOr(b, a); |
+ } |
+ |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ intptr_t min_length = Utils::Minimum(a_length, b_length); |
+ intptr_t max_length = Utils::Maximum(a_length, b_length); |
+ if (!b.IsNegative()) { |
+ ASSERT(!a.IsNegative()); |
+ intptr_t result_length = max_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ |
+ ASSERT(a_length >= b_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i) | b.GetChunkAt(i)); |
} |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i)); |
+ } |
+ return result.raw(); |
} |
- bool aflip = false; |
- int acarry = 0; |
- if (a.IsNegative()) { |
- aflip = true; |
- acarry = 1; |
+ |
+ // Bigints encode negative values by storing the absolute value and the sign |
+ // separately. To do bit operations we need to simulate numbers that are |
+ // implemented as two's complement. |
+ // The negation of a positive number x would be encoded as follows in |
+ // two's complement: n = ~(x - 1). |
+ // The inverse transformation is hence (~n) + 1. |
+ |
+ if (!a.IsNegative()) { |
+ ASSERT(b.IsNegative()); |
+ if (a.IsZero()) { |
+ return Copy(b); |
+ } |
+ // The result will be negative. |
+ // We need to convert b to two's complement. Do the bit-operation there, |
+ // and transform the resulting bits from two's complement back to separated |
+ // magnitude and sign. |
+ // a | b is therefore computed as ~((a & (~(b - 1))) + 1 which is |
+ // equal to ((~a) & (b-1)) + 1. |
+ intptr_t result_length = b_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ result.ToggleSign(); |
+ Chunk borrow = 1; |
+ Chunk result_carry = 1; |
+ for (intptr_t i = 0; i < min_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i); |
+ Chunk b_digit = b.GetChunkAt(i) - borrow; |
+ Chunk result_digit = ((~a_digit) & b_digit & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_digit & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_digit >> kDigitBitSize; |
+ } |
+ ASSERT(result_carry == 0); |
+ for (intptr_t i = min_length; i < b_length; i++) { |
+ Chunk b_digit = b.GetChunkAt(i) - borrow; |
+ Chunk result_digit = (b_digit & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_digit & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_digit >> kDigitBitSize; |
+ } |
+ ASSERT(result_carry == 0); |
+ Clamp(result); |
+ return result.raw(); |
} |
- bool bflip = false; |
- int bcarry = 0; |
- if (b.IsNegative()) { |
- bflip = true; |
- bcarry = 1; |
+ |
+ ASSERT(a.IsNegative()); |
+ ASSERT(b.IsNegative()); |
+ // The result will be negative. |
+ // We need to convert a and b to two's complement. Do the bit-operation there, |
+ // and transform the resulting bits from two's complement back to separated |
+ // magnitude and sign. |
+ // a & b is therefore computed as ~((~(a - 1)) | (~(b - 1))) + 1 which is |
+ // equal to ((a-1) & (b-1)) + 1. |
+ intptr_t result_length = min_length + 1; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ result.ToggleSign(); |
+ Chunk a_borrow = 1; |
+ Chunk b_borrow = 1; |
+ Chunk result_carry = 1; |
+ ASSERT(a_length >= b_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i) - a_borrow; |
+ Chunk b_digit = b.GetChunkAt(i) - b_borrow; |
+ Chunk result_chunk = ((a_digit & b_digit) & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_chunk & kDigitMask); |
+ a_borrow = a_digit >> (kChunkBitSize - 1); |
+ b_borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_chunk >> kDigitBitSize; |
} |
- for (int i = 0 ; i < n ; ++i) { |
- int ab = (a.Bit(i) ^ (aflip ? 1 : 0)) + acarry; |
- ASSERT(ab <= 2 && ab >= 0); |
- int bb = (b.Bit(i) ^ (bflip ? 1 : 0)) + bcarry; |
- ASSERT(bb <= 2 && bb >= 0); |
- int r = tt[(ab & 1) + ((bb & 1) << 1)]; |
- r = r + rborrow; |
- ASSERT(r >= -1 && r <= 1); |
- if ((r ^ rflip) & 1) { |
- int status = BN_set_bit(TmpBN(), i); |
- ASSERT(status == 1); |
+ result.SetChunkAt(a_length, result_carry); |
+ Clamp(result); |
+ return result.raw(); |
+} |
+ |
+ |
+RawBigint* BigintOperations::BitXor(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ |
+ if (a.IsZero()) { |
+ return Copy(b); |
+ } |
+ if (b.IsZero()) { |
+ return Copy(a); |
+ } |
+ if (a.IsNegative() && !b.IsNegative()) { |
+ return BitXor(b, a); |
+ } |
+ if ((a.IsNegative() == b.IsNegative()) && (a.Length() < b.Length())) { |
+ return BitXor(b, a); |
+ } |
+ |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ intptr_t min_length = Utils::Minimum(a_length, b_length); |
+ intptr_t max_length = Utils::Maximum(a_length, b_length); |
+ if (!b.IsNegative()) { |
+ ASSERT(!a.IsNegative()); |
+ intptr_t result_length = max_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ |
+ ASSERT(a_length >= b_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i) ^ b.GetChunkAt(i)); |
} |
- acarry = ab >> 1; |
- bcarry = bb >> 1; |
- rborrow = r >> 1; |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ result.SetChunkAt(i, a.GetChunkAt(i)); |
} |
- if (rborrow) { |
- int status = BN_set_bit(TmpBN(), n); |
- ASSERT(status == 1); |
+ Clamp(result); |
+ return result.raw(); |
} |
- if (rflip) { |
- // FIXME(benl): can be changed to use BN_set_negative() on more |
- // recent OpenSSL releases (> 1.0.0). |
- ASSERT(!BN_is_zero(TmpBN())); |
- TmpBN()->neg = 1; |
+ |
+ // Bigints encode negative values by storing the absolute value and the sign |
+ // separately. To do bit operations we need to simulate numbers that are |
+ // implemented as two's complement. |
+ // The negation of a positive number x would be encoded as follows in |
+ // two's complement: n = ~(x - 1). |
+ // The inverse transformation is hence (~n) + 1. |
+ |
+ if (!a.IsNegative()) { |
+ ASSERT(b.IsNegative()); |
+ // The result will be negative. |
+ // We need to convert b to two's complement. Do the bit-operation there, |
+ // and transform the resulting bits from two's complement back to separated |
+ // magnitude and sign. |
+ // a ^ b is therefore computed as ~((a ^ (~(b - 1))) + 1. |
+ intptr_t result_length = max_length + 1; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ result.ToggleSign(); |
+ Chunk borrow = 1; |
+ Chunk result_carry = 1; |
+ for (intptr_t i = 0; i < min_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i); |
+ Chunk b_digit = b.GetChunkAt(i) - borrow; |
+ Chunk result_digit = |
+ ((~(a_digit ^ ~b_digit)) & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_digit & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_digit >> kDigitBitSize; |
+ } |
+ for (intptr_t i = min_length; i < a_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i); |
+ Chunk b_digit = -borrow; |
+ Chunk result_digit = |
+ ((~(a_digit ^ ~b_digit)) & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_digit & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_digit >> kDigitBitSize; |
+ } |
+ for (intptr_t i = min_length; i < b_length; i++) { |
+ // a_digit = 0. |
+ Chunk b_digit = b.GetChunkAt(i) - borrow; |
+ Chunk result_digit = (b_digit & kDigitMask) + result_carry; |
+ result.SetChunkAt(i, result_digit & kDigitMask); |
+ borrow = b_digit >> (kChunkBitSize - 1); |
+ result_carry = result_digit >> kDigitBitSize; |
+ } |
+ result.SetChunkAt(max_length, result_carry); |
+ Clamp(result); |
+ return result.raw(); |
} |
- const Bigint& result = Bigint::Handle(Bigint::New(TmpBN())); |
+ |
+ ASSERT(a.IsNegative()); |
+ ASSERT(b.IsNegative()); |
+ // The result will be positive. |
+ // We need to convert a and b to two's complement, do the bit-operation there, |
+ // and simply store the result. |
+ // a ^ b is therefore computed as (~(a - 1)) ^ (~(b - 1)). |
+ intptr_t result_length = max_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ Chunk a_borrow = 1; |
+ Chunk b_borrow = 1; |
+ ASSERT(a_length >= b_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i) - a_borrow; |
+ Chunk b_digit = b.GetChunkAt(i) - b_borrow; |
+ Chunk result_chunk = (~a_digit) ^ (~b_digit); |
+ result.SetChunkAt(i, result_chunk & kDigitMask); |
+ a_borrow = a_digit >> (kChunkBitSize - 1); |
+ b_borrow = b_digit >> (kChunkBitSize - 1); |
+ } |
+ ASSERT(b_borrow == 0); |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ Chunk a_digit = a.GetChunkAt(i) - a_borrow; |
+ result.SetChunkAt(i, (~a_digit) & kDigitMask); |
+ a_borrow = a_digit >> (kChunkBitSize - 1); |
+ } |
+ ASSERT(a_borrow == 0); |
+ Clamp(result); |
return result.raw(); |
} |
-RawSmi* BigintOperations::BitOpWithSmi(Token::Kind kind, |
- const Bigint& bigint, |
- const Smi& smi) { |
- ASSERT((kind == Token::kBIT_OR) || (kind == Token::kBIT_AND)); |
- intptr_t smi_value = smi.Value(); |
- intptr_t big_value = 0; |
- // We take Smi::kBits + 1 (one more bit), in case bigint is negative. |
- ASSERT((Smi::kBits + 1) <= (sizeof(big_value) * kBitsPerByte)); |
- intptr_t num_bits = bigint.NumberOfBits(); |
- int n = Utils::Minimum(num_bits, Smi::kBits + 1); |
- for (int i = n - 1; i >= 0; i--) { |
- big_value <<= 1; |
- big_value += bigint.Bit(i); |
+RawBigint* BigintOperations::BitNot(const Bigint& bigint) { |
+ if (bigint.IsZero()) { |
+ return MinusOne(); |
} |
+ const Bigint& one_bigint = Bigint::Handle(One()); |
if (bigint.IsNegative()) { |
- big_value = -big_value; |
+ return UnsignedSubtract(bigint, one_bigint); |
+ } else { |
+ const Bigint& result = Bigint::Handle(UnsignedAdd(bigint, one_bigint)); |
+ result.ToggleSign(); |
+ return result.raw(); |
} |
- intptr_t result; |
- if (kind == Token::kBIT_OR) { |
- result = smi_value | big_value; |
+} |
+ |
+ |
+int BigintOperations::Compare(const Bigint& a, const Bigint& b) { |
+ bool a_is_negative = a.IsNegative(); |
+ bool b_is_negative = b.IsNegative(); |
+ if (a_is_negative != b_is_negative) { |
+ return a_is_negative ? -1 : 1; |
+ } |
+ |
+ if (a_is_negative) { |
+ return -UnsignedCompare(a, b); |
+ } |
+ return UnsignedCompare(a, b); |
+} |
+ |
+ |
+RawBigint* BigintOperations::AddSubtract(const Bigint& a, |
+ const Bigint& b, |
+ bool negate_b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ Bigint& result = Bigint::Handle(); |
+ // We perform the subtraction by simulating a negation of the b-argument. |
+ bool b_is_negative = negate_b ? !b.IsNegative() : b.IsNegative(); |
+ |
+ // If both are of the same sign, then we can compute the unsigned addition |
+ // and then simply adjust the sign (if necessary). |
+ // Ex: -3 + -5 -> -(3 + 5) |
+ if (a.IsNegative() == b_is_negative) { |
+ result = UnsignedAdd(a, b); |
+ result.SetSign(b_is_negative); |
+ ASSERT(IsClamped(result)); |
+ return result.raw(); |
+ } |
+ |
+ // The signs differ. |
+ // Take the number with small magnitude and subtract its absolute value from |
+ // the absolute value of the other number. Then adjust the sign, if necessary. |
+ // The sign is the same as for the number with the greater magnitude. |
+ // Ex: -8 + 3 -> -(8 - 3) |
+ // 8 + -3 -> (8 - 3) |
+ // -3 + 8 -> (8 - 3) |
+ // 3 + -8 -> -(8 - 3) |
+ int comp = UnsignedCompare(a, b); |
+ if (comp < 0) { |
+ result = UnsignedSubtract(b, a); |
+ result.SetSign(b_is_negative); |
+ } else if (comp > 0) { |
+ result = UnsignedSubtract(a, b); |
+ result.SetSign(a.IsNegative()); |
} else { |
- result = smi_value & big_value; |
+ return Zero(); |
} |
- ASSERT(Smi::IsValid(result)); |
- return Smi::New(result); |
+ ASSERT(IsClamped(result)); |
+ return result.raw(); |
} |
-RawBigint* BigintOperations::BitAnd(const Bigint& a, const Bigint& b) { |
- static bool tt_and[] = { false, false, false, true }; |
- return BitTT(a, b, tt_and); |
+int BigintOperations::UnsignedCompare(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ if (a_length < b_length) return -1; |
+ if (a_length > b_length) return 1; |
+ for (intptr_t i = a_length - 1; i >= 0; i--) { |
+ Chunk digit_a = a.GetChunkAt(i); |
+ Chunk digit_b = b.GetChunkAt(i); |
+ if (digit_a < digit_b) return -1; |
+ if (digit_a > digit_b) return 1; |
+ // Else look at the next digit. |
+ } |
+ return 0; // They are equal. |
} |
-RawInteger* BigintOperations::BitAndWithSmi(const Bigint& bigint, |
- const Smi& smi) { |
- if (smi.IsNegative()) { |
- Bigint& other = Bigint::Handle(NewFromSmi(smi)); |
- return BitAnd(bigint, other); |
+int BigintOperations::UnsignedCompareNonClamped( |
+ const Bigint& a, const Bigint& b) { |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ while (a_length > b_length) { |
+ if (a.GetChunkAt(a_length - 1) != 0) return 1; |
+ a_length--; |
} |
- return BitOpWithSmi(Token::kBIT_AND, bigint, smi); |
+ while (b_length > a_length) { |
+ if (b.GetChunkAt(b_length - 1) != 0) return -1; |
+ b_length--; |
+ } |
+ for (intptr_t i = a_length - 1; i >= 0; i--) { |
+ Chunk digit_a = a.GetChunkAt(i); |
+ Chunk digit_b = b.GetChunkAt(i); |
+ if (digit_a < digit_b) return -1; |
+ if (digit_a > digit_b) return 1; |
+ // Else look at the next digit. |
+ } |
+ return 0; // They are equal. |
} |
-RawBigint* BigintOperations::BitOr(const Bigint& a, const Bigint& b) { |
- static bool tt_or[] = { false, true, true, true }; |
- return BitTT(a, b, tt_or); |
+RawBigint* BigintOperations::UnsignedAdd(const Bigint& a, const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ if (a_length < b_length) { |
+ return UnsignedAdd(b, a); |
+ } |
+ |
+ // We might request too much space, in which case we will adjust the length |
+ // afterwards. |
+ intptr_t result_length = a_length + 1; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ |
+ Chunk carry = 0; |
+ // b has fewer digits than a. |
+ ASSERT(b_length <= a_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ Chunk sum = a.GetChunkAt(i) + b.GetChunkAt(i) + carry; |
+ result.SetChunkAt(i, sum & kDigitMask); |
+ carry = sum >> kDigitBitSize; |
+ } |
+ // Copy over the remaining digits of a, but don't forget the carry. |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ Chunk sum = a.GetChunkAt(i) + carry; |
+ result.SetChunkAt(i, sum & kDigitMask); |
+ carry = sum >> kDigitBitSize; |
+ } |
+ // Shrink the result if there was no overflow. Otherwise apply the carry. |
+ if (carry == 0) { |
+ // TODO(floitsch): We change the size of bigint-objects here. |
+ result.SetLength(a_length); |
+ } else { |
+ result.SetChunkAt(a_length, carry); |
+ } |
+ ASSERT(IsClamped(result)); |
+ return result.raw(); |
} |
-RawInteger* BigintOperations::BitOrWithSmi(const Bigint& bigint, |
- const Smi& smi) { |
- if (!smi.IsNegative()) { |
- Bigint& other = Bigint::Handle(NewFromSmi(smi)); |
- return BitOr(bigint, other); |
+RawBigint* BigintOperations::UnsignedSubtract(const Bigint& a, |
+ const Bigint& b) { |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ ASSERT(UnsignedCompare(a, b) >= 0); |
+ |
+ const int kSignBitPos = Bigint::kChunkSize * kBitsPerByte - 1; |
+ |
+ intptr_t a_length = a.Length(); |
+ intptr_t b_length = b.Length(); |
+ |
+ // We might request too much space, in which case we will adjust the length |
+ // afterwards. |
+ intptr_t result_length = a_length; |
+ const Bigint& result = Bigint::Handle(Bigint::Allocate(result_length)); |
+ |
+ Chunk borrow = 0; |
+ ASSERT(b_length <= a_length); |
+ for (intptr_t i = 0; i < b_length; i++) { |
+ Chunk difference = a.GetChunkAt(i) - b.GetChunkAt(i) - borrow; |
+ result.SetChunkAt(i, difference & kDigitMask); |
+ borrow = difference >> kSignBitPos; |
+ ASSERT((borrow == 0) || (borrow == 1)); |
} |
- return BitOpWithSmi(Token::kBIT_OR, bigint, smi); |
+ // Copy over the remaining digits of a, but don't forget the borrow. |
+ for (intptr_t i = b_length; i < a_length; i++) { |
+ Chunk difference = a.GetChunkAt(i) - borrow; |
+ result.SetChunkAt(i, difference & kDigitMask); |
+ borrow = (difference >> kSignBitPos); |
+ ASSERT((borrow == 0) || (borrow == 1)); |
+ } |
+ ASSERT(borrow == 0); |
+ Clamp(result); |
+ return result.raw(); |
} |
-RawBigint* BigintOperations::BitXor(const Bigint& a, const Bigint& b) { |
- static bool tt_xor[] = { false, true, true, false }; |
- return BitTT(a, b, tt_xor); |
+RawBigint* BigintOperations::MultiplyWithDigit( |
+ const Bigint& bigint, Chunk digit) { |
+ // TODO(floitsch): implement MultiplyWithDigit. |
+ ASSERT(digit <= kDigitMaxValue); |
+ if (digit == 0) return Zero(); |
+ |
+ Bigint& tmp = Bigint::Handle(Bigint::Allocate(1)); |
+ tmp.SetChunkAt(0, digit); |
+ return Multiply(bigint, tmp); |
} |
-RawInteger* BigintOperations::BitXorWithSmi(const Bigint& bigint, |
- const Smi& smi) { |
- Bigint& other = Bigint::Handle(NewFromSmi(smi)); |
- return BitXor(bigint, other); |
+void BigintOperations::DivideRemainder( |
+ const Bigint& a, const Bigint& b, Bigint* quotient, Bigint* remainder) { |
+ // TODO(floitsch): This function is very memory-intensive since all |
+ // intermediate bigint results are allocated in new memory. It would be |
+ // much more efficient to reuse the space of temporary intermediate variables. |
+ ASSERT(IsClamped(a)); |
+ ASSERT(IsClamped(b)); |
+ ASSERT(!b.IsZero()); |
+ |
+ int comp = UnsignedCompare(a, b); |
+ if (comp < 0) { |
+ (*quotient) ^= Zero(); |
+ (*remainder) ^= Copy(a); // TODO(floitsch): can we reuse the input? |
+ return; |
+ } else if (comp == 0) { |
+ (*quotient) ^= One(); |
+ quotient->SetSign(a.IsNegative() != b.IsNegative()); |
+ (*remainder) ^= Zero(); |
+ return; |
+ } |
+ |
+ // High level description: |
+ // The algorithm is basically the algorithm that is taught in school: |
+ // Let a the dividend and b the divisor. We are looking for |
+ // the quotient q = truncate(a / b), and |
+ // the remainder r = a - q * b. |
+ // School algorithm: |
+ // q = 0 |
+ // n = number_of_digits(a) - number_of_digits(b) |
+ // for (i = n; i >= 0; i--) { |
+ // Maximize k such that k*y*10^i is less than or equal to a and |
+ // (k + 1)*y*10^i is greater. |
+ // q = q + k * 10^i // Add new digit to result. |
+ // a = a - k * b * 10^i |
+ // } |
+ // r = a |
+ // |
+ // Instead of working in base 10 we work in base kDigitBitSize. |
+ |
+ intptr_t b_length = b.Length(); |
+ int normalization_shift = |
+ kDigitBitSize - CountBits(b.GetChunkAt(b_length - 1)); |
+ Bigint& dividend = Bigint::Handle(ShiftLeft(a, normalization_shift)); |
+ const Bigint& divisor = Bigint::Handle(ShiftLeft(b, normalization_shift)); |
+ dividend.SetSign(false); |
+ divisor.SetSign(false); |
+ |
+ intptr_t dividend_length = dividend.Length(); |
+ intptr_t divisor_length = b_length; |
+ ASSERT(divisor_length == divisor.Length()); |
+ |
+ intptr_t quotient_length = dividend_length - divisor_length + 1; |
+ *quotient ^= Bigint::Allocate(quotient_length); |
+ quotient->SetSign(a.IsNegative() != b.IsNegative()); |
+ |
+ intptr_t quotient_pos = dividend_length - divisor_length; |
+ // Find the first quotient-digit. |
+ // The first digit must be computed separately from the other digits because |
+ // the preconditions for the loop are not yet satisfied. |
+ // For simplicity use a shifted divisor, so that the comparison and |
+ // subtraction are easier. |
+ int divisor_shift_amount = dividend_length - divisor_length; |
+ Bigint& shifted_divisor = |
+ Bigint::Handle(DigitsShiftLeft(divisor, divisor_shift_amount)); |
+ Chunk first_quotient_digit = 0; |
+ while (UnsignedCompare(dividend, shifted_divisor) >= 0) { |
+ first_quotient_digit++; |
+ dividend ^= Subtract(dividend, shifted_divisor); |
+ } |
+ quotient->SetChunkAt(quotient_pos--, first_quotient_digit); |
+ |
+ // Find the remainder of the digits. |
+ |
+ Chunk first_divisor_digit = divisor.GetChunkAt(divisor_length - 1); |
+ // The short divisor only represents the first two digits of the divisor. |
+ // If the divisor has only one digit, then the second part is zeroed out. |
+ Bigint& short_divisor = Bigint::Handle(Bigint::Allocate(2)); |
+ if (divisor_length > 1) { |
+ short_divisor.SetChunkAt(0, divisor.GetChunkAt(divisor_length - 2)); |
+ } else { |
+ short_divisor.SetChunkAt(0, 0); |
+ } |
+ short_divisor.SetChunkAt(1, first_divisor_digit); |
+ // The following bigint will be used inside the loop. It is allocated outside |
+ // the loop to avoid repeated allocations. |
+ Bigint& target = Bigint::Handle(Bigint::Allocate(3)); |
+ // The dividend_length here must be from the initial dividend. |
+ for (intptr_t i = dividend_length - 1; i >= divisor_length; i--) { |
+ // Invariant: let t = i - divisor_length |
+ // then dividend / (divisor << (t * kDigitBitSize)) <= kDigitMaxValue. |
+ // Ex: dividend: 53451232, and divisor: 535 (with t == 5) is ok. |
+ // dividend: 56822123, and divisor: 563 (with t == 5) is bad. |
+ // dividend: 6822123, and divisor: 563 (with t == 5) is ok. |
+ |
+ // The dividend has changed. So recompute its length. |
+ dividend_length = dividend.Length(); |
+ Chunk dividend_digit; |
+ if (i > dividend_length) { |
+ quotient->SetChunkAt(quotient_pos--, 0); |
+ continue; |
+ } else if (i == dividend_length) { |
+ dividend_digit = 0; |
+ } else { |
+ ASSERT(i + 1 == dividend_length); |
+ dividend_digit = dividend.GetChunkAt(i); |
+ } |
+ Chunk quotient_digit; |
+ // Compute an estimate of the quotient_digit. The estimate will never |
+ // be too small. |
+ if (dividend_digit == first_divisor_digit) { |
+ // Small shortcut: the else-branch would compute a value > kDigitMaxValue. |
+ // However, by hypothesis, we know that the quotient_digit must fit into |
+ // a digit. Avoid going through repeated iterations of the adjustment |
+ // loop by directly assigning kDigitMaxValue to the quotient_digit. |
+ // Ex: 51235 / 523. |
+ // 51 / 5 would yield 10 (if computed in the else branch). |
+ // However we know that 9 is the maximal value. |
+ quotient_digit = kDigitMaxValue; |
+ } else { |
+ // Compute the estimate by using two digits of the dividend and one of |
+ // the divisor. |
+ // Ex: 32421 / 535 |
+ // 32 / 5 -> 6 |
+ // The estimate would hence be 6. |
+ DoubleChunk two_dividend_digits = dividend_digit; |
+ two_dividend_digits <<= kDigitBitSize; |
+ two_dividend_digits += dividend.GetChunkAt(i - 1); |
+ DoubleChunk q = two_dividend_digits / first_divisor_digit; |
+ if (q > kDigitMaxValue) q = kDigitMaxValue; |
+ quotient_digit = static_cast<Chunk>(q); |
+ } |
+ |
+ // Refine estimation. |
+ quotient_digit++; // The following loop will start by decrementing. |
+ Bigint& estimation_product = Bigint::Handle(); |
+ target.SetChunkAt(0, ((i - 2) < 0) ? 0 : dividend.GetChunkAt(i - 2)); |
+ target.SetChunkAt(1, ((i - 1) < 0) ? 0 : dividend.GetChunkAt(i - 1)); |
+ target.SetChunkAt(2, dividend_digit); |
+ do { |
+ quotient_digit = (quotient_digit - 1) & kDigitMask; |
+ estimation_product ^= MultiplyWithDigit(short_divisor, quotient_digit); |
+ } while (UnsignedCompareNonClamped(estimation_product, target) > 0); |
+ // At this point the quotient_digit is fairly accurate. |
+ // At the worst it is off by one. |
+ // Remove a multiple of the divisor. If the estimate is incorrect we will |
+ // subtract the divisor another time. |
+ // Let t = i - divisor_length. |
+ // dividend -= (quotient_digit * divisor) << (t * kDigitBitSize); |
+ shifted_divisor ^= MultiplyWithDigit(divisor, quotient_digit); |
+ shifted_divisor ^= DigitsShiftLeft(shifted_divisor, i - divisor_length); |
+ dividend = Subtract(dividend, shifted_divisor); |
+ if (dividend.IsNegative()) { |
+ // The estimation was still too big. |
+ quotient_digit--; |
+ // TODO(floitsch): allocate space for the shifted_divisor once and reuse |
+ // it at every iteration. |
+ shifted_divisor ^= DigitsShiftLeft(divisor, i - divisor_length); |
+ // TODO(floitsch): reuse the space of the previous dividend. |
+ dividend = Add(dividend, shifted_divisor); |
+ } |
+ quotient->SetChunkAt(quotient_pos--, quotient_digit); |
+ } |
+ ASSERT(quotient_pos == -1); |
+ Clamp(*quotient); |
+ *remainder ^= ShiftRight(dividend, normalization_shift); |
+ remainder->SetSign(a.IsNegative()); |
} |
-RawBigint* BigintOperations::BitNot(const Bigint& bigint) { |
- const Bigint& one_bigint = Bigint::Handle(One()); |
- const Bigint& result = Bigint::Handle(Add(bigint, one_bigint)); |
- result.ToggleSign(); |
- return result.raw(); |
+void BigintOperations::Clamp(const Bigint& bigint) { |
+ intptr_t length = bigint.Length(); |
+ while (length > 0 && (bigint.GetChunkAt(length - 1) == 0)) { |
+ length--; |
+ } |
+ // TODO(floitsch): We change the size of bigint-objects here. |
+ bigint.SetLength(length); |
} |
-int BigintOperations::Compare(const Bigint& a, const Bigint& b) { |
- return BN_cmp(a.BNAddr(), b.BNAddr()); |
+RawBigint* BigintOperations::Copy(const Bigint& bigint) { |
+ intptr_t bigint_length = bigint.Length(); |
+ Bigint& copy = Bigint::Handle(Bigint::Allocate(bigint_length)); |
+ for (intptr_t i = 0; i < bigint_length; i++) { |
+ copy.SetChunkAt(i, bigint.GetChunkAt(i)); |
+ } |
+ copy.SetSign(bigint.IsNegative()); |
+ return copy.raw(); |
} |
+ |
+int BigintOperations::CountBits(Chunk digit) { |
+ int result = 0; |
+ while (digit != 0) { |
+ digit >>= 1; |
+ result++; |
+ } |
+ return result; |
+} |
+ |
} // namespace dart |