<?php
require_once dirname(__DIR__) . '/base/hypothesis.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/basic/describe.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/f.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/chisquare.php';

/**
 * TwoAnova
 */
class TwoAnova extends hypothesis
{

    public static $decimal;

    public $N;

    public $σ²;

    public $σ;

    public $data;

    public $cl;

    public $isComplete;

    public function __construct($dataframe, $param)
    {
        $this->N     = isset($param->N) ? $param->N : 0;
        $this->σ²    = isset($param->σ²) ? $param->σ² : null;
        $this->σ     = isset($param->σ) ? $param->σ : null;
        $this->data  = isset($param->data) ? $param->data : null;
        $this->cl    = isset($param->cl) ? $param->cl : 95;

        if(!empty($this->data))
        {
            if(empty($this->N)) $this->N = count($this->data);
            if(empty($this->σ²)) $this->σ² = Describe::variance($this->data, false);
            if(empty($this->σ)) $this->σ = Describe::standard($this->data, false);
        }

        if(!empty($this->σ²) and empty($this->σ)) $this->σ = sqrt($this->σ²);
        if(!empty($this->σ) and empty($this->σ²)) $this->σ² = $this->σ ** 2;

        $this->isComplete = !empty($this->N) and !empty($this->σ²) and !empty($this->σ);

        parent::__construct($dataframe);
    }

    public function describe($type, $round = true)
    {
        $result = array();
        $result['n']          = $this->N;
        $result['σ²']         = $this->roundDigit($this->σ², $round);
        $result['σ']          = $this->roundDigit($this->σ, $round);
        $result['ci']         = $this->CI($type, $round);
        $result['ciVar']      = $this->CI($type, $round, false);
        $result['isComplete'] = $this->isComplete;

        $result['origin'] = array();
        $result['origin']['n']  = $this->N;
        $result['origin']['σ²'] = $this->σ²;
        $result['origin']['σ']  = $this->σ;
        $result['origin']['ci'] = $this->CI($type, false);

        return $result;
    }

    public function CI($type, $round = true, $sqrt = true)
    {
        if(!is_numeric($this->N) or !is_numeric($this->σ²)) return array('-', '-');
        $chisq = new ChiSquare($this->N - 1);

        $α    = 1 - $this->cl / 100;
        $part = ($this->N - 1) * $this->σ²;

        if($type == 'noEqual')
        {
            $low = $part / $chisq->inverse(1 - $α / 2);
            $up  = $part / $chisq->inverse($α / 2);

            $low = $sqrt ? sqrt($low) : $low;
            $up  = $sqrt ? sqrt($up) : $up;
        }

        if($type == 'gt')
        {
            $low = $part / $chisq->inverse(1 - $α);
            $low = $sqrt ? sqrt($low) : $low;
            $up  = '-';
        }

        if($type == 'lt')
        {
            $low = '-';
            $up  = $part / $chisq->inverse($α);
            $up  = $sqrt ? sqrt($up) : $up;
        }

        $up  = $this->roundDigit($up, $round);
        $low = $this->roundDigit($low, $round);

        return array($low, $up);
    }

    public function fCI($ratioType, $type, $desc, $cl, $round = true)
    {
        $isVar = $ratioType == 'variance';
        $values = array_values($desc);
        $d1 = $values[1]['origin']['n'];
        $d2 = $values[0]['origin']['n'];

        $σ²₁ = $values[0]['origin']['σ²'];
        $σ²₂ = $values[1]['origin']['σ²'];
        $σ₁  = $values[0]['origin']['σ'];
        $σ₂  = $values[1]['origin']['σ'];

        if(!is_numeric($σ²₁) or !is_numeric($σ²₂) or !is_numeric($σ₁) or !is_numeric($σ₂)) return array('ratio' => '-', 'ci' => array('-', '-'));

        $ratio = $isVar ? $values[0]['origin']['σ²'] / $values[1]['origin']['σ²'] : $values[0]['origin']['σ'] / $values[1]['origin']['σ'];

        $result = array();
        $result['ratio'] = $this->roundDigit($ratio, $round, 6);
        $result['ci']    = array('-', '-');


        $α = 1 - $cl / 100;
        $f = new F($d1 - 1, $d2 - 1);

        if($isVar) $ratio = sqrt($ratio);

        if($type == 'noEqual')
        {
            $lowF  = $f->inverse($α / 2);
            $upF = $f->inverse(1 - $α / 2);

            $up  = $ratio * sqrt($upF);
            $low = $ratio * sqrt($lowF);

            if($isVar)
            {
                $up  **= 2;
                $low **= 2;
            }

            $result['ci'][1] = $up;
            $result['ci'][0] = $low;
        }

        if($type == 'gt')
        {
            $lowF = $f->inverse($α);

            $low = $ratio * sqrt($lowF);
            if($isVar) $low **= 2;
            $result['ci'][0] = $low;
        }

        if($type == 'lt')
        {
            $upF = $f->inverse(1 - $α);

            $up  = $ratio * sqrt($upF);
            if($isVar) $up **= 2;
            $result['ci'][1] = $up;
        }

        $result['ci'][0] = $this->roundDigit($result['ci'][0], $round);
        $result['ci'][1] = $this->roundDigit($result['ci'][1], $round);

        return $result;

    }

    public function fTest($guessRatio, $type, $desc, $cl, $round = true)
    {
        $values = array_values($desc);
        $df1 = $values[0]['origin']['n'];
        $df2 = $values[1]['origin']['n'];

        $var1 = $values[0]['origin']['σ²'];
        $var2 = $values[1]['origin']['σ²'];

        $signList = array('noEqual' => '≠', 'gt' => '>', 'lt' => '<');

        $testStat = (is_numeric($var1) and is_numeric($var2)) ? $var1 / ($guessRatio ** 2 * $var2) : '-';

        $result = array();
        $result['method'] = 'F';
        $result['test']   = $this->roundDigit($testStat, $round);
        $result['p']      = '-';
        $result['DF1']    = $df1 - 1;
        $result['DF2']    = $df2 - 1;
        $result['sign']   = $signList[$type];

        if(!is_numeric($testStat)) return $result;

        $f = new F($df1 - 1, $df2 - 1);
        $cdf = $f->cdf($testStat);

        if($type == 'noEqual')
        {
            if($guessRatio > 1) $p = (1 - $cdf) * 2;
            if($guessRatio <= 1) $p = 2 * $cdf;
        }

        if($type == 'gt')
        {
            $p = 1 - $cdf;
        }
        if($type == 'lt')
        {
            $p = $cdf;
        }

        if($p < 0) $p = 0;
        if($p > 1) $p = 2 - $p;

        $result['p'] = $this->roundDigit($p, $round);
        return $result;
    }
}
