package routing

import (
	
	
	
	
	
	

	
	
	
	
	

	
	
)

// Map pools for reducing allocations in hot paths
var portMapPool = typedpool.New(func() map[geo.Point]bool {
	return make(map[geo.Point]bool, 16)
})

// borrowPortMap gets a map from the pool and clears it
func borrowPortMap() map[geo.Point]bool {
	 := portMapPool.Get()
	clear()
	return 
}

// returnPortMap returns a map to the pool
func returnPortMap( map[geo.Point]bool) {
	// Don't return oversized maps to pool to prevent memory leaks
	if len() < 128 {
		portMapPool.Put()
	}
}

/*
	Keeps the state for OVG edge search with data structure to quickly check if a given

edge is already in some route or if it intersects with other routes.
*/
type ovgEdgeRouter struct {
	flavor            RouteGenerationFlavor
	graph             *layoutgraph.Graph
	ovg               *OVG
	edges             []*layoutgraph.Edge
	routes            []*Route
	routedEdges       []*layoutgraph.Edge
	positionedLabels  []labeling.PositionedArrowheadLabel
	pointToRoute      map[geo.Point][]*Route
	turnCost          float64
	crossingCost      float64
	nonCenterPortCost float64
	edgeSet           *ovgEdgeSet
	hasNearbyEdge     map[*OVGEdge]struct{}
	overlappingRoutes map[*OVGEdge][]*Route
	fixedOverlaps     map[*layoutgraph.Node]struct{}
	fixedOverlapsSet  bool
	verticalHops      []*OVGNode
	horizontalHops    []*OVGNode
	nodeContext       []*searchNodeContext
	nodeContextValues []searchNodeContext
	searchQueue       priorityQueue
	// Usually, edge routing occurs before label placement. When the router is used
	// independently, labels are already placed and the edges should move out of the way.
	considerNodeLabels bool
	work               *routeSearchWorkGuard
}

func checkEdgeRoutingCanceled( context.Context) error {
	if  := .Err();  != nil {
		return fmt.Errorf("EdgeRouting: %w", )
	}
	return nil
}

func isContextTermination( error) bool {
	return errors.Is(, context.Canceled) || errors.Is(, context.DeadlineExceeded)
}

type searchNodeContext struct {
	verticalDistance, horizontalDistance float64
	verticalEntry, horizontalEntry       *priorityQueueEntry
}

func ( *ovgEdgeRouter) ( context.Context) error {
	if .work == nil {
		,  := newRouteSearchWorkGuard(, .flavor, maxRouteSearchWorkUnits)
		if  != nil {
			return 
		}
		.work = 
		return nil
	}
	return .work.bind()
}

func newOVGEdgeRouterWithWorkLimit(
	 context.Context,
	 RouteGenerationFlavor,
	 *OVG,
	 *layoutgraph.Graph,
	 []*Route,
	 []*layoutgraph.Edge,
	 uint64,
) ( *ovgEdgeRouter,  error) {
	,  := newRouteSearchWorkGuard(, , )
	if  != nil {
		return nil, 
	}
	defer func() {
		if  != nil {
			_ = .finish()
		}
	}()
	if  := .add(uint64(len()));  != nil {
		return nil, 
	}
	 := make([]*layoutgraph.Edge, len())
	copy(, )

	 := map[*layoutgraph.Node]struct{}{}
	for ,  := range .Clusters {
		if  := .step();  != nil {
			return nil, 
		}
		for ,  := range .Nodes {
			if  := .step();  != nil {
				return nil, 
			}
			[] = struct{}{}
		}
	}
	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		if  == nil || .From == nil || .To == nil {
			continue
		}
		if  := .reserveSum(uint64(len(.From.Edges)), uint64(len(.To.Edges)));  != nil {
			return nil, 
		}
	}
	if  := .reserveSort(len());  != nil {
		return nil, 
	}
	if  := .reserveSort(len());  != nil {
		return nil, 
	}
	sortEdges(, , )

	 := &ovgEdgeRouter{
		flavor:            ,
		graph:             ,
		ovg:               ,
		routes:            make([]*Route, 0),
		routedEdges:       make([]*layoutgraph.Edge, 0),
		positionedLabels:  make([]labeling.PositionedArrowheadLabel, 0),
		edges:             ,
		pointToRoute:      make(map[geo.Point][]*Route),
		turnCost:          .TurnCost(),
		crossingCost:      .CrossingCost(),
		nonCenterPortCost: .NonCenterPortCost(),
		edgeSet:           newOvgEdgeSet(),
		hasNearbyEdge:     make(map[*OVGEdge]struct{}),
		overlappingRoutes: make(map[*OVGEdge][]*Route),
		work:              ,
	}

	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		if  := .addRoute();  != nil {
			return nil, 
		}
	}

	if  := .check();  != nil {
		return nil, 
	}
	return , nil
}

