package quality

import (
	
	

	

	
	
)

const (
	// maxEvaluationWorkUnits bounds the complete candidate-scoring pass. The
	// evaluator is used by candidate selection and direct internal callers, so
	// the bound belongs at this shared boundary.
	// The deliberately generous ceiling preserves room for substantially larger
	// diagrams while making the accepted record limits incapable of
	// expanding into hundreds of billions of segment comparisons. Corpus peaks
	// are measured in TestEvaluateWorkLimitCoversLayoutCorpus.
	maxEvaluationWorkUnits int64 = 50_000_000

	// The checked-in completed-layout corpus is measured live by the engine test.
	// Keep a calibrated range rather than one mode-specific exact count: test
	// instrumentation may add bounded checks, while falling below the range can
	// reveal lost work accounting and exceeding it requires an explicit limit
	// review.
	calibratedPublicEvaluationCorpusFloor int64 = 50_000
	calibratedPublicEvaluationCorpusCeil  int64 = 75_000
)

func newEvaluationWorkGuard( context.Context,  int64) (*limits.WorkGuard, error) {
	,  := limits.NewWorkGuard(, "Evaluate", )
	if  != nil {
		return nil, 
	}
	if  < 0 {
		return nil, fmt.Errorf("TALA Evaluate work limit must not be negative")
	}
	return , nil
}

func chargeEvaluationWork( *limits.WorkGuard,  int) error {
	if  < 0 {
		return fmt.Errorf("TALA Evaluate work charge must not be negative")
	}
	return .Add(int64())
}

// chargeEvaluationAreaWork accounts a conservative upper bound for the legacy
// bounding-box kernel before it runs. This is deliberately charged up front:
// Area may scan all nodes for each outside label and walks each route again
// for edge and arrowhead-label bounds. Rejecting the derived cost first keeps a
// direct engine caller from entering a large, non-allocating legacy kernel that
// cannot itself return an error. WorkGuard.Finish immediately after Area still
// observes cancellation that arrives while a bounded pass is running.
func chargeEvaluationAreaWork( *layoutgraph.Graph,  *limits.WorkGuard) error {
	if  := chargeEvaluationWork(, len(.Nodes));  != nil {
		return 
	}
	for ,  := range .Nodes {
		if .Label != nil && .Label.Position.IsOutside() {
			// Bounding-box calculation can ask each of Leftmost,
			// Topmost, Rightmost, and Bottommost to scan the complete node set.
			for range 4 {
				if  := chargeEvaluationWork(, len(.Nodes));  != nil {
					return 
				}
			}
		}
	}
	for ,  := range .Edges {
		 := 1 // Edge bounding-box calculation always walks the route.
		if .Label != nil && len(.Points) != 0 && .Label.Position != label.Unset {
			++
		}
		if .SourceArrowheadLabel != nil {
			++
		}
		if .TargetArrowheadLabel != nil {
			++
		}
		for range  {
			if  := chargeEvaluationWork(, len(.Points));  != nil {
				return 
			}
		}
	}
	return nil
}

// evaluationIsDescendantOf is a cancellation- and budget-aware ancestry check.
// Candidate payload validation already rejects ancestry
// cycles, but keeping a local depth bound makes the evaluator safe when it is
// exercised directly by internal callers and tests as well.
func evaluationIsDescendantOf(
	 *layoutgraph.Node,
	 *layoutgraph.Node,
	 *limits.WorkGuard,
) (bool, error) {
	for  := 0; ; ++ {
		if  := .Step();  != nil {
			return false, 
		}
		if  ==  {
			return true, nil
		}
		if  == nil {
			return false, nil
		}
		if  >= layoutgraph.MaxTopologyDepth {
			return false, fmt.Errorf("TALA Evaluate ancestry depth exceeds limit %d", layoutgraph.MaxTopologyDepth)
		}
		 = .AncestryParent()
		if  == nil {
			return  == nil, nil
		}
	}
}