Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/big-int/bigint-func.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pow (BigInt const &x, unsigned y)
y >>= 1;
if (y == 0)
return r;
a *= a;
a = a * a;
}
}

Expand All @@ -44,7 +44,7 @@ pow (BigInt const &x, BigInt const &y, BigInt const &m)
b /= 2;
if (b.is_zero())
return r;
a *= a;
a = a * a;
}
}

Expand Down
34 changes: 34 additions & 0 deletions src/big-int/bigint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <cctype>
#include <climits>
#include <cstdlib>
#include <cstring>

// How to report errors.
Expand Down Expand Up @@ -779,6 +780,16 @@ BigInt::compare (BigInt const &b) const
void
BigInt::add (onedig_t const *dig, unsigned len, bool pos)
{
// dig aliases this->digit when called via x += x or x -= x. The
// resize below may free that buffer, turning dig into a dangling
// pointer. Self-aliased operands are not supported; spell doubling
// as x + x or use a copy of the operand.
if(dig == digit)
{
error("BigInt::add: operand must not alias *this.");
abort();
}

// Make sure the result fits into this, even with carry.
resize ((length > len ? length : len) + 1);

Expand Down Expand Up @@ -832,6 +843,15 @@ BigInt::add (onedig_t const *dig, unsigned len, bool pos)
void
BigInt::mul (onedig_t const *dig, unsigned len, bool pos)
{
// Self-aliased operands are not supported: parts of the code below
// read the operand after this->digit has been reallocated or
// modified. Spell squaring as x * x or use a copy of the operand.
if(dig == digit)
{
error("BigInt::mul: operand must not alias *this.");
abort();
}

if (len < 2)
{
// Handle small dig/len operand efficiently.
Expand Down Expand Up @@ -1129,6 +1149,13 @@ BigInt::div (BigInt const &x, BigInt const &y, BigInt &q, BigInt &r)
BigInt &
BigInt::operator/= (BigInt const &y)
{
// Self-aliased operands are not supported anywhere in this library.
if(this == &y)
{
error("BigInt::operator/=: operand must not alias *this.");
abort();
}

// Eliminate some trivial cases.
int cmp = ucompare (y);
if (cmp < 0)
Expand Down Expand Up @@ -1197,6 +1224,13 @@ BigInt::operator/= (BigInt const &y)
BigInt &
BigInt::operator%= (BigInt const &y)
{
// Self-aliased operands are not supported anywhere in this library.
if(this == &y)
{
error("BigInt::operator%=: operand must not alias *this.");
abort();
}

// Eliminate some trivial cases.
int cmp = ucompare (y);
if (cmp < 0)
Expand Down
45 changes: 45 additions & 0 deletions unit/big-int/big-int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,51 @@ TEST_CASE("arbitrary precision integers", "[core][big-int][bigint]")
REQUIRE(to_string(i) == "1");
}

// =====================================================================
// Compound assignment with an operand equal to the left-hand side.
// =====================================================================
// Self-aliased operands (x += x) are not supported and abort at
// runtime: operator+= and operator-= pass the operand's digit buffer
// into add(), which resizes (and possibly frees) this->digit before
// reading that buffer. The tests below use a distinct copy of the
// operand, which is the supported spelling; they still exercise the
// buffer growth in add() with equal-length operands.
SECTION("compound assignment with equal operands")
{
// Scanning from a string produces a buffer without spare capacity:
// 2^192 - 1 occupies six 32-bit digits, the doubled value needs
// seven, forcing the reallocation inside add().
BigInt x("6277101735386680763835789423207666416102355444464034512895");
const BigInt x_copy(x);
x += x_copy;
REQUIRE(
to_string(x) ==
"12554203470773361527671578846415332832204710888928069025790");

BigInt y("6277101735386680763835789423207666416102355444464034512895");
const BigInt y_copy(y);
y -= y_copy;
REQUIRE(y.is_zero());

// Squaring through a copy of the operand; z *= z would abort.
BigInt z("18446744073709551616"); // 2^64
const BigInt z_copy(z);
z *= z_copy;
REQUIRE(to_string(z) == "340282366920938463463374607431768211456");

// Division and remainder with equal operands take the
// value-equality fast path; w /= w and v %= v would abort.
BigInt w("340282366920938463463374607431768211456"); // 2^128
const BigInt w_copy(w);
w /= w_copy;
REQUIRE(to_string(w) == "1");

BigInt v("340282366920938463463374607431768211456");
const BigInt v_copy(v);
v %= v_copy;
REQUIRE(v.is_zero());
}

// =====================================================================
// Test cases from the clisp test suite in number.tst.
// =====================================================================
Expand Down
Loading