X-Git-Url: https://mattmccutchen.net/bigint/bigint.git/blobdiff_plain/3e1327901d299a537a8d932c49dd330f87ac3bda..cb2f0c288d4b7acfa37d7a9c8bc1024c3f332b5f:/BigUnsigned.hh diff --git a/BigUnsigned.hh b/BigUnsigned.hh index 624c87f..683ac8b 100644 --- a/BigUnsigned.hh +++ b/BigUnsigned.hh @@ -62,7 +62,7 @@ public: BigUnsigned( short x); protected: // Helpers - template void initFromPrimitive(X x); + template void initFromPrimitive (X x); template void initFromSignedPrimitive(X x); public: @@ -78,10 +78,10 @@ public: protected: // Helpers template X convertToSignedPrimitive() const; - template X convertToPrimitive() const; + template X convertToPrimitive () const; public: - // ACCESSORS + // BIT/BLOCK ACCESSORS // Expose these from NumberlikeArray directly. NumberlikeArray::getCapacity; @@ -90,10 +90,25 @@ public: /* Returns the requested block, or 0 if it is beyond the length (as if * the number had 0s infinitely to the left). */ Blk getBlock(Index i) const { return i >= len ? 0 : blk[i]; } + /* Sets the requested block. The number grows or shrinks as necessary. */ + void setBlock(Index i, Blk newBlock); // The number is zero if and only if the canonical length is zero. bool isZero() const { return NumberlikeArray::isEmpty(); } + /* Returns the length of the number in bits, i.e., zero if the number + * is zero and otherwise one more than the largest value of bi for + * which getBit(bi) returns true. */ + Index bitLength() const; + /* Get the state of bit bi, which has value 2^bi. Bits beyond the + * number's length are considered to be 0. */ + bool getBit(Index bi) const { + return (getBlock(bi / N) & (1 << (bi % N))) != 0; + } + /* Sets the state of bit bi to newBit. The number grows or shrinks as + * necessary. */ + void setBit(Index bi, bool newBit); + // COMPARISONS // Compares this to x like Perl's <=> @@ -166,8 +181,10 @@ public: void bitAnd(const BigUnsigned &a, const BigUnsigned &b); void bitOr(const BigUnsigned &a, const BigUnsigned &b); void bitXor(const BigUnsigned &a, const BigUnsigned &b); - void bitShiftLeft(const BigUnsigned &a, unsigned int b); - void bitShiftRight(const BigUnsigned &a, unsigned int b); + /* Negative shift amounts translate to opposite-direction shifts, + * except for -2^(8*sizeof(int)-1) which is unimplemented. */ + void bitShiftLeft(const BigUnsigned &a, int b); + void bitShiftRight(const BigUnsigned &a, int b); /* `a.divideWithRemainder(b, q)' is like `q = a / b, a %= b'. * / and % use semantics similar to Knuth's, which differ from the @@ -191,10 +208,6 @@ public: BigUnsigned operator &(const BigUnsigned &x) const; BigUnsigned operator |(const BigUnsigned &x) const; BigUnsigned operator ^(const BigUnsigned &x) const; - BigUnsigned operator <<(unsigned int b) const; - BigUnsigned operator >>(unsigned int b) const; - // Additional operators in an attempt to avoid overloading tangles. - // XXX Why exactly are these needed? BigUnsigned operator <<(int b) const; BigUnsigned operator >>(int b) const; @@ -207,10 +220,6 @@ public: void operator &=(const BigUnsigned &x); void operator |=(const BigUnsigned &x); void operator ^=(const BigUnsigned &x); - void operator <<=(unsigned int b); - void operator >>=(unsigned int b); - // Additional operators in an attempt to avoid overloading tangles. - // XXX Why exactly are these needed? void operator <<=(int b); void operator >>=(int b); @@ -251,12 +260,14 @@ inline BigUnsigned BigUnsigned::operator *(const BigUnsigned &x) const { return ans; } inline BigUnsigned BigUnsigned::operator /(const BigUnsigned &x) const { + if (x.isZero()) throw "BigUnsigned::operator /: division by zero"; BigUnsigned q, r; r = *this; r.divideWithRemainder(x, q); return q; } inline BigUnsigned BigUnsigned::operator %(const BigUnsigned &x) const { + if (x.isZero()) throw "BigUnsigned::operator %: division by zero"; BigUnsigned q, r; r = *this; r.divideWithRemainder(x, q); @@ -277,26 +288,16 @@ inline BigUnsigned BigUnsigned::operator ^(const BigUnsigned &x) const { ans.bitXor(*this, x); return ans; } -inline BigUnsigned BigUnsigned::operator <<(unsigned int b) const { +inline BigUnsigned BigUnsigned::operator <<(int b) const { BigUnsigned ans; ans.bitShiftLeft(*this, b); return ans; } -inline BigUnsigned BigUnsigned::operator >>(unsigned int b) const { +inline BigUnsigned BigUnsigned::operator >>(int b) const { BigUnsigned ans; ans.bitShiftRight(*this, b); return ans; } -inline BigUnsigned BigUnsigned::operator <<(int b) const { - if (b < 0) - throw "BigUnsigned::operator <<(int): Negative shift amounts are not allowed"; - return *this << (unsigned int)(b); -} -inline BigUnsigned BigUnsigned::operator >>(int b) const { - if (b < 0) - throw "BigUnsigned::operator >>(int): Negative shift amounts are not allowed"; - return *this >> (unsigned int)(b); -} inline void BigUnsigned::operator +=(const BigUnsigned &x) { add(*this, x); @@ -308,6 +309,7 @@ inline void BigUnsigned::operator *=(const BigUnsigned &x) { multiply(*this, x); } inline void BigUnsigned::operator /=(const BigUnsigned &x) { + if (x.isZero()) throw "BigUnsigned::operator /=: division by zero"; /* The following technique is slightly faster than copying *this first * when x is large. */ BigUnsigned q; @@ -316,6 +318,7 @@ inline void BigUnsigned::operator /=(const BigUnsigned &x) { *this = q; } inline void BigUnsigned::operator %=(const BigUnsigned &x) { + if (x.isZero()) throw "BigUnsigned::operator %=: division by zero"; BigUnsigned q; // Mods *this by x. Don't care about quotient left in q. divideWithRemainder(x, q); @@ -329,21 +332,87 @@ inline void BigUnsigned::operator |=(const BigUnsigned &x) { inline void BigUnsigned::operator ^=(const BigUnsigned &x) { bitXor(*this, x); } -inline void BigUnsigned::operator <<=(unsigned int b) { +inline void BigUnsigned::operator <<=(int b) { bitShiftLeft(*this, b); } -inline void BigUnsigned::operator >>=(unsigned int b) { +inline void BigUnsigned::operator >>=(int b) { bitShiftRight(*this, b); } -inline void BigUnsigned::operator <<=(int b) { - if (b < 0) - throw "BigUnsigned::operator <<=(int): Negative shift amounts are not supported"; - *this <<= (unsigned int)(b); + +/* Templates for conversions of BigUnsigned to and from primitive integers. + * BigInteger.cc needs to instantiate convertToPrimitive, and the uses in + * BigUnsigned.cc didn't do the trick; I think g++ inlined convertToPrimitive + * instead of generating linkable instantiations. So for consistency, I put + * all the templates here. */ + +// CONSTRUCTION FROM PRIMITIVE INTEGERS + +/* Initialize this BigUnsigned from the given primitive integer. The same + * pattern works for all primitive integer types, so I put it into a template to + * reduce code duplication. (Don't worry: this is protected and we instantiate + * it only with primitive integer types.) Type X could be signed, but x is + * known to be nonnegative. */ +template +void BigUnsigned::initFromPrimitive(X x) { + if (x == 0) + ; // NumberlikeArray already initialized us to zero. + else { + // Create a single block. blk is NULL; no need to delete it. + cap = 1; + blk = new Blk[1]; + len = 1; + blk[0] = Blk(x); + } } -inline void BigUnsigned::operator >>=(int b) { - if (b < 0) - throw "BigUnsigned::operator >>=(int): Negative shift amounts are not supported"; - *this >>= (unsigned int)(b); + +/* Ditto, but first check that x is nonnegative. I could have put the check in + * initFromPrimitive and let the compiler optimize it out for unsigned-type + * instantiations, but I wanted to avoid the warning stupidly issued by g++ for + * a condition that is constant in *any* instantiation, even if not in all. */ +template +void BigUnsigned::initFromSignedPrimitive(X x) { + if (x < 0) + throw "BigUnsigned constructor: " + "Cannot construct a BigUnsigned from a negative number"; + else + initFromPrimitive(x); +} + +// CONVERSION TO PRIMITIVE INTEGERS + +/* Template with the same idea as initFromPrimitive. This might be slightly + * slower than the previous version with the masks, but it's much shorter and + * clearer, which is the library's stated goal. */ +template +X BigUnsigned::convertToPrimitive() const { + if (len == 0) + // The number is zero; return zero. + return 0; + else if (len == 1) { + // The single block might fit in an X. Try the conversion. + X x = X(blk[0]); + // Make sure the result accurately represents the block. + if (Blk(x) == blk[0]) + // Successful conversion. + return x; + // Otherwise fall through. + } + throw "BigUnsigned::to: " + "Value is too big to fit in the requested type"; +} + +/* Wrap the above in an x >= 0 test to make sure we got a nonnegative result, + * not a negative one that happened to convert back into the correct nonnegative + * one. (E.g., catch incorrect conversion of 2^31 to the long -2^31.) Again, + * separated to avoid a g++ warning. */ +template +X BigUnsigned::convertToSignedPrimitive() const { + X x = convertToPrimitive(); + if (x >= 0) + return x; + else + throw "BigUnsigned::to(Primitive): " + "Value is too big to fit in the requested type"; } #endif