// Copyright (c) 2016 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// 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 components

import (
	
	

	
	basegraph 
	
)

type externalCompactionDirection uint8

const (
	externalDirectionUndefined externalCompactionDirection = iota
	externalDirectionLeft
	externalDirectionRight
	externalDirectionUp
	externalDirectionDown
)

func ( externalCompactionDirection) () bool {
	return  == externalDirectionLeft ||  == externalDirectionRight
}

type externalCompactionExtension struct {
	direction        externalCompactionDirection
	side             layered.PortSide
	edges            []layered.LEdgeID
	group            *componentCompactionGroup
	node             *componentCompactionNode
	placeholder      *componentCompactionNode
	placeholderGroup *componentCompactionGroup
}

type externalCompactedComponent struct {
	graphID     layered.LGraphID
	oldPosition geometry.Point
	offsetNode  *componentCompactionNode
}

type externalComponentsCompactor struct {
	arena             *layered.LGraphArena
	componentIDs      []layered.LGraphID
	spacing           float64
	graph             *componentCompactionGraph
	components        []externalCompactedComponent
	extensions        []*externalCompactionExtension
	horizontal        []*externalCompactionExtension
	vertical          []*externalCompactionExtension
	graphTopLeft      geometry.Point
	graphBottomRight  geometry.Point
	compactionTopLeft geometry.Point
	compactor         *componentOneDimensionalCompactor
}

func compactExternalComponentGroups(
	 *layered.LGraphArena,
	 []layered.LGraphID,
	 layered.LGraphID,
	 float64,
) error {
	for ,  := range  {
		 := .Graph()
		offsetGraph(, , .Offset)
	}
	, ,  := compactConnectedComponentsWithExternal(
		, , )
	if  != nil {
		return 
	}
	for ,  := range  {
		.Graph().Offset = 
	}
	.Graph().Size = 
	return nil
}

// compactConnectedComponentsWithExternal is ComponentsCompactor plus
// ComponentsToCGraphTransformer and OneDimensionalComponentsCompaction for
// ComponentGroupGraphPlacer's external-port path. Component coordinates are
// moved by their individual compaction offsets. The returned common offset is
// installed as every component graph's offset by the caller and is therefore
// applied exactly once by AbstractGraphPlacer.moveGraph.
func compactConnectedComponentsWithExternal(
	 *layered.LGraphArena,
	 []layered.LGraphID,
	 float64,
) (geometry.Point, geometry.Point, error) {
	 := &externalComponentsCompactor{
		arena:            ,
		componentIDs:     append([]layered.LGraphID(nil), ...),
		spacing:          ,
		graph:            &componentCompactionGraph{},
		graphTopLeft:     geometry.Point{X: positiveInfinity, Y: positiveInfinity},
		graphBottomRight: geometry.Point{X: negativeInfinity, Y: negativeInfinity},
	}
	if  := .build();  != nil {
		return geometry.Point{}, geometry.Point{}, 
	}
	.compact()
	return .apply()
}

func ( *externalComponentsCompactor) () error {
	for ,  := range .componentIDs {
		for ,  := range .arena.Graph().LayerlessNodes {
			 := .arena.Node()
			.graphTopLeft.X = math.Min(
				.graphTopLeft.X, .Position.X-.Margin.Left)
			.graphTopLeft.Y = math.Min(
				.graphTopLeft.Y, .Position.Y-.Margin.Top)
			.graphBottomRight.X = math.Max(
				.graphBottomRight.X,
				.Position.X+.Size.X+.Margin.Right)
			.graphBottomRight.Y = math.Max(
				.graphBottomRight.Y,
				.Position.Y+.Size.Y+.Margin.Bottom)
		}
	}

	for ,  := range .componentIDs {
		if  := .transformGraph();  != nil {
			return 
		}
	}

	// ELK adds all extension representors before constructing the one-
	// dimensional compactor so their offsets within the component group are
	// initialized, then removes them until the relevant directional pass.
	for ,  := range .horizontal {
		.addExtension()
	}
	for ,  := range .vertical {
		.addExtension()
	}
	.compactor = newComponentOneDimensionalCompactor(
		.graph, .spacing)

	.compactionTopLeft = geometry.Point{
		X: positiveInfinity, Y: positiveInfinity,
	}
	 := geometry.Point{X: negativeInfinity, Y: negativeInfinity}
	for ,  := range .graph.nodes {
		.compactionTopLeft.X = math.Min(
			.compactionTopLeft.X, .hitbox.X)
		.compactionTopLeft.Y = math.Min(
			.compactionTopLeft.Y, .hitbox.Y)
		.X = math.Max(.X, .hitbox.MaxX())
		.Y = math.Max(.Y, .hitbox.MaxY())
	}

	for ,  := range .horizontal {
		.removeExtension()
	}
	for ,  := range .vertical {
		.removeExtension()
	}
	return nil
}