func sortEdges( RouteGenerationFlavor,  []*layoutgraph.Edge,  map[*layoutgraph.Node]struct{}) {
	switch  {
	case ShortestToLongest:
		slices.SortStableFunc(, func(,  *layoutgraph.Edge) int {
			 := .EuclideanDistance()
			 := .EuclideanDistance()
			switch {
			case  < :
				return -1
			case  < :
				return 1
			default:
				return 0
			}
		})
	case LongestToShortest:
		slices.SortStableFunc(, func(,  *layoutgraph.Edge) int {
			 := .EuclideanDistance()
			 := .EuclideanDistance()
			switch {
			case  > :
				return -1
			case  > :
				return 1
			default:
				return 0
			}
		})
	case TopDownLeftRight:
		slices.SortStableFunc(, func(,  *layoutgraph.Edge) int {
			// Ensure that edge.From is above edge.To for both edges.
			 := .From
			 := .To
			if .TopLeft.Y > .TopLeft.Y {
				,  = , 
			}
			 := .From
			 := .To
			if .TopLeft.Y > .TopLeft.Y {
				,  = , 
			}

			if .TopLeft.Y != .TopLeft.Y {
				switch {
				case .TopLeft.Y < .TopLeft.Y:
					return -1
				case .TopLeft.Y < .TopLeft.Y:
					return 1
				default:
					return 0
				}
			}
			// a and b start in the same row.
			if  !=  {
				switch {
				case .TopLeft.X < .TopLeft.X:
					return -1
				case .TopLeft.X < .TopLeft.X:
					return 1
				default:
					return 0
				}
			}
			// Same from node, maybe different targets.
			if .TopLeft.Y == .TopLeft.Y {
				switch {
				case .TopLeft.X < .TopLeft.X:
					return -1
				case .TopLeft.X < .TopLeft.X:
					return 1
				default:
					return 0
				}
			}
			// Return the top-most target.
			switch {
			case .TopLeft.Y < .TopLeft.Y:
				return -1
			case .TopLeft.Y < .TopLeft.Y:
				return 1
			default:
				return 0
			}
		})
	}

	// Clusters go first, looks best
	slices.SortStableFunc(, func(,  *layoutgraph.Edge) int {
		 := false
		 := false

		if ,  := [.From];  {
			 = true
		} else if ,  := [.To];  {
			 = true
		}
		if ,  := [.From];  {
			 = true
		} else if ,  := [.To];  {
			 = true
		}

		switch {
		case  && !:
			return -1
		case ! && :
			return 1
		default:
			return 0
		}
	})
}

func ( *ovgEdgeRouter) ( context.Context,  *layoutgraph.Edge) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	, , ,  := routeLineChecked(
		.graph,
		,
		.routedEdges,
		.routes,
		.work.step,
		.work)

	if  != nil {
		if errors.Is(, layoutgraph.ErrInvalidCandidate) {
			 := .ovg.Centers[.From]
			 := .ovg.Centers[.To]
			var ,  *geo.Point
			 := geo.NewSegment(.Point, .Point)
			if  := .From.Intersections(*); len() > 0 {
				 = [0]
				.Start = 
			}
			if  := .To.Intersections(*); len() > 0 {
				 = [0]
			}
			// These only happen when there's overlap. It'll look terrible regardless, so just make it work
			if  == nil {
				 = .Point
			}
			if  == nil {
				 = .Point
			}
			 := []*OVGNode{, NewOVGNode(), NewOVGNode(), }
			return , geo.EuclideanDistance(.X, .Y, .X, .Y), nil
		}
		return nil, 0, 
	}
	 := []*OVGNode{
		.ovg.Centers[.From],
		.ovg.OccupiedPoints[*],
		.ovg.OccupiedPoints[*],
		.ovg.Centers[.To],
	}
	return , , nil
}

func ( *ovgEdgeRouter) ( context.Context,  bool) ( GenerateRouteResponse) {
	.Flavor = .flavor
	if  := .bindWork();  != nil {
		.Err = 
		if .work != nil {
			_ = .work.finish()
		}
		return 
	}
	.work = .work
	defer func() {
		if  := .work.finish(); .Err == nil &&  != nil {
			.Err = 
			.Routes = nil
		}
	}()

	 := 0.0
	for ,  := range .edges {
		if  := .work.step();  != nil {
			.Err = 
			return 
		}
		var  float64
		var  []*OVGNode
		var  error

		, ,  = .slingshot(, )
		if  != nil {
			if  {
				, ,  = .routeLine(, )
			}
			if  != nil {
				.Err = 
				return 
			}
		}
		if len() <= 1 {
			, ,  = .search(, )
			if isContextTermination() {
				.Err = 
				return 
			}
			if  != nil {
				if  {
					, ,  = .routeLine(, )
				}
				if  != nil {
					.Err = 
					return 
				}
			}
			if len() <= 1 {
				 := fmt.Sprintf("Route '%s' has size %v", .DebugID(), len())
				.Err = invariant.New()
				return 
			}
		}

		 += 
		 = .addRoute(&Route{
			GEdge:    ,
			OVGNodes: ,
			FromPort: *[1].Point,
			ToPort:   *[len()-2].Point,
		})
		if  != nil {
			.Err = 
			return 
		}
	}

	.Routes = .routes
	.Distance = 
	return 
}

// Among center ports, slightly favor going through center ports where the mirrored center port is occupied
// So if a node has a route going through its left center port, another route going through the right center port looks nicely symmetrical
func ( *ovgEdgeRouter) ( context.Context, ,  *layoutgraph.Node, ,  map[geo.Point]bool) (map[*OVGNode]struct{}, error) {
	 := make(map[*OVGNode]struct{})
	if  := .work.reserveSum(uint64(len(.ovg.Ports[])), uint64(len(.ovg.Ports[])));  != nil {
		return nil, 
	}
	 := .MirroredPorts()
	 := .MirroredPorts()
	for ,  := range .ovg.Ports[] {
		if  := .work.step();  != nil {
			return nil, 
		}
		// If this is a port number that has a mirror
		if ,  := [*.Point];  {
			// If the mirrored port is occupied, making this one desirable
			if ,  := [];  {
				[] = struct{}{}
			}
		}
	}
	// Do the same thing for target ports
	for ,  := range .ovg.Ports[] {
		if  := .work.step();  != nil {
			return nil, 
		}
		if ,  := [*.Point];  {
			if ,  := [];  {
				[] = struct{}{}
			}
		}
	}

	if  := checkEdgeRoutingCanceled();  != nil {
		return nil, 
	}
	return , nil
}

