// File: bigint.cpp // // Implementation of BigInt class. #include #include #include #include #include "bigint.h" #include "bimath.h" // #define DEBUG //=========================================================================== // Local function prototypes // static void CrashOnNULL(void *) ; static void StrDouble(char * & cptr, unsigned int& cbufsize) ; static unsigned char ToHex(unsigned char) ; //=========================================================================== // Local function implementations // // Exit program if pointer s is NULL. // call after using malloc() or new. // static void CrashOnNULL(void *p) { if (p == NULL) { cerr << "Could not allocate memory! Bye!" << endl ; exit(1) ; } } // Allocates space for a string with twice the amount of memory. // Copy old string to new location. // Both cptr and cbufsize are updated with new values. // static void StrDouble(char * & cptr, unsigned int& cbufsize) { char *tmp_cptr ; tmp_cptr = new char [2 * cbufsize] ; CrashOnNULL(tmp_cptr) ; memcpy(tmp_cptr, cptr, cbufsize) ; delete [] cptr ; cptr = tmp_cptr ; cbufsize *= 2 ; } static unsigned char ToHex(unsigned char c) { if ('0' <= c && c <= '9') return c - '0' ; if ('a' <= c && c <= 'f') return c - 'a' + 10 ; if ('A' <= c && c <= 'F') return c - 'A' + 10 ; return 0 ; //should do something worse! } //=========================================================================== // BigInt member functions // // Default constructor // Assumes MINLEN = sizeof(int) // BigInt::BigInt(int n) { // n defaults to 0 int *iptr ; #ifdef DEBUG cout << "Default constructor" << endl ; #endif bufsize = MINLEN ; len = MINLEN ; ptr = new unsigned char[bufsize] ; CrashOnNULL(ptr) ; iptr = (int *) ptr ; *iptr = n ; } // Alternate Constructor with allocation size and init value BigInt::BigInt(int size, int n) { int *iptr ; #ifdef DEBUG cout << "Alternate constructor, size = " << size << ", n = " << n << endl ; #endif if (size == 0) { bufsize = 0 ; len = 0 ; ptr = NULL ; return ; } bufsize = size > MINLEN ? size : MINLEN ; ptr = new unsigned char[bufsize] ; CrashOnNULL(ptr) ; len = MINLEN ; iptr = (int *) ptr ; *iptr = n ; } // Copy Constructor // BigInt::BigInt(const BigInt& bn) { unsigned char *temp ; #ifdef DEBUG cout << "Entering Copy Constructor, &bn=" << (void *) &bn << endl ; bn.dump() ; #endif ptr = NULL ; newsize(bn.len) ; len = bn.len ; CrashOnNULL(bn.ptr) ; memcpy(ptr, bn.ptr, len) ; #ifdef DEBUG cout << "Copy Constructor after copy, this=" << (void *) this << endl ; dump() ; #endif } // Type Converter/Constructor // Convert string of hex digits to BigInt // If string contains non-hex digits, results are unpredictable. BigInt::BigInt(const char *cptr) { const char *cidx ; unsigned char *temp, *idx ; int ch, ret ; unsigned int i, slen, ilen, size ; #ifdef DEBUG cout << endl << "In Char to BigInt converter" << endl ; #endif slen = strlen(cptr) ; ilen = (slen + 1) / 2 ; // round up to byte size size = ilen > MINLEN ? ilen : MINLEN ; // round up to MINLEN ptr = new unsigned char[size] ; CrashOnNULL(ptr) ; bufsize = len = size ; #ifdef DEBUG printf ("slen = %d, ilen = %d, size = %d\n", slen, ilen, size) ; dump() ; #endif idx = ptr + ilen - 1 ; // little endian! cidx = cptr ; if (slen % 2 == 1) { // odd number of hex digits? *idx = ToHex(*cidx) ; // read just one hex digit cidx++ ; idx-- ; } // Convert remaining hex digits two at a time. // while (idx >= ptr) { *idx = ToHex(*cidx)*16 + ToHex(*(cidx+1)) ; idx-- ; cidx = cidx + 2 ; } // zero fill very short numbers // for (i = ilen ; i < MINLEN ; i++) { ptr[i] = '\0' ; } } // Destructor // BigInt::~BigInt() { #ifdef DEBUG cout << "Destructor called" << endl ; #endif delete [] ptr ; } // Change buffer size to n. Original contents deleted! // void BigInt::newsize(int n) { unsigned char *temp ; int *iptr ; #ifdef DEBUG cout << "Newsize !, this=" << (void *) this << endl ; dump() ; #endif if (n == 0 ) { delete [] ptr ; len = bufsize = 0 ; ptr = NULL ; return ; } bufsize = n > MINLEN ? n : MINLEN ; temp = new unsigned char[bufsize] ; CrashOnNULL(temp) ; delete [] ptr ; ptr = temp ; iptr = (int *) ptr ; *iptr = 0 ; len = MINLEN ; } // Reduce buffer size if it's a good idea. // void BigInt::trim() { // Do nothing if string is short or uses 3/4 of buffer // if (bufsize <= MINLEN || len >= 0.75*bufsize) return ; force_trim() ; } // Always reduce to just the right buffer size. // void BigInt::force_trim() { unsigned char *newptr ; bufsize = len ; newptr = new unsigned char[bufsize] ; CrashOnNULL(newptr) ; CrashOnNULL(ptr) ; memcpy(newptr, ptr, len) ; delete [] ptr ; ptr = newptr ; } // Overloaded Operators // // assignment // BigInt& BigInt::operator =(const BigInt& bn) { #ifdef DEBUG cout << "Assignment operator called, &bn=" << (void *) &bn << endl ; bn.dump() ; #endif // Handle degenerate case of self-assignment if (this == &bn) return *this ; // need more space? if (bn.len > bufsize) newsize(bn.len) ; len = bn.len ; memcpy(ptr, bn.ptr, len) ; #ifdef DEBUG cout << "Assignment operator after copy, this:" << endl ; dump() ; #endif return *this ; } // Overloaded arithmetic operators // BigInt BigInt::operator +(const BigInt& bn) { BigInt sum(0, 0) ; unsigned int maxlen ; maxlen = len > bn.len ? len : bn.len ; sum.newsize(maxlen + 1) ; sum.len = 0 ; #ifdef DEBUG cout << "Overloaded Add operator, sum:" << endl ; sum.dump() ; #endif BI_Add(sum, *this, bn) ; sum.trim() ; #ifdef DEBUG cout << "Overloaded Add operator after BI_Add, sum:" << endl ; sum.dump() ; #endif return sum ; } BigInt BigInt::operator -(const BigInt& bn) { BigInt diff(0, 0) ; unsigned int maxlen ; maxlen = len > bn.len ? len : bn.len ; diff.newsize(maxlen + 1) ; BI_Sub(diff, *this, bn) ; diff.trim() ; return diff; } BigInt BigInt::operator *(const BigInt& bn) { BigInt product(0, 0) ; unsigned int maxlen ; maxlen = len > bn.len ? len : bn.len ; product.newsize(2*maxlen) ; BI_Mul(product, *this, bn) ; product.trim() ; return product ; } BigInt BigInt::operator /(const BigInt& bn) { BigInt quotient(0, 0), remainder(0, 0) ; unsigned int maxlen ; quotient.newsize(len) ; remainder.newsize(bn.len) ; BI_Div(quotient, remainder, *this, bn) ; quotient.trim() ; remainder.trim() ; return quotient ; } BigInt BigInt::operator %(const BigInt& bn) { BigInt quotient(0, 0), remainder(0, 0) ; unsigned int maxlen ; quotient.newsize(len) ; remainder.newsize(bn.len) ; BI_Div(quotient, remainder, *this, bn) ; quotient.trim() ; remainder.trim() ; return remainder ; } // comparisons // bool BigInt::operator <(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, *this, bn) ; return lt ? true : false ; } bool BigInt::operator <=(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, *this, bn) ; return (lt || eq) ? true : false ; } bool BigInt::operator ==(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, *this, bn) ; return eq ? true : false ; } bool BigInt::operator >=(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, bn, *this) ; return (lt || eq) ? true : false ; } bool BigInt::operator >(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, bn, *this) ; return lt ? true : false ; } bool BigInt::operator !=(const BigInt& bn) const { int lt, eq ; BI_Comp(lt, eq, *this, bn) ; return eq ? false : true ; } // Dump vital stats // void BigInt::dump() const { int i ; char onebyte[3] ; cout << "*** BigInt::dump()" << endl ; cout << "this="<< this << endl ; cout << "len=" << len << " &len=" << (void *) &len << endl ; cout << "bufsize=" << bufsize << " &bufsize=" << (void *) &bufsize << endl ; cout << "ptr=" << (void *) ptr << " &ptr=" << (void *) &ptr << endl ; if (ptr == NULL) { cout << "*ptr = NULL" << endl ; return ; } cout << "*ptr =" ; for (i = 0 ; i < len ; i++) { snprintf (onebyte, 3, "%02X", ptr[i]) ; cout << onebyte << " " ; } cout << endl << "***" << endl ; } //=========================================================================== // Overloaded I/O operators << and >> // Input: // Only works for unsigned hex. Want more? Roll your own. // Basic strategy --- store hex characters in a string and // call CharToBigInt. The main problem is that the string // can be arbitrarily long. #define INIT_CBUFSIZE 16 istream& operator >>(istream& istrm, BigInt& bn) { int ch ; unsigned int cbufsize, i, digit ; bool neg=false ; char *cptr, *tmp_cptr ; // First check for EOF // ch = istrm.get() ; // peek ahead to trigger EOF settings if (istrm.eof()) { return istrm ; } istrm.putback(ch) ; // continue as if we didn't peek // Skip leading spaces while(true) { ch = istrm.get() ; if (ch != ' ' || ch != '\t') break ; } istrm.putback(ch) ; // put back first non-space char // // Store hex digits in a string // // Initialize cbufsize = INIT_CBUFSIZE ; // number of bytes in the string cptr = new char[cbufsize] ; CrashOnNULL(cptr) ; i = 0 ; // keep reading hex digits until done // while(true) { ch = istrm.get() ; if (ch == EOF || ch == '\n') break ; // eats \n OK?? if (!( ('0' <= ch && ch <= '9') || // not hex digit? ('a' <= ch && ch <= 'f') || ('A' <= ch && ch <= 'F') ) ) { istrm.putback(ch) ; // not a digit break ; } // Enough space? If not, double buffer size. if (i >= cbufsize) StrDouble(cptr, cbufsize) ; cptr[i++] = ch ; } // Don't forget to terminate the string! if (i >= cbufsize) StrDouble(cptr, cbufsize) ; cptr[i++] ='\0' ; bn = cptr ; // Use (char *) -> BigInt converter delete [] cptr ; // don't need this anymore return istrm ; } // Dumps the contents of bn to ostrm. // snprintf used because it supports hex with leading zeroes format. // Avoided using printf directly, because there's no guarantee that // printf and cout uses the same buffer. (Nevermind, printf and ostrm.) // ostream& operator <<(ostream& ostrm, const BigInt& bn) { int i ; char onebyte[3] ; // One byte has 2 hex digits + '\0' for (i = bn.len - 1 ; i >= 0 ; i--) { // little endian! snprintf (onebyte, 3, "%02X", bn.ptr[i]) ; ostrm << onebyte ; } return ostrm ; }