-
Notifications
You must be signed in to change notification settings - Fork 273
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
// | ||
|
@@ -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; | ||
BigInt &operator= (BigInt const &) _fast; | ||
BigInt &operator= (char const *) _fast; | ||
BigInt &operator= (BigInt &&) _fast; | ||
|
||
// Input conversion from text. | ||
|
||
|
@@ -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. | ||
|
||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could these macros be implemented as template operator overloads with |
||
|
||
BigInt &operator++ () { return operator+=(1); } // preincrement | ||
BigInt &operator-- () { return operator-=(1); } // predecrement | ||
|
||
static void div (BigInt const &, BigInt const &, | ||
BigInt ", 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); | ||
} | ||
}; | ||
|
||
|
||
|
@@ -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; } | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
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
)?There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.