func ( *ovgEdgeRouter) (
	 context.Context,
	,  *layoutgraph.Node,
	,  *OVGNode,
	,  map[*layoutgraph.Node]bool,
	 bool,
) (map[geo.Point]bool, map[geo.Point]bool, map[geo.Point]bool, map[geo.Point]bool, map[geo.Point]bool, map[geo.Point]bool, error) {
	 := make(map[geo.Point]bool)
	 := make(map[geo.Point]bool)
	 := make(map[geo.Point]bool)
	 := make(map[geo.Point]bool)
	 := make(map[geo.Point]bool)
	 := make(map[geo.Point]bool)

	for ,  := range []*OVGNode{, } {
		if  := .work.step();  != nil {
			return nil, nil, nil, nil, nil, nil, 
		}
		// we might process the same route, but since it's just assigning to maps, it's fine
		for ,  := range .pointToRoute[*.Point] {
			if  := .work.step();  != nil {
				return nil, nil, nil, nil, nil, nil, 
			}
			if (.GEdge.From != ) && (.GEdge.To != ) && (.GEdge.From != ) && (.GEdge.To != ) {
				continue
			}

			// note ports of duplicate connections
			if (.GEdge.From == ) && (.GEdge.To == ) {
				[.FromPort] = true
				[.ToPort] = true
			}

			// If we're going to or coming from a cluster, then we penalize going through port nodes which
			// are not shared by other nodes of the cluster, which has the effect of making cluster nodes share
			// routes
			// ClusterPortSharing Step 1.
			// we want the same port if a cluster route has the same source or target so record it here
			if ! {
				if .GEdge.To ==  {
					if ,  := [.GEdge.From];  {
						[.ToPort] = true
					}
				}
				if .GEdge.From ==  {
					if ,  := [.GEdge.To];  {
						[.FromPort] = true
					}
				}
			}

			for ,  := range .OVGNodes {
				if  := .work.step();  != nil {
					return nil, nil, nil, nil, nil, nil, 
				}
				for ,  := range .ovg.Ports[] {
					if  := .work.step();  != nil {
						return nil, nil, nil, nil, nil, nil, 
					}
					if  ==  {
						[*.Point] = true
						break
					}
				}
				for ,  := range .ovg.Ports[] {
					if  := .work.step();  != nil {
						return nil, nil, nil, nil, nil, nil, 
					}
					if  ==  {
						[*.Point] = true
						break
					}
				}
			}
		}
	}
	if  := checkEdgeRoutingCanceled();  != nil {
		return nil, nil, nil, nil, nil, nil, 
	}
	return , , , , , , nil
}

// Most optimizations in the search function are related to reducing allocations by:
// 1. Reusing temporary slices for occupiedEdges, overlappingEdges, and nodeLabelBoxes
// 2. Using preallocated maps for fromFacingPorts, toFacingPorts, blockedSourcePorts, and blockedTargetPorts
// 3. Reusing routePoints when creating positioned arrowhead labels
// 4. Using explicit nil checks to avoid unnecessary allocations
// 5. Reducing heap allocations by reusing existing data structures
//
// These optimizations significantly reduce the number of allocations and memory usage
// in the hot path of the edge routing algorithm.

