This library contains a single class, BigInt, which works like an int of arbitrary size. At the moment, most of the basic functionality is in. There are still some operators that have not been implemented, but it’s in the works. In the next few months, I will be updating this library, re-writing it from scratch and uploading it to a repository so that everyone can see the progress. In addition, I will be making a tutorial on how this class was designed and the reasoning behind some of the decisions made.
Interface
/******************************************************************************/
/*
Author: Alejandro Hitti
Brief: Interface for the BigInt type. This object allows you to perform
unbounded integer operations. Useful when making operations that
would normally overflow a 32-bit or 64-bit integer.
All content (C) 2013-2014 Alejandro Hitti http://alejandrohitti.com.
All rights reserved.
*/
/******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
#ifndef BIG_INT_H
#define BIG_INT_H
////////////////////////////////////////////////////////////////////////////////
#include <iostream> //istream, ostream
#include <string>
#include <vector>
class BigInt
{
public:
/*----------------------------------------
Constructors
----------------------------------------*/
BigInt(); // Default
BigInt(int num); // Non-default (integer)
BigInt(const std::string &str); // Non-default (string)
/*----------------------------------------
Arithmetic
----------------------------------------*/
const BigInt& operator += (const BigInt& rhs);
const BigInt& operator -= (const BigInt& rhs);
const BigInt& operator *= (const BigInt& rhs);
const BigInt& operator *= (int num);
// TODO(AleHitti): Implement these!
// BigInt& operator /= (const BigInt& rhs);
// Pow function
/*----------------------------------------
Assignment and Conversion
----------------------------------------*/
std::string ToString() const;
int ToInt() const;
double ToDouble() const;
/*----------------------------------------
Public Helper Functions
----------------------------------------*/
void Print(std::ostream& os) const; // Make a print with scanf? Test it
bool Equal(const BigInt& rhs) const; // Used for operators == and !=
bool LessThan(const BigInt& rhs) const; // Used for operators <, >, <=, >=
int Compare(const BigInt& rhs) const; // Returns 0 if equal than rhs.
// 1 if greater than rhs.
// -1 if less than rhs.
private:
/*----------------------------------------
Private Helper Functions
----------------------------------------*/
int GetDigit(int index) const;
void ChangeDigit(int index, int value);
void AddSignificantDigit(int value);
bool IsPositive() const; // Returns true if number is positive
bool IsNegative() const; // Returns false if number is negative
int NumDigits() const; // Returns number of digits in number
void Normalize(); // Converts number into it's standard form
/*----------------------------------------
Member Variables
----------------------------------------*/
enum Sign {positive, negative}; // Possible sign values
Sign sign_; // Is number positive or negative
std::vector<char> digits_; // Stores all digits in number
int numDigits_; // Stores number of digits in number
};
//////////////////////////////////////////
// Free Global Functions
//////////////////////////////////////////
/*----------------------------------------
Arithmetic
----------------------------------------*/
BigInt operator + (const BigInt& lhs, const BigInt& rhs);
BigInt operator - (const BigInt& lhs, const BigInt& rhs);
BigInt operator * (const BigInt& lhs, const BigInt& rhs);
BigInt operator * (const BigInt& lhs, int num);
BigInt operator * (int num, const BigInt& rhs);
//BigInt operator / (const BigInt& lhs, const BigInt& rhs);
/*----------------------------------------
Comparison
----------------------------------------*/
bool operator == (const BigInt& lhs, const BigInt& rhs);
bool operator != (const BigInt& lhs, const BigInt& rhs);
bool operator < (const BigInt& lhs, const BigInt& rhs);
bool operator > (const BigInt& lhs, const BigInt& rhs);
bool operator <= (const BigInt& lhs, const BigInt& rhs);
bool operator >= (const BigInt& lhs, const BigInt& rhs);
/*----------------------------------------
I/O
----------------------------------------*/
std::ostream& operator << (std::ostream& out, const BigInt& big);
std::istream& operator >> (std::istream& in, BigInt& big);
#endif // BIG_INT_H
Implementation
/******************************************************************************/
/*
Author: Alejandro Hitti
Brief: Implementation for the BigInt type. This object allows you to perform
unbounded integer operations. Useful when making operations that
would normally overflow a 32-bit or 64-bit integer.
All content (C) 2013-2014 Alejandro Hitti http://alejandrohitti.com.
All rights reserved.
*/
/******************************************************************************/
#include "BigInt.h"
#include <cctype> // isdigit
#include <limits.h> // INT_MIN
#include <stdlib.h>
// Allows different bases
#define BASE 10
/*----------------------------------------
Constructors
----------------------------------------*/
BigInt::BigInt()
: sign_(positive),
digits_(1, '0'),
numDigits_(1)
{
// All fields initialized in initializer list
}
BigInt::BigInt(int num)
: sign_(positive),
digits_(1, '0'),
numDigits_(0)
{
// Special case for Min Int
if (INT_MIN == num)
{
*this = INT_MIN + 1;
//*this -= 1;
return;
}
// Check for negative number and change state
if (num < 0)
{
sign_ = negative;
num *= -1;
}
// Handle least-significant digit of num (handles num == 0)
AddSignificantDigit(num % BASE);
num /= BASE;
// Handle the remaining digits of num
while (num != 0)
{
AddSignificantDigit(num % BASE);
num /= BASE;
}
}
BigInt::BigInt(const std::string &str)
: sign_(positive),
digits_(str.length(), '0'),
numDigits_(0)
{
// Boundary case
if (str.length() == 0)
{
digits_.resize(1);
AddSignificantDigit(0);
return;
}
int limit = 0; // Handles the cases with leading signs ('-' or '+')
// Set negative sign if first character is '-'
if (str[0] == '-')
{
sign_ = negative;
limit = 1;
}
// Sets the limit if it has a leading plus sign
if (str[0] == '+')
limit = 1;
for (int i = str.length() - 1; i >= limit; --i)
{
// Check validity of given integer
if ( !isdigit(str[i]) )
{
std::cout << str << " Is not a valid number." << std::endl;
//abort();
}
// Add digit to the container
AddSignificantDigit(str[i] - '0');
}
// Transform number into its standard format
Normalize();
}
/*----------------------------------------
Arithmetic
----------------------------------------*/
const BigInt& BigInt::operator += (const BigInt& rhs)
{
// If adding itself, multiply by 2
if (*this == rhs)
return *this *= 2;
// Adding zero is a special case
if (rhs == 0)
return *this;
// If the signs are different, substract instead
if (IsPositive() != rhs.IsPositive())
{
*this -= (-1 * rhs);
return *this;
}
// OPTIMIZE???
// Length will be the longest # of digits
int length = (NumDigits() < rhs.NumDigits()) ? rhs.NumDigits() : NumDigits();
int carry = 0; // Carry digit
int sum; // Partial sum
// Go through all the values in both numbers
for (int i = 0; i < length; ++i)
{
sum = GetDigit(i) + rhs.GetDigit(i) + carry;
carry = sum / BASE;
sum = sum % BASE;
if (i < NumDigits())
ChangeDigit(i, sum);
else
AddSignificantDigit(sum);
}
// If there is still a carry value
if (carry != 0)
AddSignificantDigit(carry);
return *this;
}
const BigInt& BigInt::operator -= (const BigInt& rhs)
{
// Substracting self
if (*this == rhs)
return *this = 0;
// If signs are different, add instead
if (IsNegative() != rhs.IsNegative())
{
*this += (-1 * rhs);
return *this;
}
/*
* Signs are the same. Check which number is larger
* and switch to get the larger number "on top" if
* necessary since the sign can change when substracting.
*/
if (IsPositive() && (*this) < rhs ||
IsNegative() && (*this) > rhs)
{
*this = rhs - *this;
// Toggle sign
sign_ = IsPositive() ? negative : positive;
return *this;
}
int diff;
int borrow = 0;
int length = NumDigits();
// Same sign and larger number on top
for (int i = 0; i < length; ++i)
{
diff = GetDigit(i) - rhs.GetDigit(i) - borrow;
borrow = 0;
if (diff < 0)
{
diff += 10;
borrow = 1;
}
ChangeDigit(i, diff);
}
// Prevents getting trailing zeroes on the left
Normalize();
return *this;
}
const BigInt& BigInt::operator *= (const BigInt& rhs)
{
if (IsNegative() != rhs.IsNegative())
sign_ = negative;
else
sign_ = positive;
BigInt self(*this);
BigInt sum(0);
int length = rhs.NumDigits();
for (int i = 0; i < length; ++i)
{
sum += self * rhs.GetDigit(i);
self *= 10;
}
*this = sum;
return *this;
}
const BigInt& BigInt::operator *= (int num)
{
// Special Cases (change order later? handle -1?)
// Zero is a special case
if (num == 0)
return *this = 0;
if (BASE < num || num < 0)
{
*this *= BigInt(num);
return *this;
}
// One is a special case. Do nothing.
if (num == 1)
return *this;
// -1 case?
int length = NumDigits();
int product;
int carry = 0;
// Multiplication happens here
for (int i = 0; i < length; ++i)
{
product = num * GetDigit(i) + carry;
carry = product / BASE;
ChangeDigit(i, product % BASE);
}
// Takes care of extra carry past the end of the numbers
while (carry != 0)
{
AddSignificantDigit(carry % BASE);
carry /= BASE;
}
return *this;
}
/*----------------------------------------
Assignment and Conversion
----------------------------------------*/
std::string BigInt::ToString() const
{
unsigned length = NumDigits();
std::string str = "";
if (IsNegative())
str = '-';
for (int i = length - 1; i >= 0; --i)
str += static_cast<char>('0' + GetDigit(i));
return str;
}
int BigInt::ToInt() const
{
// Handles int overflow
if (INT_MAX < *this)
return INT_MAX;
// Handles int underflow
if (*this < INT_MIN)
return INT_MIN;
int result = 0;
int length = NumDigits();
// Variation of Horner's rule
for (int i = length; i >= 0; --i)
result = result * 1- + GetDigit(i);
if (IsNegative())
result *= -1;
return result;
}
double BigInt::ToDouble() const
{
// Handles int overflow
if (DBL_MAX < *this)
return DBL_MAX;
// Handles int underflow
if (*this < DBL_MIN)
return DBL_MIN;
double result = 0.0;
int length = NumDigits();
// Variation of Horner's rule
for (int i = length; i >= 0; --i)
result = result * 1- + GetDigit(i);
if (IsNegative())
result *= -1;
return result;
}
/*----------------------------------------
Public Helper Functions
----------------------------------------*/
void BigInt::Print(std::ostream& out) const
{
out << ToString();
}
bool BigInt::Equal(const BigInt& rhs) const
{
// First check for optimization
if (NumDigits() != rhs.NumDigits() ||
IsNegative() != rhs.IsNegative() )
return false;
int length = NumDigits();
// If one digit is not equal, returns false
for (int i = 0; i < length; ++i)
if (GetDigit(i) != rhs.GetDigit(i))
return false;
// All elements are equal
return true;
}
bool BigInt::LessThan(const BigInt& rhs) const
{
// If signs aren't equal, this < rhs only if this is negative
if (IsNegative() != rhs.IsNegative())
return IsNegative();
// If # of digits aren't the same, we check # of digits and sign
if (NumDigits() != rhs.NumDigits())
return (NumDigits() < rhs.NumDigits() && IsPositive()) ||
(NumDigits() > rhs.NumDigits() && IsNegative());
int length = NumDigits();
// Check all digits and compare them
for (int i = length - 1; i >= 0; --i)
{
if (GetDigit(i) < rhs.GetDigit(i))
return IsPositive();
if (GetDigit(i) > rhs.GetDigit(i))
return IsNegative();
}
// this == rhs
return false;
}
int BigInt::Compare(const BigInt& rhs) const
{
// Equal
if (*this == rhs)
return 0;
// -1 if less than rhs, 1 if greater than rhs
return (*this < rhs) ? -1 : 1;
}
/*----------------------------------------
Private Helper Functions
----------------------------------------*/
int BigInt::GetDigit(int index) const
{
if (0 <= index && index < NumDigits())
return digits_[index] - '0';
return 0;
}
void BigInt::ChangeDigit(int index, int value)
{
if (0 <= index && index < NumDigits())
digits_[index] = static_cast<char>('0' + value);
}
void BigInt::AddSignificantDigit(int value)
{
// Resize container
if (static_cast<unsigned>(numDigits_) >= digits_.size())
digits_.resize(digits_.size() * 2);
digits_[numDigits_] = static_cast<char>('0' + value);
++numDigits_;
}
bool BigInt::IsPositive() const
{
return sign_ == positive;
}
bool BigInt::IsNegative() const
{
return sign_ == negative;
}
int BigInt::NumDigits() const
{
return numDigits_;
}
void BigInt::Normalize()
{
int length = NumDigits();
int i; // Declared outside because it will be used after the loop
for (i = length - 1; i >= 0; --i)
{
// Deletes leading zeroes
if (GetDigit(i) != 0)
break;
--numDigits_;
}
// This means it was all zeroes, leave the last one
if (i < 0)
{
numDigits_ = 1;
sign_ = positive;
}
// Makes the container not be unnecessarily big if a lot of leading zeroes
// Where introduced. Will have to test for efficiency.
digits_.shrink_to_fit();
}
//////////////////////////////////////////
// Free Global Functions
//////////////////////////////////////////
/*----------------------------------------
Arithmetic
----------------------------------------*/
BigInt operator + (const BigInt& lhs, const BigInt& rhs)
{
BigInt result(lhs);
result += rhs;
return result;
}
BigInt operator - (const BigInt& lhs, const BigInt& rhs)
{
BigInt result(lhs);
result -= rhs;
return result;
}
BigInt operator * (const BigInt& lhs, const BigInt& rhs)
{
BigInt result(lhs);
result *= rhs;
return result;
}
BigInt operator * (const BigInt& lhs, int num)
{
BigInt result(lhs);
result *= num;
return result;
}
BigInt operator * (int num, const BigInt& rhs)
{
BigInt result(rhs);
result *= num;
return result;
}
/*----------------------------------------
Comparison
----------------------------------------*/
bool operator == (const BigInt& lhs, const BigInt& rhs)
{
return lhs.Equal(rhs);
}
bool operator != (const BigInt& lhs, const BigInt& rhs)
{
return !(lhs == rhs);
}
bool operator < (const BigInt& lhs, const BigInt& rhs)
{
return lhs.LessThan(rhs);
}
bool operator > (const BigInt& lhs, const BigInt& rhs)
{
return rhs < lhs;
}
bool operator <= (const BigInt& lhs, const BigInt& rhs)
{
return lhs == rhs || lhs < rhs;
}
bool operator >= (const BigInt& lhs, const BigInt& rhs)
{
return lhs == rhs || lhs > rhs;
}
/*----------------------------------------
I/O
----------------------------------------*/
std::ostream& operator << (std::ostream& out, const BigInt& big)
{
big.Print(out);
return out;
}
std::istream& operator >> (std::istream& in, BigInt& big)
{
std::string str;
in >> str;
big = BigInt(str);
return in;
}