<?php
require_once LIB_ROOT . '/dataframe/hypothesis/chisquare.php';
require_once LIB_ROOT . '/zendasmath/distribution/chisquare.php';
require_once LIB_ROOT . '/dataframe/checkdata.php';
require_once LIB_ROOT . '/dataframe/validatedata.php';

/* 卡方检验 */
class chisquareMethod
{
    /* Method name 分析方法内部名称 */
    public static $name = 'Chi-square';

    /* Method settings 方法设置参数定义 */
    public static $settings = array();

    /* Diagram config 图表配置项 */
    public static $config = array();

    /* Callback for basic statistic method 基本量统计计算回调函数 */
    public static function func($dataframe, $settings)
    {
        global $dasLang;
        global $lang;

        $result = array();

        $dataDesc = $settings['dataDesc'];
        $col1     = $settings['row'];
        $col2     = $settings['col'];
        $data     = $settings['data'];

        if($dataDesc == 'origin')
        {
            $check = new checkData();
            $check->setData($dataframe->colData($col1), $dataframe->columns[$col1], self::$settings['row']['validate']);
            $check->setData($dataframe->colData($col2), $dataframe->columns[$col2], self::$settings['col']['validate']);

            $checkRes = $check->check();
            if(is_string($checkRes)) return ValidateData::result($checkRes);

            /* 所有坐标都是从0开始算，包括maxLength也会因此少1.*/
            $sliceDF = $dataframe->sliceDataframe($dataframe, array($col1, $col2), 'any');
            $col1Column = $sliceDF->col($col1);
            $col2Column = $sliceDF->col($col2);
            $col1Data   = $col1Column->data;
            $col2Data   = $col2Column->data;

            $chisquare= new ChisquareTest($sliceDF);
            $chisquare->setDataDesc($dataDesc);

            $maxLength = count($col1Data);

            $neCol1Data = $sliceDF->col($col1)->notNull();
            $neCol2Data = $sliceDF->col($col2)->notNull();

            $col1CV = array_count_values($sliceDF->col($col1)->notNull());
            $col2CV = array_count_values($sliceDF->col($col2)->notNull());
            ksort($col1CV);
            ksort($col2CV);
            $col1CVKeys = array_keys($col1CV);
            $col2CVKeys = array_keys($col2CV);

            $col1Empty = 0;
            $col2Empty = 0;
            foreach($col1Data as $value) if(empty($value)) $col1Empty ++;
            foreach($col2Data as $value) if(empty($value)) $col2Empty ++;

            $colEmpKey = count($col2CVKeys);
            $colAllKey = count($col2CVKeys) + 1;
            $rowEmpKey = count($col1CVKeys);
            $rowAllKey = count($col1CVKeys) + 1;

            $params = new stdclass();
            $params->col1CVKeys = $col1CVKeys;
            $params->col2CVKeys = $col2CVKeys;
            $params->col1Empty  = $col1Empty;
            $params->col2Empty  = $col2Empty;
            $params->colEmpKey  = $colEmpKey;
            $params->colAllKey  = $colAllKey;
            $params->rowEmpKey  = $rowEmpKey;
            $params->rowAllKey  = $rowAllKey;
            $params->lang       = $dasLang->chisquare;

            $result    = $chisquare->getOriginResult($col1Data, $col2Data, $maxLength, $params);
            $tableData = $chisquare->getTableData($result, $params);

            /* Init tableData */
            $tableResult = new stdclass();
            $tableResult->type  = 'table';
            $tableResult->title = sprintf($dasLang->chisquare->tableText, $sliceDF->columns[$col1], $sliceDF->columns[$col2]);

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

            $columns = array();
            $columns[] = array('label' => NULL, 'type' => 'text');
            foreach($col2CVKeys as $value)
            {
                $columns[] = array('label' => $value, 'type' => 'text');
            }
            if($col2Empty) $columns[] = array('label' => $dasLang->chisquare->miss, 'type' => 'text');
            $columns[] = array('label' => $dasLang->chisquare->all, 'type' => 'text');
            $tableResult->data['columns'] = $columns;
            $tableResult->desc = $dasLang->chisquare->desc;

            $personChi = 0;
            $likeChi   = 0;
            $freedom   = (count($col1CVKeys) - 1) * (count($col2CVKeys) -1);
            $showP     = true;
            for($i = 0; $i < $rowEmpKey; $i ++)
            {
                for($j = 0; $j < $colEmpKey; $j ++)
                {
                    if($result[$i][$j][1] < 1) $showP = false;
                    $personChi += $result[$i][$j][2];
                    $likeChi   += (2 * $result[$i][$j][3]);
                }
            }
            $χ²   = new ChiSquare($freedom);
            $pPValue = round(1 - $χ²->cdf($personChi), 4);
            $pLValue = round(1 - $χ²->cdf($likeChi), 4);

            $chiData = array();
            $chiData[0][] = $dasLang->chisquare->pearson;
            $chiData[0][] = round($personChi, 3);
            $chiData[0][] = $freedom;
            if($showP) $chiData[0][] = is_nan($pPValue) ? '-' : $pPValue;
            $chiData[1][] = $dasLang->chisquare->like;
            $chiData[1][] = round($likeChi, 3);
            $chiData[1][] = $freedom;
            if($showP) $chiData[1][] = is_nan($pLValue) ? '-' : $pLValue;

            $chiResult = new stdclass();
            $chiResult->type  = 'table';
            $chiResult->title = $dasLang->chisquare->chiText;

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

            $chiColumns = array();
            $chiColumns[] = array('label' => NULL, 'type' => 'text');
            $chiColumns[] = array('label' => $dasLang->chisquare->chisquare, 'type' => 'text');
            $chiColumns[] = array('label' => $dasLang->chisquare->freedom, 'type' => 'text');
            if($showP) $chiColumns[] = array('label' => $dasLang->chisquare->pValue, 'type' => 'text');
            $chiResult->data['columns'] = $chiColumns;
        }
        else
        {
            $sliceDF   = $dataframe->sliceDataframe($dataframe, $data, 'any');

            $check = new checkData();
            $check->setDataList($dataframe->colsData($data), $dataframe->colsName($data), self::$settings['data']['validate']);

            $checkRes = $check->check();
            if(is_string($checkRes)) return ValidateData::result($checkRes);

            $chisquare = new ChisquareTest($sliceDF);
            $chisquare->setDataDesc($dataDesc);

            $maxLength = 0;
            foreach($data as $col)
            {
                $maxLength = max($maxLength, count($sliceDF->col($col)->data));
            }

            $rowCount = $maxLength;
            $colCount = count($data);
            $rowAllKey = $rowCount;
            $colAllKey = $colCount;

            $col1CVKeys = array();
            for($i = 0; $i < $maxLength; $i ++) $col1CVKeys[] = $i + 1;
            $col2CVKeys = array();
            foreach($data as $col) $col2CVKeys[] = $sliceDF->columns[$col];

            $params = new stdclass();
            $params->col1CVKeys = $col1CVKeys;
            $params->col2CVKeys = $col2CVKeys;
            $params->col1Empty  = 0;
            $params->col2Empty  = 0;
            $params->colEmpKey  = $colAllKey;
            $params->colAllKey  = $colAllKey;
            $params->rowEmpKey  = $rowAllKey;
            $params->rowAllKey  = $rowAllKey;
            $params->lang       = $dasLang->chisquare;

            $result    = $chisquare->getSummaryResult($sliceDF, $data, $maxLength);
            $tableData = $chisquare->getTableData($result, $params, true);

            /* Init tableData */
            $tableResult = new stdclass();
            $tableResult->type  = 'table';
            $tableResult->title = sprintf($dasLang->chisquare->tableText, $dasLang->chisquare->tableRow, $dasLang->chisquare->tableCol);

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

            $columns = array();
            $columns[] = array('label' => NULL, 'type' => 'text');
            foreach($col2CVKeys as $value)
            {
                $columns[] = array('label' => $value, 'type' => 'text');
            }
            $columns[] = array('label' => $dasLang->chisquare->all, 'type' => 'text');
            $tableResult->data['columns'] = $columns;
            $tableResult->desc = $dasLang->chisquare->desc;

            $personChi = 0;
            $likeChi   = 0;
            $freedom   = (count($col1CVKeys) - 1) * (count($col2CVKeys) -1);
            $showP     = true;
            for($i = 0; $i < $rowAllKey; $i ++)
            {
                for($j = 0; $j < $colAllKey; $j ++)
                {
                    if($result[$i][$j][1] < 1) $showP = false;
                    $personChi += $result[$i][$j][2];
                    $likeChi   += (2 * $result[$i][$j][3]);
                }
            }
            $χ²   = new ChiSquare($freedom);
            $pPValue = round(1 - $χ²->cdf($personChi), 3);
            $pLValue = round(1 - $χ²->cdf($likeChi), 3);

            $chiData = array();
            $chiData[0][] = $dasLang->chisquare->pearson;
            $chiData[0][] = round($personChi, 4);
            $chiData[0][] = $freedom;
            if($showP) $chiData[0][] = is_nan($pPValue) ? '-' : $pPValue;
            $chiData[1][] = $dasLang->chisquare->like;
            $chiData[1][] = round($likeChi, 4);
            $chiData[1][] = $freedom;
            if($showP) $chiData[1][] = is_nan($pLValue) ? '-' : $pLValue;

            $chiResult = new stdclass();
            $chiResult->type  = 'table';
            $chiResult->title = $dasLang->chisquare->chiText;

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

            $chiColumns = array();
            $chiColumns[] = array('label' => NULL, 'type' => 'text');
            $chiColumns[] = array('label' => $dasLang->chisquare->chisquare, 'type' => 'text');
            $chiColumns[] = array('label' => $dasLang->chisquare->freedom, 'type' => 'text');
            if($showP) $chiColumns[] = array('label' => $dasLang->chisquare->pValue, 'type' => 'text');
            $chiResult->data['columns'] = $chiColumns;

        }

        return array($tableResult, $chiResult);
    }

