<?php
require_once dirname(__DIR__) . '/base/regression.php';
require_once dirname(dirname(__DIR__)) . '/das/methods/vendor/autoload.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/gompertz.php';


class GompertzModel extends RegressionBase
{
    public $points;

    public $N;

    public $r;

    public $gom;

    public $K;

    public $a;

    public $b;

    public $cumulantData;

    public $cumulantPrevData;

    public $sortData;

    public $singlePrevData;

    public $dataframe;

    public function __construct($dataframe, $points)
    {
        parent::__construct($dataframe);

        $this->points = $points;
        $this->N      = count($points);
        $this->r      = $this->N / 3;

        $this->cumulant();
        list($s1, $s2, $s3) = $this->sResult();
        $this->gom  = new Gompertz($this->cumulantData);
        $this->K    = $this->dataframe->roundDigit($this->gom->K);
        $this->a    = $this->dataframe->roundDigit($this->gom->a);
        $this->b    = $this->dataframe->roundDigit($this->gom->b);
        $this->prediction();

    }

    public function describe($exceptRatio, $setFlaw = null)
    {
        $K    = $this->gom->K;
        $a    = $this->gom->a;
        $b    = $this->gom->b;

        $inflectionT = ($a == 0 || $b == 0) ? 0 : -1 * log(-1 * log($a), $b);
        $inflectionY = $K / exp(1);
        $totalFlaw   = max($this->cumulantData);
        $exceptFlaw  = $K * (1 - $exceptRatio);
        $leaveFlaw   = $K - $exceptFlaw;
        $diffFlaw    = $exceptFlaw - $totalFlaw;

        $needTest = 0;
        /* If diffFlaw is negative value, need test can not be calculate as expect. */
        if($diffFlaw > 0) $needTest = log(log(1 - $exceptRatio) / log($a)) / log($b) - $this->N + 1;

        $result = new stdclass();
        $result->t          = $this->dataframe->roundDigit($inflectionT);
        $result->y          = $this->dataframe->roundDigit($inflectionY);
        $result->N          = $this->N;
        $result->total      = $totalFlaw;
        $result->target     = $this->dataframe->roundDigit($exceptFlaw);
        $result->leave      = $this->dataframe->roundDigit($leaveFlaw);
        $result->diff       = $this->dataframe->roundDigit($diffFlaw);
        $result->needTest   = $this->dataframe->roundDigit($needTest);
        $result->customFlaw = '-';
        $result->customDiff = '-';
        $result->customTest = '-';

        $result->t        = $result->t == 'NaN' ? '-' : $result->t;
        $result->y        = $result->y == 'NaN' ? '-' : $result->y;
        $result->N        = $result->N == 'NaN' ? '-' : $result->N;
        $result->target   = $result->target == 'NaN' ? '-' : $result->target;
        $result->leave    = $result->leave == 'NaN' ? '-' : $result->leave;
        $result->diff     = $result->diff == 'NaN' ? '-' : $result->diff;
        $result->needTest = $result->needTest == 'NaN' ? '-' : $result->needTest;

        if(!empty($setFlaw))
        {
            $exceptDiffFlaw = $setFlaw - $totalFlaw;
            $setNeedT = log(log($setFlaw / $K) / log($a)) / log($b) - $this->N + 1;

            $result->customFlaw = $setFlaw;
            $result->customDiff = round($exceptDiffFlaw);
            $result->customTest = round($setNeedT);
        }

        return $result;
    }

    public function chartData()
    {
        $result = new stdclass();
        $result->sort         = $this->sortData;
        $result->flaw         = $this->points;
        $result->cumulant     = $this->cumulantData;
        $result->cumulantPrev = $this->cumulantPrevData;
        $result->singlePrev   = $this->singlePrevData;
        $result->limit        = array_fill(0, $this->N, $this->gom->K);

        return $result;
    }

    public function sResult()
    {
        $n = $this->N % 3;
        $m = floor($this->r);
        $s = array();
        foreach($this->cumulantData as $value)
        {
            $s[] = log($value);
        }

        $s1 = array_slice($s, 0, $m);
        $s3 = array_slice($s, -1 * $m, $m);

        $s1[] = ($n * $s[$m] / 3);
        $s3[] = ($n * $s[$this->N - $m - 1] / 3);

        $s1Sum = array_sum($s1);
        $s3Sum = array_sum($s3);
        $s2Sum = array_sum($s) - $s1Sum - $s3Sum;

        return array($s1Sum, $s2Sum, $s3Sum);
    }

    public function cumulant()
    {
        $sum = 0;
        $this->cumulantData = array();
        foreach($this->points as $point)
        {
            $sum += $point;
            $this->cumulantData[] = $sum;
        }
    }

    public function prediction()
    {
        $K    = $this->gom->K;
        $a    = $this->gom->a;
        $b    = $this->gom->b;

        $this->cumulantPrevData = array();
        $this->singlePrevData   = array();
        $this->sortData         = array();
        $last = $K * $a;
        foreach($this->points as $index => $point)
        {
            $t = $index + 1;
            $this->sortData[] = $t;
            $value = $K * pow($a, pow($b, $t));
            $this->singlePrevData[]   = $value - $last;
            $this->cumulantPrevData[] = $value;
            $last = $value;
        }

    }
}
