/** * Calculate the quotient and remainder from a +ve numerator/denominator with no decimals. * This function is used when the denominators is smaller than self::BIGNUMBER_MAX_NUM_LEN. * @param BigNumber $numerator the numerator to use. * @param BigNumber $denominator the denominator. * @param BigNumber $quotient the return quotient value. * @param BigNumber $remainder the remainder value. * @return boolean success or not. */ protected static function AbsQuotientAndRemainderWithSmallDenominator($numerator, $denominator, &$quotient, &$remainder) { // is it too long? $denominatorLen = count($denominator->_numbers); if ($denominatorLen > self::BIGNUMBER_MAX_NUM_LEN) { return false; } if ($denominatorLen == self::BIGNUMBER_MAX_NUM_LEN) { // because the numerator is the same len as BIGNUMBER_MAX_NUM_LEN // we have to make sure that the first 'BIGNUMBER_MAX_NUM_LEN' digits of the numerator are not bigger than the denominator. // $numberNumerator = $numerator->_MakeNumberAtIndexForward(0, self::BIGNUMBER_MAX_NUM_LEN); $numberDenominator = $denominator->ToInt(); if ($numberNumerator < $numberDenominator) { // this will never work as the first 'BIGNUMBER_MAX_NUM_LEN' digits are too small. return false; } } // assume that the remainder is the full amount. $remainder = clone $numerator; // get the positive denominator. $intDenominator = $denominator->Abs()->ToInt(); // convert to integer. $numerator->Abs()->Integer(); // the offset is just one more than the total len of our number. $offset = count($denominator->_numbers) + 1; $length = count($numerator->_numbers); // do a fast division. // @see http://codereview.stackexchange.com/questions/6331/very-large-a-divide-at-a-very-large-b // get the first 'x' numbers, as we are in reverse, we get the last x numbers. // we then work one number at at a time. $numbers = []; // get the first 'x' numbers. // after that we will be getting one number at a time. $number = $numerator->_MakeNumberAtIndexForward(0, $offset); // now create an array of our remaining numbers. // we will use them all one by one to calculate the 'final' denominator. $array = array_slice($numerator->_numbers, 0, $length - $offset); $oneOverIntDenominator = 1 / (double) $intDenominator; $length -= $offset; for (;;) { // the div of that number, (we use 1/x as it is a shade faster). $div = (int) ($number * $oneOverIntDenominator); // if the number is 0 it means that the section we just did // (the '$number'), was itself == to zero. if (0 == $div) { // if the number is zero, no need to waste time working things out $numbers[] = 0; } else { // add the numbers, in reverse to our final array. $tnumber = []; while ($div > 0) { $s = $div % self::BIGNUMBER_BASE; $div = (int) ((int) $div / (int) self::BIGNUMBER_BASE); $tnumber[] = $s; } $numbers = array_merge($numbers, array_reverse($tnumber)); } // are we done? if ($length <= 0) { $quotient = static::_FromSafeValues($quotient, array_reverse($numbers), 0, false); break; } // the mod of that number. $mod = $number % $intDenominator; $numerator = new BigNumber($mod); // add the number in front array_unshift($numerator->_numbers, $array[--$length]); // get that section number. $number = $numerator->ToInt(); } // clean up the quotient and the remainder. $quotient->PerformPostOperations($quotient->_decimals); // we know that the remainder is currently equal to the numerator, (see clone above). $remainder = static::AbsSub($remainder, static::AbsMul($quotient, $denominator, 0)); // success return true; }