/// <summary>
        /// Computes the maximum descent possible from the vector x in the direction dir.
        /// </summary>
        /// <param name="func">Function to find it the greatest descent possible in the given direction.</param>
        /// <param name="x">Current vector of the minimization Quasi-Newton algorithm.</param>
        /// <param name="dir">Descent direction vector for the current vector.</param>
        /// <returns>The value of the maximum descent possible.</returns>
        public static double Wolfe(CompiledFunc func, Vector x, Vector dir)
        {
            double a = 0;
            double ai = 1;
            double fPrev = 0, fCurr = 0, diff = 0;

            double fZero = func.Eval(x);
            var normDir = dir.Normalize();

            double diffZero = (func.Differentiate(x)*normDir).Sum();

            while(ai < MaxAlpha)
            {
                fPrev = func.Eval(x + a*dir);
                fCurr = func.Eval(x + ai*dir);

                if (fCurr > fZero + C1*ai*diffZero || (fCurr > fPrev && ai > 1))
                    return Zoom(func, x, dir, a, ai, fZero, diffZero);

                diff = (func.Differentiate(x + ai*dir)*normDir).Sum();

                if (Math.Abs(diff) <= -C2*diffZero)
                    return ai;

                if (diff >= 0)
                    return Zoom(func, x, dir, ai, a, fZero, diffZero);

                a = ai;
                ai *= 1.5;
            }

            return ai;
        }
        /// <summary>
        /// Computes the BFGS correction formula for inverse hessiana approximation.
        /// </summary>
        /// <param name="func">Function to find it the hessiana approximation.</param>
        /// <param name="b">Current inverse approximation of the hessiana.</param>
        /// <param name="x">Current vector of the minimization Quasi-Newton algorithm.</param>
        /// <param name="x1">Next vector of the minimization Quasi-Newton algorithm.</param>
        /// <returns>Returns a matrix representing the next step in inverse hessiana approximation.s</returns>
        public static Matrix Bfgs(CompiledFunc func, Matrix b, Vector x, Vector x1)
        {
            var sk = new Matrix(x1 - x);
            var yk = new Matrix(func.Differentiate(x1) - func.Differentiate(x));

            var t = b.Dot(sk.Transpose()).Dot(sk).Dot(b)/sk.Dot(b).Dot(sk.Transpose())[0,0];
            var t1 = yk.Transpose().Dot(yk)/yk.Dot(sk.Transpose())[0,0];

            return b - t + t1;
        }
        protected override Vector Minimize(CompiledFunc f, Vector x = null, Tuple<Vector, Vector> bounds = null)
        {
            CurrentIteration = 0;
            var b = Matrix.Identity(x.Length);

            var x1 = new Vector(x);
            Vector d;
            double a;

            while (CurrentIteration < IterationsNumber)
            {
                CurrentIteration++;

                d = -b.Dot(f.Differentiate(x));

                a = Searcher(f, x, d);
                x1 = x + a*d;

                b = Corrector(f, b, x, x1);

                if (!Algebra.IsValid(x1) || Algebra.Norm(x1 - x) <= EPS)
                    break;

                x = x1;
            }

            return x;
        }
        private static double Zoom(CompiledFunc func, Vector x, Vector dir, double aLow, double aHigh, double fZero, double diffZero)
        {
            var normDir = dir.Normalize();
            double aMid = 0;
            double fValue = 0;
            double diff = 0;

            while (Math.Abs(aLow - aHigh) > EPS)
            {
                aMid = aLow + (aHigh - aLow)/2;
                fValue = func.Eval(x + aMid*dir);

                if (fValue > fZero + C1*aMid*diffZero || fValue >= func.Eval(x + aLow*dir))
                    aHigh = aMid;
                else
                {
                    diff = (func.Differentiate(x + aMid*dir)*normDir).Sum();

                    if (Math.Abs(diff) <= -C2*diffZero)
                        return aMid;

                    if (diff*(aHigh - aLow) >= 0)
                        aHigh = aLow;

                    aLow = aMid;
                }
            }

            return aMid;
        }