// Basically Dijkstra's shortest path with some other checks for overlap and alternative routes
func ( *ovgEdgeRouter) ( context.Context,  *layoutgraph.Edge) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	 := .ovg
	 := .graph
	 := .From
	 := .To
	 := .Centers[]
	 := .Centers[]
	var  [2]*geo.Point

	// Preallocate needed data structures with appropriate capacity
	 := len(.ovg.Nodes)
	// Account zeroing the three node-indexed arrays before allocating them.
	if  := .work.reserveProduct(uint64(), 3);  != nil {
		return nil, 0, 
	}

	// Get maps from pool to reduce allocations
	 := borrowPortMap()
	defer returnPortMap()
	 := borrowPortMap()
	defer returnPortMap()

	// Arrays for tracking state during path finding
	if cap(.verticalHops) <  {
		.verticalHops = make([]*OVGNode, )
	} else {
		.verticalHops = .verticalHops[:]
		clear(.verticalHops)
	}
	if cap(.horizontalHops) <  {
		.horizontalHops = make([]*OVGNode, )
	} else {
		.horizontalHops = .horizontalHops[:]
		clear(.horizontalHops)
	}
	if cap(.nodeContext) <  {
		.nodeContext = make([]*searchNodeContext, )
	} else {
		.nodeContext = .nodeContext[:]
		clear(.nodeContext)
	}
	if cap(.nodeContextValues) <  {
		.nodeContextValues = make([]searchNodeContext, )
	} else {
		.nodeContextValues = .nodeContextValues[:]
	}
	 := .verticalHops
	 := .horizontalHops
	 := .nodeContext

	// Pre-allocate slices for temp storage with reasonable capacity
	 := make([]*layoutgraph.Edge, 0, 32)
	 := make([]*layoutgraph.Edge, 0, 32)
	 := make([]*layoutgraph.Node, 0, 32)

	// Fill nodeLabelBoxes if we need to consider node labels
	if .considerNodeLabels {
		for ,  := range .Nodes {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			if  ==  ||  ==  {
				continue
			}
			if .Label != nil && .Label.Text != "" {
				 := &layoutgraph.Node{
					Box: geo.Box{
						TopLeft: .LabelTopLeft(.Label.Position, .Label.Width, .Label.Height),
						Width:   .Label.Width,
						Height:  .Label.Height,
					},
					D2ID:  new(.Label.Text),
					Graph: ,
				}
				.SetShape(shape.SQUARE_TYPE)
				 = append(, )
			}
		}
	}

	 := false
	// That's fine, could be overlapped
	 := .Orientation()
	if  == geo.NONE {
		 = true
	}
	 := .Orientation()
	if  == geo.NONE {
		 = true
	}

	, ,  := .quickRoute(, , , )
	if  != nil {
		return nil, 0, 
	}
	if  != nil {
		return , , nil
	}

	var  layoutgraph.Nodes
	if .SourceArrowheadLabel != nil || .TargetArrowheadLabel != nil {
		,  = filterEdgeAncestorsGuarded(, .graph.Nodes, .work)
		if  != nil {
			return nil, 0, 
		}
	}

	 := .Container
	 := .Container
	,  := routeSearchIsDescendantOf(.work, , )
	if  != nil {
		return nil, 0, 
	}
	,  := routeSearchIsDescendantOf(.work, , )
	if  != nil {
		return nil, 0, 
	}

	// Fast access to find if a node is a cluster node (cluster nodes sharing routes is nice)
	, ,  := sourceAndTargetClusterNodesGuarded(, , , .work)
	if  != nil {
		return nil, 0, 
	}
	 := len() > 0
	 := len() > 0

	 := .isUndesirableClusterArrangement(, )

	// These ports are populated source/target is a cluster, and the orientation is nonambiguous (e.g. not diagonal)
	// When such a case arises, we want to prefer edges that make the connections face-to-face

	var ,  bool
	 := false
	 := 0.0
	if ( || ) && ! {
		// use facing ports for properly arranged clusters
		 = true
		 = .turnCost * 2
		 = true
		 = true
		for ,  := range .PortsByOrientation(.GetOpposite()) {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			[] = true
		}
		for ,  := range .PortsByOrientation(.GetOpposite()) {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			[] = true
		}
	}

	var  float64 = basicallyInfinity
	 := borrowPortMap()
	defer returnPortMap()
	 := borrowPortMap()
	defer returnPortMap()

	if !.HasTableColumn() {
		if  := .work.reserveProduct(uint64(len(.ovg.Ports[])), uint64(len(.ovg.Ports[])));  != nil {
			return nil, 0, 
		}
		// Copy overlapping ports into our pooled maps
		 := .OverlappingPorts()
		maps.Copy(, )
		 := .OverlappingPorts()
		maps.Copy(, )
	} else {
		, ,  := .tablePorts(, , , false)
		if  != nil {
			return nil, 0, 
		}
		if .IsBetweenTableColumns() {
			 = basicallyInfinity / 10
			 = true
			 = true
			for ,  := range .ovg.Ports[] {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if ,  := []; ! {
					[*.Point] = true
				}
			}
			for ,  := range .ovg.Ports[] {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if ,  := []; ! {
					[*.Point] = true
				}
			}
			for ,  := range .Edges {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				[.Adjacent()] = true
			}
			for ,  := range .Edges {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				[.Adjacent()] = true
			}
		} else if .FromTableColumnIndex != nil {
			 = true
			for ,  := range .ovg.Ports[] {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if ,  := []; ! {
					[*.Point] = true
				}
			}
			for ,  := range .Edges {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				[.Adjacent()] = true
			}
		} else if .ToTableColumnIndex != nil {
			 = true
			for ,  := range .ovg.Ports[] {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if ,  := []; ! {
					[*.Point] = true
				}
			}
			for ,  := range .Edges {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				[.Adjacent()] = true
			}
		}
	}

	 := idealTurnAxes(, )

	, , , , , ,  := .usedPorts(
		, , , , , , , ,
	)
	if  != nil {
		return nil, 0, 
	}
	,  := .centerSymmetricalPorts(, , , , )
	if  != nil {
		return nil, 0, 
	}

	if !.fixedOverlapsSet {
		.fixedOverlaps,  = fixedOverlapsForRoute(layoutgraph.Nodes(.Nodes), .work)
		if  != nil {
			return nil, 0, 
		}
		.fixedOverlapsSet = true
	}
	 := .fixedOverlaps

	// Create the initial source node context
	 := &.nodeContextValues[.Index]
	* = searchNodeContext{
		verticalDistance:   math.Inf(1),
		horizontalDistance: math.Inf(1),
	}
	[.Index] = 

	// Priority queue storage is reusable because a flavor routes its edges
	// sequentially.
	.searchQueue.reset()
	 := &.searchQueue

	// Each node can be visited twice -- once from each direction. Seed the
	// vertical state first to make the equal-cost source preference explicit.
	if ,  := .push(0, , false, .work);  != nil {
		return nil, 0, 
	}
	if ,  := .push(0, , true, .work);  != nil {
		return nil, 0, 
	}
	// We've already pre-allocated these slices above

	for !.empty() {
		if  := .work.step();  != nil {
			return nil, 0, 
		}
		// Get the least distance node, regardless of direction
		,  := .pop(.work)
		if  != nil {
			if errors.Is(, errRouteSearchWorkLimit) || isContextTermination() {
				return nil, 0, 
			}
			return nil, 0, invariant.New(
				fmt.Sprintf(
					"Path not found for '%s'. Error %v",
					.DebugID(),
					,
				),
			)
		}
		 := .isHorizontal
		 := .node
		 := .priority

		if  ==  {
			,  := .bestRoute(, , , , , , , )
			if  != nil {
				return nil, 0, 
			}
			return , , nil
		}

		var  *OVGNode
		if  {
			 = [.Index]
		} else {
			 = [.Index]
		}

		// Reset occupiedEdges array for reuse
		 = [:0]
		if ( != ) && ( != ) {
			 := .pointToRoute[*.Point]
			for ,  := range  {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				 = append(, .GEdge)
			}
		}

		 := len() > 0
		,  := edgeCanOverlapEdgesGuarded(, , , , .work)
		if  != nil {
			return nil, 0, 
		}

		for ,  := range .Edges {
			if  := .work.step();  != nil {
				return nil, 0, 
			}
			 := .Adjacent()
			if  ==  {
				continue
			}

			// Prevent exiting source port node at weird angles
			if ,  := .portMetadataFor();  {
				 := .directions.any(func( geo.Orientation) bool {
					// When overlapped, the requirements are relaxed a little.
					if  {
						switch  {
						case geo.Top, geo.Bottom:
							return .Point.Y != .Point.Y
						case geo.Right, geo.Left:
							return .Point.X != .Point.X
						default:
							return true
						}
					}

					switch  {
					case geo.Top:
						return .Point.Y < .Point.Y
					case geo.Bottom:
						return .Point.Y > .Point.Y
					case geo.Right:
						return .Point.X > .Point.X
					case geo.Left:
						return .Point.X < .Point.X
					default:
						return true
					}
				})
				if ! {
					continue
				}
			}

			if ,  := .portMetadataFor();  {
				if ,  := [*.Point];  {
					continue
				}

				// Prevent approaching target port node from weird angles. Coincident
				// rounded ports can represent several sides of the same node, so one
				// valid approach is enough.
				 := .directions.any(func( geo.Orientation) bool {
					if  {
						switch  {
						case geo.Top, geo.Bottom:
							return .Point.Y != .Point.Y
						case geo.Right, geo.Left:
							return .Point.X != .Point.X
						default:
							return true
						}
					}

					switch  {
					case geo.Top:
						return .Point.Y > .Point.Y
					case geo.Bottom:
						return .Point.Y < .Point.Y
					case geo.Right:
						return .Point.X < .Point.X
					case geo.Left:
						return .Point.X > .Point.X
					default:
						return true
					}
				})
				if ! {
					continue
				}
			}

			if  !=  &&  !=  &&
				!.isPortOf() &&
				!.isPortOf() {
				// Cannot go through a container unless the target or source is a descendant of that container (or is that container)
				if .Container != nil {
					,  := routeSearchIsDescendantOf(.work, , .Container)
					if  != nil {
						return nil, 0, 
					}
					,  := routeSearchIsDescendantOf(.work, , .Container)
					if  != nil {
						return nil, 0, 
					}
					if ! && ! {
						if ,  := [.Container]; ! {
							continue
						}
					}
				}
			}

			// only use adjacent nodes that are vertically or horizontally aligned with leastDistanceNode (no diagonals), unless using source/target center
			if ( != ) && ( != ) {
				if (.Point.X != .Point.X) && (.Point.Y != .Point.Y) {
					continue
				}
			}

			if .isPortOf() {
				if ,  := [*.Point];  {
					continue
				}
			}

			var  float64
			if  ==  {
				 = 1
				// To avoid overlapping duplicate edges, avoid using the same source port as a duplicate edge
				if ! && [*.Point] {
					 = basicallyInfinity
				}
				// ClusterPortSharing Step 2a.
				// penalize not using the same source port if another node in the cluster already connects to that source
				if len() > 0 {
					if ,  := [*.Point]; ! {
						// If it takes more than 2 turns to get there, not worth sharing
						 += (2 * .turnCost)
					}
				}
				if ,  := [*.Point]; ! &&  {
					 += 
				}

				// Looks nicer when arrows come out of center ports
				if !.isCenterPortOf() {
					 += .nonCenterPortCost
				} else {
					// Looks even slightly nicer when it comes out a port that has an edge going through the symmetrical port
					if ,  := []; ! {
						// Only prefer it all else being equal
						 += 1
					}
				}
			} else {
				 = .Distance
			}

			// Cannot go outside a container if both the source and target are inside that container
			// but for nested going to parent, there may not be enough paths otherwise, so we allow that
			if  != nil &&  ==  && !( || ) {
				if .Container == nil || .Container !=  {
					// Note: penalty is if adjacent is outside the source container, and the edge from least distance to adjacent enters a different container
					if .Container != .Container {
						 += .turnCost * 4
					}
				}
			}

			// Penalize routing between ports of the same node
			if .sharesPortOwner() {
				 += .turnCost * 4
			}

			// APPLY SPECIAL WEIGHTS --------------
			if  ==  {
				 = 1
				// To avoid overlapping duplicate edges, don't use the same target port as a duplicate edge
				if ! && [*.Point] {
					 = basicallyInfinity
				}
				// ClusterPortSharing Step 2b.
				// penalize not using the same target port if another node in the cluster already connects to that target
				if len() > 0 {
					if ,  := [*.Point]; ! {
						// If it takes more than 2 turns to get there, not worth sharing
						 += (2 * .turnCost)
					}
				}
				if ,  := [*.Point]; ! &&  {
					 += 
				}

				if .isPortOf() {
					if !.isCenterPortOf() {
						 += .nonCenterPortCost
					} else {
						if ,  := []; ! {
							 += 1
						}
					}
				}
			} else if .IsNodeCenter {
				 = basicallyInfinity
			} else {
				// Reset overlappingEdges array for reuse
				 = [:0]
				 := false

				if ( != ) && ( != ) {
					 := .overlappingRoutes[]
					for ,  := range  {
						if  := .work.reserveProduct(uint64(len(.OVGNodes))+1, 2);  != nil {
							return nil, 0, 
						}
						 = append(, .GEdge)
						if .IsDirected() && .isOpposingColinear(, ) {
							 = true
							break
						}
						if .IsTunnel {
							// A tunnel route cannot overlap an entirely other route
							if .isEntireColinear(, ) || .isEntireColinear(, ) {
								 = true
								break
							}
						}
					}
				}

				if ! {
					,  := edgeCanOverlapEdgesGuarded(, , , , .work)
					if  != nil {
						return nil, 0, 
					}
					 = len() > 0 && !
				}

				 := false
				if ! && ( != ) && ( != ) {
					 = len(.pointToRoute[*.Point]) > 0
				}

				if ! && ! && ! && ( != ) {
					if ,  := .hasNearbyEdge[];  {
						 = true
					}
				}

				if  {
					 = 
				} else {
					 := false
					// There's two cases of crossing:
					// 1. When an OVG node is at the intersection
					// 2. When there isn't

					// Case 1
					if  && ! && ! {
						 = true
					}

					// Case 2
					if ! && ( != ) {
						// If coming from source or going towards target, it'll intersect with other edges, and that's fine
						// Even if node is not on a route, still have to check for if it's crossing while not sharing a node.
						//       +
						//       |
						//       |
						// +-----------+
						//       |
						//       |
						//       +
						,  = .edgeSet.intersectsWithGuarded(, .work)
						if  != nil {
							return nil, 0, 
						}
					}

					if  {
						 += .crossingCost
					}
				}
			}

			// Check intersection with positioned labels (avoiding allocation)
			for  := range .positionedLabels {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if segmentIntersectsBox(.From.Point, .To.Point, &.positionedLabels[].Box) {
					 += basicallyInfinity
					break
				}
			}

			// Check intersection with node label boxes
			for ,  := range  {
				if  := .work.step();  != nil {
					return nil, 0, 
				}
				if segmentIntersectsBox(.From.Point, .To.Point, &.Box) {
					 += .turnCost
				}
			}

			// Handle arrow labels
			if .isPortOf() {
				if  := .SourceArrowheadLabel;  != nil {
					[0] = .Point
					[1] = .Point
					 := labeling.PositionArrowheadLabel(, false, [:])
					,  := positionedArrowheadLabelCostGuarded(*, , .positionedLabels, .routes, nil, .work)
					if  != nil {
						return nil, 0, 
					}
					 += 
				}
			}
			if .isPortOf() {
				if  := .TargetArrowheadLabel;  != nil {
					[0] = .Point
					[1] = .Point
					 := labeling.PositionArrowheadLabel(, true, [:])
					,  := positionedArrowheadLabelCostGuarded(*, , .positionedLabels, .routes, nil, .work)
					if  != nil {
						return nil, 0, 
					}
					 += 
				}
			}

			// Don't count coming from source or heading to target as turns
			if  != nil && (( != ) && ( != )) && ( != ) {
				 := (! && (.Point.X != .Point.X)) ||
					( && (.Point.Y != .Point.Y))
				if  {
					// Knock off a few points for turning at an ideal axis
					 := 1.0
					for ,  := range  {
						if  := .work.step();  != nil {
							return nil, 0, 
						}
						if .isX {
							if math.Abs(.Point.X-.val) <= idealTurnAxisTolerance {
								 = idealTurnMultiplier
								break
							}
						} else {
							if math.Abs(.Point.Y-.val) <= idealTurnAxisTolerance {
								 = idealTurnMultiplier
								break
							}
						}
					}
					if  != 1.0 {
						if .From.Cluster != nil && (.Cluster.Arrangement == .Cluster.DesiredArrangement) && len(.From.Cluster.Nodes)%2 == 0 {
							 = idealTurnEvenClusterMultiplier
						}
					}
					// Turns right or left
					if ! && (.Point.X != .Point.X) {
						 += (.turnCost * )
						// Not allowed to turn too close to the target, looks ugly
						// but you gotta do what you gotta do for weird situations like child routing to container
						if !( || ) {
							if .distanceToBoundary() <= turnEndpointClearance {
								 += .turnCost
							}
							if .distanceToBoundary() <= turnEndpointClearance {
								 += .turnCost
							}
						}
					}
					// Turns up or down
					if  && (.Point.Y != .Point.Y) {
						 += (.turnCost * )
						if !( || ) {
							if .distanceToBoundary() <= turnEndpointClearance {
								 += .turnCost
							}
							if .distanceToBoundary() <= turnEndpointClearance {
								 += .turnCost
							}
						}
					}
				}
			}

			// All else being equal, penalize taking routes that are too close to nodes
			if  !=  &&  !=  && len(.IsNearPort) > 0 {
				if ,  := .IsNearPort[];  {
					 += nodeProximityPenalty
				} else if ,  := .IsNearPort[];  {
					 += nodeProximityPenalty
				}
			}

			 :=  + 

			// Create node context if it doesn't exist
			if [.Index] == nil {
				 := &.nodeContextValues[.Index]
				* = searchNodeContext{
					verticalDistance:   math.Inf(1),
					horizontalDistance: math.Inf(1),
				}
				[.Index] = 
			}

			// Horizontal path
			if .Point.Y == .Point.Y {
				if  < [.Index].horizontalDistance {
					// If we already visited this node horizontally, enqueue the current hop
					// to potentially be revisited
					if [.Index] != nil {
						if ,  := .push(
							[[.Index].Index].horizontalDistance,
							[.Index],
							true,
							.work,
						);  != nil {
							return nil, 0, 
						}
					}

					// Create or update entry
					if [.Index].horizontalEntry == nil {
						,  := .push(, , true, .work)
						if  != nil {
							return nil, 0, 
						}
						[.Index].horizontalEntry = 
					} else {
						 = .decrease([.Index].horizontalEntry, , .work)
						if  != nil {
							return nil, 0, 
						}
					}

					[.Index].horizontalDistance = 
					[.Index] = 
				}
			} else { // Vertical path
				if  < [.Index].verticalDistance {
					// If we already visited this node vertically, enqueue the current hop
					// to potentially be revisited
					if [.Index] != nil {
						if ,  := .push(
							[[.Index].Index].verticalDistance,
							[.Index],
							false,
							.work,
						);  != nil {
							return nil, 0, 
						}
					}

					// Create or update entry
					if [.Index].verticalEntry == nil {
						,  := .push(, , false, .work)
						if  != nil {
							return nil, 0, 
						}
						[.Index].verticalEntry = 
					} else {
						 = .decrease([.Index].verticalEntry, , .work)
						if  != nil {
							return nil, 0, 
						}
					}

					[.Index].verticalDistance = 
					[.Index] = 
				}
			}
		}
	}

	return nil, 0, fmt.Errorf("path not found for '%s'. Queue is empty", .DebugID())
}

