package placementcost

import (
	
	
	
	
	
	
	

	
	
	
)

const invalidEdgeLengthState uint64 = 0

func placementEdgeLengthState( *layoutgraph.Graph,  layoutgraph.RoutingCostState,  EdgeLengthOptions) uint64 {
	 := fnv.New64a()
	var  [8]byte // Buffer for encoding numbers

	// Process booleans
	var  byte
	if .IncludeNodeSizes {
		 |= 1 << 0
	}
	if .EnforceMinimumGap {
		 |= 1 << 1
	}
	if .PenalizeDirection {
		 |= 1 << 2
	}
	.Write([]byte{})

	// Process graph costs
	binary.LittleEndian.PutUint64([:], math.Float64bits(.CellSize))
	.Write([:])
	binary.LittleEndian.PutUint64([:], math.Float64bits(.Turn))
	.Write([:])
	binary.LittleEndian.PutUint64([:], math.Float64bits(.NonCenterPort))
	.Write([:])
	binary.LittleEndian.PutUint64([:], math.Float64bits(.Crossing))
	.Write([:])

	// Process nodes
	for ,  := range .Nodes {
		if .TopLeft == nil {
			return invalidEdgeLengthState
		}
		binary.LittleEndian.PutUint64([:], uint64(.ID))
		.Write([:])

		binary.LittleEndian.PutUint64([:], math.Float64bits(.TopLeft.X))
		.Write([:])
		binary.LittleEndian.PutUint64([:], math.Float64bits(.TopLeft.Y))
		.Write([:])

		binary.LittleEndian.PutUint64([:], math.Float64bits(.Width))
		.Write([:])
		binary.LittleEndian.PutUint64([:], math.Float64bits(.Height))
		.Write([:])

		for ,  := range .Edges {
			binary.LittleEndian.PutUint64([:], uint64(.ID))
			.Write([:])
		}
	}

	// Process edge abductions
	for ,  := range .EdgeAbductions {
		binary.LittleEndian.PutUint64([:], uint64(.Edge.ID))
		.Write([:])
		if .OriginallyFrom != nil {
			binary.LittleEndian.PutUint64([:], uint64(.OriginallyFrom.ID))
			.Write([:])
		}
		if .OriginallyTo != nil {
			binary.LittleEndian.PutUint64([:], uint64(.OriginallyTo.ID))
			.Write([:])
		}
		if .CurrentTo != nil {
			binary.LittleEndian.PutUint64([:], uint64(.CurrentTo.ID))
			.Write([:])
		}
		if .CurrentFrom != nil {
			binary.LittleEndian.PutUint64([:], uint64(.CurrentFrom.ID))
			.Write([:])
		}
	}

	return .Sum64()
}

func edgeLengthWorkerCount(,  int) int {
	if  <= 0 {
		return 0
	}
	// Preserve the inexpensive sequential path for small graphs.
	if  <= 10 {
		return 1
	}
	if  < 1 {
		 = 1
	}
	return min(, )
}

// EdgeLength evaluates the graph's placement cost and may populate its
// graph-owned edge-length cache.
func ( context.Context,  *layoutgraph.Graph,  EdgeLengthOptions) (float64, error) {
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}

	 := placementEdgeLengthState(, .RoutingCosts(), )
	if  != invalidEdgeLengthState {
		if ,  := .LookupEdgeLengthCost();  {
			return , nil
		}
	}

	 := .CellSize
	var  float64

	 := edgeLengthWorkerCount(len(.Nodes), runtime.GOMAXPROCS(0))
	var  []float64
	if  > 1 {
		 = make([]float64, len(.Nodes))
		var  sync.WaitGroup
		var  sync.Once
		var  bool
		var  sync.Once
		var  error

		for  := range  {
			.Go(func() {
				defer func() {
					if  := recover();  != nil {
						.Do(func() {
							// A recovered value can contain credentials or invoke an
							// attacker-controlled String method. Retain only the fact
							// that this worker violated an invariant.
							 = true
						})
					}
				}()

				for  := ;  < len(.Nodes);  +=  {
					 := .Nodes[]
					,  := NodeEdgeLength(, , )
					if  != nil {
						.Do(func() {  =  })
						return
					}
					if .IncludeNodeSizes {
						,  := ColumnCrossingCost(, , .EdgeAbductions)
						if  != nil {
							.Do(func() {  =  })
							return
						}
						,  := nodeSymmetry(, , .EdgeAbductions, true)
						if  != nil {
							.Do(func() {  =  })
							return
						}
						 += 
						 -=  *  * float64(len(.Edges))
					}
					if  := .Err();  != nil {
						.Do(func() {  = fmt.Errorf("EdgeLength: %w", ) })
						return
					}

					[] = 
				}
			})
		}

		.Wait()
		if  {
			panic("TALA EdgeLength worker invariant failure")
		}
		if  != nil {
			return 0, 
		}
	} else {
		for ,  := range .Nodes {
			,  := NodeEdgeLength(, , )
			if  != nil {
				return 0, 
			}
			if .IncludeNodeSizes {
				,  := ColumnCrossingCost(, , .EdgeAbductions)
				if  != nil {
					return 0, 
				}
				,  := nodeSymmetry(, , .EdgeAbductions, true)
				if  != nil {
					return 0, 
				}
				 += 
				 -=  *  * float64(len(.Edges))
			}
			if  := .Err();  != nil {
				return 0, fmt.Errorf("EdgeLength: %w", )
			}
			 += 
		}
	}
	if  > 1 {
		for ,  := range  {
			 += 
		}
	}

	,  := GraphEdgeCrossings(, )
	if  != nil {
		return 0, 
	}
	 += .CrossingCost() * float64()
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	if  != invalidEdgeLengthState {
		.StoreEdgeLengthCost(, )
	}

	return , nil
}

