// Copyright (c) 2016, 2020 Kiel University and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package p3order

import (
	basegraph 
	
)

type BarycenterState struct {
	Node         layered.LNodeID
	SummedWeight float64
	Degree       int
	Barycenter   *float64
	Visited      bool
}

type ForsterConstraintResolver struct {
	constraintsBetweenNonDummies bool
	layoutUnits                  map[layered.LNodeID][]layered.LNodeID
	BarycenterStates             [][]BarycenterState
}

func (
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
) *ForsterConstraintResolver {
	 := false
	if len() > 0 && len([0]) > 0 {
		 := .NodeGraph([0][0])
		 = basegraph.Get(&.Graph().Properties, layered.InLayerSuccessorConstraintsBetweenNonDummies)
	}
	return &ForsterConstraintResolver{
		constraintsBetweenNonDummies: ,
		layoutUnits:                  make(map[layered.LNodeID][]layered.LNodeID),
		BarycenterStates:             make([][]BarycenterState, len()),
	}
}

func ( *ForsterConstraintResolver) ( int,  [][]layered.LNodeID) {
	.BarycenterStates[] = make([]BarycenterState, len([]))
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	,  int,
	 [][]layered.LNodeID,
) {
	 := [][]
	.BarycenterStates[][] = BarycenterState{Node: }
	if ,  := basegraph.TryGet(&.Node().Properties, layered.InLayerLayoutUnit);  {
		.layoutUnits[] = append(.layoutUnits[], )
	}
}

func (
	 *ForsterConstraintResolver,
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
) {
	for ,  := range  {
		.InitAtLayerLevel(, )
		if len() > 0 {
			 := *.Node([0]).Layer
			.Layer().ID = 
		}
		for ,  := range  {
			.Node().ID = 
			.InitAtNodeLevel(, , , )
		}
	}
}

