<?php
require_once dirname(dirname(__DIR__)) . '/zendasmath/tools.php';
require_once dirname(dirname(__DIR__)) . '/zendasmath/linearAlgebra/matrix.php';

class RegressionMatrix
{
    public static function oneMatrix($m, $n)
    {
        $matrix = array();
        for($i = 0; $i < $m; $i++)
        {
            $matrix[] = array_fill(0, $n, 1);
        }
        return zMatrixFactory::create($matrix);
    }

    public static function hMatrix($factors)
    {
        $X  = self::designMatrix($factors);
        $Xt = $X->transpose(); // Xᵀ
        $B  = $Xt->multiply($X)->inverse();// ⟮XᵀX⟯⁻¹
        $H  = $X->multiply($B)->multiply($Xt); //  X⟮XᵀX⟯⁻¹Xᵀ

        return $H;
    }

    public static function getFactorArray($factors)
    {
        $n = count(current($factors));

        foreach($factors as $name => $factor)
        {
            $values = array_count_values($factor);
            $keys   = array_keys($values);
            utf8_array_asort($keys);
            $keys = array_values($keys);
            $items[$name] = $keys;
        }

        $matrix = array(array_fill(0, $n, 1));
        foreach($factors as $key => $factor)
        {
            $item  = $items[$key];
            $r     = count($item) - 1;
            for($col = 1; $col <= $r; $col++)
            {
                $data = array();
                foreach($factor as $value)
                {
                    $index = array_search($value, $item) + 1;
                    if($index == $col)
                    {
                        $data[] = 1;
                    }
                    else if($index > $r)
                    {
                        $data[] = -1;
                    }
                    else
                    {
                        $data[] = 0;
                    }
                }
                $matrix[] = $data;
            }
        }

        return $matrix;
    }

    public static function glmFactorDM($factors)
    {
        $matrix = self::getFactorArray($factors);
        return zMatrixFactory::create($matrix)->transpose();
    }

    public static function glmCovariateDM($covariate)
    {
        $n = count(current($covariate));

        $matrix = array(array_fill(0, $n, 1));
        $matrix = array_merge($matrix, $covariate);

        return zMatrixFactory::create($matrix)->transpose();
    }

    /**
     * General linear model factor and covariate design matrix.
     *
     * @param array $items
     * @param array $factors
     * @param array $covariate
     * @access public
     * @return object
     */
    public static function glmFCDM($factors, $covariate)
    {
        $matrix = self::getFactorArray($factors);
        array_splice($matrix, 1, 0, $covariate);

        return zMatrixFactory::create($matrix)->transpose();
    }

    public static function designMatrix($values)
    {
        $row = count(current($values));
        $matrix = array();
        for($r = 0; $r < $row; $r++)
        {
            $arr = array(1);
            foreach($values as $value)
            {
                $arr[] = $value[$r];
            }
            $matrix[] = $arr;
        }
        return zMatrixFactory::create($matrix);
    }

    public static function coefMatrix($coef)
    {
        return self::array2Matrix($coef);
    }

    public static function yMatrix($values)
    {
        return self::array2Matrix($values);
    }

    public static function array2Matrix($arr)
    {
        $matrix = array();
        foreach($arr as $value)
        {
            $matrix[] = array($value);
        }
        return zMatrixFactory::create($matrix);
    }
}
