<?php
require_once dirname(__DIR__) . '/base/hypothesis.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/basic/describe.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/studentT.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/regression/correlation.php';

/**
 * pairt
 */
class pairt extends hypothesis
{
    private $dataDesc;
    private $confidence;
    private $deviation;
    private $alterHypo;
    private $tableThName; //column2Name

    private $first;
    // $first->id
    // $first->numCount
    // $first->stDev
    // $first->mean
    // $first->sigma

    private $second;

    private $difference;
    // $difference->numCount
    // $difference->value
    // $difference->sigma
    // $difference->stDev
    // $difference->freedom
    // $difference->inv
    // $difference->alterHypo

    private $test;
    // $test->t
    // $test->p

    private $lang;

    /**
     * 构造方法。
     *
     * The construct function.
     *
     * @param  object $dataframe
     * @param  string $dataDesc
     * @param  int    $confidence
     * @param  int    $deviation
     * @param  string $alterHypo
     * @param  object $lang
     * @access public
     * @return void
     */
    public function __construct($dataframe, $dataDesc, $confidence, $deviation, $alterHypo, $lang)
    {
        parent::__construct($dataframe);

        $this->first      = new stdclass();
        $this->second     = new stdclass();
        $this->difference = new stdclass();
        $this->test       = new stdclass();

        $this->dataDesc    = $dataDesc;
        $this->confidence  = $confidence;
        $this->deviation   = $deviation;
        $this->alterHypo   = $alterHypo;
        $this->lang        = $lang;
        $this->tableThName = $lang->sample;
    }

    /**
     * Set data in same col.
     *
     * @param  array  $column1Data
     * @param  array  $column2Data
     * @param  string $column1Name
     * @param  string $column2Name
     * @access public
     * @return void
     */
    public function setISCParams($column1Data, $column2Data, $column1Name, $column2Name)
    {
        $this->first->id  = $column1Name;
        $this->second->id = $column2Name;

        foreach($column1Data as $key => $value)
        {
            if(!Describe::checkNotNull($column1Data[$key]) or !Describe::checkNotNull($column2Data[$key]))
            {
                $column1Data[$key] = NULL;
                $column2Data[$key] = NULL;
            }
        }
        $column1Data = Describe::notNull($column1Data);
        $column2Data = Describe::notNull($column2Data);

        $this->first->data  = $column1Data;
        $this->second->data = $column2Data;

        $difference = array();
        foreach($column1Data as $key => $value)
        {
            $difference[] = $column1Data[$key] - $column2Data[$key];
        }

        $this->difference->data = $difference;

        $this->setCount();
        $this->setMean();
        $this->setSigma();
        $this->setSTDev();
    }

    /**
     * Set summary data.
     *
     * @param  int   $sampleSample
     * @param  float $sampleMean
     * @param  float $sampleSD
     * @access public
     * @return void
     */
    public function setSummaryParams($sampleSample, $sampleMean, $sampleSD)
    {
        $this->difference->numCount = $sampleSample;
        $this->difference->value    = $sampleMean;
        $this->difference->sigma    = $sampleSD;
    }

    /**
     * Set first and second count.
     *
     * @access public
     * @return void
     */
    private function setCount()
    {
        $this->first->numCount  = count($this->first->data);
        $this->second->numCount = count($this->second->data);
    }

    /**
     * Set first and second mean.
     *
     * @access public
     * @return void
     */
    private function setMean()
    {
        $this->first->mean  = Describe::mean($this->first->data);
        $this->second->mean = Describe::mean($this->second->data);
    }

    /**
     * Set first and second sigma.
     *
     * @access public
     * @return void
     */
    private function setSigma()
    {
        $this->first->sigma  = Describe::standard($this->first->data);
        $this->second->sigma = Describe::standard($this->second->data);
    }

    /**
     * Set first and second stDev.
     *
     * @access public
     * @return void
     */
    private function setSTDev()
    {
        $this->first->stDev  = round($this->first->sigma / sqrt($this->first->numCount), $this->decimals);
        $this->second->stDev = round($this->second->sigma / sqrt($this->second->numCount), $this->decimals);
    }

    /**
     * Set difference.
     *
     * @access public
     * @return void
     */
    public function setDifference()
    {
        $numCount = isset($this->difference->numCount) ? $this->difference->numCount : count($this->difference->data);
        $freedom  = $numCount - 1;
        $studentT = new StudentT($freedom);

        $a   = 1 - $this->confidence / 100;
        if($this->alterHypo != 'ne') $a = $a * 2;
        $t   = 1 - $a / 2;

        if($this->dataDesc == 'notSameCol')
        {
            $this->difference->value = $this->first->mean - $this->second->mean;
            $this->difference->sigma = Describe::standard($this->difference->data);
        }
        $this->difference->numCount  = $numCount;
        $this->difference->stDev     = round($this->difference->sigma / sqrt($numCount), $this->decimals);
        $this->difference->freedom   = $freedom;
        $this->difference->inv       = $studentT->inverse($t);
        $this->difference->alterHypo = $this->alterHypo;
    }