func ( *externalComponentsCompactor) (
	 layered.LGraphID,
) error {
	 := .arena.Graph()
	 := false
	for ,  := range .LayerlessNodes {
		 =  ||
			.arena.Node().Type == layered.NormalNode
	}
	if ! {
		if  := .createDummyNode();  != nil {
			return 
		}
	}

	 := newComponentCompactionHull()
	for ,  := range .LayerlessNodes {
		 := .arena.Node()
		if .Type == layered.ExternalPortNode {
			continue
		}
		.addNode()
		for ,  := range .arena.NodeOutgoingEdges() {
			if .externalEdge() {
				continue
			}
			for ,  := range .arena.Edge().BendPoints {
				.addPoint()
			}
		}
	}

	type  struct {
		 [5]float64
		 [5]float64
		  [5]float64
		   [5][]layered.LEdgeID
	}
	 := {}
	for  := range . {
		.[] = positiveInfinity
		.[] = negativeInfinity
		.[] = negativeInfinity
	}
	for ,  := range .LayerlessNodes {
		for ,  := range .arena.NodeOutgoingEdges() {
			if !.externalEdge() {
				continue
			}
			, , ,  := .externalEdgeSegments()
			if  != nil {
				return 
			}
			 := math.Max(basegraph.Get(
				&.arena.Edge().Properties,
				layered.EdgeThickness), 1)
			for ,  := range  {
				.addRectangle(segmentRectangle(
					[0], [1], ))
			}
			 := segmentRectangle(
				[0], [1], )
			if  == layered.WestSide ||  == layered.EastSide {
				.[] = math.Min(
					.[], .Y)
				.[] = math.Max(
					.[], .MaxY())
			} else {
				.[] = math.Min(
					.[], .X)
				.[] = math.Max(
					.[], .MaxX())
			}
			 := .externalNode()
			 := .arena.Node().Margin
			var  float64
			switch  {
			case layered.WestSide:
				 = .Right
			case layered.EastSide:
				 = .Left
			case layered.NorthSide:
				 = .Bottom
			case layered.SouthSide:
				 = .Top
			}
			.[] = math.Max(.[], )
			.[] = appendUniqueEdge(.[], )
		}
	}

	 := basegraph.Get(&.Properties,
		layered.ExternalPortConnections)
	 := make([]*externalCompactionExtension, 0, 4)
	for  := layered.NorthSide;  <= layered.WestSide; ++ {
		if &(1<<) == 0 {
			continue
		}
		if len(.[]) == 0 {
			return fmt.Errorf(
				"components: external side %s has no external edge", )
		}
		,  := .[], .[]
		 := .[]
		var ,  geometry.Rectangle
		switch  {
		case layered.WestSide:
			 = geometry.Rectangle{
				X: .graphTopLeft.X, Y: ,
				Width:  .topLeft.X - .graphTopLeft.X,
				Height:  - ,
			}
			 = geometry.Rectangle{
				X: .graphTopLeft.X, Y: ,
				Width: , Height:  - ,
			}
			.addPoint(.TopRight())
			.addPoint(.BottomRight())
		case layered.EastSide:
			 = geometry.Rectangle{
				X: .bottomRight.X, Y: ,
				Width:  .graphBottomRight.X - .bottomRight.X,
				Height:  - ,
			}
			 = geometry.Rectangle{
				X: .graphBottomRight.X - , Y: ,
				Width: , Height:  - ,
			}
			.addPoint(.Position())
			.addPoint(.BottomLeft())
		case layered.NorthSide:
			 = geometry.Rectangle{
				X: , Y: .graphTopLeft.Y,
				Width:   - ,
				Height: .topLeft.Y - .graphTopLeft.Y,
			}
			 = geometry.Rectangle{
				X: , Y: .graphTopLeft.Y,
				Width:  - , Height: ,
			}
			.addPoint(.BottomLeft())
			.addPoint(.BottomRight())
		case layered.SouthSide:
			 = geometry.Rectangle{
				X: , Y: .bottomRight.Y,
				Width:   - ,
				Height: .graphBottomRight.Y - .bottomRight.Y,
			}
			 = geometry.Rectangle{
				X: , Y: .graphBottomRight.Y - ,
				Width:  - , Height: ,
			}
			.addPoint(.Position())
			.addPoint(.TopRight())
		}
		 := 1.0
		 := &externalCompactionExtension{
			direction: externalDirectionForSide(),
			side:      ,
			edges:     append([]layered.LEdgeID(nil), .[]...),
			node: &componentCompactionNode{
				hitbox: , reposition: true,
				lock: componentLocks(),
			},
			placeholder: &componentCompactionNode{
				hitbox: , spacing: &,
				reposition: true, lock: componentLocks(),
			},
		}
		.placeholderGroup = &componentCompactionGroup{
			nodes:     []*componentCompactionNode{.placeholder},
			reference: .placeholder, reposition: true,
		}
		.placeholder.group = .placeholderGroup
		 = append(, )
	}

	 := splitRectilinearHull(.points)
	if len() == 0 {
		return fmt.Errorf("components: component %d has no rectilinear hull", )
	}
	 := make([]*componentCompactionNode, 0, len())
	 := componentLocks()
	for ,  := range  {
		 := &componentCompactionNode{
			hitbox: , reposition: true, lock: ,
		}
		.graph.nodes = append(.graph.nodes, )
		 = append(, )
	}
	 := .graph.addGroup(...)
	for ,  := range  {
		.group = 
		.extensions = append(.extensions, )
		if .direction.horizontal() {
			.horizontal = append(.horizontal, )
		} else {
			.vertical = append(.vertical, )
		}
	}
	.components = append(.components,
		externalCompactedComponent{
			graphID: , oldPosition: [0].Position(),
			offsetNode: [0],
		})
	return nil
}

