Changeset View
Changeset View
Standalone View
Standalone View
src/applications/fact/chart/PhabricatorAccumulateChartFunction.php
- This file was added.
<?php | |||||
final class PhabricatorAccumulateChartFunction | |||||
extends PhabricatorChartFunction { | |||||
const FUNCTIONKEY = 'accumulate'; | |||||
protected function newArguments() { | |||||
return array( | |||||
$this->newArgument() | |||||
->setName('x') | |||||
->setType('function'), | |||||
); | |||||
} | |||||
public function getDomain() { | |||||
return $this->getArgument('x')->getDomain(); | |||||
} | |||||
public function newInputValues(PhabricatorChartDataQuery $query) { | |||||
return $this->getArgument('x')->newInputValues($query); | |||||
} | |||||
public function evaluateFunction(array $xv) { | |||||
// First, we're going to accumulate the underlying function. Then | |||||
// we'll map the inputs through the accumulation. | |||||
$datasource = $this->getArgument('x'); | |||||
// Use an unconstrained query to pull all the data from the underlying | |||||
// source. We need to accumulate data since the beginning of time to | |||||
// figure out the right Y-intercept -- otherwise, we'll always start at | |||||
// "0" wherever our domain begins. | |||||
$empty_query = new PhabricatorChartDataQuery(); | |||||
$datasource_xv = $datasource->newInputValues($empty_query); | |||||
if (!$datasource_xv) { | |||||
// TODO: Maybe this should just be an error? | |||||
$datasource_xv = $xv; | |||||
epriestley: This is when you `x | accumulate` and numerically approximate the integral of some pure math… | |||||
} | |||||
$yv = $datasource->evaluateFunction($datasource_xv); | |||||
$map = array_combine($datasource_xv, $yv); | |||||
$accumulator = 0; | |||||
foreach ($map as $x => $y) { | |||||
$accumulator += $y; | |||||
$map[$x] = $accumulator; | |||||
} | |||||
// The value of "accumulate(x)" is the largest datapoint in the map which | |||||
// is no larger than "x". | |||||
Not Done Inline Actions"is the largest" amckinley: "is the largest" | |||||
$map_x = array_keys($map); | |||||
$idx = -1; | |||||
$max = count($map_x) - 1; | |||||
$yv = array(); | |||||
$value = 0; | |||||
foreach ($xv as $x) { | |||||
// While the next "x" we need to evaluate the function at lies to the | |||||
// right of the next datapoint, move the current datapoint forward until | |||||
// we're at the rightmost datapoint which is not larger than "x". | |||||
while ($idx < $max) { | |||||
if ($map_x[$idx + 1] > $x) { | |||||
break; | |||||
} | |||||
$idx++; | |||||
$value = $map[$map_x[$idx]]; | |||||
} | |||||
$yv[] = $value; | |||||
} | |||||
return $yv; | |||||
} | |||||
} |
This is when you x | accumulate and numerically approximate the integral of some pure math function. It "works" (produces a meaningful -- but possibly inaccurate -- integral using crude numerical methods) and letting you do this means I don't have to classify functions anymore, so I'm just allowing this for now.
If this is too confusing we could stop it later, but I don't think users are going to see this stuff.