private static function _irr($pval, $payarray, $fromdayone)
    {
        $low = 0.0;
        $high = 100.0;
        $lowval = self::_npv($pval, $payarray, $low, $fromdayone);
        $highval = self::_npv($pval, $payarray, $high, $fromdayone);

        // The sum of $payarray is smaller than $pval, impossible!
        if ($lowval > 0.0) {
            return (-1);
        }

        // Standard divide and conquer.
        do {
            $mid = self::_midpoint($low, $high);
            $midval  = self::_npv($pval, $payarray, $mid, $fromdayone);
            if (abs($midval) < self::$accuracy) {
                //we are close enough
                return ($mid);
            }

            if ($highval < 0.0) {
                // we are not in range, so double it
                $low = $high;
                $lowval = $highval;
                $high *= 2;
                $highval = self::_npv($pval, $payarray, $high, $fromdayone);
            } else if ($midval >= 0.0) {
                // irr is between low and mid
                $high = $mid;
                $highval = $midval;
            } else {
                // irr is between mid and high
                $low = $mid;
                $lowval = $midval;
            }
        } while ($high < 1000000);
        // bad input, insanely high interest. APR will be INSANER!
        return (-2);
    }
private static function _irr($pval, $payarray, $fromdayone)
    {
        $low = 0.0;
        $high = 100.0;
        $lowval = self::_npv($pval, $payarray, $low, $fromdayone);
        $highval = self::_npv($pval, $payarray, $high, $fromdayone);

        // The sum of $payarray is smaller than $pval, impossible!
        if ($lowval > 0.0) {
            return (-1);
        }

        // Standard divide and conquer.
        do {
            $mid = self::_midpoint($low, $high);
            $midval  = self::_npv($pval, $payarray, $mid, $fromdayone);
            if (abs($midval) < self::$accuracy) {
                //we are close enough
                return ($mid);
            }

            if ($highval < 0.0) {
                // we are not in range, so double it
                $low = $high;
                $lowval = $highval;
                $high *= 2;
                $highval = self::_npv($pval, $payarray, $high, $fromdayone);
            } else if ($midval >= 0.0) {
                // irr is between low and mid
                $high = $mid;
                $highval = $midval;
            } else {
                // irr is between mid and high
                $low = $mid;
                $lowval = $midval;
            }
        } while ($high < 1000000);
        // bad input, insanely high interest. APR will be INSANER!
        return (-2);
    }
private static function _irr ( $ pval , $ payarray , $ fromdayone ) { $ low = 0.0 ; $ high = 100.0 ; $ lowval = self :: _npv ( $ pval , $ payarray , $ low , $ fromdayone ) ; $ highval = self :: _npv ( $ pval , $ payarray , $ high , $ fromdayone ) ; // The sum of $payarray is smaller than $pval, impossible! if ( $ lowval > 0.0 ) { return ( - 1 ) ; } // Standard divide and conquer. do { $ mid = self :: _midpoint ( $ low , $ high ) ; $ midval = self :: _npv ( $ pval , $ payarray , $ mid , $ fromdayone ) ; if ( abs ( $ midval ) < self :: $ accuracy ) { //we are close enough return ( $ mid ) ; } if ( $ highval < 0.0 ) { // we are not in range, so double it $ low = $ high ; $ lowval = $ highval ; $ high *= 2 ; $ highval = self :: _npv ( $ pval , $ payarray , $ high , $ fromdayone ) ; } else if ( $ midval >= 0.0 ) { // irr is between low and mid $ high = $ mid ; $ highval = $ midval ; } else { // irr is between mid and high $ low = $ mid ; $ lowval = $ midval ; } } while ( $ high < 1000000 ) ; // bad input, insanely high interest. APR will be INSANER! return ( - 2 ) ; }
This function uses divide and conquer to numerically find the IRR,
Internal Rate of Return. It starts of by trying a low of 0% and a
high of 100%. If this isn't enough it will double the interval up
to 1000000%. Note that this is insanely high, and if you try to convert
an IRR that high to an APR you will get even more insane values,
so feed this function good data.

Return values: float irr  if it was possible to find a rate that gets
npv closer to 0 than $accuracy.
int -1     The sum of the payarray is less than the lent
amount, $pval. Hellooooooo. Impossible.
int -2     the IRR is way to high, giving up.

This algorithm works in logarithmic time no matter what inputs you give
and it will come to a good answer within ~30 steps.

@param float $pval       initial loan to customer (in any currency)
@param array $payarray   array of monthly payments from the customer
@param int   $fromdayone count interest from the first day? yes(1)/no(0)

@return float
This function uses divide and conquer to numerically find the IRR Internal Rate of Return . It starts of by trying a low of 0% and a high of 100% . If this isn t enough it will double the interval up to 1000000% . Note that this is insanely high and if you try to convert an IRR that high to an APR you will get even more insane values so feed this function good data .