package routing

import (
	
	
	

	
	
	
)

const channelEpsilon = 1e-6

// This optional postpass admits only small channel systems. Quadratic
// discovery must not consume a successful layout's routing budget.
const maxChannelSegments = 256

func ( context.Context,  *layoutgraph.Graph) error {
	 := nudgeChannelsWithLimit(, , maxRouteStageWorkUnits)
	if errors.Is(, errRouteStageWorkLimit) {
		return nil
	}
	return 
}

type channelGroup struct {
	segments               []*layoutgraph.EdgeSegment
	position, lower, upper float64
	fixed                  bool
	nodeClearance          *Range
}

type channelArc struct {
	from, to int
	// Legs of a single route may meet to remove an unnecessary bend.
	separate bool
}

type channelProblem struct {
	groups []*channelGroup
	arcs   []channelArc
}

// nudgeChannelsGuarded uses WueOrtho's separation-constraint idea with fixed
// boxes and ports: https://arxiv.org/html/2309.01671v2#S2.SS0.SSS0.Px7.
// This bounded special case solves gap feasibility on a DAG, not the paper's
// complete LP. It retains the existing drawing unless one of its feasible
// extremal or midpoint solutions improves wire length or spacing.
func nudgeChannelsGuarded( *layoutgraph.Graph,  *routeWorkGuard) error {
	for ,  := range []bool{true, false} {
		,  := buildChannelProblem(, , )
		if  != nil {
			return 
		}
		if len(.groups) == 0 {
			continue
		}
		,  := captureRouteMutations(, nil, )
		if  != nil {
			return 
		}
		,  := channelInventoryIsClosed(, , )
		if  != nil {
			return 
		}
		if ! {
			return nil
		}
		,  := copyRoutePoints(.Edges, )
		if  != nil {
			return 
		}
		,  := quality.Inspect(.ctx, )
		if  != nil {
			return 
		}
		,  := applyChannelProblem(, , , )
		if  != nil {
			return 
		}
		if ! {
			continue
		}
		,  := quality.Inspect(.ctx, )
		if  != nil {
			return 
		}
		if .RouteObstructions > .RouteObstructions ||
			.Crossings > .Crossings || .TextOcclusions > .TextOcclusions ||
			.RouteLength > .RouteLength+channelEpsilon {
			.restore()
			continue
		}
		for ,  := range .Edges {
			 := false
			for ,  := range .Points {
				if * != *[][] {
					 = true
					break
				}
			}
			if ! {
				continue
			}
			,  := removeDuplicatePointsGuarded(.Points, )
			if  != nil {
				return 
			}
			.Points = 
		}
	}
	return nil
}

// Route-stage snapshots can reach owners outside the graph's direct slices.
// Such owners are not obstacles in this bounded model, so do not risk moving
// an uninspected route or node through an aliased point.
func channelInventoryIsClosed( *layoutgraph.Graph,  routeMutationSnapshot,  *routeWorkGuard) (bool, error) {
	 := make(map[*layoutgraph.Node]bool, len(.Nodes))
	 := make(map[*layoutgraph.Edge]bool, len(.Edges))
	for ,  := range .Nodes {
		if  := .step();  != nil {
			return false, 
		}
		[] = true
	}
	for ,  := range .Edges {
		if  := .step();  != nil {
			return false, 
		}
		[] = true
	}
	for  := range .nodes {
		if  := .step();  != nil {
			return false, 
		}
		if ![] {
			return false, nil
		}
	}
	for  := range .edges {
		if  := .step();  != nil {
			return false, 
		}
		if ![] {
			return false, nil
		}
	}
	return true, nil
}

func nudgeChannelsWithLimit( context.Context,  *layoutgraph.Graph,  uint64) error {
	if  != nil &&  != nil {
		if  := .Err();  != nil {
			return 
		}
		if channelInputTooLarge() {
			// Admission scans at most maxChannelSegments edge references. Check
			// cancellation again before returning without the atomic stage.
			return .Err()
		}
	}
	return runAtomicRouteStage(, "NudgeChannels", , nil, , func( *routeWorkGuard) error {
		return nudgeChannelsGuarded(, )
	})
}

// Oversized inputs are an optional no-op, so reject them before topology
// validation or rollback snapshots allocate proportional to the whole graph.
// This is only admission, not validation: nil inputs and malformed admitted
// graphs still go through the existing atomic-stage validation boundary.
func channelInputTooLarge( *layoutgraph.Graph) bool {
	if  == nil {
		return false
	}
	if len(.Nodes) > maxChannelSegments || len(.Edges) > maxChannelSegments {
		return true
	}
	 := maxChannelSegments
	for ,  := range .Edges {
		if  == nil {
			continue
		}
		if len(.Points) >  {
			return true
		}
		 -= len(.Points)
	}
	return false
}