type constraintGroup struct {
	summedWeight             float64
	degree                   int
	nodes                    []layered.LNodeID
	outgoing                 []int
	incoming                 []int
	incomingConstraintsCount int
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 *[]layered.LNodeID,
) {
	if .constraintsBetweenNonDummies {
		.processConstraintStage(, , true)
	}
	.processConstraintStage(, , false)
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 *[]layered.LNodeID,
	 bool,
) {
	 := make([]constraintGroup, len(*))
	 := make(map[layered.LNodeID]int, len(*))
	 := make([]int, len(*))
	for ,  := range * {
		[] = constraintGroup{nodes: []layered.LNodeID{}}
		[] = 
		[] = 
	}
	.buildConstraintGraph(, , , , )
	for {
		, ,  := .findViolatedConstraint(, , )
		if ! {
			break
		}
		.handleViolatedConstraint(, &, &, , )
	}
	 := make([]layered.LNodeID, 0, len(*))
	for ,  := range  {
		 := .groupBarycenter(, , )
		for ,  := range [].nodes {
			 = append(, )
			.state(, ).Barycenter = cloneFloat()
		}
	}
	* = 
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 []constraintGroup,
	 map[layered.LNodeID]int,
	 []int,
	 bool,
) {
	for ,  := range  {
		[].outgoing = nil
		[].incomingConstraintsCount = 0
	}
	var  *layered.LNodeID
	for ,  := range  {
		 := [].nodes[0]
		if  && .Node().Type != layered.NormalNode {
			continue
		}
		for ,  := range basegraph.Get(&.Node().Properties, layered.InLayerSuccessorConstraints) {
			if  && .Node().Type != layered.NormalNode {
				continue
			}
			,  := []
			if ! {
				continue
			}
			[].outgoing = append([].outgoing, )
			[].incomingConstraintsCount++
		}
		if ! && .Node().Type == layered.NormalNode {
			if  != nil {
				for ,  := range .layoutUnits[*] {
					for ,  := range .layoutUnits[] {
						,  := []
						,  := []
						if  &&  {
							[].outgoing = append([].outgoing, )
							[].incomingConstraintsCount++
						}
					}
				}
			}
			 := 
			 = &
		}
	}
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 []constraintGroup,
	 []int,
) (int, int, bool) {
	 := make([]int, 0)
	for ,  := range  {
		[].incoming = nil
		if len([].outgoing) > 0 && [].incomingConstraintsCount == 0 {
			 = append(, )
		}
	}
	for len() > 0 {
		 := [0]
		 = [1:]
		 := .groupBarycenter(, , )
		for ,  := range [].incoming {
			 := .groupBarycenter(, , )
			if  == nil ||  == nil {
				continue
			}
			if float32(*) == float32(*) {
				if groupListIndex(, ) > groupListIndex(, ) {
					return , , true
				}
			} else if * > * {
				return , , true
			}
		}
		for ,  := range [].outgoing {
			[].incoming = append([]int{}, [].incoming...)
			if [].incomingConstraintsCount == len([].incoming) {
				 = append(, )
			}
		}
	}
	return 0, 0, false
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 *[]constraintGroup,
	 *[]int,
	,  int,
) {
	 := .mergeGroups(, , , )
	 := .groupBarycenter(, *, )
	 := false
	for  := 0;  < len(*); {
		 := (*)[]
		if  ==  ||  ==  {
			* = append((*)[:], (*)[+1:]...)
			continue
		}
		 := .groupBarycenter(, *, )
		if ! &&  != nil &&  != nil && * > * {
			* = append(*, 0)
			copy((*)[+1:], (*)[:])
			(*)[] = 
			 = true
			++
			continue
		}
		 := &(*)[].outgoing
		 := removeFirstGroup(, )
		 := removeFirstGroup(, )
		if  ||  {
			* = append(*, )
			(*)[].incomingConstraintsCount++
		}
		++
	}
	if ! {
		* = append(*, )
	}
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 *[]constraintGroup,
	,  int,
) int {
	,  := (*)[], (*)[]
	 := append(append([]layered.LNodeID(nil), .nodes...), .nodes...)
	var  []int
	if .outgoing != nil {
		 = append([]int(nil), .outgoing...)
		removeFirstGroup(&, )
		for ,  := range .outgoing {
			if  ==  {
				continue
			}
			if containsGroup(, ) {
				(*)[].incomingConstraintsCount--
			} else {
				 = append(, )
			}
		}
	} else if .outgoing != nil {
		 = append([]int(nil), .outgoing...)
		removeFirstGroup(&, )
	}
	 := .summedWeight + .summedWeight
	 := .degree + .degree
	 := .groupBarycenter(, *, )
	 := .groupBarycenter(, *, )
	 := len(*)
	* = append(*, constraintGroup{summedWeight: , degree: , nodes: , outgoing: })
	var  *float64
	switch {
	case  > 0:
		 = floatPtr( / float64())
	case  != nil &&  != nil:
		 = floatPtr((* + *) / 2)
	case  != nil:
		 = floatPtr(*)
	case  != nil:
		 = floatPtr(*)
	}
	if  != nil {
		for ,  := range  {
			.state(, ).Barycenter = floatPtr(*)
		}
	}
	return 
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 []constraintGroup,
	 int,
) *float64 {
	return .state(, [].nodes[0]).Barycenter
}

func ( *ForsterConstraintResolver) (
	 *layered.LGraphArena,
	 layered.LNodeID,
) *BarycenterState {
	 := .Layer(*.Node().Layer).ID
	return &.BarycenterStates[][.Node().ID]
}

func groupListIndex( []int,  int) int {
	for ,  := range  {
		if  ==  {
			return 
		}
	}
	return -1
}

func removeFirstGroup( *[]int,  int) bool {
	for ,  := range * {
		if  ==  {
			* = append((*)[:], (*)[+1:]...)
			return true
		}
	}
	return false
}

func containsGroup( []int,  int) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}

func floatPtr( float64) *float64 { return & }
func cloneFloat( *float64) *float64 {
	if  == nil {
		return nil
	}
	return floatPtr(*)
}