From c57719f0084f91c97479eac253e6b069ff125e3c Mon Sep 17 00:00:00 2001
From: Levente Pap <levente.pap@iota.org>
Date: Thu, 18 Jun 2020 18:22:31 +0200
Subject: [PATCH] graph pkg from autopeering-sim

---
 packages/graph/graph.go | 149 ++++++++++++++++++++++++++++++++++++++++
 packages/graph/list.go  |  54 +++++++++++++++
 2 files changed, 203 insertions(+)
 create mode 100644 packages/graph/graph.go
 create mode 100644 packages/graph/list.go

diff --git a/packages/graph/graph.go b/packages/graph/graph.go
new file mode 100644
index 00000000..ed62ad4c
--- /dev/null
+++ b/packages/graph/graph.go
@@ -0,0 +1,149 @@
+package graph
+
+import (
+	"runtime"
+	"sync"
+)
+
+type nodeId int32
+
+type SymbolTable map[string]nodeId
+
+func (s SymbolTable) getId(name string) nodeId {
+	id, ok := s[name]
+	if !ok {
+		id = nodeId(len(s))
+		s[name] = id
+	}
+	return id
+}
+
+type Graph struct {
+	SymbolTable
+	Nodes
+}
+
+func New(IDs []string) *Graph {
+	g := &Graph{
+		SymbolTable: make(SymbolTable, len(IDs)),
+		Nodes:       make(Nodes, len(IDs)),
+	}
+	for index, id := range IDs {
+		g.Nodes[index].ID = nodeId(index)
+		g.SymbolTable[id] = nodeId(index)
+	}
+	return g
+}
+
+func (g *Graph) AddEdge(a, b string) {
+	aid := g.SymbolTable.getId(a)
+	bid := g.SymbolTable.getId(b)
+
+	g.Nodes.AddEdge(aid, bid)
+}
+
+type Node struct {
+	ID nodeId
+
+	// adjacent edges
+	Adj []nodeId
+}
+
+func (n *Node) add(adjNode *Node) {
+	for _, id := range n.Adj {
+		if id == adjNode.ID {
+			return
+		}
+	}
+	n.Adj = append(n.Adj, adjNode.ID)
+}
+
+type Nodes []Node
+
+func (nl Nodes) get(id nodeId) *Node {
+	return &nl[id]
+}
+
+func (nl Nodes) AddEdge(a, b nodeId) {
+	an := nl.get(a)
+	bn := nl.get(b)
+
+	an.add(bn)
+	bn.add(an)
+}
+
+// diameter is the maximum length of a shortest path in the network
+func (nl Nodes) Diameter() int {
+
+	cpus := runtime.NumCPU()
+	numNodes := len(nl)
+	nodesPerCpu := numNodes / cpus
+
+	results := make([]int, cpus)
+	wg := &sync.WaitGroup{}
+	wg.Add(cpus)
+	start := 0
+	for cpu := 0; cpu < cpus; cpu++ {
+		end := start + nodesPerCpu
+		if cpu == cpus-1 {
+			end = numNodes
+		}
+
+		go func(cpu int, start, end nodeId) {
+			defer wg.Done()
+			var diameter int
+			q := &list{}
+			depths := make([]bfsNode, numNodes)
+			for id := start; id < end; id++ {
+				// Need to reset the bfsData between runs
+				for i := range depths {
+					depths[i] = -1
+				}
+
+				df := nl.longestShortestPath(nodeId(id), q, depths)
+				if df > diameter {
+					diameter = df
+				}
+			}
+			results[cpu] = diameter
+		}(cpu, nodeId(start), nodeId(end))
+		start += nodesPerCpu
+	}
+
+	wg.Wait()
+
+	diameter := 0
+	for _, result := range results {
+		if result > diameter {
+			diameter = result
+		}
+	}
+	return diameter
+}
+
+// bfs tracking data
+type bfsNode int16
+
+func (nodes Nodes) longestShortestPath(start nodeId, q *list, depths []bfsNode) int {
+
+	n := nodes.get(start)
+	depths[n.ID] = 0
+	q.pushBack(n)
+
+	for {
+		newN := q.getHead()
+		if newN == nil {
+			break
+		}
+		n = newN
+
+		for _, id := range n.Adj {
+			if depths[id] == -1 {
+				depths[id] = depths[n.ID] + 1
+				q.pushBack(nodes.get(id))
+			}
+		}
+	}
+
+	return int(depths[n.ID])
+}
diff --git a/packages/graph/list.go b/packages/graph/list.go
new file mode 100644
index 00000000..5e15feff
--- /dev/null
+++ b/packages/graph/list.go
@@ -0,0 +1,54 @@
+package graph
+
+type listElt struct {
+	next *listElt
+	node *Node
+}
+
+type list struct {
+	head *listElt
+	tail *listElt
+
+	free *listElt
+}
+
+func (l *list) getHead() *Node {
+	elt := l.head
+	if elt == nil {
+		return nil
+	}
+
+	// Remove elt from the list
+	l.head = elt.next
+	if l.head == nil {
+		l.tail = nil
+	}
+	// Add elt to the free list
+	elt.next = l.free
+	l.free = elt
+
+	n := elt.node
+	elt.node = nil
+	return n
+}
+
+func (l *list) pushBack(n *Node) {
+	// Get a free listElt to use to point to this node
+	elt := l.free
+	if elt == nil {
+		elt = &listElt{}
+	} else {
+		l.free = elt.next
+		elt.next = nil
+	}
+
+	// Add the element to the tail of the list
+	elt.node = n
+	if l.tail == nil {
+		l.tail = elt
+		l.head = elt
+	} else {
+		l.tail.next = elt
+		l.tail = elt
+	}
+}
-- 
GitLab