// If two non-diagonal nodes are below (MinRouteNodeClearance+1) * 2 from each other and connected, there won't be a direct path, and it'll take a roundabout path which can block other paths.
// To alleviate this, we directly connect fromFacingPort to toFacingPort, no need to search
func ( *ovgEdgeRouter) ( context.Context,  *layoutgraph.Edge,  bool,  geo.Orientation) ([]*OVGNode, float64, error) {
	if  := .bindWork();  != nil {
		return nil, 0, 
	}
	if  := checkEdgeRoutingCanceled();  != nil {
		return nil, 0, 
	}
	 := .ovg
	 := .graph
	 := .From
	 := .To
	 := .Centers[]
	 := .Centers[]
	if ! && !.IsDiagonal() && .DistanceTo(, true) < 2*(layoutgraph.MinRouteNodeClearance+1) && (.DistanceTo(, true) > layoutgraph.MinArrowheadClearance) {
		 := make([]*layoutgraph.Edge, 0)
		for ,  := range .routes {
			if  := .work.add(uint64(len(.OVGNodes)) + 1);  != nil {
				return nil, 0, 
			}
			 := layoutgraph.NewEdge(.GEdge.From, .GEdge.To)
			.Points = .createSegmentEndpoints()
			.SourceArrowhead = .GEdge.SourceArrowhead
			.TargetArrowhead = .GEdge.TargetArrowhead
			.SourceArrowheadLabel = .GEdge.SourceArrowheadLabel
			.TargetArrowheadLabel = .GEdge.TargetArrowheadLabel
			.Label = .GEdge.Label
			 = append(, )
		}
		if  := checkEdgeRoutingCanceled();  != nil {
			return nil, 0, 
		}
		, , ,  := routeLineChecked(
			,
			,
			,
			nil,
			.work.step,
			.work)

		if  == nil {
			return []*OVGNode{
				,
				NewOVGNode(),
				NewOVGNode(),
				,
			}, , nil
		}
		if isContextTermination() {
			return nil, 0, 
		}
	}
	return nil, 0, nil
}

