/**
* Adding up very large, positive numbers.
* Class is work in progress ;)
*
* Author: Erik Roelofs
*/
class BigInt {
// size of the chunks the string/number is broken up into (ie: into sets of 8 digits)
private static $iChunkSize = 8;
// filled automatically; pow ( 10, chunksize )
private static $iChunkMaxAmount = 0;
/**
* Each of these values is four digits in the bug number.
* The order of values is in REVERSE!
*/
private $aValues = array();
/**
* Constructing a new big int
* Determine the max chunk value, if we do not have it yet
*
*/
public function __construct() {
if ( self::$iChunkMaxAmount == 0 ) {
self::$iChunkMaxAmount = pow( 10, self::$iChunkSize );
}
}
/**
* Create a BigInt from a string
*
* @param string $sString
* @return BigInt $oBigInt
*/
public static function createBigIntFromString( $sString ) {
// make sure it is of proper type
if ( !ctype_digit( $sString ) ) {
throw new Exception( 'Given string is not composed of digits.' );
}
// find the nearest multiple of the chunksize, to split the string properly
// otherwise splittig 100 in chunks of 2 yields 10, 0 instead of 1, 00.
$iLength = strlen( $sString );
if ( ( $iLength % self::$iChunkSize ) != 0 ) {
$iLength = ceil ( $iLength / self::$iChunkSize ) * self::$iChunkSize;
$sString= str_pad( $sString, $iLength, '0', STR_PAD_LEFT );
}
$aDigits = str_split ( $sString , self::$iChunkSize );
// make BigInt, return it.
$oBigInt = new self;
$oBigInt->setSets( array_reverse( $aDigits ) );
return $oBigInt;
}
/**
* Return a new BigInt, with the value of the two given BigInts added together
*
* @param BigInt $oBigInt1
* @param BigInt $oBigInt2
* @return BigInt $oResult
*/
public static function add( BigInt $oBigInt1, BigInt $oBigInt2 ) {
// store the result here
$oResult = new BigInt;
// determine which of the two is the longest; we'll run over that one
if ( $oBigInt1->getLength() > $oBigInt2->getLength() ) {
$oRunner = $oBigInt1;
$oFollower = $oBigInt2;
}
else {
$oRunner = $oBigInt2;
$oFollower = $oBigInt1;
}
// carryover from the last operation
$iCarry = 0;
// run over the longest, and add each set to the set of the shorter
foreach ( $oRunner->getSets() as $iSetNum => $iValue ) {
// get the value of the follower on this position
$iOtherValue = $oFollower->getSet( $iSetNum );
// determine the total for the current set
$iTotal = $iValue + $iOtherValue + $iCarry;
// if the total is more then the max chunk size, then the number will overflow and we'll have to determine the new carry
if ( $iTotal >= self::$iChunkMaxAmount ) {
$iCarry = ( floor ( $iTotal / ( self::$iChunkMaxAmount ) ) );
$iTotal = $iTotal % ( self::$iChunkMaxAmount );
}
else {
$iCarry = 0;
}
// add this chunk to the resultset
$oResult->addSet( $iTotal );
}
// now, if all the numbers are done and we still have a carry, tack that to the front of the result
if ( $iCarry > 0 ) {
$oResult->addSet( $iCarry );
}
// return the result
return $oResult;
}
/**
* Get the set of numbers at position $iSet
* For example; if the BigInt is 125042, then 0 would be 5042, and 1 would be 12
* Will return 0 if the number isn't that long.
*
* @param integer $iSet
* @return integer
*/
private function getSet( $iSet ) {
if ( isset ( $this->aValues[ $iSet ] ) ) {
return $this->aValues[ $iSet ];
}
return 0;
}
/**
* Return all the sets in this number
*
* return array $aSets
*/
private function getSets() {
return $this->aValues;
}
/**
* Set all the sets for this BigInt
*
* @param array $aValues
*/
private function setSets( $aValues ) {
$this->aValues = $aValues;
}
/**
* Add a new set to this BigInt
* (Remember that it will be added to the front of the number, but the back of the array)
*
* @param integer
*/
private function addSet( $iValue ) {
$this->aValues[] = $iValue;
}
/**
* Return the length of this BigInt
*
* return integer $iLength
*/
private function getLength() {
return count( $this->aValues );
}
/**
* Return or output the value of the BigInt
*
* @param boolean $bEcho, whether to echo the result or return it as a string
*/
public function show( $bEcho = true ) {
$bPad = false;
if ( $bEcho ) {
$aValues = $this->getSets();
while ( ( $iValue = array_pop ( $aValues ) ) !== null ) {
if ( $bPad ) {
$iValue = str_pad( $iValue, self::$iChunkSize, '0', STR_PAD_LEFT );
}
echo $iValue;
$bPad = true;
}
}
else {
$sReturn = '';
$aValues = $this->getSets();
while ( ( $iValue = array_pop ( $aValues ) ) !== null ) {
if ( $bPad ) {
$iValue = str_pad( $iValue, self::$iChunkSize, '0', STR_PAD_LEFT );
}
$bPad = true;
$sReturn .= $iValue;
}
return $sReturn;
}
}
}