<?php
require_once dirname(__DIR__) . '/base/hypothesis.php';
require_once dirname(dirname(__DIR__)) . '/das/methods/vendor/autoload.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/basic/describe.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/distribution/chisquare.php';

class poisson extends hypothesis
{
    public $dataframe;

    public $var;

    public $chose;

    public $freqLine;

    public $lang;

    public $poisson;
    /*
     * $poisson->groups
     * $poisson->data
     * $poisson->numCount
     * $poisson->mean
     */
    public $test;

    public function __construct($var, $chose, $freqLine, $lang)
    {
        $this->var       = $var;
        $this->chose     = $chose;
        $this->freqLine  = $freqLine;
        $this->lang      = $lang;
        $this->poisson   = new stdclass();
    }

    /**
     * Set dataframe.
     *
     * @access public
     * @return object
     */
    public function setDataframe($dataframe)
    {
        $this->dataframe = $dataframe;
    }

    /**
     * Set count and mean for poisson.
     *
     * @access public
     * @return object
     */
    public function setDescriptive()
    {
        $this->poisson->groups = array();

        if($this->chose == 'false')
        {
            $this->poisson->data = Describe::notNull($this->dataframe->col($this->var)->data);
            $this->setPoissonGroups($this->poisson->data);
        }
        else
        {
            $groupValueData = $this->dataframe->col($this->var)->data;
            $groupCountData = $this->dataframe->col($this->freqLine)->data;
            $data = array();

            foreach($groupValueData as $index => $groupValue)
            {
                $groupCount = $groupCountData[$index];
                if(Describe::checkNotNull($groupValue) and Describe::checkNotNull($groupCount))
                {
                    for($i = 0; $i < $groupCount; $i ++) $data[] = $groupValue;
                }
            }

            $this->poisson->data = $data;
            $this->setPoissonGroups($data);
        }
        sort($this->poisson->data);
        $this->poisson->numCount = count($this->poisson->data);
        $this->poisson->mean     = Describe::mean($this->poisson->data);

        /* Set poisson groups P, lessP,lesseqP,greatP,greateqP.*/
        $sumP = 0;
        foreach($this->poisson->groups as $key => $groups)
        {
            $p = $this->getP($key);
            $this->poisson->groups[$key]->P = $p;

            $this->poisson->groups[$key]->lessP  = $sumP;
            $this->poisson->groups[$key]->greatP = 1 - $sumP;
            $sumP += $p;

            $this->poisson->groups[$key]->lesseqP  = $sumP;
            $this->poisson->groups[$key]->greateqP = 1 - $sumP;

        }

        /* Set oi and chisquare.*/
        foreach($this->poisson->groups as $key => $group)
        {
            $ei = $group->numCount;
            $oi = $this->poisson->numCount * $group->P;

            $this->poisson->groups[$key]->oi        = $oi;
            $this->poisson->groups[$key]->chisquare = pow($oi-$ei, 2) / $ei;
        }
        a($this->poisson);
    }

    /**
     * Set poisson groups.
     *
     * @access public
     * @return object
     */
    public function setTest()
    {
        $this->test = new stdclass();
        $chi = 0;
        foreach($this->poisson->groups as $group)
        {
            $chi += $group->chisquare;
        }

        $freedom = count($this->poisson->groups) - 1;
        $chisquare = new Chisquare($freedom);

        $this->test->freedom   = $freedom;
        $this->test->chisquare = $chi;
        $this->test->p         = $chisquare->cdf($chi);
    }


    /**
     * Set poisson groups.
     *
     * @access public
     * @return object
     */
    public function setPoissonGroups($data)
    {
        $countValues = array_count_values($data);
        ksort($countValues, 1);
        $index = 0;
        foreach($countValues as $value => $count)
        {
            $group = new stdclass();
            $group->index    = $index;
            $group->numCount = $count;
            $group->value    = $value; //oi
            $this->poisson->groups[] = $group;

            $index ++;
        }
    }

    /**
     * Get method result.
     *
     * @access public
     * @return object
     */
    public function setObserveGroups()
    {
        /* Get first group. */
        $first = new stdclass();
        $last  = new stdclass();

        $numCount   = $this->poisson->numCount;
        $gallaryKey = count($this->poisson->groups) - 3;

        foreach($this->poisson->groups as $key => $group)
        {
            if($numCount * $group->lesseqP >= 2 or $key == $gallaryKey)
            {
                $first->index = $key;
                $first->value = $group->value;
                break;
            }
        }

        //TODO,最后一组算不出来
        /*foreach(array_reverse($this->poisson->groups) as $group)
        {
            if($group->value <= 1 + $first->value) break;
            if($numCount * $group->greatP >=2)
            {
                $last->index = $key;
                $last->value = $group->value;
                break;
            }
        }
         */
    }

    /**
     * Get method result.
     *
     * @access public
     * @return float
     */
    public function getP($i)
    {
        $λ = $this->poisson->mean;
        return exp(-$λ) * pow($λ, $i) / $this->getFactorial($i);
    }

    /**
     * Get factorial.
     *
     * @access public
     * @return int
     */
    public function getFactorial($i)
    {
        $result = 1;
        for($j = 1; $j <= $i; $j ++) $result *= $j;
        return $result;
    }

    /**
     * Get method result.
     *
     * @access public
     * @return object
     */
    public function getMethod()
    {
        $methodResult = new stdclass();
        $methodResult->type  = 'text';
        $methodResult->title = $this->lang->method;
        $methodResult->data = array();

        $methodResult->data['content'] = sprintf($this->lang->methodDesc, $this->dataframe->columns[$this->freqLine]);

        return $methodResult;
    }

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

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

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

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

        return $result;
    }

    /**
     * Get observed and expected count result.
     *
     * @access public
     * @return object
     */
    public function getObservedAndExpectedCount()
    {
        $data = array();
        $data[0][] = 1;
        $data[0][] = 1;

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

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

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

        return $result;
    }

    /**
     * Get test result.
     *
     * @access public
     * @return object
     */
    public function getTest()
    {
        $data = array();
        $data[0][] = $this->test->freedom;
        $data[0][] = $this->test->chisquare;
        $data[0][] = $this->test->p;

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

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

        $columns = array();
        $columns[] = array('label' => $this->lang->freedom, 'type' => 'text');
        $columns[] = array('label' => $this->lang->chi, 'type' => 'text');
        $columns[] = array('label' => $this->lang->pValue, 'type' => 'text');
        $result->data['columns'] = $columns;

        return $result;
    }
}
