/* {{{ proto number pow(number base, number exponent)
   Returns base raised to the power of exponent. Returns
   integer result when possible. */

PHP_FUNCTION(pow)
{
	zval **base, **exp;
	
	if (ZEND_NUM_ARGS() != 2) {
		WRONG_PARAM_COUNT;
	} 
	/* can't fail if num args is correct */
	zend_get_parameters_ex(2,&base,&exp);
	convert_scalar_to_number_ex(base);
	convert_scalar_to_number_ex(exp);


	/* If exp != int, we should do floating pow, using c-func pow() */
	if ( Z_TYPE(*exp) != IS_LONG) {
		double dbase;
		dbase = Z_TYPE(*base) == IS_LONG ? (double) Z_LVAL(*base)
										 :          Z_DVAL(*base);
		if ( dbase <= 0.0 ) {
			/* Note that with the old behaviour, php pow() returned bogus
			   results. Try pow(-1,2.5) in PHP <= 4.0.6 ... */
			php_error(E_WARNING,"Trying to raise a nonpositive value to a broken power");
			/* There is *really* nothing logical that can be done */
			RETURN_FALSE;
		}

		/* Value's are okay, let's use pow()
		   pow() should be used as exp( log(base) * exp ) now... 
		   FIXME: maybe simply use that? pow() might be doing
		   strange things, and we've now already tracked down the problem
		   so that we know which formula to use... */
		RETURN_DOUBLE(pow(dbase, Z_DVAL(*exp)));
	}

	/* We have integer exponent, meaning that pow is always possible */

	/* If base if float, result should be float, and C-pow() works fine */
	if (Z_TYPE(*base) == IS_DOUBLE)
		RETURN_DOUBLE(pow(Z_DVAL(*base), Z_DVAL(*exp)));
	
	/* pow(int,int) */

	/* will be needed anyway later, so it's better to define them now already */
	long base = Z_LVAL(*base);
	long exp  = Z_LVAL(*exp);

	if (!exp) {
		RETURN_LONG(1); /* FIXME (important): should it be 1L here? */
	}

	switch (base)
	{
		case -1:
			RETURN_LONG( exp & 1 ? -1 : 1 ); /* if exp=odd ... */
		case 0:
			if (exp < 0) {
				php_error(E_WARNING,"Division by zero (pow(0,-1))");
				RETURN_FALSE;
			} else {
				RETURN_LONG(0);
			}
		case 1:
			RETURN_LONG(1);
		default:
			/* abs(base) > 1 */
			double dval = pow( (double) base, (double) exp);
			if (exp < 0 || dval > (double) LONG_MAX ) {
				/* 1/n ( abs(n) > 1 ) || overflow */
				RETURN_DOUBLE(dval);
			}
			/* FIXME: result could be so large that dval is bogus,
			   and you still come here. The modus of the returned
			   integer will be valid though :), even in decent time */
			/* Fasted algorithm there is, runs in O(logn), where n=exp */
			long result = 1;
			while (exp > 0) {
				if (exp & 1) /* odd */
					result *= base;
				exp >>= 1;
				base *= base;
			}
			RETURN_LONG(result);
	}
}

/* }}} */

