// ------------------ // Purenum 0.4d alpha // ------------------ // integer.h // ------------------ // the "CONFIGURATION" section below must be correct // see file: "integer.txt" for more info about class Integer #ifndef INTEGER_H #define INTEGER_H //////////////////////////////////////////////////////////////////////////////// // CONFIGURATION (must be reconfigured for each system this is compiled on!) // CONFIGURATION (must be reconfigured for each system this is compiled on!) // CONFIGURATION (must be reconfigured for each system this is compiled on!) // // INTEGER_H_BITS -- What is the hardware bit width of the CPU chip? Must // correctly specify the bit width of INTEGER_H_DIGITTYPE (see below) or // this software will not work. (the default digit type is 'unsigned int') // Most modern computers are 32-bit, as of November 2000. // //#define INTEGER_H_BITS 8 // base 256 math (2^8) //#define INTEGER_H_BITS 16 // base 65536 math (2^16) #define INTEGER_H_BITS 32 // base 4294967296 math (2^32) //#define INTEGER_H_BITS 64 // base 18446744073709551616 math (2^64) // // INTEGER_H_INITZERO -- For easier programming, all Integers may be // automatically initialized to zero while being constructed, whenever the // programmer does not specify an immediate initializer. This prevents the // tricky bugs which can result from the use of uninitialized values, but is // slower. Zeroing is the default because ease-of-use is the top priority for // this software. Experts who are confident of their ability to remember to // initialize all values before use can disable this option for a (usually // small) speed increase. // #define INTEGER_H_INITZERO // init Integers to zero during construction // // INTEGER_H_DIGITTYPE -- // //#define INTEGER_H_DIGITTYPE unsigned char // type of Integer::atom //#define INTEGER_H_DIGITTYPE unsigned short // type of Integer::atom #define INTEGER_H_DIGITTYPE unsigned int // type of Integer::atom //#define INTEGER_H_DIGITTYPE unsigned long // type of Integer::atom // //#define INTEGER_H_SDIGITTYPE signed char // type of Integer::satom //#define INTEGER_H_SDIGITTYPE signed short // type of Integer::satom #define INTEGER_H_SDIGITTYPE signed int // type of Integer::satom //#define INTEGER_H_SDIGITTYPE signed long // type of Integer::satom // // USE FAST BIGNUM ALGORITHMS -- Should the fast, but complicated and tricky, // code for wide multiplication and wide division be enabled? Without enabling // these the simple, reliable, and very, very, very slow code is used. Very, // very, very slow! Does not affect thin integer math, which is always fast. // FIXME: remove the slow wide mul/div code before final release // #define INTEGER_H_FASTMUL // enable fast wide multiplication code #define INTEGER_H_FASTDIV // enable fast wide division code // // FAKE CPU BIT WIDTH -- Pretend to be on a CPU with a smaller hardware bit // width, specified here. The true hardware bit width must be specified // correctly in INTEGER_H_BITS (above) or this software will not work. // FIXME: the INTEGER_H_FAKEBITS functionality is incomplete, beware. // //#define INTEGER_H_FAKEBITS 8 // base 256 math (2^8) //#define INTEGER_H_FAKEBITS 16 // base 65536 math (2^16) //#define INTEGER_H_FAKEBITS 32 // base 4294967296 math (2^32) // // DECIMAL DEBUGGING -- During development some parts of this software used // decimal math (almost 4-bit math) because it was easier to debug numbers when // they could be directly printed in decimal form. Probably does not work // anymore and will be removed sometime. FIXME // //#define INTEGER_H_DECIMAL // DO NOT DEFINE, FOR DEBUGGING PURPOSES ONLY // //////////////////////////////////////////////////////////////////////////////// #define PURENUM_STRING_CONSTRUCTOR_EXPLICIT // (SR) FIXME needs to be put into rascal sources... ;) #ifndef PURENUM_STRING_CONSTRUCTOR_EXPLICIT #define EX #else // PURENUM_STRING_CONSTRUCTOR_EXPLICIT #define EX explicit #endif // PURENUM_STRING_CONSTRUCTOR_EXPLICIT // FIXME: delete the following include? #include class Integer { public: typedef INTEGER_H_DIGITTYPE atom; typedef INTEGER_H_SDIGITTYPE satom; enum sign { positive, negative }; inline Integer(); // see INTEGER_H_INITZERO above inline Integer(sign, atom); // init from a hardware number inline Integer(atom); // init from a hardware value inline Integer(satom); // init from a hardware value inline Integer(const Integer &); // init from another Integer EX inline Integer(const char *ascii); // init from a string number inline ~Integer(); // destructor // conversion operators // FIXME: need signed int conversion operator inline operator bool() const; inline operator signed int() const; inline operator unsigned int() const; // unary math operators (members) inline Integer &operator++(); // prefix inline Integer operator++(int); // postfix inline Integer &operator--(); // prefix inline Integer operator--(int); // postfix // binary math operators (members) inline Integer &operator=(const Integer &); inline Integer &operator=(const atom &); inline Integer &operator=(const satom &); inline Integer &operator+=(const Integer &); inline Integer &operator+=(const atom &); inline Integer &operator+=(const satom &); inline Integer &operator-=(const Integer &); inline Integer &operator-=(const atom &); inline Integer &operator-=(const satom &); inline Integer &operator*=(const Integer &); inline Integer &operator*=(const atom &); inline Integer &operator*=(const satom &); inline Integer &operator/=(const Integer &); inline Integer &operator/=(const atom &); inline Integer &operator/=(const satom &); inline Integer &operator%=(const Integer &); inline Integer &operator%=(const atom &); inline Integer &operator%=(const satom &); // friends: unary math operators (global functions) friend Integer operator~(const Integer &); friend bool operator!(const Integer &); friend Integer operator-(const Integer &); friend Integer operator+(const Integer &); // friends: binary math operators (global functions) friend Integer operator+(const Integer &, const Integer &); friend Integer operator+(const Integer &, const atom &); friend Integer operator+(const atom &, const Integer &); friend Integer operator+(const Integer &, const satom &); friend Integer operator+(const satom &, const Integer &); friend Integer operator-(const Integer &, const Integer &); friend Integer operator-(const Integer &, const atom &); friend Integer operator-(const atom &, const Integer &); friend Integer operator-(const Integer &, const satom &); friend Integer operator-(const satom &, const Integer &); friend Integer operator*(const Integer &, const Integer &); friend Integer operator*(const Integer &, const atom &); friend Integer operator*(const atom &, const Integer &); friend Integer operator*(const Integer &, const satom &); friend Integer operator*(const satom &, const Integer &); friend Integer operator/(const Integer &, const Integer &); friend Integer operator/(const Integer &, const atom &); friend Integer operator/(const atom &, const Integer &); friend Integer operator/(const Integer &, const satom &); friend Integer operator/(const satom &, const Integer &); friend Integer operator%(const Integer &, const Integer &); friend Integer operator%(const Integer &, const atom &); friend Integer operator%(const atom &, const Integer &); friend Integer operator%(const Integer &, const satom &); friend Integer operator%(const satom &, const Integer &); friend bool operator==(const Integer &, const Integer &); friend bool operator==(const Integer &, const atom &); friend bool operator==(const atom &, const Integer &); friend bool operator==(const Integer &, const satom &); friend bool operator==(const satom &, const Integer &); friend bool operator!=(const Integer &, const Integer &); friend bool operator!=(const Integer &, const atom &); friend bool operator!=(const atom &, const Integer &); friend bool operator!=(const Integer &, const satom &); friend bool operator!=(const satom &, const Integer &); friend bool operator>(const Integer &, const Integer &); friend bool operator>(const Integer &, const atom &); friend bool operator>(const atom &, const Integer &); friend bool operator>(const Integer &, const satom &); friend bool operator>(const satom &, const Integer &); friend bool operator>=(const Integer &, const Integer &); friend bool operator>=(const Integer &, const atom &); friend bool operator>=(const atom &, const Integer &); friend bool operator>=(const Integer &, const satom &); friend bool operator>=(const satom &, const Integer &); friend bool operator<(const Integer &, const Integer &); friend bool operator<(const Integer &, const atom &); friend bool operator<(const atom &, const Integer &); friend bool operator<(const Integer &, const satom &); friend bool operator<(const satom &, const Integer &); friend bool operator<=(const Integer &, const Integer &); friend bool operator<=(const Integer &, const atom &); friend bool operator<=(const atom &, const Integer &); friend bool operator<=(const Integer &, const satom &); friend bool operator<=(const satom &, const Integer &); friend bool operator&&(const Integer &, const Integer &); friend bool operator&&(const Integer &, const bool); friend bool operator&&(const bool, const Integer &); friend bool operator||(const Integer &, const Integer &); friend bool operator||(const Integer &, const bool); friend bool operator||(const bool, const Integer &); // FIXME: move these to a seperate optional file? // other/math functions (global functions) friend ostream &operator<<(ostream &, const Integer &); // FIXME delete? friend void swap(Integer &, Integer &); friend Integer abs(const Integer &); friend Integer min(const Integer &, const Integer &); friend Integer max(const Integer &, const Integer &); friend Integer fac(const Integer &); friend Integer pow(const Integer &, const Integer &); // exceptions // FIXME: throw outofmemory when 'new' fails? or not? class exception { }; class divisionbyzero : public exception { }; // division by zero class outofmemory : public exception { }; // software integer overflow class outofrange : public exception { }; // hardware integer overflow class badstring : public exception { }; // non-numeric initializer class negativefactorial : public exception {}; // can't do x! for x < 0 // ascii string input and output FIXME: not internationalized char *ascii(const Integer base = 10) const; // out new[] void ascii(atom size, char *, const Integer base = 10) const; // output void ascii(const char *, const Integer base = 10); // input #if 0 // string input and output, digits are zero-based (no character set) char *string(atom base = 10) const; // output to new[] void string(atom size, char *, atom base = 10) const; // buffer output void string(const char *, atom base = 10); // buffer input // bitstream input and output, a string() with a header and a footer atom *bitstream(atom base = maxatom_) const; void bitstream(atom size, atom *a, atom base = maxatom_) const; void bitstream(const atom *a, atom base = maxatom_); #endif // 0 private: // internal representation of the integer value (this is the entire state) atom memorysize_; // number of atoms in memory_ bool negative_; // is this Integer negative? union optimizedmemory_ // .fast needs no heap allocation, speedy! { atom fast; // storage for a thin integer (one digit) atom *slow; // storage for a wide integer (many digits) } memory_; static const atom maxatom_; // largest legal value in an Integer digit #ifdef INTEGER_H_FASTMUL static const atom smallmaxatom_; // used for wide multiply optimization #endif // INTEGER_H_FASTMUL #ifdef INTEGER_H_FAKEBITS static const atom truemaxatom_; // see INTEGER_H_FAKEBITS above #endif // INTEGER_H_FAKEBITS // private thin math (single-atom) subroutines used by public functions inline void addatomzero(atom &left, bool &carry) const; inline void subatomzero(atom &left, bool &borrow) const; inline void addatoms(atom &left, const atom &right, bool &carry) const; inline void subatoms(atom &left, const atom &right, bool &borrow) const; inline void mulatoms(atom &left, const atom &right, atom &carry) const; // private wide math (multi-atom) subroutines used by public functions inline void incinner(atom *&); inline void decinner(atom *&); inline void addinner(const Integer &, atom *&, const atom *&); inline void subinner(const Integer &, atom *&, const atom *&); #ifdef INTEGER_H_FASTDIV inline void divbyatom(const atom &, Integer &, Integer &); inline void divinner(const Integer &, Integer &, Integer &); #endif // INTEGER_H_FASTDIV // private wide math (multi-atom) functions used by public functions void inc(); // operator++ prefix void dec(); // operator-- prefix void add(const Integer &); // operator+= void sub(const Integer &); // operator-= void mul(const Integer &); // operator*= void div(const Integer &); // operator/= void mod(const Integer &); // operator%= bool equ(const Integer &) const; // operator== and operator!= bool grt(const Integer &) const; // operator> bool gre(const Integer &) const; // operator>= bool let(const Integer &) const; // operator< bool lee(const Integer &) const; // operator<= // private wide math (multi-atom) functions used by private functions inline bool largerthan(const Integer &right) const; // size greaterthan inline bool largerorequal(const Integer &right) const; inline bool smallerthan(const Integer &right) const; // size lessthan inline bool smallerorequal(const Integer &right) const; // private utility functions inline void shiftup(atom places); // append zeros (in base: maxatom+1) inline void shiftdown(atom places); // truncate places (in base: maxatom+1) }; //#if 0 // functions to help measure code speed (tricks compiler into not optimizing) void touch(const Integer &); // empty function, for testing only void touch(const Integer::atom &); // empty function, for testing only //#endif // 0 // unary math operators (global functions) inline Integer operator~(const Integer &left); inline bool operator!(const Integer &left); inline Integer operator-(const Integer &left); inline Integer operator+(const Integer &left); // binary math operators (global functions) inline Integer operator+(const Integer &left, const Integer &right); inline Integer operator+(const Integer &left, const Integer::atom &rightatom); inline Integer operator+(const Integer::atom &leftatom, const Integer &right); inline Integer operator+(const Integer &left, const Integer::satom &rightatom); inline Integer operator+(const Integer::satom &leftatom, const Integer &right); inline Integer operator-(const Integer &left, const Integer &right); inline Integer operator-(const Integer &left, const Integer::atom &rightatom); inline Integer operator-(const Integer::atom &leftatom, const Integer &right); inline Integer operator-(const Integer &left, const Integer::satom &rightatom); inline Integer operator-(const Integer::satom &leftatom, const Integer &right); inline Integer operator*(const Integer &left, const Integer &right); inline Integer operator*(const Integer &left, const Integer::atom &rightatom); inline Integer operator*(const Integer::atom &leftatom, const Integer &right); inline Integer operator*(const Integer &left, const Integer::satom &rightatom); inline Integer operator*(const Integer::satom &leftatom, const Integer &right); inline Integer operator/(const Integer &left, const Integer &right); inline Integer operator/(const Integer &left, const Integer::atom &rightatom); inline Integer operator/(const Integer::atom &leftatom, const Integer &right); inline Integer operator/(const Integer &left, const Integer::satom &rightatom); inline Integer operator/(const Integer::satom &leftatom, const Integer &right); inline Integer operator%(const Integer &left, const Integer &right); inline Integer operator%(const Integer &left, const Integer::atom &rightatom); inline Integer operator%(const Integer::atom &leftatom, const Integer &right); inline Integer operator%(const Integer &left, const Integer::satom &rightatom); inline Integer operator%(const Integer::satom &leftatom, const Integer &right); inline bool operator==(const Integer &left, const Integer &right); inline bool operator==(const Integer &left, const Integer::atom &rightatom); inline bool operator==(const Integer::atom &leftatom, const Integer &right); inline bool operator==(const Integer &left, const Integer::satom &rightatom); inline bool operator==(const Integer::satom &leftatom, const Integer &right); inline bool operator!=(const Integer &left, const Integer &right); inline bool operator!=(const Integer &left, const Integer::atom &rightatom); inline bool operator!=(const Integer::atom &leftatom, const Integer &right); inline bool operator!=(const Integer &left, const Integer::satom &rightatom); inline bool operator!=(const Integer::satom &leftatom, const Integer &right); inline bool operator>(const Integer &left, const Integer &right); inline bool operator>(const Integer &left, const Integer::atom &rightatom); inline bool operator>(const Integer::atom &leftatom, const Integer &right); inline bool operator>(const Integer &left, const Integer::satom &rightatom); inline bool operator>(const Integer::satom &leftatom, const Integer &right); inline bool operator>=(const Integer &left, const Integer &right); inline bool operator>=(const Integer &left, const Integer::atom &rightatom); inline bool operator>=(const Integer::atom &leftatom, const Integer &right); inline bool operator>=(const Integer &left, const Integer::satom &rightatom); inline bool operator>=(const Integer::satom &leftatom, const Integer &right); inline bool operator<(const Integer &left, const Integer &right); inline bool operator<(const Integer &left, const Integer::atom &rightatom); inline bool operator<(const Integer::atom &leftatom, const Integer &right); inline bool operator<(const Integer &left, const Integer::satom &rightatom); inline bool operator<(const Integer::satom &leftatom, const Integer &right); inline bool operator<=(const Integer &left, const Integer &right); inline bool operator<=(const Integer &left, const Integer::atom &rightatom); inline bool operator<=(const Integer::atom &leftatom, const Integer &right); inline bool operator<=(const Integer &left, const Integer::satom &rightatom); inline bool operator<=(const Integer::satom &leftatom, const Integer &right); inline bool operator&&(const Integer &left, const Integer &right); inline bool operator&&(const Integer &left, const bool right); inline bool operator&&(const bool left, const Integer &right); inline bool operator||(const Integer &left, const Integer &right); inline bool operator||(const Integer &left, const bool right); inline bool operator||(const bool left, const Integer &right); inline ostream &operator<<(ostream &, const Integer &); inline void swap(Integer &, Integer &); inline Integer abs(const Integer &); inline Integer min(const Integer &, const Integer &); inline Integer max(const Integer &, const Integer &); inline Integer fac(const Integer &); inline Integer pow(const Integer &, const Integer &); #ifdef INTEGER_H_INITZERO inline Integer::Integer() : memorysize_(1), negative_(false) { memory_.fast = 0; } #else inline Integer::Integer() : memorysize_(1) { } #endif // INTEGER_H_INITZERO inline Integer::Integer(Integer::sign s, Integer::atom a) : memorysize_(1), negative_(s == negative && a != 0) { memory_.fast = a; } #ifndef INTEGER_H_DECIMAL inline Integer::Integer(Integer::atom a) : memorysize_(1), negative_(false) { memory_.fast = a; } #else inline Integer::Integer(Integer::atom a) : negative_(false) { if (a <= maxatom_) { memorysize_ = 1; memory_.fast = a; } else { atom digits = 1; atom tempa; for (tempa = a; tempa > maxatom_; tempa /= maxatom_ + 1) ++digits; atom *newmemory = new atom[digits]; atom count; tempa = a; for (count = 0; count < digits; ++count) { newmemory[count] = tempa % (maxatom_ + 1); tempa /= maxatom_ + 1; } memorysize_ = digits; memory_.slow = newmemory; } } #endif // INTEGER_H_DECIMAL inline Integer::Integer(Integer::satom i) { #ifndef INTEGER_H_FAKEBITS if (i >= 0) { memorysize_ = 1; memory_.fast = i; negative_ = false; return; } else { // test correctness of the hardware negation operation Integer::satom a = i, b = i; b = -b; Integer::satom c = a + b; if (c == 0) { memorysize_ = 1; memory_.fast = b; negative_ = true; return; } } throw outofrange(); // what is this crazy hardware? // NOTE: One of the most confusing things about signed integers is that on // most systems (two's-complement systems) there are a different // number of positive numbers available than negative ones. So this // constructor can not simply invert the sign of negative int numbers // passed to it or it will not be portable, it must go thru // contortions to preserve both the magnitude and the sign. Imagine // the case where -32768 is passed in but where the highest positive // number able to be stored in a signed int is 32767 (this case // happens to be true for 16-bit signed ints on most PCs) so // inverting the -32768 sign will not work or could even crash. ANSI // C++ does not define the result, but on my x86 PC it happens that // -(-32768) is equal to (-32768), clearly unacceptable. Similarly // for 32-bit ints... -(-2147483648) == (-2147483648). #else // INTEGER_H_FAKEBITS throw outofrange(); #endif // INTEGER_H_FAKEBITS } inline Integer::Integer(const Integer &other) : memorysize_(other.memorysize_), negative_(other.negative_) { if (memorysize_ == 1) memory_.fast = other.memory_.fast; else // FIXME: make a function call to this smarter logic { memory_.slow = new atom[memorysize_]; for (atom a = 0; a < memorysize_; ++a) memory_.slow[a] = other.memory_.slow[a]; } } inline Integer::~Integer() { // (minor) FIXME: use function call here if shorter than delete [] for x86? if (memorysize_ > 1) delete [] memory_.slow; } inline Integer::operator bool() const { if (memorysize_ == 1) // optimize for fast single-atom case { if (memory_.fast == 0) return false; else return true; } return true; // zero is always stored within one memory location } inline Integer::operator signed int() const { // FIXME: this is not portable but should work on most computers for now if (memorysize_ != 1) throw outofrange(); signed int i = memory_.fast; if (i < 0) throw outofrange(); return i; } inline Integer::operator unsigned int() const { #ifndef INTEGER_H_FAKEBITS // FIXME: is this correct when atom is not an unsigned int? if (memorysize_ != 1) throw outofrange(); return memory_.fast; #else // INTEGER_H_FAKEBITS atom base = maxatom_ + 1; atom result = 0, shift = 0, a, loop; Integer i = *this; for (;;) { if (i.memorysize_ == 1) a = i.memory_.fast; else a = i.memory_.slow[0]; for (loop = 0; loop < shift; ++loop) { if (a > truemaxatom_ / base) throw outofrange(); a *= base; } if (result > truemaxatom_ - a) throw outofrange(); result += a; if (i.memorysize_ == 1) return result; i.shiftdown(1); ++shift; } #endif // INTEGER_H_FAKEBITS } inline Integer & Integer::operator++() // prefix { if (memorysize_ == 1) // optimize for fast single-atom case { if (!negative_) { atom &leftatom = memory_.fast; if (leftatom < maxatom_) // overflow prediction { ++leftatom; return *this; } } else { atom &leftatom = memory_.fast; --leftatom; if (leftatom == 0) negative_ = false; return *this; } } inc(); // fall back to slow multi-atom case return *this; } inline Integer Integer::operator++(int) // postfix { Integer result = *this; this->operator++(); return result; } inline Integer & Integer::operator--() // prefix { if (memorysize_ == 1) // optimize for fast single-atom case { if (negative_) { atom &leftatom = memory_.fast; if (leftatom < maxatom_) // overflow prediction { ++leftatom; return *this; } } else { atom &leftatom = memory_.fast; if (leftatom != 0) // underflow prediction { --leftatom; if (leftatom == 0) negative_ = false; return *this; } else { leftatom = 1U; negative_ = true; return *this; } } } dec(); // fall back to slow multi-atom case return *this; } inline Integer Integer::operator--(int) // postfix { Integer result = *this; this->operator--(); return result; } inline Integer & Integer::operator=(const Integer &right) { if (right.memorysize_ == 1) { if (memorysize_ > 1) delete [] memory_.slow; memorysize_ = right.memorysize_; negative_ = right.negative_; memory_.fast = right.memory_.fast; } else // FIXME: make a function call to this smarter logic { if (memory_.slow != right.memory_.slow) // catch assignment to self { if (memorysize_ > 1) delete [] memory_.slow; memorysize_ = right.memorysize_; negative_ = right.negative_; memory_.slow = new atom[memorysize_]; for (atom a = 0; a < memorysize_; ++a) memory_.slow[a] = right.memory_.slow[a]; } } return *this; } inline Integer & Integer::operator=(const atom &rightatom) { if (memorysize_ > 1) delete [] memory_.slow; memorysize_ = 1; negative_ = false; #ifndef INTEGER_H_FAKEBITS memory_.fast = rightatom; #else // INTEGER_H_FAKEBITS if (rightatom <= maxatom_) { memory_.fast = rightatom; } else { memory_.fast = 0; atom base = maxatom_ + 1; atom shift = 0; atom a = rightatom; Integer i = 0; while (a > maxatom_) { i = (a % base); i.shiftup(shift); *this += i; a /= base; ++shift; } } #endif // INTEGER_H_FAKEBITS return *this; } inline Integer & Integer::operator=(const satom &rightatom) { return *this = Integer(rightatom); } inline Integer & Integer::operator+=(const Integer &right) { if (memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (negative_ == right.negative_) { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (leftatom <= maxatom_ - rightatom) // overflow prediction { leftatom += rightatom; return *this; } } else { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (leftatom >= rightatom) // underflow prediction { leftatom -= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { leftatom = rightatom - leftatom; negative_ = !negative_; return *this; } } } add(right); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator+=(const atom &rightatom) { if (memorysize_ == 1) // optimize for fast single-atom case { if (!negative_) { atom &leftatom = memory_.fast; if (leftatom <= maxatom_ - rightatom) // overflow prediction { leftatom += rightatom; return *this; } } else { atom &leftatom = memory_.fast; if (leftatom >= rightatom) // underflow prediction { leftatom -= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { leftatom = rightatom - leftatom; negative_ = !negative_; return *this; } } } add(Integer(rightatom)); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator+=(const satom &rightatom) { return *this += Integer(rightatom); } inline Integer & Integer::operator-=(const Integer &right) { if (memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (negative_ != right.negative_) { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (leftatom <= maxatom_ - rightatom) // overflow prediction { leftatom += rightatom; return *this; } } else { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (leftatom >= rightatom) // underflow prediction { leftatom -= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { leftatom = rightatom - leftatom; negative_ = !negative_; return *this; } } } sub(right); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator-=(const atom &rightatom) { if (memorysize_ == 1) // optimize for fast single-atom case { if (negative_) { atom &leftatom = memory_.fast; if (leftatom <= maxatom_ - rightatom) // overflow prediction { leftatom += rightatom; return *this; } } else { atom &leftatom = memory_.fast; if (leftatom >= rightatom) // underflow prediction { leftatom -= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { leftatom = rightatom - leftatom; negative_ = !negative_; return *this; } } } sub(Integer(rightatom)); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator-=(const satom &rightatom) { return *this -= Integer(rightatom); } inline Integer & Integer::operator*=(const Integer &right) { if (memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (rightatom == 0 || leftatom <= maxatom_ / rightatom) { // overflow prediction leftatom *= rightatom; if (leftatom == 0) negative_ = false; else if (right.negative_) negative_ = !negative_; return *this; } } mul(right); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator*=(const atom &rightatom) { if (memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; if (rightatom == 0 || leftatom <= maxatom_ / rightatom) { // overflow prediction leftatom *= rightatom; if (leftatom == 0) negative_ = false; return *this; } } mul(Integer(rightatom)); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator*=(const satom &rightatom) { return *this *= Integer(rightatom); } inline Integer & Integer::operator/=(const Integer &right) { if (memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (rightatom != 0) // division by zero prediction { leftatom /= rightatom; if (leftatom == 0) negative_ = false; else if (right.negative_) negative_ = !negative_; return *this; } else { // if (leftatom == 0) // dividing zero by zero is okay // return *this; throw divisionbyzero(); } } div(right); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator/=(const atom &rightatom) { if (memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; if (rightatom != 0) // division by zero prediction { leftatom /= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { // if (leftatom == 0) // dividing zero by zero is okay // return *this; throw divisionbyzero(); } } div(Integer(rightatom)); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator/=(const satom &rightatom) { return *this /= Integer(rightatom); } inline Integer & Integer::operator%=(const Integer &right) { if (memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; const atom &rightatom = right.memory_.fast; if (rightatom != 0) // division by zero prediction { leftatom %= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { // if (leftatom == 0) // dividing zero by zero is okay // return *this; throw divisionbyzero(); } } mod(right); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator%=(const atom &rightatom) { if (memorysize_ == 1) // optimize for fast single-atom case { atom &leftatom = memory_.fast; if (rightatom != 0) // division by zero prediction { leftatom %= rightatom; if (leftatom == 0) negative_ = false; return *this; } else { // if (leftatom == 0) // dividing zero by zero is okay // return *this; throw divisionbyzero(); } } mod(Integer(rightatom)); // fall back to slow multi-atom case return *this; } inline Integer & Integer::operator%=(const satom &rightatom) { return *this %= Integer(rightatom); } inline Integer operator~(const Integer &left) // FIXME see integer.txt complement info { Integer result = left; if (result.memorysize_ == 1) // optimize for fast single-atom case { #ifndef INTEGER_H_DECIMAL result.memory_.fast = ~result.memory_.fast; return result; #else result.memory_.fast = Integer::maxatom_; result.memory_.fast -= left.memory_.fast; return result; #endif // INTEGER_H_DECIMAL } Integer::atom a = 0; for (; a < result.memorysize_; ++a) #ifndef INTEGER_H_DECIMAL result.memory_.slow[a] = ~result.memory_.slow[a]; #else { result.memory_.slow[a] = Integer::maxatom_; result.memory_.slow[a] -= left.memory_.slow[a]; } #endif // INTEGER_H_DECIMAL return result; } inline bool operator!(const Integer &left) { if (left.memorysize_ == 1) // optimize for fast single-atom case { if (!left.memory_.fast) return false; else return true; } Integer right = 0U; return !left.equ(right); // fall back to slow multi-atom case } inline Integer operator-(const Integer &left) { Integer result = left; if (result.memorysize_ == 1 && result.memory_.fast == 0) result.negative_ = false; else result.negative_ = !result.negative_; return result; } inline Integer operator+(const Integer &left) { Integer result = left; return result; } inline Integer operator+(const Integer &left, const Integer &right) { Integer result = left; result += right; return result; } inline Integer operator+(const Integer &left, const Integer::atom &rightatom) { Integer result = left; result += rightatom; return result; } inline Integer operator+(const Integer::atom &leftatom, const Integer &right) { Integer result = leftatom; result += right; return result; } inline Integer operator+(const Integer &left, const Integer::satom &rightatom) { Integer result = left; result += rightatom; return result; } inline Integer operator+(const Integer::satom &leftatom, const Integer &right) { Integer result = leftatom; result += right; return result; } inline Integer operator-(const Integer &left, const Integer &right) { Integer result = left; result -= right; return result; } inline Integer operator-(const Integer &left, const Integer::atom &rightatom) { Integer result = left; result -= rightatom; return result; } inline Integer operator-(const Integer::atom &leftatom, const Integer &right) { Integer result = leftatom; result -= right; return result; } inline Integer operator-(const Integer &left, const Integer::satom &rightatom) { Integer result = left; result -= rightatom; return result; } inline Integer operator-(const Integer::satom &leftatom, const Integer &right) { Integer result = leftatom; result -= right; return result; } inline Integer operator*(const Integer &left, const Integer &right) { Integer result = left; result *= right; return result; } inline Integer operator*(const Integer &left, const Integer::atom &rightatom) { Integer result = left; result *= rightatom; return result; } inline Integer operator*(const Integer::atom &leftatom, const Integer &right) { Integer result = leftatom; result *= right; return result; } inline Integer operator*(const Integer &left, const Integer::satom &rightatom) { Integer result = left; result *= rightatom; return result; } inline Integer operator*(const Integer::satom &leftatom, const Integer &right) { Integer result = leftatom; result *= right; return result; } inline Integer operator/(const Integer &left, const Integer &right) { Integer result = left; result /= right; return result; } inline Integer operator/(const Integer &left, const Integer::atom &rightatom) { Integer result = left; result /= rightatom; return result; } inline Integer operator/(const Integer::atom &leftatom, const Integer &right) { Integer result = leftatom; result /= right; return result; } inline Integer operator/(const Integer &left, const Integer::satom &rightatom) { Integer result = left; result /= rightatom; return result; } inline Integer operator/(const Integer::satom &leftatom, const Integer &right) { Integer result = leftatom; result /= right; return result; } inline Integer operator%(const Integer &left, const Integer &right) { Integer result = left; result %= right; return result; } inline Integer operator%(const Integer &left, const Integer::atom &rightatom) { Integer result = left; result %= rightatom; return result; } inline Integer operator%(const Integer::atom &leftatom, const Integer &right) { Integer result = leftatom; result %= right; return result; } inline Integer operator%(const Integer &left, const Integer::satom &rightatom) { Integer result = left; result %= rightatom; return result; } inline Integer operator%(const Integer::satom &leftatom, const Integer &right) { Integer result = leftatom; result %= right; return result; } inline bool operator==(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return false; return left.memory_.fast == right.memory_.fast; } return left.equ(right); // fall back to slow multi-atom case } inline bool operator==(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return false; else return left.memory_.fast == rightatom; } Integer right = rightatom; return left.equ(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left == Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator==(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return false; else return leftatom == right.memory_.fast; } Integer left = leftatom; return left.equ(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) == right; #endif // INTEGER_H_FAKEBITS } inline bool operator==(const Integer &left, const Integer::satom &rightatom) { return left == Integer(rightatom); } inline bool operator==(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) == right; } inline bool operator!=(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return true; return left.memory_.fast != right.memory_.fast; } return !left.equ(right); // fall back to slow multi-atom case } inline bool operator!=(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return true; else return left.memory_.fast != rightatom; } Integer right = rightatom; return !left.equ(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left != Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator!=(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return true; else return leftatom != right.memory_.fast; } Integer left = leftatom; return !left.equ(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) != right; #endif // INTEGER_H_FAKEBITS } inline bool operator!=(const Integer &left, const Integer::satom &rightatom) { return left != Integer(rightatom); } inline bool operator!=(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) != right; } inline bool operator>(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return !left.negative_; if (!left.negative_) return left.memory_.fast > right.memory_.fast; else return left.memory_.fast < right.memory_.fast; } return left.grt(right); // fall back to slow multi-atom case } inline bool operator>(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return false; else return left.memory_.fast > rightatom; } Integer right = rightatom; return left.grt(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left > Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator>(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return true; else return leftatom > right.memory_.fast; } Integer left = leftatom; return left.grt(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) > right; #endif // INTEGER_H_FAKEBITS } inline bool operator>(const Integer &left, const Integer::satom &rightatom) { return left > Integer(rightatom); } inline bool operator>(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) > right; } inline bool operator>=(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return !left.negative_; if (!left.negative_) return left.memory_.fast >= right.memory_.fast; else return left.memory_.fast <= right.memory_.fast; } return left.gre(right); // fall back to slow multi-atom case } inline bool operator>=(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return false; else return left.memory_.fast >= rightatom; } Integer right = rightatom; return left.gre(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left >= Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator>=(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return true; else return leftatom >= right.memory_.fast; } Integer left = leftatom; return left.gre(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) == right; #endif // INTEGER_H_FAKEBITS } inline bool operator>=(const Integer &left, const Integer::satom &rightatom) { return left >= Integer(rightatom); } inline bool operator>=(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) == right; } inline bool operator<(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return left.negative_; if (!left.negative_) return left.memory_.fast < right.memory_.fast; else return left.memory_.fast > right.memory_.fast; } return left.let(right); // fall back to slow multi-atom case } inline bool operator<(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return true; else return left.memory_.fast < rightatom; } Integer right = rightatom; return left.let(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left < Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator<(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return false; else return leftatom < right.memory_.fast; } Integer left = leftatom; return left.let(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) == right; #endif // INTEGER_H_FAKEBITS } inline bool operator<(const Integer &left, const Integer::satom &rightatom) { return left < Integer(rightatom); } inline bool operator<(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) == right; } inline bool operator<=(const Integer &left, const Integer &right) { if (left.memorysize_ == 1 && right.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_ != right.negative_) return left.negative_; if (!left.negative_) return left.memory_.fast <= right.memory_.fast; else return left.memory_.fast >= right.memory_.fast; } return left.lee(right); // fall back to slow multi-atom case } inline bool operator<=(const Integer &left, const Integer::atom &rightatom) { #ifndef INTEGER_H_FAKEBITS if (left.memorysize_ == 1) // optimize for fast single-atom case { if (left.negative_) return true; else return left.memory_.fast <= rightatom; } Integer right = rightatom; return left.lee(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return left <= Integer(rightatom); #endif // INTEGER_H_FAKEBITS } inline bool operator<=(const Integer::atom &leftatom, const Integer &right) { #ifndef INTEGER_H_FAKEBITS if (right.memorysize_ == 1) // optimize for fast single-atom case { if (right.negative_) return false; else return leftatom <= right.memory_.fast; } Integer left = leftatom; return left.lee(right); // fall back to slow multi-atom case #else // INTEGER_H_FAKEBITS return Integer(leftatom) <= right; #endif // INTEGER_H_FAKEBITS } inline bool operator<=(const Integer &left, const Integer::satom &rightatom) { return left <= Integer(rightatom); } inline bool operator<=(const Integer::satom &leftatom, const Integer &right) { return Integer(leftatom) <= right; } inline bool operator&&(const Integer &left, const Integer &right) { return (bool)left && (bool)right; } inline bool operator&&(const Integer &left, const bool right) { return (bool)left && right; } inline bool operator&&(const bool left, const Integer &right) { return left && (bool)right; } inline bool operator||(const Integer &left, const Integer &right) { return (bool)left || (bool)right; } inline bool operator||(const Integer &left, const bool right) { return (bool)left || right; } inline bool operator||(const bool left, const Integer &right) { return left || (bool)right; } //#include // FIXME: delete this line? inline ostream & operator<<(ostream &s, const Integer &i) // FIXME: needs io manips? { if (i.memorysize_ == 1) // optimize for fast single-atom case { if (i.negative_) s << "-"; return s << i.memory_.fast; } char *output; output = i.ascii(); // FIXME: this defaults allowing base 10 output only s << output; delete [] output; return s; #ifdef BETTERCODE const Integer base = 10U; Integer i2, digits = 1U; for (i2 = i; i2 >= base; i2 /= base) // FIXME: incorrectly optimized digits++; const unsigned int fastresultsize = 100; unsigned char fastresult[fastresultsize]; unsigned char *result; if (digits > fastresultsize) result = new unsigned char[(Integer::atom)digits]; // FIXME: previous line can go out of range else result = fastresult; Integer::atom place = digits; for (i2 = i; i2 >= base; i2 /= base) result[--place] = ((Integer::atom)(i2 % base)); result[--place] = ((Integer::atom)i2); for (; place < digits; ++place) s << (unsigned char)(result[place] + '0'); if (digits > fastresultsize) delete [] result; #endif // BETTERCODE } inline void swap(Integer &left, Integer &right) { bool negative = right.negative_; right.negative_ = left.negative_; left.negative_ = negative; Integer::atom memorysize = right.memorysize_; right.memorysize_ = left.memorysize_; left.memorysize_ = memorysize; Integer::optimizedmemory_ memory; if (memorysize == 1) memory.fast = right.memory_.fast; else memory.slow = right.memory_.slow; if (right.memorysize_ == 1) right.memory_.fast = left.memory_.fast; else right.memory_.slow = left.memory_.slow; if (left.memorysize_ == 1) left.memory_.fast = memory.fast; else left.memory_.slow = memory.slow; } inline Integer abs(const Integer &other) { Integer result = other; if (result.negative_) result.negative_ = false; return result; } inline Integer min(const Integer &left, const Integer &right) { if (left < right) return left; else return right; } inline Integer max(const Integer &left, const Integer &right) { if (left > right) return left; else return right; } inline Integer fac(const Integer &value) { if (value < 0) throw Integer::negativefactorial(); Integer result = 1; if (value > 1) { Integer next = value; do { result *= next; --next; } while (next > 1); } return result; } inline Integer pow(const Integer &value, const Integer &exponent) { if (exponent == 0) return 1; Integer result = value; Integer count = abs(exponent); for (--count; count > 0; --count) result *= value; if (exponent < 0) return 1 / result; // always rounds to zero! return result; } inline // FIXME: probably should not be inline? Integer::Integer(const char *asciiz) { // FIXME: decimal string assumed, need to allow base 2, 8, 16, etc. // FIXME: also need to check that the base is not larger than maxatom memorysize_ = 1; negative_ = false; memory_.fast = 0; unsigned long len = 0; for (; asciiz[len] != 0; ++len) if (asciiz[len] == ' ') throw badstring(); // no spaces allowed if (len == 0) throw badstring(); Integer &result = *this, digit, shift, i2; unsigned long i = len - 1; Integer::atom value; for (;; i--) { if (asciiz[i] == '-') // FIXME: what is this crap? { if (result.memorysize_ != 1 || result.memory_.fast != 0) result.negative_ = true; break; } value = asciiz[i] - '0'; shift = 1; for (i2 = 0; i2 < digit; i2++) shift *= 10; // FIXME: only works for base 10 strings result += Integer(value) * shift; ++digit; if (i == 0) break; } // memorysize_ = 1; // *this = result; } #ifdef PURENUM_SETTING_INT_DEFAULT typedef Integer Int; #endif // PURENUM_SETTING_INT_DEFAULT #undef EX #endif // INTEGER_H