func ( *externalComponentsCompactor) (
	 layered.LGraphID,
) error {
	 := .arena.Graph()
	if len(.LayerlessNodes) != 1 {
		return fmt.Errorf(
			"components: component %d without regular nodes has %d external nodes",
			, len(.LayerlessNodes))
	}
	 := .LayerlessNodes[0]
	 := .arena.Node()
	if .Type != layered.ExternalPortNode || len(.Ports) == 0 {
		return fmt.Errorf(
			"components: component %d has no usable external-port dummy", )
	}
	 := .arena.AddNode()
	 := .arena.Node()
	.Size = geometry.Point{
		X: math.Max(1, .Size.X),
		Y: math.Max(1, .Size.Y),
	}
	.Position = .Position
	 := basegraph.Get(&.Properties, layered.ExternalPortSide)
	switch  {
	case layered.WestSide:
		.Position.X += 2
	case layered.NorthSide:
		.Position.Y += 2
	case layered.EastSide:
		.Position.X -= 2
	case layered.SouthSide:
		.Position.Y -= 2
	}
	.LayerlessNodes = append(.LayerlessNodes, )
	 := .arena.AddPort()
	.arena.SetPortNode(, &)
	 := .Ports[0]
	 := .arena.Port()
	.Position = .arena.Port().Position
	.Anchor = .arena.Port().Anchor
	 := .arena.AddEdge()
	.arena.SetEdgeSource(, &)
	.arena.SetEdgeTarget(, &)
	return nil
}

func ( *externalComponentsCompactor) () {
	.compactor.lockingStrategy = func(
		 *componentCompactionNode,
		 componentCompactDirection,
	) bool {
		return .group.outDegree != 0
	}
	 := 0
	for {
		 := .compactRun()
		++
		if !(( < 2 ||  > connectedComponentsEpsilon) &&
			 < connectedComponentsMaxIterations) {
			break
		}
	}
	.compactor.lockingStrategy = func(
		 *componentCompactionNode,
		 componentCompactDirection,
	) bool {
		return .lock[] ||
			(.group.outDegree != 0 && .lock[])
	}
	.compactRun()
}

