/// <summary>
        /// Рассчитывает оптимальное значение функции.
        /// </summary>
        /// <returns>оптимальное значение функции</returns>
        public double GetOptimal()
        {
            // узнаём область поиска
            var triplet = GetArea();

            _logger.Log($"найдена область поиска: {triplet}");
            // если область поиска маленькая,
            // не продолжать искать
            while (!IsTooSmall(triplet))
            {
                // если количество операций превысило максимальное, выкинуть ошибку
                if (IsCountDown())
                {
                    throw new ApplicationException("не удалось найти оптимального решения");
                }

                // делим область на две части: левую и правую
                var left = new Triplet {
                    A = triplet.A, B = triplet.Center
                };
                var right = new Triplet {
                    A = triplet.Center, B = triplet.B
                };
                // выбираем лучшую между левой и правой
                triplet = FindGood(left, right);
                _logger.Log($"выбрана тройка {triplet}");
            }

            return(triplet.Center);
        }
        /// <summary>
        /// Рассчитывает область поиска или кидает исключение по превышении количества операций.
        /// </summary>
        /// <returns>область поиска оптимума</returns>
        public Triplet GetArea()
        {
            // записываем шаг поиска
            var step = _step;
            // создаём тройку с нуля
            var triplet = new Triplet {
                A = _start, B = _start + step * 2
            };

            _logger.Log($"начальная тройка: {triplet}");
            // пока не найдём удачную тройку, ищем
            while (!IsLucky(triplet))
            {
                // если количество операций превысило максимальное, кинуть ошибку
                if (IsCountDown())
                {
                    throw new ApplicationException("не удалось найти область поиска");
                }

                // двигаем провотиположную A сторону на количество шагов
                triplet.B += step;
                _logger.Log($"новая тройка поиска: {triplet}");
                // удваиваем шаг
                step *= 2;
            }

            ResetCounter();
            return(triplet);
        }
 /// <summary>
 /// Выбирает из двух троек лучшую.
 /// </summary>
 /// <remark>
 /// Учитывает настройку, какой параметр ищем: max / min.
 /// </remark>
 /// <param name="left">одна тройка</param>
 /// <param name="right">другая тройка</param>
 /// <returns>тройка, которая оказалась лучше</returns>
 private Triplet FindGood(Triplet left, Triplet right)
 {
     // либо оба лучшие, либо оба не лучшие
     if (!(IsLucky(left) ^ IsLucky(right)))
     {
         return
             (_lookingForMax
             ? _function(left.Center) > _function(right.Center) ? left : right
             : _function(left.Center) < _function(right.Center) ? left : right);
     }
     else if (IsLucky(left))
     {
         return(left);
     }
     else if (IsLucky(right))
     {
         return(right);
     }
     else
     {
         throw new ApplicationException($"произошла неизвестная ошибка при выборе между {left} и {right}");
     }
 }
 /// <summary>
 /// Узнаёт, не мала ли тройка.
 /// </summary>
 /// <param name="triplet">тройка</param>
 /// <returns>истина, если тройка меньше точности поиска</returns>
 private bool IsTooSmall(Triplet triplet) =>
 Abs(triplet.B - triplet.A) <= _precision;
 /// <summary>
 /// Удачна ли тройка для этой функции.
 /// </summary>
 /// <remark>
 /// Учитывает настройку, какой параметр ищем: max / min.
 /// </remark>
 /// <param name="triplet">тройка</param>
 /// <returns>истина, если тройка удачная</returns>
 private bool IsLucky(Triplet triplet) =>
 _lookingForMax
         ? _function(triplet.Center) >= _function(triplet.A) &&
 _function(triplet.Center) >= _function(triplet.B)
         : _function(triplet.Center) <= _function(triplet.A) &&
 _function(triplet.Center) <= _function(triplet.B);