// Copyright (c) 2010, 2015 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 
	
	
)

const randomBarycenterAmount = float32(0.07)

type BarycenterHeuristic struct {
	Arena       *layered.LGraphArena
	Resolver    *ForsterConstraintResolver
	Random      *javacompat.Random
	Distributor *BarycenterPortDistributor
	ModelOrder  *ModelOrderBarycenterHeuristic
}

func (
	 *layered.LGraphArena,
	 *ForsterConstraintResolver,
	 *javacompat.Random,
	 *BarycenterPortDistributor,
) *BarycenterHeuristic {
	return &BarycenterHeuristic{Arena: , Resolver: , Random: , Distributor: }
}

func ( *BarycenterHeuristic) (
	 *[]layered.LNodeID,
	, ,  bool,
) {
	if  {
		.randomizeBarycenters(*)
	} else {
		.calculateBarycenters(*, )
		.fillUnknownBarycenters(*, )
	}
	if len(*) <= 1 {
		return
	}
	if .ModelOrder != nil {
		.ModelOrder.sort(, *)
		if .ModelOrder.ForceNodeModelOrder {
			return
		}
	} else {
		javacompat.GWTStableSort(*, .compareBarycenters)
	}
	.Resolver.ProcessConstraints(.Arena, )
}

func ( *BarycenterHeuristic) (
	 [][]layered.LNodeID,
	 int,
	,  bool,
) bool {
	if !isFirstSweepLayer(len(), , ) {
		 :=  + 1
		if  {
			 =  - 1
		}
		.Distributor.CalculatePortRanks(
			.Arena, [], portTypeForSweep(),
		)
	}
	 := !
	if len([]) > 0 && .Arena.Node([][0]).Type == layered.ExternalPortNode {
		 = true
	}
	 := append([]layered.LNodeID(nil), []...)
	.MinimizeLayer(&, , false, )
	copy([], )
	return false
}

func ( *BarycenterHeuristic) (
	 [][]layered.LNodeID,
	 bool,
) bool {
	 := 0
	if ! && len() > 0 {
		 = len() - 1
	}
	 := append([]layered.LNodeID(nil), []...)
	.MinimizeLayer(&, false, true, )
	copy([], )
	return false
}

func ( *BarycenterHeuristic) ( []layered.LNodeID) {
	for ,  := range  {
		 := .Random.NextDouble()
		 := .Resolver.state(.Arena, )
		.Barycenter = floatPtr()
		.SummedWeight = 
		.Degree = 1
	}
}

func ( *BarycenterHeuristic) (
	 []layered.LNodeID,
	 bool,
) {
	if  {
		 := -1.0
		for ,  := range  {
			 := .Resolver.state(.Arena, )
			if .Barycenter == nil {
				 :=  + 1
				for ,  := range [+1:] {
					if  := .Resolver.state(.Arena, ).Barycenter;  != nil {
						 = *
						break
					}
				}
				 := ( + ) / 2
				.Barycenter = floatPtr()
				.SummedWeight = 
				.Degree = 1
			}
			 = *.Barycenter
		}
		return
	}
	 := 0.0
	for ,  := range  {
		if  := .Resolver.state(.Arena, ).Barycenter;  != nil && * >  {
			 = *
		}
	}
	 += 2
	for ,  := range  {
		 := .Resolver.state(.Arena, )
		if .Barycenter == nil {
			 := float64(.Random.NextFloat())* - 1
			.Barycenter = floatPtr()
			.SummedWeight = 
			.Degree = 1
		}
	}
}

func ( *BarycenterHeuristic) ( []layered.LNodeID,  bool) {
	for ,  := range  {
		.Resolver.state(.Arena, ).Visited = false
	}
	for ,  := range  {
		.calculateBarycenter(, )
	}
}

func ( *BarycenterHeuristic) ( layered.LNodeID,  bool) {
	 := .Resolver.state(.Arena, )
	if .Visited {
		return
	}
	.Visited = true
	.Degree = 0
	.SummedWeight = 0
	.Barycenter = nil
	for ,  := range .Arena.Node().Ports {
		if  {
			for ,  := range .Arena.Port().IncomingEdges {
				.addFixedPort(, *.Arena.Edge().Source, )
			}
		} else {
			for ,  := range .Arena.Port().OutgoingEdges {
				.addFixedPort(, *.Arena.Edge().Target, )
			}
		}
	}
	for ,  := range basegraph.Get(&.Arena.Node().Properties, layered.BarycenterAssociates) {
		if !sameLayer(.Arena.Node().Layer, .Arena.Node().Layer) {
			continue
		}
		.(, )
		 := .Resolver.state(.Arena, )
		 = .Resolver.state(.Arena, )
		.Degree += .Degree
		.SummedWeight += .SummedWeight
	}
	 = .Resolver.state(.Arena, )
	if .Degree > 0 {
		 := .Random.NextFloat()*randomBarycenterAmount - randomBarycenterAmount/2
		.SummedWeight += float64()
		.Barycenter = floatPtr(.SummedWeight / float64(.Degree))
	}
}

func ( *BarycenterHeuristic) (
	 layered.LNodeID,
	 layered.LPortID,
	 bool,
) {
	 := *.Arena.Port().Node
	 := .Resolver.state(.Arena, )
	if sameLayer(.Arena.Node().Layer, .Arena.Node().Layer) {
		if  ==  {
			return
		}
		.calculateBarycenter(, )
		 := .Resolver.state(.Arena, )
		.Degree += .Degree
		.SummedWeight += .SummedWeight
		return
	}
	.SummedWeight += float64(.Distributor.PortRanks[.Arena.Port().ID])
	.Degree++
}

func ( *BarycenterHeuristic) (,  layered.LNodeID) int {
	 := .Resolver.state(.Arena, ).Barycenter
	 := .Resolver.state(.Arena, ).Barycenter
	if  != nil &&  != nil {
		return compareFloat(*, *)
	}
	if  != nil {
		return -1
	}
	if  != nil {
		return 1
	}
	return 0
}