private async void buttonWaterhouseStartModeling_Click(object sender, EventArgs e)
        {
            textBoxWaterhouseOutputInfoGoods.Text  = "";
            textBoxWaterhouseOutputPurchase.Text   = "";
            textBoxWaterhouseStatisticForDays.Text = "";
            OutputInTextBox output = new OutputInTextBox();

            output.OutputPurchase  = textBoxWaterhouseOutputPurchase;
            output.OutputInfoGoods = textBoxWaterhouseOutputInfoGoods;
            output.OutputStatistic = textBoxWaterhouseStatisticForDays;

            ParamForModeling forModeling = new ParamForModeling();

            forModeling.days = (int)numericUpDownWaterhouseDays.Value;
            // Количество товаров
            forModeling.kolTypesGoods = (int)numericUpDownWaterhouseTypesGoods.Value;
            // Минимальное количество товаров на складе до закупки
            forModeling.minimumQuantity = (int)numericUpDownWaterhouseMinLeft.Value;
            // Количество единиц закупаемых товаров
            forModeling.procurementSize = (int)numericUpDownPurchaseInStorage.Value;
            // Количество клиентов
            forModeling.countCustomer = (int)numericUpDownWaterhouseCustomerCount.Value;
            // Максимальное количество товаро которое может купить 1 клиент
            forModeling.kolPurchaseCustomer = (int)numericUpDownPredelPurchase.Value;
            // Стартовое количество единиц товаров
            forModeling.startKolGoods = (int)numericUpDownWaterhouseStartKolGoods.Value;

            if (radioButtonWaterhouseTriangle.Checked)
            {
                forModeling.generate = new Simpson();
            }
            else if (radioButtonWaterhouseNormal.Checked)
            {
                forModeling.generate = new Normal();
            }
            else
            {
                forModeling.generate = new Uniform();
            }

            await Waterhouse.AsyncModelingWork(forModeling, output);
        }
        // Моделирование работы оптового склада в заданном периоде (Асинхронный метод)
        static internal Task AsyncModelingWork(object Param, object Output)
        {
            return(Task.Run(() =>
            {
                // Принимаем обьект, содержащий в себе стартовые значения
                ParamForModeling startParam = (ParamForModeling)Param;
                // Принимаем объект, содержащий в себе текстбоксы в которые будет происходить вывод
                OutputInTextBox output = (OutputInTextBox)Output;
                // Здесь хранятся объекты класса InfoProduct по каждому товару
                List <InfoProduct> infoProducts = new List <InfoProduct>();
                // Здесь хранятся объекты класса Product для каждого товара по 1
                List <Product> products = new List <Product>();
                // Заполняем обе коллекции под каждый товар
                for (int i = 0; i < startParam.kolTypesGoods; i++)
                {
                    products.Add(new Product {
                        number = i,
                        kol = startParam.startKolGoods,
                        minQuantity = startParam.minimumQuantity,
                        procurement = startParam.procurementSize
                    });
                    infoProducts.Add(new InfoProduct {
                        numberProduct = i,
                        procurement = 0,
                        purchase = 0,
                        shortage = 0
                    });
                }
                // Максимальное количество товаров, которое может купить один клиент
                int maximumTypesProducts = Convert.ToInt32(startParam.kolTypesGoods * 0.7);
                // Здесь хранится количество отмененных заказов для каждого товара
                Dictionary <int, int> canceledOrdersProduct = new Dictionary <int, int>();
                for (int i = 0; i < startParam.kolTypesGoods; i++)
                {
                    canceledOrdersProduct.Add(i, 0);
                }

                // Счетчик общего количества отмененных товаров
                int canceledOrder = 0;

                // Цикл для прохода по заданным дням
                for (int currDay = 0; currDay < startParam.days; currDay++)
                {
                    // Выводим на основную форму текущий день
                    output.OutputPurchase.Invoke((MethodInvoker)(() =>
                    {
                        output.OutputPurchase.Text += "День " + (currDay + 1) + Environment.NewLine;
                    }));

                    // Обнуляем количество отмененных заказов для каждого товара
                    for (int i = 0; i < canceledOrdersProduct.Count; i++)
                    {
                        canceledOrdersProduct[i] = 0;
                    }

                    // Привоз нового товара тех позиций где текущее количество меньше параметра: "Минимальное количество на складе"
                    for (int i = 0; i < products.Count; i++)
                    {
                        if (products[i].kol <= products[i].minQuantity)
                        {
                            products[i].kol += products[i].procurement;
                            infoProducts[i].procurement += products[i].procurement;
                        }
                    }
                    // Покупки клиентами товаров
                    for (int i = 0; i < startParam.countCustomer; i++)
                    {
                        // Генерируем число Видов товара который купит данный клиент
                        int kolTypesProduct = (int)startParam.generate.GenerateNumber(maximumTypesProducts);
                        // Если ноль то пропускаем данного клиента
                        if (kolTypesProduct == 0)
                        {
                            continue;
                        }
                        for (int type = 0; type < kolTypesProduct; type++)
                        {
                            // Генерируем номер продукта который в данный момент купит клиент
                            int currProduct = (int)startParam.generate.GenerateNumber(startParam.kolTypesGoods);
                            // Генерируем количество выбранного товара
                            int currQuantity = (int)startParam.generate.GenerateNumber(startParam.kolPurchaseCustomer);
                            // Если выпало 0 пропускаем данный товар
                            if (currQuantity == 0)
                            {
                                continue;
                            }
                            // Если клиент хочет купить больше чем у нас есть
                            if (products[currProduct].kol < currQuantity)
                            {
                                // Сохраняем количество недокупленных товаров
                                infoProducts[currProduct].shortage += currQuantity - products[currProduct].kol;
                                // Сохраняем количество которое все таки удалось купить
                                infoProducts[currProduct].purchase += products[currProduct].kol;
                                // Выводи сообщение о неудачной продаже
                                output.OutputPurchase.Invoke((MethodInvoker)(() =>
                                {
                                    output.OutputPurchase.Text += $"Товар {currProduct +1} закончился. Купили {products[currProduct].kol} шт. вместо {currQuantity}" + Environment.NewLine;
                                }));
                                // Этого товара больше нет
                                products[currProduct].kol = 0;
                                // +1 неудовлетворенный заказ
                                canceledOrder++;
                                // Указываем что этот заказ пришелся на данный товар
                                canceledOrdersProduct[currProduct]++;
                                continue;
                            }
                            // Если товара хватает выводим сообщение о покупке
                            output.OutputPurchase.Invoke((MethodInvoker)(() =>
                            {
                                output.OutputPurchase.Text += $"Куплен товар {currProduct +1} в количестве: {currQuantity} шт. " + Environment.NewLine;
                            }));
                            // Сохраняем количество купленных товаров
                            infoProducts[currProduct].purchase += currQuantity;
                            // Уменьшаем количество текущих товаров
                            products[currProduct].kol -= currQuantity;
                        }
                    }

                    // Изменение параметров: "Мин. количество товара" и "объем закупок"
                    for (int i = 0; i < products.Count; i++)
                    {
                        // Если у данного товара сегодня было 30% неудовлетворенных заказов
                        if (canceledOrdersProduct[i] > (startParam.countCustomer * 0.3))
                        {
                            // Увеличиваем оба параметра на 30%
                            products[i].procurement = Convert.ToInt32(products[i].procurement * 1.3);
                            products[i].minQuantity = Convert.ToInt32(products[i].minQuantity * 1.3);
                        }
                        // Если фактический объем превышает минимальные более чем в два раза
                        else if (products[i].kol > products[i].minQuantity * 2)
                        {
                            // Уменьшаем оба параметра на 5%
                            products[i].procurement = Convert.ToInt32(products[i].procurement * 0.95);
                            products[i].minQuantity = Convert.ToInt32(products[i].minQuantity * 0.95);
                        }
                    }
                }
                // Переменные для подсчета общего количество проданных и непроданных товаров
                int TotalPurchase = 0;
                int TotalShortage = 0;
                for (int i = 0; i < infoProducts.Count; i++)
                {
                    TotalPurchase += infoProducts[i].purchase;
                    TotalShortage += infoProducts[i].shortage;
                    // Выводим информацию по каждому товару
                    output.OutputInfoGoods.Invoke((MethodInvoker)(() =>
                    {
                        output.OutputInfoGoods.Text += $"Товар {i +1}" + Environment.NewLine;
                        output.OutputInfoGoods.Text += $"Купили {infoProducts[i].purchase} шт." + Environment.NewLine;
                        output.OutputInfoGoods.Text += $"Заказали {infoProducts[i].procurement} шт." + Environment.NewLine;
                        output.OutputInfoGoods.Text += $"Недокупили {infoProducts[i].shortage} шт." + Environment.NewLine;
                    }));
                }
                // Вычисляем процент купленных товаров от общего числа заказов
                double percentRealize = (double)TotalPurchase / (TotalPurchase + TotalShortage);
                // Выводим информацию о проценте реализации и общем количестве неудовлетворенных товаров
                output.OutputInfoGoods.Invoke((MethodInvoker)(() =>
                {
                    output.OutputInfoGoods.Text += $"Реализовано {Math.Round(percentRealize,4)}%" + Environment.NewLine;
                    output.OutputInfoGoods.Text += $"Всего неудовлетворительных заказов: {canceledOrder}";
                }));
                // Выводим измененные параметры для каждого товара
                for (int i = 0; i < products.Count; i++)
                {
                    output.OutputStatistic.Invoke((MethodInvoker)(() =>
                    {
                        output.OutputStatistic.Text += $"Товар {i + 1}" + Environment.NewLine;
                        output.OutputStatistic.Text += $"Минимальное количество на складе {products[i].minQuantity} шт." + Environment.NewLine;
                        output.OutputStatistic.Text += $"Объем закупок {products[i].procurement} шт." + Environment.NewLine;
                    }));
                }
            }));
        }