func ( *externalComponentsCompactor) () float64 {
	for ,  := range .graph.groups {
		.delta = 0
		.deltaNormalized = 0
	}

	.addPlaceholders(true)
	for ,  := range .vertical {
		.addExtension()
	}
	.compactor.calculateGroupOffsets()
	.compactor.forceConstraintsRecalculation()
	.compactor.changeDirection(componentDirectionLeft)
	.compactor.compact()
	.compactor.changeDirection(componentDirectionRight)
	.compactor.applyLockingStrategy()
	.compactor.compact()
	.compactor.changeDirection(componentDirectionLeft)
	.compactor.applyLockingStrategy()
	.compactor.compact()
	.compactor.changeDirection(componentDirectionLeft)
	for ,  := range .vertical {
		.removeExtension()
	}
	.removePlaceholders(true)
	.updateExternalExtensions(true)
	.updatePlaceholders(false)

	.addPlaceholders(false)
	for ,  := range .horizontal {
		.addExtension()
	}
	.compactor.calculateGroupOffsets()
	 := 0.0
	for ,  := range .graph.groups {
		 += math.Abs(.deltaNormalized)
		.delta = 0
		.deltaNormalized = 0
	}
	.compactor.changeDirection(componentDirectionUp)
	.compactor.forceConstraintsRecalculation()
	.compactor.compact()
	.compactor.changeDirection(componentDirectionDown)
	.compactor.applyLockingStrategy()
	.compactor.compact()
	.compactor.changeDirection(componentDirectionUp)
	.compactor.applyLockingStrategy()
	.compactor.compact()
	.compactor.changeDirection(componentDirectionLeft)
	for ,  := range .horizontal {
		.removeExtension()
	}
	.removePlaceholders(false)
	.updateExternalExtensions(false)
	.updatePlaceholders(true)
	.compactor.forceConstraintsRecalculation()
	for ,  := range .graph.groups {
		 += math.Abs(.deltaNormalized)
	}
	return 
}

func ( *externalComponentsCompactor) (
	 *externalCompactionExtension,
) {
	.graph.nodes = append(.graph.nodes, .node)
	.group.nodes = append(.group.nodes, .node)
	.node.group = .group
}

func ( *externalComponentsCompactor) (
	 *externalCompactionExtension,
) {
	.graph.nodes = removeCompactionNode(
		.graph.nodes, .node)
	.group.nodes = removeCompactionNode(
		.group.nodes, .node)
}

func ( *externalComponentsCompactor) ( bool) {
	for ,  := range .extensions {
		if .direction.horizontal() !=  {
			continue
		}
		.graph.nodes = append(
			.graph.nodes, .placeholder)
		.graph.groups = append(
			.graph.groups, .placeholderGroup)
	}
}

func ( *externalComponentsCompactor) ( bool) {
	for ,  := range .extensions {
		if .direction.horizontal() !=  {
			continue
		}
		.graph.nodes = removeCompactionNode(
			.graph.nodes, .placeholder)
		.graph.groups = removeCompactionGroup(
			.graph.groups, .placeholderGroup)
	}
}

func ( *externalComponentsCompactor) ( bool) {
	for ,  := range .extensions {
		if .direction.horizontal() !=  {
			continue
		}
		 := .group.deltaNormalized
		if .direction.horizontal() {
			.placeholder.hitbox.Y += 
		} else {
			.placeholder.hitbox.X += 
		}
	}
}

func ( *externalComponentsCompactor) (
	 bool,
) {
	for ,  := range .extensions {
		if .direction.horizontal() !=  {
			continue
		}
		 := .group.deltaNormalized
		switch .direction {
		case externalDirectionLeft:
			.node.hitbox.X = .compactionTopLeft.X
			.node.hitbox.Width = math.Max(
				1, .node.hitbox.Width+)
		case externalDirectionRight:
			.node.hitbox.X += 
			.node.hitbox.Width = math.Max(
				1, .node.hitbox.Width-)
		case externalDirectionUp:
			.node.hitbox.Y = .compactionTopLeft.Y
			.node.hitbox.Height = math.Max(
				1, .node.hitbox.Height+)
		case externalDirectionDown:
			.node.hitbox.Y += 
			.node.hitbox.Height = math.Max(
				1, .node.hitbox.Height-)
		}
	}
}