    /**
     * Check whether the data meets the requirements.
     *
     * @param  object $dataframe
     * @param  string $dataDesc
     * @param  int    $col1
     * @param  int    $col2
     * @param  int    $col3
     * @param  int    $col4
     * @access public
     * @return object
     */
    public static function checkData($dataframe, $dataDesc, $col1, $col2, $data)
    {
        global $dasLang;
        if($dataDesc == 'origin')
        {
            $sliceDF = $dataframe->sliceDataframe($dataframe, array($col1, $col2), $data);
            $col1CV = array_count_values($sliceDF->col($col1)->notNull());
            $col2CV = array_count_values($sliceDF->col($col2)->notNull());

            if(count($col1CV) <= 1) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorFreedom, $dataframe->columns[$col1]));
            if(count($col2CV) <= 1) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorFreedom, $dataframe->columns[$col2]));
        }
        else
        {
            $sliceDF   = $dataframe->sliceDataframe($dataframe, $data, 'any');
            $maxLength = 0;
            foreach($data as $col)
            {
                $maxLength = max($maxLength, count($sliceDF->col($col)->data));
            }
            if(count($data) <= 1) return array('result' => 'fail', 'message' => $dasLang->chisquare->errorLine);
            foreach($data as $col)
            {
                $colData = $sliceDF->col($col);
                if($colData->haveNegative) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorNegative, $sliceDF->columns[$col]));
                if($colData->haveString) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorString, $sliceDF->columns[$col]));
                if(count($colData->data) != $maxLength) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorNull, $sliceDF->columns[$col]));
                foreach($colData->data as $value)
                {
                    if(!self::checkNotNull($value)) return array('result' => 'fail', 'message' => sprintf($dasLang->chisquare->errorNull, $sliceDF->columns[$col]));
                }
            }
        }
    }


    public static function checkNotNull($value)
    {
        return (is_numeric($value) or (!is_numeric($value) and !empty($value)));
    }

    /**
     * Get settings.
     *
     * @param object $lang
     * @access public
     * @return object
     */
    public static function getSettings()
    {
        global $dasLang;
        $lang = $dasLang->chisquare;

        /* Basic settings */
        self::$settings['dataDesc'] = array('name' => 'dataDesc', 'label' => $lang->dataDesc);
        self::$settings['row']      = array('name' => 'row',      'label' => $lang->row,  'required' => true);
        self::$settings['col']      = array('name' => 'col',      'label' => $lang->col,  'required' => true);
        self::$settings['data']     = array('name' => 'data',     'label' => $lang->data, 'required' => true);

        /* Type settings */
        self::$settings['dataDesc'] += array('type' => 'enum', 'enumOptions' => array('origin' => $lang->origin, 'summary' => $lang->summary), 'defaultValue' => 'origin');
        self::$settings['row']      += array('type' => 'column', 'listType' => 'column', 'columnType' => 'number');
        self::$settings['col']      += array('type' => 'column', 'listType' => 'column', 'columnType' => 'number');
        self::$settings['data']     += array('type' => 'list', 'listType' => 'column',  'columnType' => 'number');

        /* Limit settings */
        self::$settings['row']      += array('conditions' => array('dataDesc' => 'origin'), 'distinct' => 'col');
        self::$settings['col']      += array('conditions' => array('dataDesc' => 'origin'), 'distinct' => 'row');
        self::$settings['data']     += array('conditions' => array('dataDesc' => 'summary'), 'listLength' => 2);

        /* Col grid settings */
        self::$settings['dataDesc'] += array('col' => 6);

        /* More settings */

        /* Data validate */
        self::$settings['row']  += array('validate' => array('continuous', 'columnUnique >= 2', 'rowEqual'));
        self::$settings['col']  += array('validate' => array('continuous', 'columnUnique >= 2', 'rowEqual'));
        self::$settings['data'] += array('validate' => array('continuous', 'number', 'rowEqual'));

        return self::$settings;
    }

    /**
     * Get config.
     *
     * @access public
     * @return object
     */
    public static function getConfig()
    {
        global $dasLang;
        $lang = $dasLang->config;

        //return self::$config;
    }
}