func countEdgeCrossingsExcludingSharedNodes( context.Context,  []*layoutgraph.Edge) (int64, error) {
	var  int64
	for  := 0;  < len()-1; ++ {
		if  := scoringCancellationError(, );  != nil {
			return 0, 
		}
		 := []
		for  :=  + 1;  < len(); ++ {
			if  := scoringCancellationError(, --1);  != nil {
				return 0, 
			}
			 := []

			// Skip if edges share a common node
			if .From == .From || .From == .To ||
				.To == .From || .To == .To {
				continue
			}

			// Create segments and check if they cross
			 := geo.NewSegment(.From.Center(), .To.Center())
			 := geo.NewSegment(.From.Center(), .To.Center())

			if layoutgraph.SegmentsCross(.Start, .End, .Start, .End) {
				++
			}
		}
	}
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	return , nil
}

type graphEdgeCrossingScratch struct {
	levelGroup map[int]int
	groups     [][]*layoutgraph.Edge
	active     int
}

const maxPooledGraphEdgeCrossingEntries = 4096

var graphEdgeCrossingScratchPool = typedpool.New(func() *graphEdgeCrossingScratch {
	return &graphEdgeCrossingScratch{levelGroup: make(map[int]int)}
})

func putGraphEdgeCrossingScratch( *graphEdgeCrossingScratch) {
	if len(.levelGroup) > maxPooledGraphEdgeCrossingEntries ||
		cap(.groups) > maxPooledGraphEdgeCrossingEntries {
		return
	}
	 := maxPooledGraphEdgeCrossingEntries - cap(.groups)
	for ,  := range .groups {
		if cap() >  {
			return
		}
		 -= cap()
	}

	for  := 0;  < .active; ++ {
		clear(.groups[][:cap(.groups[])])
		.groups[] = .groups[][:0]
	}
	clear(.levelGroup)
	.active = 0
	graphEdgeCrossingScratchPool.Put()
}

// GraphEdgeCrossings counts crossings between edges that do not share a node.
func ( context.Context,  *layoutgraph.Graph) (int64, error) {
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	if len(.Edges) == 0 {
		if  := .Err();  != nil {
			return 0, fmt.Errorf("EdgeLength: %w", )
		}
		return 0, nil
	}

	 := graphEdgeCrossingScratchPool.Get()
	defer putGraphEdgeCrossingScratch()

	for ,  := range .Edges {
		if  := scoringCancellationError(, );  != nil {
			return 0, 
		}
		if .From == nil || .To == nil || .From.TopLeft == nil || .To.TopLeft == nil {
			continue
		}
		 := .From.ContainerLevel()
		 := .To.ContainerLevel()
		if  !=  {
			continue
		}

		,  := .levelGroup[]
		if ! {
			 = .active
			if  == len(.groups) {
				.groups = append(.groups, nil)
			} else {
				.groups[] = .groups[][:0]
			}
			.active++
			.levelGroup[] = 
		}
		.groups[] = append(.groups[], )
	}

	var  int64
	for ,  := range .levelGroup {
		,  := countEdgeCrossingsExcludingSharedNodes(, .groups[])
		if  != nil {
			return 0, 
		}
		 += 
	}

	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	return , nil
}

// ContainerAlignmentCost scores misalignment between peer containers.
func ( context.Context,  *layoutgraph.Graph) (float64, error) {
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	 := 0.0
	for  := 0;  < len(.Nodes)-1; ++ {
		if  := scoringCancellationError(, );  != nil {
			return 0, 
		}
		 := .Nodes[]
		if !.IsContainer() {
			continue
		}
		for  :=  + 1;  < len(.Nodes); ++ {
			if  := scoringCancellationError(, --1);  != nil {
				return 0, 
			}
			 := .Nodes[]
			if !.IsContainer() {
				continue
			}
			if .EffectiveContainer() != .EffectiveContainer() {
				continue
			}
			if .Width == .Width && .Height == .Height {
				if .TopLeft.X != .TopLeft.X && .TopLeft.Y != .TopLeft.Y {
					 += .NonCenterPortCost()
				}
			}
		}
	}
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	return , nil
}