func ( *externalComponentsCompactor) () (
	geometry.Point,
	geometry.Point,
	error,
) {
	 := geometry.Point{X: positiveInfinity, Y: positiveInfinity}
	 := geometry.Point{X: negativeInfinity, Y: negativeInfinity}
	for ,  := range .graph.nodes {
		.X = math.Min(.X, .hitbox.X)
		.Y = math.Min(.Y, .hitbox.Y)
		.X = math.Max(.X, .hitbox.MaxX())
		.Y = math.Max(.Y, .hitbox.MaxY())
	}
	for ,  := range .extensions {
		 := .placeholder
		.X = math.Min(.X, .hitbox.X)
		.Y = math.Min(.Y, .hitbox.Y)
		.X = math.Max(.X, .hitbox.MaxX())
		.Y = math.Max(.Y, .hitbox.MaxY())
	}
	 := geometry.Point{X: -.X, Y: -.Y}
	 := .Sub()
	for ,  := range .components {
		 := .offsetNode.hitbox.Position()
		 := .Sub(.oldPosition).Add()
		offsetGraph(.arena, .graphID, )
		for ,  := range .arena.Graph(.graphID).LayerlessNodes {
			 := .arena.Node()
			if .Type != layered.ExternalPortNode {
				continue
			}
			.Position = .externalPortPosition(
				.Position,
				basegraph.Get(&.Properties, layered.ExternalPortSide))
		}
	}

	 := geometry.Point{}
	 := make(map[layered.LEdgeID]bool)
	for ,  := range .extensions {
		for ,  := range .edges {
			if [] {
				continue
			}
			[] = true
			 := .arena.Edge()
			if .Source == nil || .Target == nil {
				continue
			}
			 := make(geometry.Chain, 0, len(.BendPoints)+2)
			 = append(,
				componentPortAbsoluteAnchor(.arena, *.Source))
			 = append(, .BendPoints...)
			 = append(,
				componentPortAbsoluteAnchor(.arena, *.Target))
			for  := 1;  < len(); ++ {
				,  := [-1], []
				if math.Abs(.X-.X) <= connectedComponentsEpsilon {
					.X = math.Min(.X, .X)
					.X = math.Max(.X, .X)
				} else if math.Abs(.Y-.Y) <= connectedComponentsEpsilon {
					.Y = math.Min(.Y, .Y)
					.Y = math.Max(.Y, .Y)
				}
			}
		}
	}
	.X = -.X
	.Y = -.Y
	 = .Add()
	return , , nil
}

func ( *externalComponentsCompactor) (
	 layered.LEdgeID,
) bool {
	 := .arena.Edge()
	return .Source != nil && .Target != nil &&
		(.arena.Node(.arena.EdgeSourceNode()).Type ==
			layered.ExternalPortNode ||
			.arena.Node(.arena.EdgeTargetNode()).Type ==
				layered.ExternalPortNode)
}

func ( *externalComponentsCompactor) (
	 layered.LEdgeID,
) layered.LNodeID {
	 := .arena.EdgeSourceNode()
	if .arena.Node().Type == layered.ExternalPortNode {
		return 
	}
	return .arena.EdgeTargetNode()
}

func ( *externalComponentsCompactor) (
	 layered.LEdgeID,
) (layered.PortSide, [][2]geometry.Point, [2]geometry.Point, error) {
	 := .arena.Edge()
	if .Source == nil || .Target == nil {
		return layered.UndefinedSide, nil, [2]geometry.Point{},
			fmt.Errorf("components: external edge %d has an unset endpoint", )
	}
	 := *.Source
	 := .arena.Node(
		.arena.EdgeSourceNode()).Type == layered.ExternalPortNode
	if ! {
		 = *.Target
	}
	 := *.arena.Port().Node
	 := basegraph.Get(
		&.arena.Node().Properties,
		layered.ExternalPortSide)
	 := componentPortAbsoluteAnchor(.arena, *.Source)
	 := componentPortAbsoluteAnchor(.arena, *.Target)
	if  {
		 = .externalPortPosition(, )
		 = componentPortPositionOnMargin(.arena, *.Target)
	} else {
		 = componentPortPositionOnMargin(.arena, *.Source)
		 = .externalPortPosition(, )
	}
	 := make(geometry.Chain, 0, len(.BendPoints)+2)
	 = append(, )
	 = append(, .BendPoints...)
	 = append(, )
	 := make([][2]geometry.Point, 0, len()-2)
	var  [2]geometry.Point
	for  := 0; +1 < len(); ++ {
		 := [2]geometry.Point{[], [+1]}
		if  &&  == 0 || ! &&  == len()-2 {
			 = 
		} else {
			 = append(, )
		}
	}
	return , , , nil
}

func ( *externalComponentsCompactor) (
	 geometry.Point,
	 layered.PortSide,
) geometry.Point {
	switch  {
	case layered.NorthSide:
		.Y = math.Min(.graphTopLeft.Y, .Y)
	case layered.EastSide:
		.X = math.Max(.graphBottomRight.X, .X)
	case layered.SouthSide:
		.Y = math.Max(.graphBottomRight.Y, .Y)
	case layered.WestSide:
		.X = math.Min(.X, .graphTopLeft.X)
	}
	return 
}

