package hierarchy

import (
	
	
	

	
	
)

// rankSimplex is the graphical network-simplex formulation from Gansner,
// Koutsofios, North, and Vo, "A Technique for Drawing Directed Graphs,"
// IEEE TSE 19(3), 1993, section 2.3. It works directly with feasible ranks,
// tight spanning trees, and the cut values of tree edges.
type rankSimplex struct {
	problem       *rankProblem
	incidentStart []int
	incidentEdges []int
	balance       []int64
	scratch       rankSimplexScratch
}

// rankSimplexScratch is reused across tree exchanges. A rankSimplex belongs to
// one solve, so none of these slices are shared across calls to rankDAG.
type rankSimplexScratch struct {
	parent          []int
	parentEdge      []int
	order           []int
	stack           []int
	subtreeBalance  []int64
	cutValue        []int64
	head            []bool
	queue           []int
	computedBalance []int64
}

func ( *rankProblem) ( []int64,  *limits.OptimizationWorkGuard) ([]int64, int64, error) {
	,  := newRankSimplex(, )
	if  != nil {
		return nil, 0, 
	}
	 := slices.Clone()
	,  := .feasibleTree(, )
	if  != nil {
		return nil, 0, 
	}
	if  := .optimize(, , );  != nil {
		return nil, 0, 
	}
	,  := .certify(, , )
	if  != nil {
		return nil, 0, 
	}
	return , , nil
}

func newRankSimplex( *rankProblem,  *limits.OptimizationWorkGuard) (*rankSimplex, error) {
	 := len(.nodes)
	 := make([]int, )
	 := make([]int64, )
	for ,  := range .edges {
		if  := .Step();  != nil {
			return nil, 
		}
		[.from]++
		[.to]++
		var  bool
		[.from],  = checkedAddInt64([.from], .weight)
		if ! {
			return nil, errors.New("supply overflow")
		}
		[.to],  = checkedSubInt64([.to], .weight)
		if ! {
			return nil, errors.New("demand overflow")
		}
	}

	 := make([]int, +1)
	for ,  := range  {
		if  := .Step();  != nil {
			return nil, 
		}
		[+1] = [] + 
	}
	 := make([]int, len(.edges)*2)
	 := slices.Clone([:])
	for ,  := range .pivotOrder {
		if  := .Step();  != nil {
			return nil, 
		}
		 := .edges[]
		[[.from]] = 
		[.from]++
		[[.to]] = 
		[.to]++
	}

	 := int64(0)
	for ,  := range  {
		if  := .Step();  != nil {
			return nil, 
		}
		var  bool
		,  = checkedAddInt64(, )
		if ! {
			return nil, errors.New("balance overflow")
		}
	}
	if  != 0 {
		return nil, invariant.Errorf("unbalanced supplies: %d", )
	}
	return &rankSimplex{
		problem:       ,
		incidentStart: ,
		incidentEdges: ,
		balance:       ,
		scratch: rankSimplexScratch{
			parent:          make([]int, ),
			parentEdge:      make([]int, ),
			order:           make([]int, 0, ),
			stack:           make([]int, 0, ),
			subtreeBalance:  make([]int64, ),
			cutValue:        make([]int64, len(.edges)),
			head:            make([]bool, ),
			queue:           make([]int, 0, ),
			computedBalance: make([]int64, ),
		},
	}, nil
}

