package hierarchy

import (
	
	
	
	

	
	
	
)

// rankResult is the complete output of hierarchical rank assignment. Levels
// are zero based; levelCount is zero only for an empty graph.
type rankResult struct {
	nodeToLevel map[*layoutgraph.Node]int
	levelCount  int
}

// rankDAG minimizes the weighted sum of edge spans subject to every edge
// spanning at least one level. It returns a normalized deterministic optimum.
// The input must be a connected simple DAG.
//
// The solver follows the feasible-tree network-simplex formulation in
// Gansner et al., IEEE TSE 19(3), 1993. At termination it independently
// constructs the dual flow induced by the final tree; primal/dual equality is
// an exact optimality certificate, not just a feasibility check.
func rankDAG( context.Context,  *layoutgraph.Graph) (rankResult, error) {
	,  := rankDAGWithLimit(, , limits.MaxOptimizationWorkUnits)
	if  != nil {
		return rankResult{}, fmt.Errorf("TALA RankDAG failed: %w", )
	}
	return , nil
}

func rankDAGWithLimit( context.Context,  *layoutgraph.Graph,  uint64) (rankResult, error) {
	,  := limits.NewOptimizationWorkGuard(, "RankDAG", )
	if  != nil {
		return rankResult{}, 
	}
	,  := newRankProblem(, )
	if  != nil {
		return rankResult{}, 
	}
	if len(.nodes) == 0 {
		if  := .Finish();  != nil {
			return rankResult{}, 
		}
		return rankResult{nodeToLevel: make(map[*layoutgraph.Node]int)}, nil
	}

	,  := .longestPathLevels()
	if  != nil {
		return rankResult{}, 
	}
	if len(.edges) == 0 {
		if  := .Finish();  != nil {
			return rankResult{}, 
		}
		return rankResult{
			nodeToLevel: map[*layoutgraph.Node]int{.nodes[0]: 0},
			levelCount:  1,
		}, nil
	}

	, ,  := .solve(, )
	if  != nil {
		return rankResult{}, 
	}
	, ,  := .makeResult(, )
	if  != nil {
		return rankResult{}, 
	}
	if  !=  {
		return rankResult{}, invariant.Errorf("optimality certificate failed: primal cost %d, dual value %d", , )
	}
	if  := .Finish();  != nil {
		return rankResult{}, 
	}
	return , nil
}

type rankEdge struct {
	from   int
	to     int
	weight int64
	id     layoutgraph.EntityID
}

const rankMinSpan int64 = 1

type rankProblem struct {
	nodes         []*layoutgraph.Node
	edges         []rankEdge
	outgoingStart []int
	pivotOrder    []int
}

func ( *rankProblem) ( []int64,  *limits.OptimizationWorkGuard) (rankResult, int64, error) {
	 := int64(math.MaxInt64)
	 := int64(math.MinInt64)
	for ,  := range  {
		if  := .Step();  != nil {
			return rankResult{}, 0, 
		}
		 = min(, )
		 = max(, )
	}

	 := int64(0)
	for ,  := range .edges {
		if  := .Step();  != nil {
			return rankResult{}, 0, 
		}
		,  := checkedSubInt64([.to], [.from])
		if ! {
			return rankResult{}, 0, errors.New("edge span overflow")
		}
		if  < rankMinSpan {
			return rankResult{}, 0, invariant.Errorf("infeasible span %d", )
		}
		,  := checkedMulInt64(.weight, )
		if ! {
			return rankResult{}, 0, errors.New("objective overflow")
		}
		,  = checkedAddInt64(, )
		if ! {
			return rankResult{}, 0, errors.New("objective overflow")
		}
	}

	,  := checkedSubInt64(, )
	if ! ||  > int64(math.MaxInt-1) {
		return rankResult{}, 0, errors.New("level count overflow")
	}
	 := rankResult{
		nodeToLevel: make(map[*layoutgraph.Node]int, len(.nodes)),
		levelCount:  int() + 1,
	}
	for ,  := range  {
		if  := .Step();  != nil {
			return rankResult{}, 0, 
		}
		,  := checkedSubInt64(, )
		if ! {
			return rankResult{}, 0, errors.New("normalized level overflow")
		}
		if  > int64(math.MaxInt) {
			return rankResult{}, 0, errors.New("normalized level overflow")
		}
		.nodeToLevel[.nodes[]] = int()
	}
	return , , nil
}

func checkedSubInt64(,  int64) (int64, bool) {
	if  > 0 &&  < math.MinInt64+ ||  < 0 &&  > math.MaxInt64+ {
		return 0, false
	}
	return  - , true
}

func checkedAddInt64(,  int64) (int64, bool) {
	if  > 0 &&  > math.MaxInt64- ||  < 0 &&  < math.MinInt64- {
		return 0, false
	}
	return  + , true
}

func checkedMulInt64(,  int64) (int64, bool) {
	if  == 0 ||  == 0 {
		return 0, true
	}
	if  == -1 &&  == math.MinInt64 ||  == -1 &&  == math.MinInt64 {
		return 0, false
	}
	 :=  * 
	return , / == 
}