using System.Collections.Concurrent; using System.Diagnostics; using System.Globalization; namespace StellaOps.ElkSharp; internal static partial class ElkEdgeRouterIterative { private static CandidateSolution ApplyFinalBoundarySlotPolish( CandidateSolution solution, ElkPositionedNode[] nodes, ElkLayoutDirection direction, double minLineClearance, int maxRounds = 3) { var current = solution; var nodesById = nodes.ToDictionary(node => node.Id, StringComparer.Ordinal); for (var round = 0; round < maxRounds; round++) { var boundarySlotSeverity = new Dictionary(StringComparer.Ordinal); ElkEdgeRoutingScoring.CountBoundarySlotViolations(current.Edges, nodes, boundarySlotSeverity, 10); if (boundarySlotSeverity.Count == 0) { break; } var batchedRootEdgeIds = boundarySlotSeverity .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Take(MaxWinnerPolishBatchedRootEdges) .Select(pair => pair.Key) .ToArray(); var batchedFocusEdgeIds = ResolveBoundarySlotRepairFocus( current.Edges, nodesById, batchedRootEdgeIds); if (batchedFocusEdgeIds.Length > 0) { var batchedCandidateEdges = BuildFinalBoundarySlotCandidate( current.Edges, nodes, direction, minLineClearance, batchedFocusEdgeIds, allowLateRestabilizedClosure: false); if (TryPromoteFinalBoundarySlotCandidate(current, batchedCandidateEdges, nodes, out var batchedPromoted)) { current = batchedPromoted; continue; } } var improved = false; foreach (var edgeId in boundarySlotSeverity .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Select(pair => pair.Key)) { var focusEdgeIds = ResolveBoundarySlotRepairFocus( current.Edges, nodesById, [edgeId]); if (focusEdgeIds.Length == 0) { continue; } var candidateEdges = BuildFinalBoundarySlotCandidate( current.Edges, nodes, direction, minLineClearance, focusEdgeIds, allowLateRestabilizedClosure: false); if (!TryPromoteFinalBoundarySlotCandidate(current, candidateEdges, nodes, out var promoted)) { continue; } current = promoted; improved = true; break; } if (!improved) { break; } } return current; } private static string[] ResolveBoundarySlotRepairFocus( IReadOnlyCollection edges, IReadOnlyDictionary nodesById, IReadOnlyCollection rootEdgeIds) { if (rootEdgeIds.Count == 0) { return []; } if (TryResolveGatewayBoundarySlotLocalFocus(edges, nodesById, rootEdgeIds, out var localFocus)) { return localFocus; } return ExpandWinningSolutionFocus(edges, rootEdgeIds).ToArray(); } private static bool TryResolveGatewayBoundarySlotLocalFocus( IReadOnlyCollection edges, IReadOnlyDictionary nodesById, IReadOnlyCollection rootEdgeIds, out string[] focusEdgeIds) { focusEdgeIds = []; if (rootEdgeIds.Count == 0) { return false; } var edgesById = edges.ToDictionary(edge => edge.Id, StringComparer.Ordinal); var localFocus = new HashSet(StringComparer.Ordinal); foreach (var edgeId in rootEdgeIds) { if (!edgesById.TryGetValue(edgeId, out var edge)) { return false; } var touchesGateway = false; if (!string.IsNullOrWhiteSpace(edge.SourceNodeId) && nodesById.TryGetValue(edge.SourceNodeId, out var sourceNode) && ElkShapeBoundaries.IsGatewayShape(sourceNode)) { touchesGateway = true; } if (!touchesGateway && !string.IsNullOrWhiteSpace(edge.TargetNodeId) && nodesById.TryGetValue(edge.TargetNodeId, out var targetNode) && ElkShapeBoundaries.IsGatewayShape(targetNode)) { touchesGateway = true; } if (!touchesGateway) { return false; } localFocus.Add(edgeId); } focusEdgeIds = localFocus .OrderBy(edgeId => edgeId, StringComparer.Ordinal) .ToArray(); return focusEdgeIds.Length > 0; } private static CandidateSolution ApplyFinalPostSlotHardRulePolish( CandidateSolution solution, ElkPositionedNode[] nodes, ElkLayoutDirection direction, double minLineClearance, int maxRounds = 3) { var current = solution; for (var round = 0; round < maxRounds; round++) { var severityByEdgeId = new Dictionary(StringComparer.Ordinal); var pressure = ElkEdgeRoutingScoring.CountEdgeNodeCrossings(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountBadBoundaryAngles(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountGatewaySourceExitViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountTargetApproachJoinViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountSharedLaneViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountBoundarySlotViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountTargetApproachBacktrackingViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountExcessiveDetourViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountBelowGraphViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountUnderNodeViolations(current.Edges, nodes, severityByEdgeId, 10) + ElkEdgeRoutingScoring.CountLongDiagonalViolations(current.Edges, nodes, severityByEdgeId, 10); ElkLayoutDiagnostics.LogProgress( $"Winner post-slot hard-rule round {round + 1} start: pressure={pressure} retry={DescribeRetryState(current.RetryState)} focus={severityByEdgeId.Count}"); if (pressure == 0) { break; } var preferFastTerminalOnly = ShouldPreferFastTerminalOnlyHardRuleClosure(current.RetryState); var batchedRootEdgeIds = severityByEdgeId .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Take(MaxWinnerPolishBatchedRootEdges) .Select(pair => pair.Key) .ToArray(); var batchedFocusEdgeIds = preferFastTerminalOnly ? batchedRootEdgeIds : ExpandWinningSolutionFocus(current.Edges, batchedRootEdgeIds).ToArray(); if (batchedFocusEdgeIds.Length > 0) { if (preferFastTerminalOnly) { ElkLayoutDiagnostics.LogProgress( $"Winner post-slot hard-rule round {round + 1} fast-terminal focus=[{string.Join(", ", batchedFocusEdgeIds)}]"); var quickBatchedCandidateEdges = BuildFastTerminalOnlyHardRuleCandidate( current.Edges, nodes, direction, minLineClearance, batchedFocusEdgeIds); if (TryPromoteFinalHardRuleCandidate(current, quickBatchedCandidateEdges, nodes, out var quickBatchedPromoted)) { current = quickBatchedPromoted; continue; } var focusedTerminalClosureEdges = CloseRemainingTerminalViolations( current.Edges, nodes, direction, minLineClearance, batchedFocusEdgeIds); if (TryPromoteFinalHardRuleCandidate(current, focusedTerminalClosureEdges, nodes, out var focusedTerminalPromoted)) { current = focusedTerminalPromoted; continue; } var exactRestabilizedEdges = BuildFinalRestabilizedCandidate( current.Edges, nodes, direction, minLineClearance, batchedFocusEdgeIds); if (TryPromoteFinalHardRuleCandidate(current, exactRestabilizedEdges, nodes, out var exactRestabilizedPromoted)) { current = exactRestabilizedPromoted; continue; } var quickBatchedScore = ElkEdgeRoutingScoring.ComputeScore(quickBatchedCandidateEdges, nodes); var quickBatchedRetryState = BuildRetryState( quickBatchedScore, HighwayProcessingEnabled ? ElkEdgeRouterHighway.DetectRemainingBrokenHighways(quickBatchedCandidateEdges, nodes).Count : 0); var changedFocusEdgeIds = batchedFocusEdgeIds .Where(edgeId => HasEdgeGeometryChanged(current.Edges, quickBatchedCandidateEdges, edgeId)) .ToArray(); ElkLayoutDiagnostics.LogProgress( $"Winner post-slot hard-rule round {round + 1} fast-terminal made no promotion: candidate={DescribeRetryState(quickBatchedRetryState)} changed=[{string.Join(", ", changedFocusEdgeIds)}]"); } else { ElkLayoutDiagnostics.LogProgress( $"Winner post-slot hard-rule round {round + 1} full-restabilize focus=[{string.Join(", ", batchedFocusEdgeIds)}]"); var batchedCandidateEdges = BuildFinalRestabilizedCandidate( current.Edges, nodes, direction, minLineClearance, batchedFocusEdgeIds); if (TryPromoteFinalHardRuleCandidate(current, batchedCandidateEdges, nodes, out var batchedPromoted)) { current = batchedPromoted; continue; } } } var orderedSeverityEdgeIds = severityByEdgeId .OrderByDescending(pair => pair.Value) .ThenBy(pair => pair.Key, StringComparer.Ordinal) .Select(pair => pair.Key) .ToArray(); var improved = false; if (preferFastTerminalOnly) { var candidateEdgeIds = orderedSeverityEdgeIds .Take(MaxWinnerPolishFastTerminalSingleEdgeCandidates) .ToArray(); ElkLayoutDiagnostics.LogProgress( $"Winner post-slot hard-rule round {round + 1} evaluating {candidateEdgeIds.Length}/{orderedSeverityEdgeIds.Length} fast-terminal single-edge candidates in parallel"); if (TryPromoteFastTerminalCandidates( current, nodes, direction, minLineClearance, candidateEdgeIds, out var parallelPromoted)) { current = parallelPromoted; improved = true; } } else { foreach (var edgeId in orderedSeverityEdgeIds) { var focusEdgeIds = ExpandWinningSolutionFocus(current.Edges, [edgeId]).ToArray(); if (focusEdgeIds.Length == 0) { continue; } var candidateEdges = BuildFinalRestabilizedCandidate( current.Edges, nodes, direction, minLineClearance, focusEdgeIds); if (!TryPromoteFinalHardRuleCandidate(current, candidateEdges, nodes, out var promoted)) { continue; } current = promoted; improved = true; break; } } if (!improved) { break; } } return current; } }