public async Task <Responce> GlobalTestGraphAsync([FromBody] InputGraph input)
        {
            var converted = await GraphToMatrixAsync(input);

            return(await Task.Run(() =>
            {
                try
                {
                    // Проверка аргумента на null
                    _ = input ?? throw new ArgumentNullException(nameof(input));

                    // Значение глобального теста
                    IBalanceSolver solver = new AccordBalanceSolver();
                    var output = solver.GlobalTest(converted.X0, converted.A, converted.Measurability,
                                                   converted.Tolerance);

                    return new Responce
                    {
                        Type = "result",
                        Data = output
                    };
                }
                catch (Exception e)
                {
                    return new Responce
                    {
                        Type = "error",
                        Data = e.Message
                    };
                }
            }));
        }
        public async Task <Responce> GlrTestBestGraphAsync([FromBody] InputGraph input)
        {
            var converted = await GraphToMatrixAsync(input);

            return(await Task.Run(() =>
            {
                try
                {
                    // Проверка аргумента на null
                    _ = converted ?? throw new ArgumentNullException(nameof(converted));

                    IBalanceSolver solver = new AccordBalanceSolver();

                    var flows = solver.GetFlows(converted.A).ToList();

                    var globalTest = solver.GlobalTest(converted.X0, converted.A, converted.Measurability,
                                                       converted.Tolerance);

                    var nodesCount = converted.A.GetLength(0);
                    var glr = solver.GlrTest(converted.X0, converted.A, converted.Measurability,
                                             converted.Tolerance, flows, globalTest);

                    var results = new List <GlrOutputFlow>();

                    while (globalTest >= 1)
                    {
                        // Находим максимальное значение GLR теста в массиве
                        var(i, j) = glr.ArgMax();

                        // Если у нас не осталось значений больше нуля, то выходим
                        if (glr[i, j] <= 0)
                        {
                            break;
                        }

                        // Добавляем новый поток
                        var aColumn = new double[nodesCount];
                        aColumn[i] = 1;
                        aColumn[j] = -1;

                        converted.A = converted.A.InsertColumn(aColumn);
                        converted.X0 = converted.X0.Append(0).ToArray();
                        converted.Measurability = converted.Measurability.Append(0).ToArray();
                        converted.Tolerance = converted.Tolerance.Append(0).ToArray();

                        var newFlow = new GlrOutputFlow
                        {
                            Id = Guid.NewGuid().ToString(),
                            Name = "New flow",
                            Number = converted.A.Length - 1,
                            Info = $"{i} -> {j}"
                        };

                        // Если у нас есть существующий поток, то выводим информацию о нем
                        var existingFlowIdx = flows.FindIndex(x => x.Item1 == i && x.Item2 == j);
                        if (existingFlowIdx != -1)
                        {
                            var(_, _, existingFlow) = flows[existingFlowIdx];

                            newFlow.Id = converted.Guids[existingFlow];
                            newFlow.Name = converted.Names[existingFlow];
                            newFlow.Number = existingFlow;
                        }

                        results.Add(newFlow);

                        // Считаем новое значение глобального теста
                        globalTest = solver.GlobalTest(converted.X0, converted.A, converted.Measurability,
                                                       converted.Tolerance);

                        // Считаем новое значение GLR теста
                        glr = solver.GlrTest(converted.X0, converted.A, converted.Measurability,
                                             converted.Tolerance, flows, globalTest);
                    }

                    return new Responce
                    {
                        Type = "result",
                        Data = results
                    };
                }
                catch (Exception e)
                {
                    return new Responce
                    {
                        Type = "error",
                        Data = e.Message
                    };
                }
            }));
        }
        public async Task <Responce> GlrTestGraphAsync([FromBody] InputGraph input)
        {
            var converted = await GraphToMatrixAsync(input);

            return(await Task.Run(() =>
            {
                try
                {
                    _ = converted ?? throw new ArgumentNullException(nameof(converted));

                    IBalanceSolver solver = new AccordBalanceSolver();

                    const int maxSubNodesCount = 3;
                    const int maxTreeDepth = 5;

                    var flows = solver.GetFlows(converted.A).ToList();
                    var nodesCount = converted.A.GetLength(0);

                    var root = new MutableEntityTreeNode <Guid, TreeElement>(x => x.Id, new TreeElement());
                    var currentNode = root;

                    while (currentNode != null)
                    {
                        var newA = converted.A;
                        var newX0 = converted.X0;
                        var newMeasurability = converted.Measurability;
                        var newTolerance = converted.Tolerance;

                        foreach (var(fi, fj) in currentNode.Item.Flows)
                        {
                            var aColumn = new double[nodesCount];
                            aColumn[fi] = 1;
                            aColumn[fj] = -1;

                            newA = newA.InsertColumn(aColumn);
                            newX0 = newX0.Append(0).ToArray();
                            newMeasurability = newMeasurability.Append(0).ToArray();
                            newTolerance = newTolerance.Append(0).ToArray();
                        }

                        var globalTest = solver.GlobalTest(newX0, newA, newMeasurability,
                                                           newTolerance);

                        var glr = solver.GlrTest(newX0, newA, newMeasurability,
                                                 newTolerance, flows, globalTest);

                        // Поиск следующего максимума
                        var(i, j) = (0, 0);
                        for (var k = 0; k < currentNode.Children.Count + 1; k++)
                        {
                            (i, j) = glr.ArgMax();

                            if (glr[i, j] <= 0)
                            {
                                break;
                            }

                            // Если итерация не последняя, сбрасываем значение
                            if (k != currentNode.Children.Count)
                            {
                                glr[i, j] = 0.0;
                            }
                        }

                        // Проверяем можно ли добавить дочерний узел в дерево
                        if (currentNode.Children.Count < maxSubNodesCount &&
                            currentNode.Level < maxTreeDepth && glr[i, j] > 0 && globalTest >= 1)
                        {
                            var node = new TreeElement(new List <(int, int)>(currentNode.Item.Flows), globalTest - glr[i, j]);
                            node.Flows.Add((i, j));

                            currentNode = currentNode.AddChild(node);
                        }
                        else
                        {
                            currentNode = currentNode.Parent;
                        }
                    }

                    //Находим все листья и выводим их
                    var leafs = root.Where(x => x.IsLeaf);
                    var results = new List <GlrOutput>();

                    foreach (var leaf in leafs)
                    {
                        var result = new List <GlrOutputFlow>();
                        var flowsToAdd = new List <Variable>();

                        foreach (var flow in leaf.Item.Flows)
                        {
                            var(i, j) = flow;

                            var newFlow = new GlrOutputFlow
                            {
                                Id = Guid.NewGuid().ToString(),
                                Name = "New flow",
                                Number = -1,
                                Info = $"{i} -> {j}"
                            };

                            // Если у нас есть существующий поток, то выводим информацию о нем
                            var existingFlowIdx = flows.FindIndex(x => x.Item1 == i && x.Item2 == j);
                            if (existingFlowIdx != -1)
                            {
                                var(_, _, existingFlow) = flows[existingFlowIdx];

                                newFlow.Id = converted.Guids[existingFlow];
                                newFlow.Name = converted.Names[existingFlow];
                                newFlow.Number = existingFlow;

                                // Формируем информацию о добавляемом потоке
                                var variable = new Variable
                                {
                                    Id = Guid.NewGuid().ToString(),
                                    SourceId = converted.NodesGuids[i],
                                    DestinationId = converted.NodesGuids[j],
                                    Name = converted.Names[existingFlow] + " (additional)",
                                    MetrologicRange = new Models.Range
                                    {
                                        Min = converted.LowerMetrologic[existingFlow] - converted.X0[existingFlow],
                                        Max = converted.UpperMetrologic[existingFlow] - converted.X0[existingFlow]
                                    },
                                    TechnologicRange = new Models.Range
                                    {
                                        Min = converted.LowerTechnologic[existingFlow] - converted.X0[existingFlow],
                                        Max = converted.UpperTechnologic[existingFlow] - converted.X0[existingFlow]
                                    },
                                    Tolerance = converted.Tolerance[existingFlow],
                                    IsMeasured = true,
                                    VarType = "FLOW"
                                };

                                flowsToAdd.Add(variable);
                            }

                            result.Add(newFlow);
                        }

                        results.Add(new GlrOutput
                        {
                            FlowsInfo = result,
                            FlowsToAdd = flowsToAdd,
                            TestValue = leaf.Item.TestValue
                        });
                    }

                    return new Responce
                    {
                        Type = "result",
                        Data = results.OrderBy(x => x.TestValue)
                    };
                }
                catch (Exception e)
                {
                    return new Responce
                    {
                        Type = "error",
                        Data = e.Message
                    };
                }
            }));
        }