$start, and $points is an integer > 1 * * @param callable|array $source The source of our approximation. Should be either * a callback function or a set of arrays. * @param array $args The arguments of our callback function: start, * end, and n. Example: [0, 8, 5]. If $source is a * set of arrays, $args will default to []. * * @return array * * @throws Exception\BadDataException if $source is not callable or a set of arrays */ public static function getPoints($source, array $args = []): array { // Guard clause - source must be callable or array of points if (!(is_callable($source) || is_array($source))) { throw new Exception\BadDataException('Input source is incorrect. You need to input either a callback function or a set of arrays'); } // Source is already an array: nothing to do if (is_array($source)) { return $source; } // Construct points from callable function $function = $source; $start = $args[0]; $end = $args[1]; $n = $args[2]; return self::functionToPoints($function, $start, $end, $n); } /** * Evaluate our callback function at n evenly spaced points on the interval between start and end * * @param callable $function f(x) callback function * @param float $start the start of the interval * @param float $end the end of the interval * @param int $n the number of function evaluations * * @return array */ protected static function functionToPoints(callable $function, float $start, float $end, int $n): array { $points = []; $h = ($end - $start) / ($n - 1); for ($i = 0; $i < $n; $i++) { $xᵢ = $start + $i * $h; $f⟮xᵢ⟯ = $function($xᵢ); $points[$i] = [$xᵢ, $f⟮xᵢ⟯]; } return $points; } /** * Validate that there are enough input arrays (points), that each point array * has precisely two numbers, and that no two points share the same first number * (x-component) * * @param array $points Array of arrays (points) * @param int $degree The minimum number of input arrays * * @throws Exception\BadDataException if there are less than two points * @throws Exception\BadDataException if any point does not contain two numbers * @throws Exception\BadDataException if two points share the same first number (x-component) */ public static function validate(array $points, int $degree = 2) { if (count($points) < $degree) { throw new Exception\BadDataException("You need to have at least $degree sets of coordinates (arrays) for this technique"); } $x_coordinates = []; foreach ($points as $point) { if (count($point) !== 2) { throw new Exception\BadDataException('Each array needs to have have precisely two numbers, an x- and y-component'); } $x_component = $point[self::X]; if (in_array($x_component, $x_coordinates)) { throw new Exception\BadDataException('Not a function. Your input array contains more than one coordinate with the same x-component.'); } $x_coordinates[] = $x_component; } } /** * Sorts our coordinates (arrays) by their x-component (first number) such * that consecutive coordinates have an increasing x-component. * * @param array[] $points * * @return array[] */ protected static function sort(array $points): array { usort($points, function ($a, $b) { return $a[self::X] <=> $b[self::X]; }); return $points; } }