public static function differentiate($target, $source, ...$args)
{
// Get an array of points from our $source argument
$points = self::getPoints($source, $args);
// Validate input, sort points, make sure spacing is constant, and make
// sure our target is contained in an interval supplied by our $source
self::validate($points, $degree = 3);
$sorted = self::sort($points);
self::isSpacingConstant($sorted);
self::isTargetInPoints($target, $sorted);
// Descriptive constants
$x = self::X;
$y = self::Y;
// Initialize
$n = count($sorted);
$h = ($sorted[2][$x] - $sorted[0][$x]) / 2;
/*
* If the 2nd point is our $target, use the Midpoint Formula:
*
* 1 h²
* f′(x₀) = - [f(x₀+h)-f(x₀-h)] - - f⁽³⁾(ζ₁)
* 2h 6
*
* where ζ₁ lies between x₀ - h and x₀ + h
*
* If the 1st or 3rd point is our $target, use the Endpoint Formula:
* Note that when the 3rd point is our $target, we use a negative h.
*
* 1 h²
* f′(x₀) = - [-3f(x₀)+4f(x₀+h)-f(x₀+2h)] + - f⁽³⁾(ζ₀)
* 2h 3
*
* where ζ₀ lies between x₀ and x₀ + 2h
*/
// If the 2nd point is our $target, use the Midpoint Formula
if ($sorted[1][$x] == $target) {
$f⟮x₀⧿h⟯ = $sorted[0][$y];
$f⟮x₀⧾h⟯ = $sorted[2][$y];
$derivative = ($f⟮x₀⧾h⟯ - $f⟮x₀⧿h⟯) / (2 * $h);
// If the 1st or 3rd point is our $target, use the Endpoint Formula
} else {
// The 1st point is our $target
if ($sorted[0][$x] == $target) {
$f⟮x₀⟯ = $sorted[0][$y];
$f⟮x₀⧾h⟯ = $sorted[1][$y];
$f⟮x₀⧾2h⟯ = $sorted[2][$y];
// If the 3rd point is our $target, use negative h
} else {
$h = -$h;
$f⟮x₀⟯ = $sorted[2][$y];
$f⟮x₀⧾h⟯ = $sorted[1][$y];
$f⟮x₀⧾2h⟯ = $sorted[0][$y];
}
$derivative = (-3 * $f⟮x₀⟯ + 4 * $f⟮x₀⧾h⟯ - $f⟮x₀⧾2h⟯) / (2 * $h);
}
return $derivative;
}