<?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';

/**
 * Twosample
 */
class twosample extends hypothesis
{
    private $dataDesc;
    private $confidence;
    private $deviation;
    private $alterHypo;
    private $column1Name;
    private $tableThName; //column2Name

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

    private $second;

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

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

    private $lang;

    public $decimals;

    /**
     * 构造方法。
     *
     * 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)
    {
        $column2Values  = array_count_values(Describe::notNull($column2Data));
        $column2Keys    = array_keys($column2Values);

        $this->column1Name = $column1Name;
        $this->tableThName = $column2Name;

        $this->first->id  = (string)($column2Keys[0] < $column2Keys[1] ? $column2Keys[0] : $column2Keys[1]);
        $this->second->id = (string)($column2Keys[0] > $column2Keys[1] ? $column2Keys[0] : $column2Keys[1]);

        $firstData  = array();
        $secondData = array();

        foreach($column2Data as $key => $value)
        {
            if($value === $this->first->id)  $firstData[]  = $column1Data[$key];
            if($value === $this->second->id) $secondData[] = $column1Data[$key];
        }

        if(count(array_count_values($firstData)) == 1)  return array('result' => 'fail', 'message' => $this->lang->allSame);
        if(count(array_count_values($secondData)) == 1) return array('result' => 'fail', 'message' => $this->lang->allSame);

        $this->first->data  = $firstData;
        $this->second->data = $secondData;

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

        return array('result' => 'success');
    }

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

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

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

    /**
     * Set summary data.
     *
     * @param  array  $params
     * @access public
     * @return void
     */
    public function setSummaryParams($params)
    {
        $this->first->id       = $params->sample1Name;
        $this->first->numCount = $params->sample1Sample;
        $this->first->mean     = $params->sample1Mean;
        $this->first->sigma    = $params->sample1SD;

        $this->second->id       = $params->sample2Name;
        $this->second->numCount = $params->sample2Sample;
        $this->second->mean     = $params->sample2Mean;
        $this->second->sigma    = $params->sample2SD;

        $this->setSTDev();
    }

    /**
     * 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()
    {
        $var1  = pow($this->first->sigma, 2) / $this->first->numCount;
        $var2  = pow($this->second->sigma, 2) / $this->second->numCount;

        $freedom = intval(pow($var1 + $var2, 2) / (pow($var1, 2) / ($this->first->numCount - 1) + pow($var2, 2) / ($this->second->numCount - 1)));
        $studentT = new StudentT($freedom);

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

        $this->difference->value = $this->first->mean - $this->second->mean;
        $this->difference->stDev = sqrt($var1 + $var2);
        $this->difference->freedom = $freedom;
        $this->difference->inv = $studentT->inverse($t);
        $this->difference->alterHypo = $this->alterHypo;
    }

    /**
     * Set test.
     *
     * @access public
     * @return void
     */
    public function setTest()
    {
        $this->test->t       = round(($this->difference->value - $this->deviation) / $this->difference->stDev, $this->decimals);
        $this->test->freedom = $this->difference->freedom;

        $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 method result.
     *
     * @access public
     * @return object
     */
    public function getMethod()
    {
        $methodResult = new stdclass();
        $methodResult->type  = 'text';
        $methodResult->title = $this->lang->method;
        $methodResult->data = array();
        if($this->dataDesc == 'inSameCol')  $text = sprintf($this->lang->methodDesc[$this->dataDesc], $this->tableThName, $this->first->id, $this->column1Name, $this->tableThName, $this->second->id, $this->column1Name);
        if($this->dataDesc == 'notSameCol') $text = sprintf($this->lang->methodDesc[$this->dataDesc], $this->first->id, $this->second->id);
        if($this->dataDesc == 'summary')    $text = sprintf($this->lang->methodDesc[$this->dataDesc], $this->first->id, $this->second->id);

        $methodResult->data['content'] = $text;

        return $methodResult;
    }

    /**
     * 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;

        if($this->dataDesc == 'inSameCol') $title = $this->lang->desc . ': ' . $this->column1Name;
        else                               $title = $this->lang->desc;

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

        $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();
        $data[0][] = $this->difference->value;
        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();
        $columns[] = array('label' => $this->lang->deviation, '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->test->t;
        $testData[0][] = $this->test->freedom;
        $testData[0][] = $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->freedom, 'type' => 'number');
        $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;
    }
}
