/**
* Generalized Hypergeometric Function
*
* https://en.wikipedia.org/wiki/Generalized_hypergeometric_function
*
* ∞
* ____
* \ ∏ap⁽ⁿ⁾ * zⁿ
* pFq(a₁,a₂,...ap;b₁,b₂,...,bq;z)= > ------------
* / ∏bq⁽ⁿ⁾ * n!
* ‾‾‾‾
* n=0
*
* Where a⁽ⁿ⁾ is the Pochhammer Function or Rising Factorial
*
* We are evaluating this as a series:
*
* (a + n - 1) * z
* ∏n = ∏n₋₁ * -----------------
* (b + n - 1) * n
*
* n (a + n - 1) * z
* ₁F₁ = ₁F₁n₋₁ + ∏ ----------------- = ₁F₁n₋₁ + ∏n
* 1 (b + n - 1) * n
*
* @param int $p the number of parameters in the numerator
* @param int $q the number of parameters in the denominator
* @param array $params a collection of the a, b, and z parameters
*
* @return number
*
* @throws BadParameterException if the number of parameters is incorrect
*/
public static function generalizedHypergeometric(int $p, int $q, ...$params)
{
$n = count($params);
if ($n !== $p + $q + 1) {
$expected_num_params = $p + $q + 1;
throw new Exception\BadParameterException("Number of parameters is incorrect. Expected {$expected_num_params}; got {$n}");
}
$a = array_slice($params, 0, $p);
$b = array_slice($params, $p, $q);
$z = $params[$n - 1];
$tol = 1.0E-8;
$n = 1;
$sum = 0;
$product = 1;
do {
$sum += $product;
$a_sum = array_product(Single::add($a, $n - 1));
$b_sum = array_product(Single::add($b, $n - 1));
$product *= $a_sum * $z / $b_sum / $n;
$n++;
} while ($product / $sum > $tol);
return $sum;
}