// NOTE: Неизвстно, как преобразовать Func<Vector<double>, double> к Func<PointN, double>
        // Поэтому будут использованы две аналогичные функции
        // Для них используются ужасно похожие лямбды.
        // Возможно, этот конфликт можно решить
        // TODO: надо бы сравнить производительность алгоритмов
        // Можно привлечь сюда число итераций например
        // TODO: см в тетради и реши все тестовые задачи
        // COMPLETE
        #region TEST2_1
        static void Test2_1()
        {
            // Input data
            FunctionNDByAlex  funcND        = (PointN point) => { return(4 * System.Math.Pow(point.At(0) - 5.0, 2.0) + System.Math.Pow(point.At(1) - 6.0, 2.0)); };
            FunctionNDMathNet funcNDMathNet = (VectorNMathNet point) => { return(4 * System.Math.Pow(point.At(0) - 5.0, 2.0) + System.Math.Pow(point.At(1) - 6.0, 2.0)); };
            PointN            x0            = new PointN(100.0, 100.0);
            VectorNMathNet    x0MathNet     = Helpers.PointNToMathNet(x0);
            const double      eps           = 0.01;

            // Testing
            TestOptimalGradientMethod(funcND, x0, eps);
            TestVariableMetricMethod(funcNDMathNet, x0MathNet, eps);
        }
        // Эти функции тестируют одноименные методы
        // И осуществляют форматированный вывод результатов их работы
        // Работает с NDimensionalPrimitives.PointN;
        static void TestOptimalGradientMethod(FunctionNDByAlex funcND, PointN x0, double eps)
        {
            OptimalGradientMethod ogm = new OptimalGradientMethod(funcND, x0);

            sw.Start();
            PointN result = ogm.FindMin(eps);

            sw.Stop();

            Console.Write(String.Join("\n", ogm.Log)); // Выводим все сообщения от этого метода
            Console.WriteLine("\n---------------------------------------------");
            Console.WriteLine("Optimal gradient method: x_min = {0}", result);
            Console.WriteLine("F(x_min) = {0}", funcND(result));
            Console.WriteLine("Time: {0}ms", sw.ElapsedMilliseconds);
            Console.WriteLine("=============================================");
        }
        // FAILED (первый несет чушь)
        // 1) Градиент все время прыгает во все стороны, а xk все наращивается
        // Все потому, что вектор указыает направление наискорейшего возрастания и очень большой
        // xk быстро наращивается
        // 2) yk или Hk или M_yk_t становится слишком мал
        // вследствие этого, на одной из итераций (когда близко к минимуму), мы получаем деление на нуль
        // Поэтому я выставил eps = 0.5 для того, чтобы метод остановился до деления на нуль
        // Вывод: Методы чуть менее чем полностью не приспособлены для минимизации не униминимальных функций
        #region TEST2_1_1
        static void Test2_1_1()
        {
            // Input data
            FunctionNDByAlex funcND = (PointN point) =>
            {
                return(SMath.Pow(SMath.Pow(point.At(0), 2) + point.At(1) - 11.0, 2.0)
                       + SMath.Pow(point.At(0) + SMath.Pow(point.At(1), 2) - 7.0, 2.0));
            };
            FunctionNDMathNet funcNDMathNet = (VectorNMathNet point) =>
            {
                return(SMath.Pow(SMath.Pow(point.At(0), 2) + point.At(1) - 11.0, 2.0)
                       + SMath.Pow(point.At(0) + SMath.Pow(point.At(1), 2) - 7.0, 2.0));
            };

            PointN         x0        = new PointN(3.0, 3.0);
            VectorNMathNet x0MathNet = Helpers.PointNToMathNet(x0);
            const double   eps       = 0.1; // TODO при слишком малых eps происходит деление на 0 в формуле пересчета Hk

            // Testing
            TestOptimalGradientMethod(funcND, x0, eps);
            TestVariableMetricMethod(funcNDMathNet, x0MathNet, eps);
        }