/// <summary>
        /// Linear regression with Y and one or more independent variables
        /// equation: y = b0 + b1x1 + b2x2 + ... + b(p-1)x(p-1) + e
        /// equation in matrix form: Y = XB + E
        /// </summary>
        /// <param name="dependent">SeriesVariable. dependent variable</param>
        /// <param name="independents">SeriesVariables. independent variable</param>
        public LinearRegressionModel(SeriesVariable dependent, SeriesVariables independents)
        {
            //Vector vY = new Vector(dependent.SeriesValuesNoNaN.ToArray());
            //int n = vY.Tuples;
            //int p = independents.Count + 1;
            //Matrix mX = new Matrix(n, p);
            //for (int i = 0; i < p; i++) //kolom
            //{
            //    for (int j = 0; j < n; j++) //baris
            //    {
            //        if (i == 0)
            //            mX[j, i] = 1.0;
            //        else
            //            mX[j, i] = independents[i - 1][j];
            //    }
            //}

            // changed to deal with missing values
            Vector vY = new Vector(dependent.SeriesValuesNoNaN.ToArray());
            int n = vY.Tuples;
            int p = independents.Count + 1;
            Matrix mX = new Matrix(n, p);
            for (int i = 0; i < p; i++) //kolom
            {
                for (int j = 0; j < n; j++) //baris
                {
                    if (i == 0)
                        mX[j, i] = 1.0;
                    else
                        mX[j, i] = independents[i - 1].SeriesValuesNoNaN[j];
                }
            }

            this.lse = new LeastSquareEstimator(mX, vY);

            this.f = this.lse.MSR / this.lse.MSE;
            this.sigOfF = Distribution.F.PValue(this.f, p - 1, n - p);

            double sumOfEtMinusEt_1Square = 0;
            double sumOfESquare = this.lse.ESquare[0];
            for (int i = 1; i < n; i++)
            {
                sumOfEtMinusEt_1Square += Math.Pow(this.lse.E[i] - this.lse.E[i - 1],2.0);
                sumOfESquare += this.lse.ESquare[i];
            }
            this.durbinWatson = sumOfEtMinusEt_1Square / sumOfESquare;

            this.stdErrorOfParameters = new double[p];
            this.t = new double[p];
            this.sigOfT = new double[p];
            this.lbOfParameters = new double[p];
            this.ubOfParameters = new double[p];
            for (int i = 0; i < p; i++)
            {
                this.stdErrorOfParameters[i] = Math.Sqrt(this.lse.SbSquare[i, i]);
                this.t[i] = this.lse.B[i] / this.stdErrorOfParameters[i];
                this.sigOfT[i] = Distribution.T.PValue(this.t[i], n - p, Distribution.T.TestType.TwoSided);
                double error = Distribution.T.InversCDF(0.975, n - p);
                this.ubOfParameters[i] = this.lse.B[i] + error * this.stdErrorOfParameters[i];
                this.lbOfParameters[i] = this.lse.B[i] - error * this.stdErrorOfParameters[i];
            }

            this.vifForPredictors = new double[p - 1];
            this.partialCorr = new double[p - 1];
            this.corr = new double[p - 1];
            for (int i = 1; i < p; i++)
            {
                Vector y = new Vector(mX.GetColData(i));
                Matrix x = new Matrix(n, p - 1);
                int col = 0;
                for (int j = 0; j < p; j++)
                {
                    if (j != i)
                    {
                        for (int k = 0; k < n; k++)
                        {
                            x[k, col] = mX[k, j];
                        }
                        col++;
                    }
                }
                Matrix xtx = x.GetTranspose() * x;
                Vector xty = x.GetTranspose() * y;
                Vector b = xtx.Solve(xty);
                Matrix mJ = new Matrix(n, n);
                mJ.InitializeAllValue(1.0);
                double yty = Vector.DoubleMultiply(y.GetTranspose(), y);
                Vector jy = mJ * y;
                double ssto = yty - Vector.DoubleMultiply(y.GetTranspose(), jy) / n;
                double sse = yty - Vector.DoubleMultiply(b.GetTranspose(), xty);
                double ssr = ssto - sse;
                double rSquare = ssr / ssto;
                this.vifForPredictors[i - 1] = 1 / (1 - rSquare);
                this.corr[i - 1] = computePearsonCorr(vY,y);

                Vector xty2 = x.GetTranspose() * vY;
                Vector b2 = xtx.Solve(xty2);
                double yty2 = Vector.DoubleMultiply(vY.GetTranspose(), vY);
                double sse2 = yty2 - Vector.DoubleMultiply(b2.GetTranspose(), xty2);
                double partDetermination = (sse2 - this.lse.SSE) / sse2;
                this.partialCorr[i - 1] = Math.Sqrt(partDetermination);

            }

            this.expectedResidual = new double[n];
            for (int k = 1; k <= n; k++)
            {
                this.expectedResidual[k - 1] = this.lse.StandardError * Distribution.Normal.InversCDF((k - 0.375) / (n + 0.25));
            }
        }
        /// <summary>
        /// Prosedur untuk menghitung inisialisasi nilai awal
        /// Nilai awal ditentukan dengan menggunakan lse pada 6 observasi pertama
        /// </summary>
        private void Init()
        {
            Matrix x = new Matrix(this.n, 2);
            for (int i = 0; i < this.n; ++i)
            {
                x[i, 0] = 1;
                x[i, 1] = i + 1;
            }

            LeastSquareEstimator lse = new LeastSquareEstimator(x, y);

            this.smoothing[0] = this.a * this.y[0] + ((1 - this.a) * (lse.B[0] + lse.B[1]));
            this.trend[0] = this.g * (this.smoothing[0] - lse.B[0]) + (1 - this.g) * lse.B[1];
            this.predicted[0] = lse.B[0] + lse.B[1];
            this.residual[0] = this.y[0] - this.predicted[0];
        }
        /// <summary>
        /// Mengestimasi parameter
        /// </summary>
        private void estimateParameters()
        {
            if (this.linearRdb.Checked)
            {
                x = new Matrix(this.y.Tuples, 2);
                for (int i = 0; i < this.y.Tuples; ++i)
                {
                    x[i, 0] = 1;
                    x[i, 1] = i + 1;
                }

                lse = new LeastSquareEstimator(x, this.y);

                this.trendProperties.initialModel = 1;
                this.trendProperties.includedObservations = this.y.Tuples;
                this.trendProperties.parameters = lse.B.GetData();
                this.trendProperties.sse = lse.SSE;
                this.trendProperties.mse = lse.MSE;
                this.trendProperties.r = lse.R;
                this.trendProperties.rSquare = lse.RSquare;
                this.trendProperties.rSquareAdjusted = 1 - (Math.Sqrt(lse.SSE) / lse.SSTO);

                this.predicted = lse.YCap.GetData();
                this.residual = lse.E.GetData();
            }
            else if (this.quadraticRdb.Checked)
            {
                x = new Matrix(this.y.Tuples, 3);
                for (int i = 0; i < this.y.Tuples; ++i)
                {
                    x[i, 0] = 1;
                    x[i, 1] = i + 1;
                    x[i, 2] = Math.Pow(i + 1, 2);
                }
                lse = new LeastSquareEstimator(x, this.y);

                this.trendProperties.initialModel = 2;
                this.trendProperties.includedObservations = this.y.Tuples;
                this.trendProperties.parameters = lse.B.GetData();
                this.trendProperties.sse = lse.SSE;
                this.trendProperties.mse = lse.MSE;
                this.trendProperties.r = lse.R;
                this.trendProperties.rSquare = lse.RSquare;
                this.trendProperties.rSquareAdjusted = 1 - (Math.Sqrt(lse.SSE) / lse.SSTO);

                this.predicted = lse.YCap.GetData();
                this.residual = lse.E.GetData();
            }
            else if (this.expGrowthRdb.Checked)
            {
                x = new Matrix(this.y.Tuples, 2);
                for (int i = 0; i < this.y.Tuples; ++i)
                {
                    x[i, 0] = 1;
                    x[i, 1] = i + 1;
                }

                Vector z = new Vector(this.y.Tuples);
                for (int i = 0; i < this.y.Tuples; ++i)
                    z[i] = Math.Log10(this.y[i]);

                lse = new LeastSquareEstimator(x, z);
                this.trendProperties.parameters = lse.B.GetData();
                this.trendProperties.includedObservations = this.y.Tuples;
                this.trendProperties.initialModel = 4;

                for (int i = 0; i < 2; ++i)
                    this.trendProperties.parameters[i] = Math.Pow(10, this.trendProperties.parameters[i]);

                predicted = new double[this.y.Tuples];
                residual = new double[this.y.Tuples];
                for (int i = 0; i < this.y.Tuples; ++i)
                {
                    this.predicted[i] = this.trendProperties.parameters[0] * Math.Pow(this.trendProperties.parameters[1], (i + 1));
                    this.residual[i] = this.y[i] - this.predicted[i];
                }

                this.trendProperties.sse = lse.SSE;
                this.trendProperties.mse = lse.MSE;
                this.trendProperties.r = lse.R;
                this.trendProperties.rSquare = lse.RSquare;
                this.trendProperties.rSquareAdjusted = 1 - (Math.Sqrt(lse.SSE) / lse.SSTO);
            }
            else if (this.cubicRdb.Checked)
            {
                x = new Matrix(this.y.Tuples, 4);
                for (int i = 0; i < this.y.Tuples; ++i)
                {
                    x[i, 0] = 1;
                    x[i, 1] = i + 1;
                    x[i, 2] = Math.Pow(i + 1, 2);
                    x[i, 3] = Math.Pow(i + 1, 3);
                }
                lse = new LeastSquareEstimator(x, this.y);

                this.trendProperties.initialModel = 3;
                this.trendProperties.includedObservations = this.y.Tuples;
                this.trendProperties.parameters = lse.B.GetData();
                this.trendProperties.sse = lse.SSE;
                this.trendProperties.mse = lse.MSE;
                this.trendProperties.r = lse.R;
                this.trendProperties.rSquare = lse.RSquare;
                this.trendProperties.rSquareAdjusted = 1 - (Math.Sqrt(lse.SSE) / lse.SSTO);

                this.predicted = lse.YCap.GetData();
                this.residual = lse.E.GetData();
            }
        }
        /// <summary>
        /// Inisialisasi nilai awal untuk tipe model multiplikatif
        /// Nilai awal musiman ditentukan dengan menggunakan Dummy Variabel dari data detrend
        /// </summary>
        private void InitMultiplicative()
        {
            //Menghitung nilai detrend
            Matrix x = new Matrix(this.n, 2);
            for (int i = 0; i < this.n; ++i)
            {
                x[i, 0] = 1;
                x[i, 1] = i + 1;
            }

            LeastSquareEstimator lse1 = new LeastSquareEstimator(x, this.y);

            Vector detrend = new Vector(this.n);
            for (int i = 0; i < this.n; i++)
                detrend[i] = this.y[i] / lse1.YCap[i];

            //Mencari indeks musiman dengan Dummy Regresi
            Matrix z = new Matrix(this.n, this.l);

            for (int i = 0; i < this.n; i++)
            {
                for (int j = 0; j < this.l; j++)
                {
                    if (j == this.term[i])
                        z[i, j] = 1;
                    else
                        z[i, j] = 0;
                }
            }

            LeastSquareEstimator lse2 = new LeastSquareEstimator(z, detrend);
            double[] seasonalIndex = lse2.B.GetData();

            this.EstimateValue();

            double smoothing0 = lse.B[0];
            double trend0 = lse.B[1];

            this.smoothing[0] = (this.alpha * this.y[0] / seasonalIndex[0]) + (1 - this.alpha) * (smoothing0 + trend0);
            this.trend[0] = this.gamma * (this.smoothing[0] - smoothing0) + (1 - this.gamma) * trend0;
            this.seasonal[0] = (this.beta * this.y[0] / this.smoothing[0]) + (1 - this.beta) * seasonalIndex[0];
            this.predicted[0] = (smoothing0 + trend0) * seasonalIndex[0];
            this.residual[0] = this.y[0] - this.predicted[0];

            for (int i = 1; i < l; i++)
            {
                this.smoothing[i] = (this.alpha * this.y[i] / seasonalIndex[i]) + (1 - this.alpha) * (this.smoothing[i - 1] + this.trend[i - 1]);
                this.trend[i] = this.gamma * (this.smoothing[i] - this.smoothing[i - 1]) + (1 - this.gamma) * this.trend[i - 1];
                this.seasonal[i] = (this.beta * this.y[i] / this.smoothing[i]) + (1 - this.beta) * seasonalIndex[i];
                this.predicted[i] = (this.smoothing[i - 1] + this.trend[i - 1]) * seasonalIndex[i];
                this.residual[i] = this.y[i] - this.predicted[i];
            }
        }
        /// <summary>
        /// Prosedur untuk menghitung inisialisasi Pemulusan dan trend periode 0 (nol)
        /// Dihitung lse untuk data sebanyak panjang musiman pertama
        /// </summary>
        private void EstimateValue()
        {
            //Menghitung nilai detrend
            Matrix xL = new Matrix(this.l, 2);
            for (int i = 0; i < this.l; ++i)
            {
                xL[i, 0] = 1;
                xL[i, 1] = i + 1;
            }

            Vector yL = new Vector(this.l);
            for (int i = 0; i < this.l; ++i)
                yL[i] = this.y[i];

            lse = new LeastSquareEstimator(xL, yL);
        }
        /// <summary>
        /// Prosedur untuk menghitung inisialisasi nilai awal
        /// nilai awal ditentukan dengan menggunakan lse
        /// </summary>
        private void Init()
        {
            Matrix x = new Matrix(this.n, 2);
            for (int i = 0; i < this.n; ++i)
            {
                x[i, 0] = 1;
                x[i, 1] = i + 1;
            }

            LeastSquareEstimator lse = new LeastSquareEstimator(x, y);
            double[] parameters = lse.B.GetData();

            double w = 1 - this.a;
            double a0 = (double)(parameters[0] - ((w / this.a) * parameters[1]));
            double b0 = (double)(parameters[0] - (2 * (w / this.a) * parameters[1]));

            this.smoothing1[0] = this.a * this.y[0] + w * a0;
            this.smoothing2[0] = this.a * this.smoothing1[0] + w * b0; ;
        }
        private void ComponentTrend()
        {
            switch (this.initialTrend)
            {
                case 1:
                    {
                        //Model tren linier
                        x = new Matrix(this.n, 2);
                        for (int i = 0; i < this.n; ++i)
                        {
                            x[i, 0] = 1;
                            x[i, 1] = i + 1;
                        }
                        Vector z = new Vector(this.deseasonal);
                        lse = new LeastSquareEstimator(x, z);
                        this.parameters = lse.B.GetData();

                        for (int i = 0; i < this.n; i++)
                            this.trend[i] = this.parameters[0] + this.parameters[1] * (i + 1);

                        break;
                    }
                case 2:
                    {
                        //model tren kuadratik
                        x = new Matrix(this.n, 3);
                        for (int i = 0; i < this.n; ++i)
                        {
                            x[i, 0] = 1;
                            x[i, 1] = i + 1;
                            x[i, 2] = Math.Pow(i + 1, 2);
                        }
                        Vector z = new Vector(this.deseasonal);
                        lse = new LeastSquareEstimator(x, z);
                        this.parameters = lse.B.GetData();

                        for (int i = 0; i < this.n; i++)
                            this.trend[i] = this.parameters[0] + this.parameters[1] * (i + 1) + this.parameters[2] * Math.Pow(i + 1, 2);

                        break;
                    }
                case 3:
                    {
                        //model tren kubik
                        x = new Matrix(this.n, 4);
                        for (int i = 0; i < this.n; ++i)
                        {
                            x[i, 0] = 1;
                            x[i, 1] = i + 1;
                            x[i, 2] = Math.Pow(i + 1, 2);
                            x[i, 3] = Math.Pow(i + 1, 3);
                        }
                        Vector z = new Vector(this.deseasonal);
                        lse = new LeastSquareEstimator(x, z);
                        this.parameters = lse.B.GetData();

                        for (int i = 0; i < this.n; i++)
                            this.trend[i] = this.parameters[0] + this.parameters[1] * (i + 1) + this.parameters[2] * Math.Pow(i + 1, 2)
                                + this.parameters[3] * Math.Pow(i + 1, 3);

                        break;
                    }
                case 4:
                    {
                        //model tren eksponensial
                        x = new Matrix(this.n, 2);
                        for (int i = 0; i < this.n; ++i)
                        {
                            x[i, 0] = 1;
                            x[i, 1] = i + 1;
                        }

                        Vector z = new Vector(this.n);
                        for (int i = 0; i < this.n; ++i)
                            z[i] = Math.Log10(this.deseasonal[i]);

                        lse = new LeastSquareEstimator(x, z);
                        this.parameters = lse.B.GetData();
                        for (int i = 0; i < 2; ++i)
                            this.parameters[i] = Math.Pow(10, this.parameters[i]);

                        for (int i = 0; i < this.n; i++)
                            this.trend[i] = this.parameters[0] * Math.Pow(this.parameters[1], (i + 1));

                        break;
                    }
                default:
                    goto case 1;
            }
        }