func channelCoordinate( *geo.Point,  bool) float64 {
	if  {
		return .X
	}
	return .Y
}

func buildChannelProblem( *layoutgraph.Graph,  bool,  *routeWorkGuard) (channelProblem, error) {
	var  channelProblem
	,  := edgeSegmentsGuarded(layoutgraph.Edges(.Edges), !, )
	if  != nil {
		return , 
	}
	 := make(map[*geo.Point]bool)
	for ,  := range .Nodes {
		[.TopLeft] = true
	}
	 := make(map[*layoutgraph.Edge]bool)
	for ,  := range .Edges {
		if  := .step();  != nil {
			return , 
		}
		if len(.Points) < 2 {
			continue
		}
		[.Points[0]], [.Points[len(.Points)-1]] = true, true
		,  := .NodeToTree[.From]
		,  := .NodeToTree[.To]
		[] = .IsCurve || .IsLoop() ||  ||  ||
			(isSpecialEdgeForBalancing(, ) && !hasFixedBalancingPorts())
		for  := 1;  < len(.Points); ++ {
			if  := .step();  != nil {
				return , 
			}
			,  := .Points[-1], .Points[]
			if (.X != .X && .Y != .Y) || * == * {
				[] = true
			}
		}
	}
	 := make([]int, len())
	for  := range  {
		[] = 
	}
	var  func(int) int
	 = func( int) int {
		for [] !=  {
			[] = [[]]
			 = []
		}
		return 
	}
	for ,  := range  {
		for  := 0;  < ; ++ {
			if  := .step();  != nil {
				return , 
			}
			 := []
			if channelCoordinate(.Start, ) == channelCoordinate(.Start, ) &&
				.Segment.Overlaps(.Segment, , segmentSpacingBuffer) {
				[()] = ()
			}
		}
	}
	 := make(map[int]*channelGroup)
	var  []*channelGroup
	for ,  := range  {
		if  := .step();  != nil {
			return , 
		}
		 := [()]
		if  == nil {
			 = &channelGroup{position: channelCoordinate(.Start, )}
			[()] = 
			 = append(, )
		}
		.segments = append(.segments, )
		.fixed = .fixed || [.Owner()] || [.Start] || [.End]
		for ,  := range .Nodes {
			if  := .step();  != nil {
				return , 
			}
			if .Width != 1 || .Height != 1 {
				continue
			}
			for ,  := range []*geo.Point{.Start, .End} {
				if (.X == .TopLeft.X || .X == .TopLeft.X+1) && (.Y == .TopLeft.Y || .Y == .TopLeft.Y+1) {
					.fixed = true
				}
			}
		}
	}
	,  := nodeSegmentsGuarded(layoutgraph.Nodes(.Nodes), !, )
	if  != nil {
		return , 
	}
	 := 
	for ,  := range  {
		if .fixed {
			continue
		}
		 := &Range{start: math.Inf(-1), end: math.Inf(1)}
		for ,  := range .segments {
			, ,  := routeSegmentBounds(.Segment, , segmentSpacingBuffer, )
			if  != nil {
				return , 
			}
			// A narrow gap elsewhere in this component must not let wire
			// shortening pull a route against a previously comfortable border.
			// Preserve short existing gaps; larger ones may contract to the
			// ordinary spacing distance. These are absolute bounds, separate
			// from the shared gap variable, so clearance is not counted twice.
			if !math.IsInf(, -1) {
				.start = math.Max(.start, +math.Min(.position-, segmentSpacingBuffer))
			}
			if !math.IsInf(, 1) {
				.end = math.Min(.end, -math.Min(-.position, segmentSpacingBuffer))
			}
		}
		.nodeClearance = 
	}
	 := func( *channelGroup) {
		for ,  := range .segments {
			 = append(, &.Segment)
		}
	}
	for ,  := range  {
		if .fixed {
			()
		}
	}
	// A real wall must bound both sides. An arbitrary margin outside the
	// drawing would turn repeated nudging into outward drift.
	for  := 0;  < 2; ++ {
		for ,  := range  {
			if .fixed {
				continue
			}
			.lower, .upper = math.Inf(-1), math.Inf(1)
			for ,  := range .segments {
				, ,  := routeSegmentBounds(.Segment, , segmentSpacingBuffer, )
				if  != nil {
					return , 
				}
				.lower, .upper = math.Max(.lower, ), math.Min(.upper, )
			}
			if math.IsInf(.lower, 0) || math.IsInf(.upper, 0) || .lower > .position || .upper < .position || .lower >= .upper {
				.fixed = true
				()
			}
		}
	}
	for ,  := range  {
		if !.fixed {
			.groups = append(.groups, )
		}
	}
	if  := stableSortRouteValues(.groups, func(,  *channelGroup) bool { return .position < .position }, );  != nil {
		return , 
	}
	for ,  := range .groups {
		for  :=  + 1;  < len(.groups); ++ {
			 := .groups[]
			,  := false, true
			 := .segments[0].Owner()
			for ,  := range .segments {
				 =  && .Owner() == 
			}
			for ,  := range .segments {
				 =  && .Owner() == 
			}
			 := segmentSpacingBuffer
			if  {
				 = 0
			}
			for ,  := range .segments {
				for ,  := range .segments {
					if  := .step();  != nil {
						return , 
					}
					 =  || .Segment.Overlaps(.Segment, , )
				}
			}
			if  {
				.arcs = append(.arcs, channelArc{, , !})
			}
		}
	}
	return , nil
}