func ( *ovgEdgeRouter) (,  *layoutgraph.Node) bool {
	// If the cluster nodes are in a bad arrangement for the gSource->gTarget edge, e.g. c1 and c2 to t
	// then they should not be treated as clusters for the purposes of route searching
	// NOTE that this will only take effect if cluster optimizing is included as a prior stage
	//
	// ┌──────────┐
	// │    c1    │
	// │          │
	// └──────────┘
	//
	// ┌──────────┐
	// │   c2     │
	// │          │
	// └──────────┘
	//
	//    ┌───┐
	//    │   │
	//    │t  │
	//    └───┘
	if .Cluster != nil {
		if !clusterHasDesirableArrangementTo(.Cluster, ) {
			return true
		}
	}
	if .Cluster != nil {
		if !clusterHasDesirableArrangementTo(.Cluster, ) {
			return true
		}
	}
	return false
}

func clusterHasDesirableArrangementTo( *layoutgraph.Cluster,  *layoutgraph.Node) bool {
	 := .Vessel
	if !.IsActive() {
		// The vessel may be out of sync after cluster placement. Cluster nodes
		// remain arranged in visual order, so their first and last boxes define
		// the same routing extent used by the original implementation.
		 := .Nodes[0].TopLeft.Copy()
		 := .Nodes[len(.Nodes)-1]
		 := .TopLeft.Copy()
		.X += .Width
		.Y += .Height
		 = &layoutgraph.Node{Box: geo.Box{
			TopLeft: ,
			Width:   .X - .X,
			Height:  .Y - .Y,
		}}
	}
	switch .Orientation() {
	case geo.Top, geo.Bottom:
		return .Arrangement == layoutgraph.Row
	case geo.Left, geo.Right:
		return .Arrangement == layoutgraph.Column
	default:
		return false
	}
}

