/// <summary>
        /// Обработчик событий при изменении "Количество ограничений"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void numericCountRestrictions_ValueChanged(object sender, EventArgs e)
        {
            // Создаём ячейки
            int countVariables    = Decimal.ToInt32(numericCountVariables.Value);
            int countRestrictions = Decimal.ToInt32(numericCountRestrictions.Value);

            DataGridWorker.CreateGrids(_dataGridViewTargetFunction, countVariables, true);
            DataGridWorker.CreateGrids(_dataGridViewRestrictions, countVariables, countRestrictions, true);
            DataGridWorker.CreateGrids(_dataGridViewCornerDot, countVariables, true);
        }
        /// <summary>
        /// Обработчик событий при изменении пункта "Добавить угловую точку"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkBoxCornerDot_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxCornerDot.Checked == true)
            {
                if (_dataGridViewCornerDot.Columns.Count == 0)
                {
                    int countVariables = Decimal.ToInt32(numericCountVariables.Value);
                    DataGridWorker.CreateGrids(_dataGridViewCornerDot, countVariables, true);
                }

                _dataGridViewCornerDot.Visible = true;
            }
            else
            {
                _dataGridViewCornerDot.Visible = false;
            }
        }
        /// <summary>
        /// Сохранение задачи в файл
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveFileToolStripMenuButton_Click(object sender, EventArgs e)
        {
            // Считываем коэффициенты с ячеек
            List <List <string> > targetFunction = DataGridWorker.ReadGridsFrom(_dataGridViewTargetFunction);
            List <List <string> > restrictions   = DataGridWorker.ReadGridsFrom(_dataGridViewRestrictions);

            // В зависимости от выбранного режима добавляем ->max или ->min
            if (radioButtonMax.Checked)
            {
                targetFunction[0].Add("->max");
            }
            else
            {
                targetFunction[0].Add("->min");
            }

            // Добавляем "=" перед последним коэффициентом в ограничения
            foreach (List <string> equation in restrictions)
            {
                equation.Insert(equation.Count - 1, "=");
            }

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                string filePath = saveFileDialog.FileName;

                using (TextWriter tw = new StreamWriter(filePath))
                {
                    string joinString = String.Join(" ", targetFunction[0].ToArray());

                    tw.WriteLine(joinString);

                    foreach (List <string> equation in restrictions)
                    {
                        joinString = String.Join(" ", equation.ToArray());
                        tw.WriteLine(joinString);
                    }

                    tw.Close();
                }

                saveFileDialog.FileName = "";
                MessageBox.Show("Файл успешно сохранён.", "Сохранить задачу в файл.", MessageBoxButtons.OK);
            }
        }
        /// <summary>
        /// Конструктор для авто-режима с задачей
        /// </summary>
        /// <param name="problem"></param>
        public AutoStepWindow(LinearProgrammingProblem problem)
        {
            this.Problem = problem;

            // Инициализируем компоненты окна
            InitializeComponent();

            // Заносим параметры в ячейки
            DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.Restrictions, true);

            if (Problem.CornerDot != null)
            {
                Problem.TransformColumnsForCornerDot(_dataGridViewProblem);
            }

            //Процесс выполнения.
            Implementation();
        }
        /// <summary>
        /// Конструктор для окна пошагового режима с задачей
        /// </summary>
        /// <param name="problem">Задача линейного программирования</param>
        public StepByStepWindow(LinearProgrammingProblem problem)
        {
            this.Problem = problem;

            // изначально мы на нулевом шаге
            this.Step = 0;

            // Инициализируем компоненты окна
            InitializeComponent();

            // Добавляем в ячейки данные
            tabControl.TabPages[0].Text = "Матрица коэффициентов системы ограничения равенств.";
            DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.Restrictions, true);

            if (Problem.CornerDot != null)
            {
                Problem.TransformColumnsForCornerDot(_dataGridViewProblem);
            }
        }
        /// <summary>
        /// При загрузке главного окна
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormMain_Load(object sender, EventArgs e)
        {
            // Путь до справки. Для helpProvider
            helpProvider.HelpNamespace = new Uri(Path.Combine(ExeDirectory, "data\\help\\mainpagehelp.html")).LocalPath;

            // Для openFileDialog
            openFileDialog.InitialDirectory = new Uri(Path.Combine(ExeDirectory, "data\\")).LocalPath;

            // Для saveFileDialog
            saveFileDialog.InitialDirectory = new Uri(Path.Combine(ExeDirectory, "data\\")).LocalPath;

            // Создаём ячейки
            int countVariables    = Decimal.ToInt32(numericCountVariables.Value);
            int countRestrictions = Decimal.ToInt32(numericCountRestrictions.Value);

            DataGridWorker.CreateGrids(_dataGridViewTargetFunction, countVariables, false);
            DataGridWorker.CreateGrids(_dataGridViewRestrictions, countVariables, countRestrictions, false);

            // Настройки по умолчанию
            radioButtonMin.Checked            = true;
            radioButtonStepByStepMode.Checked = true;
        }
        /// <summary>
        /// Основные шаги
        /// </summary>
        public void Implementation()
        {
            try
            {
                //Прямой ход метода Гаусса для приведения к треугольному виду.
                Problem.Gauss();
                //Выражение базисных переменных и приведение к диагональному виду.
                Problem.HoistingMatrix();
                // Обновляем параметры в ячейках
                DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.Restrictions, false);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, $"Ошибка при решении системы методом Гаусса.", MessageBoxButtons.OK);
                return;
            }

            try
            {
                // Создаём симплекс таблицу
                Problem.TransformGridForSimplexTable(_dataGridViewProblem);
                Problem.SimplexTable = new SimplexTable(_dataGridViewProblem);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, $"Ошибка создания симплекс таблицы!", MessageBoxButtons.OK);
                return;
            }

            int responce;
            int step = 1;

            while (true)
            {
                // Проверяем симплекс таблицу на решённость
                if ((responce = Problem.SimplexTable.ResponseCheck()) == 0)
                {
                    try
                    {
                        //выбор любого опорного
                        Problem.SimplexTable.SelectFirstSupportElement();
                        //меняем местами переменные
                        Problem.SimplexTable.ChangeHeaders(_dataGridViewProblem);
                        // высчитывание по опорному
                        Problem.SimplexTable.CalculateSimplexTable();
                        // Обновление ячеек
                        DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.SimplexTable.SimplexTableElements, false);

                        step++;
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, $"Ошибка при выборе опорных элементов на {step} шаге.", MessageBoxButtons.OK);
                        return;
                    }
                }
                else if (responce == 1)
                {
                    tabControl.TabPages[0].Text = "Ответ готов!";

                    if (Problem.Min == false)
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response();
                    }
                    else
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response() * (-1);
                    }

                    if (cornerDotAnswerWasAdded == false)
                    {
                        //добавляем угловую точку решения (X*)
                        DataGridWorker.SetParamToGrids(
                            _dataGridViewCornerDot,
                            Problem.ResponseCornerDot(_dataGridViewProblem),
                            true);
                        cornerDotAnswerWasAdded = true;
                    }

                    break;
                }

                else if (responce == -1)
                {
                    MessageBox.Show("Задача не разрешима!");
                    tabControl.TabPages[0].Text = "Задача не разрешима!";
                    break;
                }
            }
        }
        /// <summary>
        /// Кнопка "Далее"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonNext_Click(object sender, EventArgs e)
        {
            switch (Step)
            {
            case 0:
                try
                {
                    // Буферизируем данные
                    BufferingTableValues(Problem.ToFractionList(DataGridWorker.ReadGridsFrom(_dataGridViewProblem)));
                    // Прямой ход Гаусса
                    Problem.Gauss();
                    // Обновляем параметры
                    DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.Restrictions, false);
                    Step++;
                    tabControl.TabPages[0].Text = "Шаг 1: Прямой ход метода Гаусса.";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка на {Step} шаге!", MessageBoxButtons.OK);
                }
                break;

            case 1:
                try
                {
                    // Буферизируем данные
                    BufferingTableValues(Problem.ToFractionList(DataGridWorker.ReadGridsFrom(_dataGridViewProblem)));
                    // Выражение базисных переменных + обратный ход Гаусса
                    Problem.HoistingMatrix();
                    // Обновляем параметры
                    DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.Restrictions, false);
                    Step++;
                    tabControl.TabPages[0].Text = "Шаг 2: Выражение базисных переменных.";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка на {Step} шаге!", MessageBoxButtons.OK);
                }
                break;

            case 2:

                try
                {
                    // Буферизируем данные
                    BufferingTableValues(Problem.ToFractionList(DataGridWorker.ReadGridsFrom(_dataGridViewProblem)));
                    BufferingHeaders(_dataGridViewProblem);
                    if (simplexTableWasDraw == false)
                    {
                        Problem.TransformGridForSimplexTable(_dataGridViewProblem);
                        Problem.SimplexTable = new SimplexTable(_dataGridViewProblem);
                        simplexTableWasDraw  = true;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка создания симплекс таблицы на {Step} шаге!", MessageBoxButtons.OK);
                    return;
                }

                // Если симплекс таблица создалась и отрисовалась успешно
                // Проверяеем её на ответ
                switch (Problem.SimplexTable.ResponseCheck())
                {
                case 0:
                    // Продолжаем искать решение
                    Step++;
                    tabControl.TabPages[0].Text = "Шаг 3: Симплекс-таблица.";
                    break;

                case 1:
                    // Если ответ готов сразу без выбора опорного элемента
                    tabControl.TabPages[0].Text = "Ответ готов!";
                    Step++;
                    labelAnswer.Visible       = true;
                    groupBoxCornerDot.Visible = true;
                    buttonNext.Enabled        = false;

                    // Подставляем ответ
                    if (Problem.Min == false)
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response();
                    }
                    else
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response() * (-1);
                    }

                    // Выводим угловую точку ответа (X*)
                    if (cornerDotAnswerWasAdded == false)
                    {
                        //добавляем точку
                        DataGridWorker.SetParamToGrids(
                            _dataGridViewCornerDot,
                            Problem.ResponseCornerDot(_dataGridViewProblem),
                            true);
                        cornerDotAnswerWasAdded = true;
                    }

                    break;

                case -1:
                    Step++;
                    MessageBox.Show("Задача не разрешима!");
                    tabControl.TabPages[0].Text = "Задача не разрешима!";
                    buttonNext.Enabled          = false;
                    break;
                }

                break;

            case 3:
                try
                {
                    BufferingTableValues(Problem.SimplexTable.SimplexTableElements);
                    //выбор опорного
                    Problem.SimplexTable.SelectionOfTheSupportElements(_dataGridViewProblem);
                    Step++;
                    tabControl.TabPages[0].Text = $"Шаг {Step}: Выбор опорного элемента";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка при выборе опорных элементов на {Step} шаге.", MessageBoxButtons.OK);
                }
                break;

            default:

                try
                {
                    // Проверяем, выбран ли опорный элемент
                    Problem.SimplexTable.SupportElementPressedOrNot(_dataGridViewProblem);
                    // Меняем хэдэры колонки и строки местами
                    Problem.SimplexTable.ChangeHeaders(_dataGridViewProblem);
                    // Буферизируем симплекс таблицу
                    BufferingTableValues(Problem.SimplexTable.SimplexTableElements);
                    // Удаляем подсвеченные ячейки
                    UncolorGreenGrids(_dataGridViewProblem);
                    // Вычисление симплекс таблицы по выбранному опорному элементу
                    Problem.SimplexTable.CalculateSimplexTable();
                    // Обновление ячеек
                    DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.SimplexTable.SimplexTableElements, false);
                }

                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка при выборе опорного элемента на шаге {Step}", MessageBoxButtons.OK);
                    return;
                }

                switch (Problem.SimplexTable.ResponseCheck())
                {
                case 0:
                    Step++;
                    tabControl.TabPages[0].Text = $"Шаг {Step}: Выбор опорного элемента";
                    //выбор опорного
                    Problem.SimplexTable.SelectionOfTheSupportElements(_dataGridViewProblem);
                    break;

                case 1:
                    tabControl.TabPages[0].Text = "Ответ готов!";
                    Step++;
                    labelAnswer.Visible       = true;
                    groupBoxCornerDot.Visible = true;
                    if (Problem.Min == false)
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response();
                    }
                    else
                    {
                        labelAnswer.Text = "Ответ :" + Problem.SimplexTable.Response() * (-1);
                    }

                    if (cornerDotAnswerWasAdded == false)
                    {
                        //добавляем угловую точку решения (X*)
                        DataGridWorker.SetParamToGrids(
                            _dataGridViewCornerDot,
                            Problem.ResponseCornerDot(_dataGridViewProblem),
                            true);
                        cornerDotAnswerWasAdded = true;
                    }

                    buttonNext.Enabled = false;
                    break;

                case -1:
                    Step++;
                    MessageBox.Show("Задача не разрешима!");
                    tabControl.TabPages[0].Text = "Задача не разрешима!";
                    buttonNext.Enabled          = false;
                    break;
                }

                break;
            }
        }
        /// <summary>
        /// Кнопка "Назад"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonBack_Click(object sender, EventArgs e)
        {
            switch (Step)
            {
            case 0:
                // Отменить операцию закрытия окна
                bool CancelСlosing = false;

                var result = MessageBox.Show(
                    "Предыдущего шага нет. Возврат приведёт к закрытию текущей задачи. Вы уверены?",
                    "Закрыть задачу?",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question);

                if (result == DialogResult.No)
                {
                    // Если пользователь не хочет закрывать окно, отменяем операцию
                    CancelСlosing = true;
                }

                if (!CancelСlosing)
                {
                    this.Close();
                }
                break;

            case 1:
                // Возвращение с шага 1
                try
                {
                    // Достаём данные из буфера и заносим их в ячейки
                    DataGridWorker.SetParamToGrids(_dataGridViewProblem, GetOutOfTheBuffer(), false);
                    // Заносим данные из буфера в данные задачи
                    Problem.Restrictions = Problem.ToFractionList(DataGridWorker.ReadGridsFrom(_dataGridViewProblem));
                    // Убавляем шаг
                    Step--;
                    tabControl.TabPages[0].Text = "Матрица коэффициентов системы ограничений равенств.";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка при возвращении с шага {Step}.", MessageBoxButtons.OK);
                }
                break;

            case 2:
                try
                {
                    // Достаём данные из буфера и заносим их в ячейки
                    DataGridWorker.SetParamToGrids(_dataGridViewProblem, GetOutOfTheBuffer(), false);
                    // Заносим данные из буфера в данные задачи
                    Problem.Restrictions = Problem.ToFractionList(DataGridWorker.ReadGridsFrom(_dataGridViewProblem));
                    // Убавляем шаг
                    Step--;
                    tabControl.TabPages[0].Text = "Шаг 1: Прямой ход метода Гаусса.";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, $"Ошибка при возвращении с шага {Step}.", MessageBoxButtons.OK);
                }
                break;

            case 3:

                // Достаём данные из буфера и заносим их в ячейки
                DataGridWorker.SetParamToGrids(_dataGridViewProblem, GetOutOfTheBuffer(), true);
                // Достаём хэдэры из буфера
                DataGridWorker.SetHeaders(_dataGridViewProblem, GetOutOfTheBufferHeaders());
                // Обнуляем данные симплекс таблицы
                Problem.SimplexTable.SimplexTableElements = new List <List <Fraction> >();
                simplexTableWasDraw = false;

                Step--;
                tabControl.TabPages[0].Text = "Шаг 2: Выражение базисных переменных.";

                // Если ответ был готов сразу и уже отобразился - скрываем его
                labelAnswer.Visible       = false;
                groupBoxCornerDot.Visible = false;
                buttonNext.Enabled        = true;

                break;

            case 4:
                UncolorGreenGrids(_dataGridViewProblem);
                GetOutOfTheBuffer();
                Step--;
                tabControl.TabPages[0].Text = "Шаг 3: Симплекс-таблица.";
                break;

            default:

                UncolorGreenGrids(_dataGridViewProblem);

                // Достаём из буффера элементы симплекс таблицы
                Problem.SimplexTable.SimplexTableElements = GetOutOfTheBuffer();

                // Достаём данные из буфера и заносим их в ячейки
                DataGridWorker.SetParamToGrids(_dataGridViewProblem, Problem.SimplexTable.SimplexTableElements, false);
                // Меняем хэдэры обратно, доставая их из буффера
                Problem.SimplexTable.ChangeHeadersFromBuffer(_dataGridViewProblem);
                // Выбор опорного
                Problem.SimplexTable.SelectionOfTheSupportElements(_dataGridViewProblem);

                Step--;
                tabControl.TabPages[0].Text = $"Шаг {Step}: Выбор опорного элемента";
                labelAnswer.Visible         = false;
                groupBoxCornerDot.Visible   = false;
                buttonNext.Enabled          = true;

                break;
            }
        }
        /// <summary>
        /// Кнопка "Решить"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonSolve_Click(object sender, EventArgs e)
        {
            // Задача на минимум?
            bool min = radioButtonMin.Checked;
            // Пошаговый режим?
            bool stepByStepModeOn = radioButtonStepByStepMode.Checked;
            // Задана угловая точка?
            bool cornerDotOn = checkBoxCornerDot.Checked;

            // Создаём объект задачи
            LinearProgrammingProblem problem;

            // Инициализируем
            try
            {
                // Данные задачи
                List <List <string> > targetFunction = DataGridWorker.ReadGridsFrom(_dataGridViewTargetFunction);
                List <List <string> > retrictions    = DataGridWorker.ReadGridsFrom(_dataGridViewRestrictions);
                List <List <string> > cornerDot;

                // Если включена угловая точка
                if (cornerDotOn)
                {
                    cornerDot = DataGridWorker.ReadGridsFrom(_dataGridViewCornerDot);

                    problem = new LinearProgrammingProblem(targetFunction, retrictions, cornerDot, min);

                    // Использование LINQ
                    var query = problem.CornerDot[0].Where(fraction => !fraction.Equals(new Fraction(0)));
                    if (problem.Restrictions.Count != query.Count())
                    {
                        throw new Exception("Невозможно выразить базис с данной угловой точкой. " +
                                            "Количество ненулевых параметров начальной угловой точки не равно количеству линейно независимых строк");
                    }
                }
                else
                {
                    problem = new LinearProgrammingProblem(targetFunction, retrictions, null, min);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Ошибка при создании задачи!", MessageBoxButtons.OK);
                return;
            }

            // Меню
            // Проверяем режим
            switch (stepByStepModeOn)
            {
            // Если выбран пошаговый режим
            case true:

                StepByStepWindow stepByStepWindow = new StepByStepWindow(problem);
                stepByStepWindow.ShowDialog();

                break;


            // Если выбран авто-режим
            case false:

                AutoStepWindow autoStepWindow = new AutoStepWindow(problem);
                autoStepWindow.ShowDialog();

                break;
            }
        }
        /// <summary>
        /// Открыть задачу из файла
        /// </summary>
        /// <param name="fileName"></param>
        private void OpenTaskFromPath(string fileName)
        {
            string fileText = File.ReadAllText(fileName);

            if (fileText == "")
            {
                throw new Exception("Файл пуст.\nПожалуйста, проверьте файл и попробуйте ещё раз.");
            }

            // Чистим массив от лишних символов
            fileText = fileText.Replace("\r", "");
            fileText = fileText.Replace("= ", "");

            // Чистим от последнего /n, если пользователь вдруг нажал Enter после уравнения
            if (fileText[fileText.Length - 1] == '\n')
            {
                fileText = fileText.Remove(fileText.Length - 1);
            }

            // Чистим от лишних пробелов
            fileText = string.Join(" ", fileText.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));

            // Определяем макс или мин и очищаем от этого строку
            if (fileText.Contains("->max") || fileText.Contains("-> max"))
            {
                radioButtonMax.Checked = true;
                fileText = fileText.Replace(" ->max", "");
                fileText = fileText.Replace(" -> max", "");
            }
            else
            {
                if (fileText.Contains("->min") || fileText.Contains("-> min"))
                {
                    radioButtonMin.Checked = true;
                    fileText = fileText.Replace(" ->min", "");
                    fileText = fileText.Replace(" -> min", "");
                }
                else
                {
                    throw new Exception("В файле отсутствует условие максимизации или минимизации. Пожалуйста, установите это условие вручную.");
                }
            }

            // Делим цельный текст на массив строк с уравнениями
            string[] tmpText = fileText.Split('\n');

            // Создаём список всего уравнения
            List <List <string> > AllTask = new List <List <string> >();

            // Заносим данные в список
            foreach (string equation in tmpText)
            {
                AllTask.Add(new List <string>(equation.Split(' ')));
            }

            // Делим на целевую и ограничения
            List <List <string> > targetFunction = new List <List <string> >();

            targetFunction.Add(new List <string>());
            targetFunction[0] = AllTask[0];

            // Удаляем целевую функцию из списка
            AllTask.Remove(AllTask.First());

            List <List <string> > restrictions = new List <List <string> >(AllTask);

            // Заносим целевую функцию в Ячейки
            DataGridWorker.SetParamToGrids(_dataGridViewTargetFunction, targetFunction, true);
            // Создаём угловую точку
            DataGridWorker.CreateGrids(_dataGridViewCornerDot, targetFunction[0].Count, true);
            // Ограничения
            DataGridWorker.SetParamToGrids(_dataGridViewRestrictions, restrictions, true);
        }