// solveChannelGap computes the earliest and latest feasible solutions of the
// same difference constraints. Their midpoint is feasible by convexity.
func solveChannelGap( channelProblem,  float64,  *routeWorkGuard) ([]float64, []float64, bool, error) {
	 := len(.groups)
	if  := .add(uint64() * 2);  != nil {
		return nil, nil, false, 
	}
	,  := make([]float64, ), make([]float64, )
	for ,  := range .groups {
		[], [] = .lower+, .upper-
		if .nodeClearance != nil {
			[] = math.Max([], .nodeClearance.start)
			[] = math.Min([], .nodeClearance.end)
		}
	}
	for ,  := range .arcs {
		if  := .step();  != nil {
			return nil, nil, false, 
		}
		 := 0.0
		if .separate {
			 = 
		}
		[.to] = math.Max([.to], [.from]+)
	}
	for  := len(.arcs) - 1;  >= 0; -- {
		 := .arcs[]
		if  := .step();  != nil {
			return nil, nil, false, 
		}
		 := 0.0
		if .separate {
			 = 
		}
		[.from] = math.Min([.from], [.to]-)
	}
	for  := range  {
		if [] > [] {
			return nil, nil, false, nil
		}
	}
	return , , true, nil
}

func channelGap( channelProblem,  []float64) float64 {
	 := math.Inf(1)
	for ,  := range .groups {
		 = math.Min(, math.Min([]-.lower, .upper-[]))
	}
	for ,  := range .arcs {
		if .separate {
			 = math.Min(, [.to]-[.from])
		}
	}
	return 
}

func applyChannelProblem( *layoutgraph.Graph,  channelProblem,  bool,  *routeWorkGuard) (bool, error) {
	 := make([]int, len(.groups))
	for  := range  {
		[] = 
	}
	var  func(int) int
	 = func( int) int {
		for [] !=  {
			[] = [[]]
			 = []
		}
		return 
	}
	for ,  := range .arcs {
		[(.to)] = (.from)
	}
	 := make(map[int][]int)
	var  []int
	for  := range .groups {
		 := ()
		if ,  := []; ! {
			 = append(, )
		}
		[] = append([], )
	}
	 := false
	for ,  := range  {
		var  channelProblem
		 := make(map[int]int)
		for ,  := range [] {
			[] = len(.groups)
			.groups = append(.groups, .groups[])
		}
		for ,  := range .arcs {
			if ,  := [.from];  {
				.arcs = append(.arcs, channelArc{, [.to], .separate})
			}
		}
		 := make([]float64, len(.groups))
		for ,  := range .groups {
			[] = .position
		}
		 := math.Max(0, channelGap(, ))
		,  := , math.Inf(1)
		for ,  := range .groups {
			 = math.Min(, (.upper-.lower)/2)
		}
		for  := 0;  < 32 && - > channelEpsilon; ++ {
			 := ( + ) / 2
			, , ,  := solveChannelGap(, , )
			if  != nil {
				return false, 
			}
			if  {
				 = 
			} else {
				 = 
			}
		}
		,  := channelWireLength(, nil, )
		if  != nil {
			return false, 
		}
		 := 
		var  map[*geo.Point]*geo.Point
		for ,  := range []float64{, } {
			, , ,  := solveChannelGap(, , )
			if  != nil {
				return false, 
			}
			if ! {
				continue
			}
			for ,  := range []float64{0, 1, 0.5} {
				 := make([]float64, len())
				for  := range  {
					[] = []*(1-) + []*
				}
				, ,  := channelPointProposal(, , , )
				if  != nil {
					return false, 
				}
				if ! {
					continue
				}
				,  := channelWireLength(, , )
				if  != nil {
					return false, 
				}
				 := channelGap(, )
				// Pixel-level rounding changes alone are not a spacing benefit.
				if  > +channelEpsilon || (math.Abs(-) <= channelEpsilon &&  < +1) {
					continue
				}
				,  := channelProposalSafe(, , )
				if  != nil {
					return false, 
				}
				if ! {
					continue
				}
				, ,  = , , 
			}
		}
		if  != nil {
			for ,  := range  {
				if  := .step();  != nil {
					return false, 
				}
				* = *
			}
			 = true
		}
	}
	return , nil
}