// bestRoute finds the lowest-cost route to target, choosing between vertical
// and horizontal hops.
func ( *ovgEdgeRouter) (
	 context.Context,
	,  *layoutgraph.Node,
	,  *OVGNode,
	 []*searchNodeContext,
	,  []*OVGNode) ([]*OVGNode, error) {
	if  := .bindWork();  != nil {
		return nil, 
	}
	 := make([]*OVGNode, 0)

	 := idealTurnAxes(, )

	 := 
	var  *OVGNode
	var  *OVGNode
	 := make(map[*OVGNode]struct{})
	for  != nil {
		if  := .work.step();  != nil {
			return nil, 
		}
		if ,  := [];  {
			// Cycle detected, take corrective action
			break
		}
		[] = struct{}{}
		 = append(, )

		var  bool
		 := [.Index]
		 := .verticalDistance
		 := .horizontalDistance
		if ([.Index] != nil) && ([.Index] != nil) {
			if  <  {
				 = [.Index]
				 = false
			} else {
				 = [.Index]
				 = true
			}
		} else if [.Index] != nil {
			 = [.Index]
			 = false
		} else {
			 = [.Index]
			 = true
		}

		// Because the cost of a turn is baked into the node (node T) it turns into, and not the intersection (node I)
		// When another route comes in without a turn, it'll set lower weights from node T onwards, erasing the cost of turn,
		// But node I is not changed, and the best route would still be the one with the turn
		// So we reintroduce the cost to node I here. That way, we only turn if the cost of it PLUS the turn is better than the route without the turn
		// If next is source, we're at a port and disregard the turn
		if ( != nil) && ( != nil) && ( != ) {
			// If the next one leads to a turn ...
			if ((.Point.X == .Point.X) && ) ||
				((.Point.Y == .Point.Y) && !) {
				var  float64
				var  *OVGNode
				 := 1.0

				for ,  := range  {
					if  := .work.step();  != nil {
						return nil, 
					}
					if .isX {
						if math.Abs(.Point.X-.val) <= idealTurnAxisTolerance {
							 = idealTurnMultiplier
							break
						}
					} else {
						if math.Abs(.Point.Y-.val) <= idealTurnAxisTolerance {
							 = idealTurnMultiplier
							break
						}
					}
				}
				if  != 1.0 {
					if .Cluster != nil && (.Cluster.Arrangement == .Cluster.DesiredArrangement) && len(.Cluster.Nodes)%2 == 0 {
						 = idealTurnEvenClusterMultiplier
					}
				}
				// And the margin is only by turn distance ...
				if  && (geo.PrecisionCompare(+.turnCost*, , geo.PRECISION) > 0) {
					// Then we should use the other direction
					 = 
					 = [.Index]
				}
				if ! && (geo.PrecisionCompare(+.turnCost*, , geo.PRECISION) > 0) {
					 = 
					 = [.Index]
				}
				// If alternative is basically infinity, it'll cycle
				if  != nil &&  < basicallyInfinity {
					 = 
				}
			}
		}

		 = 
		 = 
	}
	for ,  := 0, len()-1;  < ; ,  = +1, -1 {
		if  := .work.step();  != nil {
			return nil, 
		}
		[], [] = [], []
	}
	return , nil
}

/*
	OVG routes only overlap if both OVG nodes share the same route.

n1
|
|
n2----n3

As all routes are either vertical or horizontal, there is no overlap between n1 and n3 because they can't share a route.
In this case, checking for overlaps is just a check for shared routes of start and end (set intersection).

However, the example below, breaks the assumption above.
Assuming t1 and t2 are tunnel nodes they generate an overlap with n1-n2 that is not a simple
set intersection operation. In this case, we need to check for all vertical edges at the same X.

t1
|
|
n1
|
n2
|
|
t2
*/
func ( *ovgEdgeRouter) (,  *OVGNode) ([]*Route, error) {
	var  []*Route
	 := make(map[*Route]struct{})

	// find all overlapping edges
	,  := .edgeSet.overlappingEdgesGuarded(NewOVGEdge(, ), .work)
	if  != nil {
		return nil, 
	}
	for ,  := range  {
		if  := .work.step();  != nil {
			return nil, 
		}
		for ,  := range .pointToRoute[*.From.Point] {
			if  := .work.step();  != nil {
				return nil, 
			}
			for ,  := range .pointToRoute[*.To.Point] {
				if  := .work.step();  != nil {
					return nil, 
				}
				// find the route that contains the edge
				// only add the route if not seen before
				if ,  := []; ! &&  ==  {
					[] = struct{}{}
					 = append(, )
				}
			}
		}
	}
	return , nil
}

