Skip to content

Big-int: Fix copy-assign, add move ops, enable implicit conversions #1341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 12, 2017
Merged
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
36 changes: 10 additions & 26 deletions src/big-int/bigint.cc
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,12 @@ BigInt::BigInt (BigInt const &y)
memcpy (digit, y.digit, length * sizeof (onedig_t));
}

BigInt::BigInt (BigInt &&y)
: BigInt()
{
swap(y);
}

BigInt::BigInt (char const *s, onedig_t b)
: size (adjust_size (small)),
length (0),
Expand All @@ -473,40 +479,18 @@ BigInt::BigInt (char const *s, onedig_t b)
scan (s, b);
}


BigInt &
BigInt::operator= (llong_t l)
{
reallocate (small);
assign (l);
return *this;
}

BigInt &
BigInt::operator= (ullong_t ul)
{
reallocate (small);
assign (ul);
return *this;
}

BigInt &
BigInt::operator= (BigInt const &y)
{
if (&y != this)
{
reallocate (y.length);
length = y.length;
positive = y.positive;
memcpy (digit, y.digit, length * sizeof (onedig_t));
}
BigInt copy(y);
swap(copy);
return *this;
}

BigInt &
BigInt::operator= (char const *s)
BigInt::operator= (BigInt &&y)
{
scan (s);
swap(y);
return *this;
}

Expand Down
192 changes: 98 additions & 94 deletions src/big-int/bigint.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef BIGINT_HH
#define BIGINT_HH

#include <utility>

// This one is pretty simple but has a fair divide implementation.
// Though I'm not ambitious enough to do that FFT-like stuff.
//
Expand Down Expand Up @@ -166,12 +168,11 @@ public:
BigInt (llong_t) _fast;
BigInt (ullong_t) _fast;
BigInt (BigInt const &) _fast;
BigInt (BigInt &&) _fast;
BigInt (char const *, onedig_t = 10) _fast;

BigInt &operator= (llong_t) _fast;
BigInt &operator= (ullong_t) _fast;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In line with the recent update: shouldn't those also be kept (and be implemented via swap)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The best way to implement copy-assign safely is to set up another object with the desired state after assignment, and then to do a nothrow swap to exchange the states. That way, exceptions can only be thrown during the "setting up a new object" stage, which doesn't affect the logical state of the current object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this in mind, there's no benefit between having explicit overloads for assignment from primitive types, and allowing BigInts to be implicitly constructed during assignment. We'll end up allocating a fresh temporary object in both cases.

BigInt &operator= (BigInt const &) _fast;
BigInt &operator= (char const *) _fast;
BigInt &operator= (BigInt &&) _fast;

// Input conversion from text.

Expand Down Expand Up @@ -220,8 +221,9 @@ public:

// Eliminate need for explicit casts when comparing.

int compare (int n) const { return compare (llong_t (n)); }
int compare (unsigned n) const { return compare (ullong_t (n)); }
int compare (unsigned long n) const { return compare (static_cast<ullong_t>(n)); }
int compare (int n) const { return compare (static_cast<llong_t> (n)); }
int compare (unsigned n) const { return compare (static_cast<ullong_t>(n)); }

// Tests. These are faster than comparing with 0 or of course %2.

Expand All @@ -236,109 +238,51 @@ public:
BigInt &negate() { if(!is_zero()) positive = !positive; return *this; }
BigInt operator-() const { return BigInt (*this).negate(); }

BigInt &operator+= (llong_t) _fast;
BigInt &operator-= (llong_t) _fast;
BigInt &operator*= (llong_t) _fast;
BigInt &operator/= (llong_t) _fast;
BigInt &operator%= (llong_t) _fast;

BigInt &operator= (unsigned long x) { return (*this)=(ullong_t)x; }
BigInt &operator+= (unsigned long x) { return (*this)+=(ullong_t)x; }
BigInt &operator-= (unsigned long x) { return (*this)-=(ullong_t)x; }
BigInt &operator*= (unsigned long x) { return (*this)*=(ullong_t)x; }
BigInt &operator/= (unsigned long x) { return (*this)/=(ullong_t)x; }
BigInt &operator%= (unsigned long x) { return (*this)%=(ullong_t)x; }

BigInt &operator+= (ullong_t) _fast;
BigInt &operator-= (ullong_t) _fast;
BigInt &operator*= (ullong_t) _fast;
BigInt &operator/= (ullong_t) _fast;
BigInt &operator%= (ullong_t) _fast;

BigInt &operator+= (BigInt const &) _fast;
BigInt &operator-= (BigInt const &) _fast;
BigInt &operator*= (BigInt const &) _fast;
BigInt &operator/= (BigInt const &) _fasta;
BigInt &operator%= (BigInt const &) _fasta;
#define IN_PLACE_OPERATOR(TYPE) \
BigInt &operator+= (TYPE) _fast; \
BigInt &operator-= (TYPE) _fast; \
BigInt &operator*= (TYPE) _fast; \
BigInt &operator/= (TYPE) _fast; \
BigInt &operator%= (TYPE) _fast;