// feasibleTree implements figure 2-2 of Gansner et al. Starting from the
// longest-path feasible ranking, it grows a maximal tree of tight edges. When
// that tree does not span, it shifts the tree by the minimum boundary slack so
// at least one more edge becomes tight. The graph stays feasible throughout.
func ( *rankSimplex) ( []int64,  *limits.OptimizationWorkGuard) ([]bool, error) {
	 := len(.problem.nodes)
	 := make([]bool, len(.problem.edges))
	 := make([]bool, )
	[0] = true
	 := 1
	 := []int{0}

	 := func() error {
		for  := 0;  < len(); ++ {
			if  := .Step();  != nil {
				return 
			}
			 := []
			for  := .incidentStart[];  < .incidentStart[+1]; ++ {
				if  := .Step();  != nil {
					return 
				}
				 := .incidentEdges[]
				 := .problem.edges[]
				 := .from
				if  ==  {
					 = .to
				}
				if [] {
					continue
				}
				,  := rankSlack(, )
				if  != nil {
					return 
				}
				if  != 0 {
					continue
				}
				[] = true
				[] = true
				++
				 = append(, )
			}
		}
		 = [:0]
		return nil
	}
	if  := ();  != nil {
		return nil, 
	}

	 := make([]int, 0, len(.problem.edges))
	for  <  {
		 := int64(math.MaxInt64)
		 = [:0]
		for ,  := range .problem.pivotOrder {
			if  := .Step();  != nil {
				return nil, 
			}
			 := .problem.edges[]
			if [.from] == [.to] {
				continue
			}
			,  := rankSlack(, )
			if  != nil {
				return nil, 
			}
			if  <  {
				 = 
				 = append([:0], )
			} else if  ==  {
				 = append(, )
			}
		}
		if len() == 0 {
			return nil, invariant.New("could not construct a spanning feasible tree")
		}

		 := .problem.edges[[0]]
		 := [.from]
		 := 
		if ! {
			var  bool
			,  = checkedSubInt64(0, )
			if ! {
				return nil, errors.New("feasible-tree shift overflow")
			}
		}
		for ,  := range  {
			if  := .Step();  != nil {
				return nil, 
			}
			if ! {
				continue
			}
			var  bool
			[],  = checkedAddInt64([], )
			if ! {
				return nil, errors.New("feasible-tree level overflow")
			}
		}

		// All minimum-slack boundary edges whose slack decreased are now
		// tight. Add them in canonical edge order, then traverse any tight
		// component reached through their outside endpoints.
		for ,  := range  {
			if  := .Step();  != nil {
				return nil, 
			}
			 := .problem.edges[]
			if [.from] == [.to] {
				continue
			}
			 := [.from]
			if  != 0 &&  !=  {
				continue
			}
			,  := rankSlack(, )
			if  != nil {
				return nil, 
			}
			if  != 0 {
				continue
			}
			 := .from
			if  {
				 = .to
			}
			[] = true
			[] = true
			++
			 = append(, )
		}
		if len() == 0 {
			return nil, invariant.New("feasible-tree shift did not add a node")
		}
		if  := ();  != nil {
			return nil, 
		}
	}
	return , nil
}

// optimize performs the feasible-tree exchanges in figure 2-1. The paper
// requires an anti-cycling rule for degenerate (zero-shift) pivots. We use the
// finite smallest-index rule of Bland (Mathematics of Operations Research
// 2(2), 1977): the first negative-cut tree edge leaves, and minimum-slack
// entering-edge ties use the first edge. rankProblem's stable-ID pivot order
// makes both choices deterministic and independent of input slice order.
func ( *rankSimplex) ( []int64,  []bool,  *limits.OptimizationWorkGuard) error {
	for {
		if  := .computeCutValues(, );  != nil {
			return 
		}
		 := .scratch.cutValue
		 := -1
		for ,  := range .problem.pivotOrder {
			if  := .Step();  != nil {
				return 
			}
			if [] && [] < 0 {
				 = 
				break
			}
		}
		if  < 0 {
			return nil
		}

		if  := .computeHeadComponent(, , );  != nil {
			return 
		}
		 := .scratch.head
		 := -1
		 := int64(math.MaxInt64)
		for ,  := range .problem.pivotOrder {
			if  := .Step();  != nil {
				return 
			}
			 := .problem.edges[]
			if [] || ![.from] || [.to] {
				continue
			}
			,  := rankSlack(, )
			if  != nil {
				return 
			}
			if  <  {
				 = 
				 = 
			}
		}
		if  < 0 {
			return invariant.Errorf("negative cut on edge %d has no entering edge", .problem.edges[].id)
		}

		for ,  := range  {
			if  := .Step();  != nil {
				return 
			}
			if ! {
				continue
			}
			var  bool
			[],  = checkedAddInt64([], )
			if ! {
				return errors.New("simplex level overflow")
			}
		}
		if ,  := rankSlack(.problem.edges[], );  != nil {
			return 
		} else if  != 0 {
			return invariant.Errorf("entering edge %d has slack %d after exchange", .problem.edges[].id, )
		}
		[] = false
		[] = true
	}
}