    /**
     * Set test.
     *
     * @access public
     * @return void
     */
    public function setTest()
    {
        $du = $this->difference->value - $this->deviation;
        if($this->dataDesc == 'notSameCol')
        {
            $r  = zCorrelation::r($this->first->data, $this->second->data);
            $sd = pow($this->first->sigma, 2) +  pow($this->second->sigma, 2) - 2 * $this->first->sigma * $this->second->sigma * $r;
        }
        else
        {
            $sd = $this->difference->sigma;
        }

        $this->test->freedom = $this->difference->freedom;

        if($this->difference->sigma == 0)
        {
            $this->test->t = '-';
            $this->test->p = '-';
        }
        else
        {
            $this->test->t = $du / sqrt($sd / $this->difference->numCount);

            $studentT = new StudentT($this->test->freedom);
            $t        = $this->test->t;
            if($this->alterHypo == 'lt')
            {
                $p = $studentT->cdf($t);
            }
            else if($this->alterHypo == 'gt')
            {
                $p = 1 - $studentT->cdf($t);
            }
            else
            {
                $p = $t > 0 ? (2 * (1 - $studentT->cdf($t))) : (2 * $studentT->cdf($t));
            }

            $this->test->p = round($p, $this->decimals);
        }
    }

    /**
     * Get discriptive result.
     *
     * @access public
     * @return object
     */
    public function getDescriptiveStatistics()
    {
        $data = array();
        $data[0][] = $this->first->id;
        $data[0][] = $this->first->numCount;
        $data[0][] = $this->first->mean;
        $data[0][] = $this->first->sigma;
        $data[0][] = $this->first->stDev;
        $data[1][] = $this->second->id;
        $data[1][] = $this->second->numCount;
        $data[1][] = $this->second->mean;
        $data[1][] = $this->second->sigma;
        $data[1][] = $this->second->stDev;

        $result = new stdclass();
        $result->type  = 'table';
        $result->title = $this->lang->desc;

        $result->data = array();
        $result->data['data'] = $data;

        $columns = array();
        $columns[] = array('label' => $this->tableThName, 'type' => 'text');
        $columns[] = array('label' => $this->lang->sam, 'type' => 'text');
        $columns[] = array('label' => $this->lang->mean, 'type' => 'text');
        $columns[] = array('label' => $this->lang->sd, 'type' => 'text');
        $columns[] = array('label' => $this->lang->stDev, 'type' => 'text');
        $result->data['columns'] = $columns;

        return $result;
    }

    /**
     * Get difference result.
     *
     * @access public
     * @return object
     */
    public function getDifference()
    {
        $factor = $this->difference->stDev * $this->difference->inv;

        $data = array();
        if($this->dataDesc == 'summary') $data[0][] = $this->difference->numCount;
        $data[0][] = $this->difference->value;
        $data[0][] = $this->difference->sigma;
        $data[0][] = $this->difference->stDev;

        if($this->alterHypo == 'lt') $data[0][] = round($this->difference->value + $factor, $this->decimals);
        if($this->alterHypo == 'gt') $data[0][] = round($this->difference->value - $factor, $this->decimals);
        if($this->alterHypo == 'ne') $data[0][] = '(' . round($this->difference->value - $factor, $this->decimals) . ', ' .  round($this->difference->value + $factor, $this->decimals) . ')';

        $result = new stdclass();
        $result->type  = 'table';
        $result->title = $this->lang->deviationES;

        $result->data = array();
        $result->data['data'] = $data;

        $columns = array();
        if($this->dataDesc == 'summary') $columns[] = array('label' => $this->lang->sam, 'type' => 'text');
        $columns[] = array('label' => $this->lang->mean, 'type' => 'text');
        $columns[] = array('label' => $this->lang->sd, 'type' => 'text');
        $columns[] = array('label' => $this->lang->stDev, 'type' => 'text');

        if($this->alterHypo == 'lt') $columns[] = array('label' => sprintf($this->lang->CIUpper, $this->confidence . '%'), 'type' => 'text');
        if($this->alterHypo == 'gt') $columns[] = array('label' => sprintf($this->lang->CILower, $this->confidence . '%'), 'type' => 'text');
        if($this->alterHypo == 'ne') $columns[] = array('label' => sprintf($this->lang->deviationCI, $this->confidence . '%'), 'type' => 'text');
        $result->data['columns'] = $columns;

        return $result;
    }

    /**
     * Get test result.
     *
     * @access public
     * @return object
     */
    public function getTest()
    {
        $testData   = array();
        $testData[0][] = $this->dataframe->roundDigit($this->test->t);
        $testData[0][] = $this->dataframe->roundDigit($this->test->p);

        $testResult = new stdclass();
        $testResult->type  = 'table';
        $testResult->title = $this->lang->test;

        $testResult->data = array();
        $testResult->data['data'] = $testData;

        $testColumns = array();
        $testColumns[] = array('label' => $this->lang->tValue, 'type' => 'text');
        $testColumns[] = array('label' => $this->lang->pValue, 'type' => 'number');

        $testResult->data['columns'] = $testColumns;
        $testResult->desc            = sprintf($this->lang->testDesc, $this->deviation, $this->lang->convert[$this->alterHypo], $this->deviation);

        return $testResult;
    }
}