IN_PLACE_OPERATOR(const BigInt &)
IN_PLACE_OPERATOR(llong_t)
IN_PLACE_OPERATOR(ullong_t)
#undef IN_PLACE_OPERATOR

#define OVERLOAD_IN_PLACE_OPERATOR(FROM, TO) \
BigInt &operator+=(FROM x) { return operator+=(static_cast<TO>(x)); } \
BigInt &operator-=(FROM x) { return operator-=(static_cast<TO>(x)); } \
BigInt &operator*=(FROM x) { return operator*=(static_cast<TO>(x)); } \
BigInt &operator/=(FROM x) { return operator/=(static_cast<TO>(x)); } \
BigInt &operator%=(FROM x) { return operator%=(static_cast<TO>(x)); }

OVERLOAD_IN_PLACE_OPERATOR(unsigned long, ullong_t)
OVERLOAD_IN_PLACE_OPERATOR(int, llong_t)
OVERLOAD_IN_PLACE_OPERATOR(unsigned, ullong_t)
#undef OVERLOAD_IN_PLACE_OPERATOR
Copy link
Contributor

@LAJW LAJW Sep 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these macros be implemented as template operator overloads with typename std::enable_if<std::is_integral<T>::value>::type?


BigInt &operator++ () { return operator+=(1); } // preincrement
BigInt &operator-- () { return operator-=(1); } // predecrement

static void div (BigInt const &, BigInt const &,
BigInt &quot, BigInt &rem) _fasta;

// Avoid the need for explicit casts to [u]llong_t.

// disabled by DK
//operator int() const { return int (operator llong_t()); }
//operator unsigned() const { return unsigned (operator ullong_t()); }

BigInt &operator = (int n) { return operator = (llong_t (n)); }
BigInt &operator+= (int n) { return operator+= (llong_t (n)); }
BigInt &operator-= (int n) { return operator-= (llong_t (n)); }
BigInt &operator*= (int n) { return operator*= (llong_t (n)); }
BigInt &operator/= (int n) { return operator/= (llong_t (n)); }
BigInt &operator%= (int n) { return operator%= (llong_t (n)); }

BigInt &operator = (unsigned n) { return operator = (ullong_t (n)); }
BigInt &operator+= (unsigned n) { return operator+= (ullong_t (n)); }
BigInt &operator-= (unsigned n) { return operator-= (ullong_t (n)); }
BigInt &operator*= (unsigned n) { return operator*= (ullong_t (n)); }
BigInt &operator/= (unsigned n) { return operator/= (ullong_t (n)); }
BigInt &operator%= (unsigned n) { return operator%= (ullong_t (n)); }

// Binary arithmetic operators. These are entirely syntactic sugar.
// Though there's joy in repetition -- let the preprocessor enjoy.

#define decl_binary(T) \
BigInt operator+ (T b) const { return BigInt (*this) += b; } \
BigInt operator- (T b) const { return BigInt (*this) -= b; } \
BigInt operator* (T b) const { return BigInt (*this) *= b; } \
BigInt operator/ (T b) const { return BigInt (*this) /= b; } \
BigInt operator% (T b) const { return BigInt (*this) %= b; }
decl_binary (int);
decl_binary (unsigned);
decl_binary (llong_t);
decl_binary (ullong_t);
decl_binary (BigInt const &);
#undef decl_binary

BigInt operator+ (unsigned long b) const { return BigInt (*this) += (ullong_t)b; } \
BigInt operator- (unsigned long b) const { return BigInt (*this) -= (ullong_t)b; } \
BigInt operator* (unsigned long b) const { return BigInt (*this) *= (ullong_t)b; } \
BigInt operator/ (unsigned long b) const { return BigInt (*this) /= (ullong_t)b; } \
BigInt operator% (unsigned long b) const { return BigInt (*this) %= (ullong_t)b; }

// Binary comparision operators.

#define decl_binary(T) \
bool operator< (T b) const { return compare (b) < 0; } \
bool operator> (T b) const { return compare (b) > 0; } \
bool operator<= (T b) const { return compare (b) <= 0; } \
bool operator>= (T b) const { return compare (b) >= 0; } \
bool operator== (T b) const { return compare (b) == 0; } \
bool operator!= (T b) const { return compare (b) != 0; }
decl_binary (int);
decl_binary (unsigned);
decl_binary (llong_t);
decl_binary (ullong_t);
decl_binary (BigInt const &);
#undef decl_binary