func ( *ovgEdgeRouter) ( *Route) error {
	 := make(map[*OVGNode]struct{}, len(.OVGNodes))
	 := .OVGNodes
	if .GEdge.IsLoop() {
		// The source and target centers are the same node for a loop.
		 = [1:]
	}
	for ,  := range  {
		if  := .work.step();  != nil {
			return 
		}
		if ,  := [];  {
			return fmt.Errorf("found duplicate OVGNode (%+v) at index %d in route %s", .Point, , .GEdge.DebugID())
		}
		[] = struct{}{}
	}

	if  := .GEdge.SourceArrowheadLabel;  != nil {
		if  := .work.add(uint64(len(.OVGNodes)));  != nil {
			return 
		}
		 := .createSegmentEndpoints()
		 := labeling.PositionArrowheadLabel(.GEdge, false, )
		.positionedLabels = append(.positionedLabels, *)
	}
	if  := .GEdge.TargetArrowheadLabel;  != nil {
		if  := .work.add(uint64(len(.OVGNodes)));  != nil {
			return 
		}
		 := .createSegmentEndpoints()
		 := labeling.PositionArrowheadLabel(.GEdge, true, )
		.positionedLabels = append(.positionedLabels, *)
	}

	.routes = append(.routes, )
	.routedEdges = append(.routedEdges, .GEdge)
	for  := 0;  < len(.OVGNodes); ++ {
		if  := .work.step();  != nil {
			return 
		}
		 := .OVGNodes[]
		.pointToRoute[*.Point] = append(.pointToRoute[*.Point], )

		if  > 0 &&  < len(.OVGNodes)-2 {
			// only keep edges of the OVG path, ignore edges from port to center
			 := .OVGNodes[+1]
			if  := .edgeSet.addGuarded(NewOVGEdge(, ), .work);  != nil {
				return 
			}
		}
	}

	if !.GEdge.IsInvisible {
		for  := 1;  < len(.OVGNodes)-2; ++ {
			if  := .work.step();  != nil {
				return 
			}
			 := .OVGNodes[]
			 := .OVGNodes[+1]
			if .X == .X {
				// vertical
				,  := .Y, .Y
				if  <  {
					,  = , 
				}
				// Route coordinates may be fractional, so scan the indexed axes rather
				// than iterating integer coordinates around routeNode.X.
				for ,  := range .ovg.VerticalEdges {
					if  := .work.step();  != nil {
						return 
					}
					if math.Abs(-.X) > pathNodeProximityFloor {
						continue
					}
					for ,  := range  {
						if  := .work.step();  != nil {
							return 
						}
						,  := .From.Y, .To.Y
						if  <  {
							,  = , 
						}
						if !( <  ||  < ) {
							.hasNearbyEdge[] = struct{}{}
							if  == .X {
								.overlappingRoutes[] = append(.overlappingRoutes[], )
							}
						}
					}
				}
			} else {
				,  := .X, .X
				if  <  {
					,  = , 
				}
				for ,  := range .ovg.HorizontalEdges {
					if  := .work.step();  != nil {
						return 
					}
					if math.Abs(-.Y) > pathNodeProximityFloor {
						continue
					}
					for ,  := range  {
						if  := .work.step();  != nil {
							return 
						}
						,  := .From.X, .To.X
						if  <  {
							,  = , 
						}
						if !( <  ||  < ) {
							.hasNearbyEdge[] = struct{}{}
							if  == .Y {
								.overlappingRoutes[] = append(.overlappingRoutes[], )
							}
						}
					}
				}
			}
		}
	}
	return .work.check()
}

// tablePorts finds the ports to connect the columns from source to target.
// By default, it returns ports on both sides of `source` and `target`, `facingPorts` forces it to return
// ports on facing sides, if they exists (for example, if source.Orientation(target) = geo.NONE, there are no facing ports)
func ( *ovgEdgeRouter) ( *layoutgraph.Edge, ,  *layoutgraph.Node,  bool) (,  map[*OVGNode]struct{},  error) {
	 := func( *layoutgraph.Node,  int,  []geo.Orientation) (map[*OVGNode]struct{}, error) {
		 := make(map[*OVGNode]struct{})
		for ,  := range  {
			// Port-index derivation scans the node's incident edges to account for
			// loop offsets before indexing the already-bounded port slice.
			if  := .work.add(uint64(len(.Edges)) + 1);  != nil {
				return nil, 
			}
			,  := nodeshape.TablePortIndex(.Shape, , )
			if ! {
				 = .PortIndices()[]
			}
			[.ovg.Ports[][]] = struct{}{}
		}
		return , nil
	}

	var ,  []geo.Orientation
	if ! {
		 = []geo.Orientation{geo.Right, geo.Left}
		 = []geo.Orientation{geo.Right, geo.Left}
	} else {
		switch .Orientation() {
		case geo.Left, geo.TopLeft, geo.BottomLeft:
			 = []geo.Orientation{geo.Right}
			 = []geo.Orientation{geo.Left}
		case geo.Right, geo.TopRight, geo.BottomRight:
			 = []geo.Orientation{geo.Left}
			 = []geo.Orientation{geo.Right}
		}
	}

	if .FromTableColumnIndex != nil {
		,  = (, *.FromTableColumnIndex, )
		if  != nil {
			return nil, nil, 
		}
	}
	if .ToTableColumnIndex != nil {
		,  = (, *.ToTableColumnIndex, )
		if  != nil {
			return nil, nil, 
		}
	}
	return , , nil
}