type componentCompactionHull struct {
	points      []hullPoint
	topLeft     geometry.Point
	bottomRight geometry.Point
}

func newComponentCompactionHull() *componentCompactionHull {
	return &componentCompactionHull{
		topLeft:     geometry.Point{X: positiveInfinity, Y: positiveInfinity},
		bottomRight: geometry.Point{X: negativeInfinity, Y: negativeInfinity},
	}
}

func ( *componentCompactionHull) ( geometry.Point) {
	.points = append(.points, newHullPoint(.X, .Y))
	.topLeft.X = math.Min(.topLeft.X, .X)
	.topLeft.Y = math.Min(.topLeft.Y, .Y)
	.bottomRight.X = math.Max(.bottomRight.X, .X)
	.bottomRight.Y = math.Max(.bottomRight.Y, .Y)
}

func ( *componentCompactionHull) ( geometry.Rectangle) {
	.addPoint(.Position())
	.addPoint(.TopRight())
	.addPoint(.BottomLeft())
	.addPoint(.BottomRight())
}

func ( *componentCompactionHull) ( *layered.LNode) {
	.addRectangle(geometry.Rectangle{
		X:      .Position.X - .Margin.Left,
		Y:      .Position.Y - .Margin.Top,
		Width:  .Size.X + .Margin.Left + .Margin.Right,
		Height: .Size.Y + .Margin.Top + .Margin.Bottom,
	})
}

func componentPortAbsoluteAnchor(
	 *layered.LGraphArena,
	 layered.LPortID,
) geometry.Point {
	 := .Port()
	return .Node(*.Node).Position.Add(.Position).Add(.Anchor)
}

func componentPortPositionOnMargin(
	 *layered.LGraphArena,
	 layered.LPortID,
) geometry.Point {
	 := .Port()
	 := componentPortAbsoluteAnchor(, )
	 := .Node(*.Node).Margin
	switch .Side {
	case layered.NorthSide:
		.Y -= .Top
	case layered.EastSide:
		.X += .Right
	case layered.SouthSide:
		.Y += .Bottom
	case layered.WestSide:
		.X -= .Left
	}
	return 
}

func segmentRectangle(
	,  geometry.Point,
	 float64,
) geometry.Rectangle {
	return geometry.Rectangle{
		X:      math.Min(.X, .X) - /2,
		Y:      math.Min(.Y, .Y) - /2,
		Width:  math.Abs(.X-.X) + ,
		Height: math.Abs(.Y-.Y) + ,
	}
}

func externalDirectionForSide( layered.PortSide) externalCompactionDirection {
	switch  {
	case layered.NorthSide:
		return externalDirectionUp
	case layered.EastSide:
		return externalDirectionRight
	case layered.SouthSide:
		return externalDirectionDown
	case layered.WestSide:
		return externalDirectionLeft
	default:
		return externalDirectionUndefined
	}
}

func componentLocks( uint8) [5]bool {
	 := [5]bool{false, true, true, true, true}
	switch  {
	case sidesNone:
		return 
	case sidesNorth:
		return [5]bool{false, true, true, true, false}
	case sidesEast:
		return [5]bool{false, false, true, true, true}
	case sidesSouth:
		return [5]bool{false, true, true, false, true}
	case sidesWest:
		return [5]bool{false, true, false, true, true}
	case sidesNorthEast:
		return [5]bool{false, false, true, true, false}
	case sidesEastSouth:
		return [5]bool{false, false, true, false, true}
	case sidesSouthWest:
		return [5]bool{false, true, false, false, true}
	case sidesNorthWest:
		return [5]bool{false, true, false, true, false}
	default:
		return 
	}
}

func appendUniqueEdge(
	 []layered.LEdgeID,
	 layered.LEdgeID,
) []layered.LEdgeID {
	for ,  := range  {
		if  ==  {
			return 
		}
	}
	return append(, )
}

func removeCompactionNode(
	 []*componentCompactionNode,
	 *componentCompactionNode,
) []*componentCompactionNode {
	for ,  := range  {
		if  ==  {
			copy([:], [+1:])
			return [:len()-1]
		}
	}
	return 
}

func removeCompactionGroup(
	 []*componentCompactionGroup,
	 *componentCompactionGroup,
) []*componentCompactionGroup {
	for ,  := range  {
		if  ==  {
			copy([:], [+1:])
			return [:len()-1]
		}
	}
	return 
}