<?php
require_once dirname(__DIR__) . '/base/spcchart.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/basic/describe.php';

/**
 * XBarR
 */
class xBarR extends spcChart
{
    public $decimals;

    public $dataframe;

    /**
     * Xbar-R chart σ method;
     *
     * @param array $cols
     * @param string $type
     * @param bool $round
     * @access public
     * @return array
     */
    public function sigma($cols, $type, $round = true)
    {
        $sub = $this->subgroup($cols, $type);
        $r   = $this->r($cols, $type);

        if($sub->sizeFixed)
        {
            $rbar = Describe::mean($r);
            $d₂ = $this->dataframe->D₂(count($cols));
            $σ  = $rbar / $d₂;
        }
        else
        {
            $subgroup = $sub->subgroup;

            $sum1 = 0;
            $sum2 = 0;
            foreach($subgroup as $i => $group)
            {
                if($group < 2) continue;
                $d₂ = $this->dataframe->D₂($group);
                $d₃ = $this->dataframe->D₃($group);
                $f  = pow($d₂, 2) / pow($d₃, 2);

                $sum1 += $f * $r[$i] / $d₂;
                $sum2 += $f;
            }
            $σ = $sum1 / $sum2;
        }

        return $round ? round($σ, $this->decimals) : $σ;
    }

    public function xBar($cols, $type, $round = true)
    {
        $result = $this->dataframe->cols($cols, $type);

        $n   = 0;
        $sum = 0;
        foreach($result as $data)
        {
            foreach($data->trimdata as $value) if(is_numeric($value)) $n += 1;
            $sum += $data->sum;
        }

        $xBar = $sum / $n;
        $xBar = $round ? round($xBar, $this->decimals) : $xBar;

        return $xBar;
    }

    /**
     * X bar chart center line method.
     *
     * @param float $xBar
     * @param float $σ
     * @param int $n
     * @param float $multiple
     * @access public
     * @return object
     */
    public function xBarCL($xBar, $σ, $n, $multiple)
    {
        $result = new stdclass();

        $sqrtN = sqrt($n);
        $part  = (float)$multiple * $σ / $sqrtN;

        $result->CL  = $xBar;
        $result->UCL = $xBar + $part;
        $result->LCL = $xBar - $part;

        return $result;
    }
    /**
     * R chart center line method.
     *
     * @param float $σ
     * @param int $n
     * @param float $multiple
     * @access public
     * @return object
     */
    public function rCL($σ, $n, $multiple)
    {
        $result = new stdclass();

        $d2   = $this->dataframe->D₂($n);
        $d3   = $this->dataframe->D₃($n);
        $part = (float)$multiple * $σ * $d3;

        $result->CL  = $this->rBar($σ, $n, $d2);
        $result->UCL = $result->CL + $part;
        $result->LCL = $result->CL - $part;
        $result->LCL = max(0, $result->LCL);

        return $result;
    }

    /**
     * X bar chart limit line method.
     *
     * @param array $vars
     * @param float $μ
     * @param float $σ
     * @param float $multiple
     * @param float $defaultMultiple
     * @access public
     * @return object
     */
    public function xBarLimit($vars, $μ, $σ, $multiple, $defaultMultiple = 3)
    {
        $type = 'number';
        $sub  = $this->subgroup($vars, $type);

        $multiples = $multiple;
        if(!in_array($defaultMultiple, $multiple)) $multiples[] = $defaultMultiple;

        $SLs = array();
        if($sub->sizeFixed)
        {
            $n = current($sub->subgroup);
            foreach($multiples as $m)
            {
                $CLResult = $this->xBarCL($μ, $σ, $n, $m);
                $SLs[$m] = array($CLResult->UCL, $CLResult->LCL);
            }
        }
        else
        {
            foreach($multiples as $m)
            {
                $USLs = array();
                $LSLs = array();
                foreach($sub->subgroup as $n)
                {
                    if($n < 1)
                    {
                        $USLs[] = null;
                        $LSLs[] = null;
                        continue;
                    }

                    $CLResult = $this->xBarCL($μ, $σ, $n, $m);

                    $USLs[] = $CLResult->UCL;
                    $LSLs[] = $CLResult->LCL;
                }
                $SLs[$m] = array($USLs, $LSLs);
            }
        }

        $result       = new stdclass();
        $result->CL   = $μ;
        $result->ULCL = $SLs[$defaultMultiple];
        $result->SLs  = $SLs;

        return $result;
    }

    /**
     * R chart limit line method.
     *
     * @param array $vars
     * @param float $σ
     * @param float $multiple
     * @param float $defaultMultiple
     * @access public
     * @return object
     */
    public function rLimit($vars, $σ, $multiple, $defaultMultiple = 3)
    {
        $type = 'number';
        $sub  = $this->subgroup($vars, $type);

        $multiples = $multiple;
        if(!in_array($defaultMultiple, $multiple)) $multiples[] = $defaultMultiple;

        $SLs = array();
        if($sub->sizeFixed)
        {
            $n = current($sub->subgroup);
            foreach($multiples as $m)
            {
                $CLResult = $this->rCL($σ, $n, $m);
                $SLs[$m] = array($CLResult->UCL, $CLResult->LCL);
            }
            $CL = $this->rBar($σ, $n);
        }
        else
        {
            foreach($multiples as $m)
            {
                $USLs = array();
                $LSLs = array();
                foreach($sub->subgroup as $n)
                {
                    if($n < 2)
                    {
                        $USLs[] = null;
                        $LSLs[] = null;
                        continue;
                    }

                    $CLResult = $this->rCL($σ, $n, $m);

                    $USLs[] = $CLResult->UCL;
                    $LSLs[] = $CLResult->LCL;
                }
                $SLs[$m] = array($USLs, $LSLs);
            }

            $CL = array();
            foreach($sub->subgroup as $n)
            {
                if($n < 2)
                {
                    $CL[] = null;
                    continue;
                }
                $CL[] = $this->rBar($σ, $n);
            }
        }

        $result       = new stdclass();
        $result->CL   = $CL;
        $result->ULCL = $SLs[$defaultMultiple];
        $result->SLs  = $SLs;

        return $result;
    }

    /**
     * R bar method.
     *
     * @param float $σ
     * @param int $n
     * @param float $d2
     * @access public
     * @return object
     */
    public function rBar($σ, $n, $d2 = null)
    {
        if(empty($d2)) $d2 = $this->dataframe->D₂($n);
        return $d2 * $σ;
    }

    /**
     * SPC sub group R method;
     * https://support.minitab.com/zh-cn/minitab/21/help-and-how-to/quality-and-process-improvement/control-charts/how-to/variables-charts-for-subgroups/xbar-r-chart/methods-and-formulas/estimating-sigma/
     *
     * @param array $cols
     * @param string $type
     * @param bool $round
     * @access public
     * @return array
     */
    public function r($cols, $type, $keyName = null)
    {
        $r = array();
        foreach($this->dataframe->data as $index => $data)
        {
            $sub = array();
            foreach($cols as $i => $col)
            {
                if(isset($data[$col]))
                {
                    $value = $data[$col];
                    if(strpos($value, '%') !== false) $value = (string)(trim($value, '%') / 100);

                    if(is_numeric($value)) $sub[] = $value;
                }
            }

            $rSub = count($sub) < 2 ? null : max($sub) - min($sub);

            if(!empty($keyName)) $this->dataframe->data[$index][$keyName] = $rSub;
            $r[] = $rSub;
        }
        return $r;
    }
}