func channelPointProposal( channelProblem,  []float64,  bool,  *routeWorkGuard) (map[*geo.Point]*geo.Point, bool, error) {
	 := make(map[*geo.Point]*geo.Point)
	for ,  := range .groups {
		for ,  := range .segments {
			for ,  := range []*geo.Point{.Start, .End} {
				if  := .step();  != nil {
					return nil, false, 
				}
				 := *
				if  {
					.X = []
				} else {
					.Y = []
				}
				if ,  := [];  && * !=  {
					return nil, false, nil
				}
				[] = &
			}
		}
	}
	return , true, nil
}

func channelWireLength( *layoutgraph.Graph,  map[*geo.Point]*geo.Point,  *routeWorkGuard) (float64, error) {
	 := 0.0
	for ,  := range .Edges {
		for  := 1;  < len(.Points); ++ {
			if  := .step();  != nil {
				return 0, 
			}
			,  := .Points[-1], .Points[]
			if  := [];  != nil {
				 = 
			}
			if  := [];  != nil {
				 = 
			}
			 += math.Hypot(.X-.X, .Y-.Y)
		}
	}
	return , nil
}

func channelProposalSafe( *layoutgraph.Graph,  map[*geo.Point]*geo.Point,  *routeWorkGuard) (bool, error) {
	 := make([]*layoutgraph.Edge, len(.Edges))
	 := make([]bool, len(.Edges))
	for ,  := range .Edges {
		 := *
		.Points = append([]*geo.Point(nil), .Points...)
		for ,  := range .Points {
			if  := .step();  != nil {
				return false, 
			}
			if  := [];  != nil {
				.Points[] = 
				[] = [] || * != *
			}
		}
		[] = &
		if ![] {
			continue
		}
		if .IsCurve {
			return false, nil
		}
		,  := changedRouteIsClear(, &, .Points, true, )
		if  != nil || ! {
			return , 
		}
		,  := 0.0, 0.0
		var  []*geo.Point
		for ,  := range .Points {
			if  := .step();  != nil {
				return false, 
			}
			if math.IsNaN(.X) || math.IsNaN(.Y) || math.IsInf(.X, 0) || math.IsInf(.Y, 0) {
				return false, nil
			}
			if  > 0 {
				,  := .Points[-1], .Points[-1]
				 := math.Hypot(.Points[].X-.X, .Points[].Y-.Y)
				 := math.Hypot(.X-.X, .Y-.Y)
				 += 
				 += 
				if  > channelEpsilon && +channelEpsilon < math.Min(, segmentSpacingBuffer) {
					return false, nil
				}
			}
			if len() == 0 || *[len()-1] != * {
				 = append(, )
			}
		}
		if  > +channelEpsilon {
			return false, nil
		}
		for  := 2;  < len(); ++ {
			, ,  := [-2], [-1], []
			if (.X-.X)*(.X-.X)+(.Y-.Y)*(.Y-.Y) < 0 {
				return false, nil
			}
		}
		for  := 0; +1 < len(); ++ {
			for  :=  + 2; +1 < len(); ++ {
				if  := .step();  != nil {
					return false, 
				}
				if (geo.Segment{Start: [], End: [+1]}).Intersects(geo.Segment{Start: [], End: [+1]}) {
					return false, nil
				}
			}
		}
	}
	for ,  := range .Edges {
		for  :=  + 1;  < len(.Edges); ++ {
			if ![] && ![] {
				continue
			}
			 := .Edges[]
			if .IsCurve || .IsCurve {
				return false, nil
			}
			,  := 0, 0
			for  := 0; +1 < len(.Points); ++ {
				for  := 0; +1 < len(.Points); ++ {
					if  := .step();  != nil {
						return false, 
					}
					if isNonSharedCrossing(, , , ) {
						++
					}
					if isNonSharedCrossing([], [], , ) {
						++
					}
					 := balanceCollinearOverlap(.Points[], .Points[+1], .Points[], .Points[+1])
					 := balanceCollinearOverlap([].Points[], [].Points[+1], [].Points[], [].Points[+1])
					if  == 0 &&  > 0 {
						return false, nil
					}
					 := (geo.Segment{Start: .Points[], End: .Points[+1]}).Intersects(geo.Segment{Start: .Points[], End: .Points[+1]})
					 := (geo.Segment{Start: [].Points[], End: [].Points[+1]}).Intersects(geo.Segment{Start: [].Points[], End: [].Points[+1]})
					if ! &&  {
						return false, nil
					}
				}
			}
			if  >  {
				return false, nil
			}
		}
	}
	return true, nil
}