bool operator< (unsigned long b) const { return compare ((ullong_t)b) < 0; } \
bool operator> (unsigned long b) const { return compare ((ullong_t)b) > 0; } \
bool operator<= (unsigned long b) const { return compare ((ullong_t)b) <= 0; } \
bool operator>= (unsigned long b) const { return compare ((ullong_t)b) >= 0; } \
bool operator== (unsigned long b) const { return compare ((ullong_t)b) == 0; } \
bool operator!= (unsigned long b) const { return compare ((ullong_t)b) != 0; }

// Returns the largest x such that 2^x <= abs() or 0 if input is 0
// Not part of original BigInt.
unsigned floorPow2 () const _fast;

// Sets the number to the power of two given by the exponent
// Not part of original BigInt.
void setPower2 (unsigned exponent) _fast;

void swap (BigInt &other)
{
std::swap(other.size, size);
std::swap(other.length, length);
std::swap(other.digit, digit);
std::swap(other.positive, positive);
}
};


Expand All @@ -350,5 +294,65 @@ BigInt sqrt (BigInt const &) _fast;
BigInt gcd (const BigInt &, const BigInt &) _fast;
BigInt modinv (const BigInt &, const BigInt &) _fast;

// Binary arithmetic operators

inline BigInt operator+ (const BigInt &lhs, const BigInt &rhs) { return BigInt(lhs) += rhs; }
inline BigInt operator- (const BigInt &lhs, const BigInt &rhs) { return BigInt(lhs) -= rhs; }
inline BigInt operator* (const BigInt &lhs, const BigInt &rhs) { return BigInt(lhs) *= rhs; }
inline BigInt operator/ (const BigInt &lhs, const BigInt &rhs) { return BigInt(lhs) /= rhs; }
inline BigInt operator% (const BigInt &lhs, const BigInt &rhs) { return BigInt(lhs) %= rhs; }

// Because the operators `+` and `*` are associative, we can do fast math, no
// matter which side the BigInt is on. For the rest of the operators, which
// are non-associative, we can only get speedups if the primitive type is on
// the RHS.
#define BINARY_ARITHMETIC_OPERATORS(OTHER) \
inline BigInt operator+ (const BigInt &lhs, OTHER rhs) { return BigInt(lhs) += rhs; } \
inline BigInt operator+ (OTHER lhs, const BigInt &rhs) { return BigInt(rhs) += lhs; } \
inline BigInt operator* (const BigInt &lhs, OTHER rhs) { return BigInt(lhs) *= rhs; } \
inline BigInt operator* (OTHER lhs, const BigInt &rhs) { return BigInt(rhs) *= lhs; } \
inline BigInt operator- (const BigInt &lhs, OTHER rhs) { return BigInt(lhs) -= rhs; } \
inline BigInt operator/ (const BigInt &lhs, OTHER rhs) { return BigInt(lhs) /= rhs; } \
inline BigInt operator% (const BigInt &lhs, OTHER rhs) { return BigInt(lhs) %= rhs; }

BINARY_ARITHMETIC_OPERATORS(BigInt::llong_t)
BINARY_ARITHMETIC_OPERATORS(BigInt::ullong_t)
BINARY_ARITHMETIC_OPERATORS(unsigned long)
BINARY_ARITHMETIC_OPERATORS(int)
BINARY_ARITHMETIC_OPERATORS(unsigned)
#undef BINARY_ARITHMETIC_OPERATORS

// Binary comparison operators

inline bool operator< (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) < 0; }
inline bool operator> (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) > 0; }
inline bool operator<= (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) <= 0; }
inline bool operator>= (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) >= 0; }
inline bool operator== (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) == 0; }
inline bool operator!= (const BigInt &lhs, const BigInt &rhs) { return lhs.compare(rhs) != 0; }


// These operators are all associative, so we can define them all for
// primitives on the LHS and RHS.
#define COMPARISON_OPERATORS(OTHER) \
inline bool operator< (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) < 0; } \
inline bool operator> (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) > 0; } \
inline bool operator<= (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) <= 0; } \
inline bool operator>= (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) >= 0; } \
inline bool operator== (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) == 0; } \
inline bool operator!= (const BigInt &lhs, OTHER rhs) { return lhs.compare(rhs) != 0; } \
inline bool operator< (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) < 0; } \
inline bool operator> (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) > 0; } \
inline bool operator<= (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) <= 0; } \
inline bool operator>= (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) >= 0; } \
inline bool operator== (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) == 0; } \
inline bool operator!= (OTHER lhs, const BigInt &rhs) { return -rhs.compare(lhs) != 0; }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this looks like a decent template candidate.

COMPARISON_OPERATORS(BigInt::llong_t)
COMPARISON_OPERATORS(BigInt::ullong_t)
COMPARISON_OPERATORS(unsigned long)
COMPARISON_OPERATORS(int)
COMPARISON_OPERATORS(unsigned)
#undef COMPARISON_OPERATORS

#endif//ndef BIGINT_HH