// computeCutValues roots the undirected feasible tree at node zero. Summing
// the prescribed dual supplies from the leaves inward gives every tree edge's
// cut value in linear time and stores the result in solver.scratch.cutValue.
func ( *rankSimplex) ( []bool,  *limits.OptimizationWorkGuard) error {
	 := len(.problem.nodes)
	 := 0
	for ,  := range  {
		if  := .Step();  != nil {
			return 
		}
		if  {
			++
		}
	}
	if  != -1 {
		return invariant.Errorf("basis has %d tree edges, want %d", , -1)
	}

	const  = -2
	 := .scratch.parent
	 := .scratch.parentEdge
	for  := range  {
		if  := .Step();  != nil {
			return 
		}
		[] = 
		[] = -1
	}
	[0] = -1
	 := .scratch.order[:0]
	 := append(.scratch.stack[:0], 0)
	for len() > 0 {
		if  := .Step();  != nil {
			return 
		}
		 := [len()-1]
		 = [:len()-1]
		 = append(, )
		for  := .incidentStart[];  < .incidentStart[+1]; ++ {
			if  := .Step();  != nil {
				return 
			}
			 := .incidentEdges[]
			if ![] ||  == [] {
				continue
			}
			 := .problem.edges[]
			 := .from
			if  ==  {
				 = .to
			}
			if [] !=  {
				return invariant.Errorf("basis contains a cycle through edge %d", .id)
			}
			[] = 
			[] = 
			 = append(, )
		}
	}
	if len() !=  {
		return invariant.New("basis is disconnected")
	}

	 := .scratch.subtreeBalance
	for ,  := range .balance {
		if  := .Step();  != nil {
			return 
		}
		[] = 
	}
	 := .scratch.cutValue
	clear()
	for  := len() - 1;  > 0; -- {
		if  := .Step();  != nil {
			return 
		}
		 := []
		 := []
		 := .problem.edges[]
		 := []
		if .from ==  {
			[] = 
		} else if .to ==  {
			var  bool
			[],  = checkedSubInt64(0, )
			if ! {
				return errors.New("cut-value overflow")
			}
		} else {
			return invariant.Errorf("malformed tree edge %d", .id)
		}
		var  bool
		[[]],  = checkedAddInt64([[]], )
		if ! {
			return errors.New("subtree-balance overflow")
		}
	}
	if [0] != 0 {
		return invariant.Errorf("tree balance is %d, want zero", [0])
	}
	return nil
}

// computeHeadComponent stores the head component formed by removing leaving in
// solver.scratch.head.
func ( *rankSimplex) ( []bool,  int,  *limits.OptimizationWorkGuard) error {
	 := .scratch.head
	clear()
	 := .problem.edges[].to
	[] = true
	 := append(.scratch.queue[:0], )
	for  := 0;  < len(); ++ {
		if  := .Step();  != nil {
			return 
		}
		 := []
		for  := .incidentStart[];  < .incidentStart[+1]; ++ {
			if  := .Step();  != nil {
				return 
			}
			 := .incidentEdges[]
			if  ==  || ![] {
				continue
			}
			 := .problem.edges[]
			 := .from
			if  ==  {
				 = .to
			}
			if ![] {
				[] = true
				 = append(, )
			}
		}
	}
	if len() == len(.problem.nodes) {
		return invariant.Errorf("leaving edge %d does not cut the tree", .problem.edges[].id)
	}
	return nil
}

// certify constructs the ranking LP's dual flow using only the final tight
// spanning tree. A tree cut value is exactly the unique flow on that edge that
// satisfies the authored edge-weight balances. Nonnegative cut values provide
// dual feasibility; equality with the independently computed primal objective
// in rankDAG then certifies exact optimality.
func ( *rankSimplex) ( []int64,  []bool,  *limits.OptimizationWorkGuard) (int64, error) {
	if  := .computeCutValues(, );  != nil {
		return 0, 
	}
	 := .scratch.cutValue
	 := .scratch.computedBalance
	clear()
	 := int64(0)
	for ,  := range .problem.edges {
		if  := .Step();  != nil {
			return 0, 
		}
		 := int64(0)
		if [] {
			 = []
			if  < 0 {
				return 0, invariant.Errorf("final tree edge %d has negative cut value %d", .id, )
			}
			,  := rankSpan(, )
			if  != nil {
				return 0, 
			}
			if  != rankMinSpan {
				return 0, invariant.Errorf("final tree edge %d is not tight", .id)
			}
		}
		var  bool
		[.from],  = checkedAddInt64([.from], )
		if  {
			[.to],  = checkedSubInt64([.to], )
		}
		if ! {
			return 0, errors.New("certificate balance overflow")
		}
		,  := checkedMulInt64(, rankMinSpan)
		if  {
			,  = checkedAddInt64(, )
		}
		if ! {
			return 0, errors.New("dual value overflow")
		}
	}
	for ,  := range .balance {
		if  := .Step();  != nil {
			return 0, 
		}
		if [] !=  {
			return 0, invariant.Errorf("certificate balance mismatch on node %s: got %d, want %d", .problem.nodes[].DebugID(), [], )
		}
	}
	return , nil
}

func rankSlack( rankEdge,  []int64) (int64, error) {
	,  := rankSpan(, )
	if  != nil {
		return 0, 
	}
	,  := checkedSubInt64(, rankMinSpan)
	if ! {
		return 0, errors.New("edge slack overflow")
	}
	if  < 0 {
		return 0, invariant.Errorf("infeasible span %d", )
	}
	return , nil
}

func rankSpan( rankEdge,  []int64) (int64, error) {
	,  := checkedSubInt64([.to], [.from])
	if ! {
		return 0, errors.New("edge span overflow")
	}
	return , nil
}