diff --git a/Examples/BellmanFordShortestPath/bellmanFord.js b/Examples/BellmanFordShortestPath/bellmanFord.js
new file mode 100644
index 00000000..4b2e7254
--- /dev/null
+++ b/Examples/BellmanFordShortestPath/bellmanFord.js
@@ -0,0 +1,86 @@
+/*
+ * Bellman-Ford Single Source Shortest Path Algorithm
+ *
+ * Written by ctonetti
+ *
+ * (Can detect negative cycles in graph)
+ *
+ * Time Complexity: O(|E||V|)
+ *
+ * This algorithm finds the shortest path between
+ * a source node to all other nodes in a graph by
+ * relaxing each edge |V| times.
+ *
+ */
+
+var INF = 1000000
+var dist = {}
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+}
+
+function printDistances(G, start) {
+
+ G.nodes().forEach(function(node) {
+ if (node.id != start.id) {
+ if (dist[node.id] != INF) {
+ Console.log("Distance: " + start.id + " -> " + node.id + ": " + dist[node.id])
+ } else {
+ Console.log("Distance: " + start.id + " -> " + node.id + ": INF")
+ }
+ }
+ })
+}
+
+// It is possible to create a custom function to generate weights
+function createEdgeWeight(G) {
+ G.edges().forEach(function(edges) {
+ edges.weight = 1
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function bellmanFord(G, start) {
+ // Initialize node values with infinity
+ G.nodes().forEach(function(node) {
+ dist[node.id] = INF
+ })
+
+ dist[start.id] = 0
+ mark(start)
+
+ for (var i = 0; i < G.nodes().length; i++) {
+ G.edges().forEach(function(edge) {
+ if (dist[edge.from().id] + edge.weight < dist[edge.to().id]) {
+ dist[edge.to().id] = dist[edge.from().id] + edge.weight
+ }
+ })
+ }
+
+ // Check for negative cycles
+ var negative_cycle = false
+ G.edges().forEach(function(edge) {
+ if (dist[edge.from().id] + edge.weight < dist[edge.to().id]) {
+ Console.log("Graph contains negative cycle!")
+ negative_cycle = true
+ }
+ })
+
+ // print the results in the output window
+ if (!negative_cycle) {
+ printDistances(G, start)
+ }
+}
+
+reset(Document)
+createEdgeWeight(Document)
+bellmanFord(Document, Document.nodes()[0])
diff --git a/Examples/BreadthFirstSearch/bfs.graph b/Examples/BreadthFirstSearch/bfs.graph
deleted file mode 100644
index adaed83f..00000000
--- a/Examples/BreadthFirstSearch/bfs.graph
+++ /dev/null
@@ -1,119 +0,0 @@
-[Document Properties]
-top : -227.062
-bottom : 256.844
-left : -495
-right : 572.5
-DataStructurePlugin : Graph
-
-[DataType 0]
-Name : Innocent Fish
-IconName : rocs_wanda
-Color : #000000
-Properties : name=
-
-[DataType 1]
-Name : Mine
-IconName : rocs_mines
-Color : #000000
-Properties : name=
-
-[PointerType 0]
-Name : Connection
-Color : #808080
-LineStyle : 1
-Direction : bidirectional
-Properties :
-
-[DataStructure 0]
-X-plugin-type : 0
-name : lake
-
-[Data 1]
-type : 0
-x : 58
-y : 27
-width : 0.3
-id : 1
-color : #00aa00
-name : B
-
-[Data 2]
-type : 0
-x : 59
-y : 134
-width : 0.3
-id : 2
-color : #00aa00
-name : E
-
-[Data 4]
-type : 0
-x : 163
-y : 136
-width : 0.3
-id : 4
-color : #00aa00
-name : F
-
-[Data 5]
-type : 0
-x : -31
-y : -28
-width : 0.3
-id : 5
-color : #00aa00
-name : A
-
-[Data 6]
-type : 0
-x : 162
-y : 29
-width : 0.3
-id : 6
-color : #00aa00
-name : C
-
-[Data 3]
-type : 1
-x : 253
-y : 75
-width : 0.3
-id : 3
-color : #cc0000
-name : D
-
-[Pointer 5->1]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 1->6]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 6->4]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 4->2]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 2->1]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 3->6]
-type : 0
-color : #808080
-width : 1
-
-[Pointer 3->4]
-type : 0
-color : #808080
-width : 1
-
diff --git a/Examples/BreadthFirstSearch/bfs.js b/Examples/BreadthFirstSearch/bfs.js
index 7ed986b4..26fd0ebd 100644
--- a/Examples/BreadthFirstSearch/bfs.js
+++ b/Examples/BreadthFirstSearch/bfs.js
@@ -1,65 +1,55 @@
-/**
- * Breadth-first search
+/*
+ * Breadth-First Search
+ *
+ * Written by ctonetti
+ *
+ * Time Complexity: O(|E| + |V|)
+ *
+ * This algorithm is used to traverse an graph in an
+ * tree-like structure by exploring the nodes in
+ * "layers", prioritizing the exploration of the
+ * nearest neighbor of a node.
*
- * __(')< written by lambda fairy
- * \___)
*/
-function reset_graph(g) {
- function reset(thing) {
- thing.set_type(0)
- thing.color = '#999'
- }
- g.nodes().forEach(reset)
- g.edges().forEach(reset)
-}
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
-function add_mine(g) {
- var mine = g.nodes()[2] // Completely arbitrary
- mine.set_type(1)
+ G.nodes().forEach(function(node) {
+ node.seen = false
+ })
}
-function bfs(start, mark, is_match) {
- var queue = [start]
- start.seen = true; mark(start); interrupt()
- var cursor
- while (queue.length > 0) {
- cursor = queue.shift()
- if (is_match(cursor)) {
- interrupt()
- return true
- }
- cursor.neighbors().forEach(function(node) {
- if (!node.seen) {
- node.seen = true; mark(node)
- queue.push(node)
- }
- }); interrupt()
- }
- return false
+function mark(node) {
+ node.color = '#c00'
}
-function set_type_to(value) {
- return function(thing) { thing.set_type(value) }
+function unmark(node) {
+ node.color = '#fff'
}
-reset_graph(lake)
-add_mine(lake)
+function bfs(G, start) {
+
+ var queue = [start]
+ start.seen = true
-bfs(lake.nodes()[0],
- function(node) {
- Console.log('Found ' + node.name)
- node.color = '#f90'
- },
- function(node) {
- var match = false
- if (node.type() == 1) {
- Console.log('-- Deactivated mine at ' + node.name)
- node.color = '#c00'
- return true
- } else {
- Console.log('-- Cleared ' + node.name)
- node.color = '#0a0'
- return false
- }
- })
+ while (queue.length > 0) {
+
+ var top = queue.shift();
+ top.seen = true
+
+ mark(top)
+
+ top.neighbors()
+ .filter(function(x) {
+ return !(x.seen)
+ })
+ .forEach(function(x) {
+ queue.push(x)
+ })
+ }
+}
+
+reset(Document)
+bfs(Document, Document.nodes()[0])
diff --git a/Examples/BreadthFirstSearch/bfs.rocs b/Examples/BreadthFirstSearch/bfs.rocs
deleted file mode 100644
index ee7723c9..00000000
Binary files a/Examples/BreadthFirstSearch/bfs.rocs and /dev/null differ
diff --git a/Examples/BreadthFirstSearch/journal.html b/Examples/BreadthFirstSearch/journal.html
deleted file mode 100644
index fd2ffe41..00000000
--- a/Examples/BreadthFirstSearch/journal.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-This project demonstrates **breadth-first search**, a simple algorithm for searching through a graph.
-
-
-## Story
-
-A few days ago, someone accidentally dropped a mine in the middle of a lake (hey, The Matrix makes less sense than this and it earned millions). This poses a serious threat to the inhabitants of the surrounding area – especially the ducks – so the local council has tasked us with locating and deactivating the charge. To help with the search, we have a fancy object detector that can scan the whole lake at once. Unfortunately, it can't tell the difference between a mine and an innocent fish, so we have to check each point ourselves.
-
-
-## How it works
-
-In breadth-first search, we keep track of which nodes to examine next using a queue. Then we proceed as follows:
-
-1. Choose one node from the graph, and add it to the queue. This will be our starting point.
-2. Pop a node from the queue and examine it.
- * If it's a mine, we're done!
- * Otherwise, take the nodes next to it. If we haven't checked that node before, add it to the queue – we'll check it later.
-3. If the queue is empty, then we've searched the whole lake without getting a match. There's probably nothing to worry about.
-4. If the queue is not empty, repeat from step 2.
-
\ No newline at end of file
diff --git a/Examples/DepthFirstsSearch/dfs.js b/Examples/DepthFirstsSearch/dfs.js
new file mode 100644
index 00000000..281f722d
--- /dev/null
+++ b/Examples/DepthFirstsSearch/dfs.js
@@ -0,0 +1,63 @@
+/*
+ * Depth-First Search
+ *
+ * Written by ctonetti
+ *
+ * Time Complexity: O(|E| + |V|)
+ *
+ * This algorithm is used to traverse an graph in an
+ * tree-like structure by exploring the first branches
+ * it encounter in each node before backtracking when
+ * this is not possible anymore. This process continues
+ * until there is no more graph to be explored.
+ *
+ * This is an useful algorithm, as it can be used to
+ * detect loops, separate connected components, detect
+ * bridges and articulation vertexes, etc.
+ *
+ */
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+
+ G.nodes().forEach(function(node) {
+ node.seen = false
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function dfs(G, start) {
+
+ var stk = [start]
+ start.seen = true
+
+ while (stk.length > 0) {
+ // Get next node
+ var top = stk.pop()
+ top.seen = true
+
+ // Mark the node
+ mark(top)
+
+ // Process
+ top.neighbors()
+ .filter(function(x) { // Only get the unseen neighbors
+ return !(x.seen)
+ })
+ .forEach(function(x) { // Push the unseen nodes
+ stk.push(x)
+ })
+
+ }
+}
+
+reset(Document)
+dfs(Document, Document.nodes()[0])
diff --git a/Examples/DijkstraShortestPath/dijkstra.js b/Examples/DijkstraShortestPath/dijkstra.js
new file mode 100644
index 00000000..8b52392f
--- /dev/null
+++ b/Examples/DijkstraShortestPath/dijkstra.js
@@ -0,0 +1,164 @@
+/*
+ * Dijkstra Single Source Shortest Path Algorithm
+ *
+ * Written by ctonetti
+ *
+ * (Won't accept negative edges in the graph)
+ *
+ * Time Complexity:
+ * (Depends on the data structure used)
+ * - Our Priority Queue: O(V^2)
+ * - Binary Heap: O(|E|log|V|)
+ * - Fibonacci Heap: O(|E|+|V|log|V|)
+ *
+ * This algorithm finds the shortest path between
+ * a source node to all other nodes in a graph.
+ * It iteratively updates the distances to the nodes
+ * and always take the least cost node to build
+ * paths.
+ *
+ */
+
+var INF = 1000000
+var dist = {}
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+}
+
+function getIndex(node, array) {
+ for (var i = 0; i < array.length; i++) {
+ if (array[i].element.id == node.id) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+function printDistances(G, start) {
+
+ G.nodes().forEach(function(node) {
+ if (node.id != start.id) {
+ if (dist[node.id] != INF) {
+ Console.log("Distance: " + start.id + " -> " + node.id + ": " + dist[node.id])
+ } else {
+ Console.log("Distance: " + start.id + " -> " + node.id + ": INF")
+ }
+ }
+ })
+}
+
+// It is possible to create a custom function to generate weights
+function createEdgeWeight(G) {
+ G.edges().forEach(function(edges) {
+ edges.weight = 1
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function Element(e, p) {
+ this.element = e
+ this.priority = p
+}
+
+// Unoptimized data structure
+function PriorityQueue() {
+
+ this.items = []
+
+ // Time Complexity: O(N)
+ this.enqueue = function(node, weight) {
+ var e = new Element(node, weight);
+ var contain = false;
+
+ for (var i = 0; i < this.items.length; i++) {
+ if (this.items[i].priority > e.priority) {
+ this.items.splice(i, 0, e);
+ contain = true;
+ break;
+ }
+ }
+
+ if (!contain) {
+ this.items.push(e);
+ }
+ }
+
+ // Time Complexity: O(1)
+ this.dequeue = function() {
+ if (this.isEmpty())
+ return "Underflow";
+ return this.items.shift();
+ }
+
+ // Time Complexity: O(N)
+ this.update = function(node, weight) {
+ var idx = getIndex(node, this.items)
+
+ // In this case, we only update the PQ if the new priority
+ // is lower
+ if (idx != -1 && weight < this.items[idx].priority) {
+ this.items.splice(idx, 1)
+ this.enqueue(node, weight)
+ return true
+ }
+ return false
+ }
+
+ // Time Complexity: O(1)
+ this.isEmpty = function() {
+ return this.items.length == 0;
+ }
+
+ // Time Complexity: O(N)
+ this.print = function() {
+ this.items.forEach(function(item) {
+ Console.log(item.element.from().id + " - " + item.element.to().id)
+ Console.log(item.priority)
+ Console.log("----")
+ })
+ }
+
+}
+
+function dijkstra(G, start) {
+
+ var pq = new PriorityQueue()
+
+ // Initialize node values with infinity
+ G.nodes().forEach(function(node) {
+ pq.enqueue(node, INF)
+ })
+
+ pq.update(start, 0)
+ mark(start) // Initial is marked
+
+ while (!pq.isEmpty()) {
+
+ var e = pq.dequeue()
+
+ // Update distances
+ dist[e.element.id] = e.priority
+
+ // Relax each neighbor
+ e.element.outEdges().forEach(function(edge) {
+ pq.update(edge.to(), edge.weight + e.priority)
+ })
+ }
+
+ // print the results in the output window
+ printDistances(G, start)
+}
+
+reset(Document)
+createEdgeWeight(Document)
+dijkstra(Document, Document.nodes()[0])
diff --git a/Examples/FloydWarshallMultipleSourcesShortestPath/floydWarshall.js b/Examples/FloydWarshallMultipleSourcesShortestPath/floydWarshall.js
new file mode 100644
index 00000000..112fbac9
--- /dev/null
+++ b/Examples/FloydWarshallMultipleSourcesShortestPath/floydWarshall.js
@@ -0,0 +1,104 @@
+/*
+ * Floyd-Warshall Multiple Sources Shortest Paths Algorithm
+ *
+ * Written by ctonetti
+ *
+ * Time Complexity: O(V^3)
+ *
+ * This algorithm finds the shortest paths between
+ * every pair of nodes in a graph by iteratively
+ * "allowing" a number of nodes to be used in each
+ * path, constructing a matrix of distances.
+ *
+ */
+
+var INF = 1000000
+var dist = {}
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+
+ var V = G.nodes().length
+ var N = G.nodes()
+
+ // Create raw weight matrix
+ for (var i = 0; i < V; i++) {
+ dist[N[i].id] = {}
+ for (var j = 0; j < V; j++) {
+ if (N[i].id == N[j].id) {
+ dist[N[i].id][N[j].id] = 0
+ } else {
+ dist[N[i].id][N[j].id] = INF
+ }
+ }
+ }
+
+ createEdgeWeight(Document)
+
+ G.edges().forEach(function(edge) {
+ dist[edge.from().id][edge.to().id] = edge.weight
+ dist[edge.to().id][edge.from().id] = edge.weight
+ })
+}
+
+function printDistances(G) {
+ var V = G.nodes().length
+ var N = G.nodes()
+
+ Console.log("Distance Matrix:")
+
+ line = "id "
+ for (var i = 0; i < V; i++) {
+ line += N[i].id + " "
+ }
+ Console.log(line)
+
+ for (var i = 0; i < V; i++) {
+ var line = N[i].id + " "
+ for (var j = 0; j < V; j++) {
+ if (dist[N[i].id][N[j].id] == INF) {
+ line += "INF "
+ } else {
+ line += dist[N[i].id][N[j].id] + " "
+ }
+ }
+ Console.log(line)
+ }
+}
+
+// It is possible to create a custom function to generate weights
+function createEdgeWeight(G) {
+ G.edges().forEach(function(edges) {
+ edges.weight = 1
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function floydWarshall(G) {
+
+ var V = G.nodes().length
+ var N = G.nodes()
+
+ for (var k = 0; k < V; k++) {
+ for (var i = 0; i < V; i++) {
+ for (var j = 0; j < V; j++) {
+ if (dist[N[i].id][N[j].id] > dist[N[i].id][N[k].id] + dist[N[k].id][N[j].id]) {
+ dist[N[i].id][N[j].id] = dist[N[i].id][N[k].id] + dist[N[k].id][N[j].id]
+ }
+ }
+ }
+ }
+
+ printDistances(G)
+}
+
+reset(Document)
+floydWarshall(Document)
diff --git a/Examples/HopcraftKarpBipartiteMatching/hopcroftKarp.js b/Examples/HopcraftKarpBipartiteMatching/hopcroftKarp.js
new file mode 100644
index 00000000..fe525281
--- /dev/null
+++ b/Examples/HopcraftKarpBipartiteMatching/hopcroftKarp.js
@@ -0,0 +1,203 @@
+/*
+ * Hopcroft-Karp Maximum Bipartite Matching
+ *
+ * Written by ctonetti
+ *
+ * Time Complexity: O(|E|sqrt(|V|))
+ *
+ * A matching is a set of edges chosen in such a way that no
+ * two edges share an endpoint. A matching is of maximum size
+ * when it has the maximum number of edges possible.
+ *
+ * This algorithm starts with an empty matching and builds a
+ * maximum matching by finding augmenting paths iteratively.
+ * The algorithm ends when there is no augmenting paths left.
+ *
+ */
+
+var L = [] // Left side of the bipartite graph
+var R = [] // Right side of the bipartite graph
+
+var M = {} // Matching
+var D = {} // Distances
+
+var INF = 10000000
+var NIL = -1
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+
+ G.nodes().forEach(function(node) {
+ node.seen = false
+ node.bip = 0
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function printMatchings() {
+
+ Console.log("Matchings:")
+
+ L.forEach(function(node) {
+ if (M[node.id] != NIL) {
+ Console.log(" " + node.id + " --- " + M[node.id].id)
+ }
+ })
+}
+
+// Modified BFS to divide the graph in two
+// disjoint sets of nodes. This function also
+// checks if the graph is really bipartite
+function divideBipartite(G) {
+
+ if (G.nodes().length == 0) {
+ Console.log("Empty Graph!")
+ return false
+ }
+
+ var N = G.nodes()
+ var Q = [N[0]]
+
+ N[0].bip = 1
+
+ while (Q.length > 0) {
+
+ var top = Q.shift()
+
+ top.seen = true
+ mark(top)
+
+ neigh = top.neighbors()
+
+ // Color the neighbors of top
+ for (var i = 0; i < neigh.length; i++) {
+ if (neigh[i].bip == 0) { // Not assigned a set yet
+ neigh[i].bip = (top.bip == 1 ? 2 : 1)
+ } else { // Assigned, check if correct
+ if (neigh[i].bip == top.bip && neigh[i].id != top.id) {
+ return false
+ }
+ }
+
+ if (!neigh[i].seen) {
+ Q.push(neigh[i])
+ }
+ }
+ }
+
+ G.nodes().forEach(function(node) {
+ if (node.bip == 1) {
+ L.push(node)
+ }
+
+ if (node.bip == 2) {
+ R.push(node)
+ }
+
+ if (node.bip == 0) {
+ Console.log("Unconnected nodes!")
+ }
+ })
+
+ return true
+}
+
+// Modified BFS to check if an augmenting path exist
+// BFS treat the graph as layers, helping in identifying
+// alternating paths. Only consider one side of the
+// bipartite graph.
+function hasAugmentingPath(G) {
+
+ Q = [] // Queue
+
+ for (var i = 0; i < L.length; i++) {
+ if (M[L[i].id] == NIL) {
+ D[L[i].id] = 0
+ Q.push(L[i]) // Add free vertex to queue
+ } else {
+ D[L[i].id] = INF
+ }
+ }
+
+ D[NIL] = INF
+
+ while (Q.length > 0) {
+
+ var t = Q.pop()
+
+ if (t != NIL) {
+ t.neighbors().forEach(function(neigh) {
+ if (D[M[neigh.id]] == INF) {
+ D[M[neigh.id]] = D[t.id] + 1
+ Q.push(M[neigh.id])
+ }
+ })
+ }
+ }
+
+ return (D[NIL] != INF)
+}
+
+// Get the augmenting path that begins in node
+// "node" and apply to the matching. If no such
+// path exist, the function returns false, true
+// otherwise.
+function applyAugmentingPath(G, node) {
+
+ if (node != NIL) {
+
+ var neighs = node.neighbors()
+
+ for (var i = 0; i < neighs.length; i++) {
+
+ var neigh = neighs[i]
+
+ if (D[M[neigh.id]] == D[node.id] + 1) {
+ M[neigh.id] = node
+ M[node.id] = neigh
+ return true;
+ }
+ }
+
+ D[node.id] = INF
+ return false
+ }
+
+ return true
+}
+
+// Main function
+function hopcroftKarp(G) {
+
+ // Initialize all nodes as not matched
+ G.nodes().forEach(function(node) {
+ M[node.id] = NIL
+ })
+
+ var matches = 0
+
+ // While a augmenting path exist,
+ // update matching
+ while(hasAugmentingPath(G)) {
+ for (var i = 0; i < L.length; i++) {
+ if (M[L[i].id] == NIL && applyAugmentingPath(G, L[i])) {
+ matches++ // Count number of matches
+ }
+ }
+ }
+
+ return matches
+}
+
+reset(Document)
+divideBipartite(Document)
+Console.log("Total Matchings: " + hopcroftKarp(Document))
+printMatchings()
diff --git a/Examples/KruskalSpanningTree/kruskal.js b/Examples/KruskalSpanningTree/kruskal.js
new file mode 100644
index 00000000..fcfc1e16
--- /dev/null
+++ b/Examples/KruskalSpanningTree/kruskal.js
@@ -0,0 +1,208 @@
+/*
+ * Kruskal`s Minimum Spanning Tree
+ *
+ * Written by ctonetti
+ *
+ * (Remember, this algorithm only works correctly in connected graphs.)
+ *
+ * Time Complexity: O(V^2)
+ * (Depends on the data structure used and the type of
+ * sorting algorithm. Optimally is O(E*ack(V), where
+ * ack() is the ackermann function, if there is guarantee
+ * that the edges can be sorted in linear time (counting
+ * sort or radix sort))
+ *
+ * This algorithm finds a minimum spanning tree of
+ * a graph. A spanning tree of a graph is a tree
+ * that connects all the nodes of an original graph
+ * by selecting edges.
+ *
+ * A spanning tree is minimal when the sum of the
+ * weights of it's edges is the least among all the
+ * possible spanning tree's of a graph.
+ *
+ */
+
+function reset(G, weight) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function getIndex(node, array) {
+ for (var i = 0; i < array.length; i++) {
+ if (array[i].element.id == node.id) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+// It is possible to create a custom function to generate weights
+function createEdgeWeight(G) {
+ G.edges().forEach(function(edges) {
+ edges.weight = 1
+ })
+}
+
+function Element(e, p) {
+ this.element = e
+ this.priority = p
+}
+
+// Unoptimized data structure
+function PriorityQueue() {
+
+ this.items = []
+
+ // Time Complexity: O(N)
+ this.enqueue = function(node, weight) {
+ var e = new Element(node, weight);
+ var contain = false;
+
+ for (var i = 0; i < this.items.length; i++) {
+ if (this.items[i].priority > e.priority) {
+ this.items.splice(i, 0, e);
+ contain = true;
+ break;
+ }
+ }
+
+ if (!contain) {
+ this.items.push(e);
+ }
+ }
+
+ // Time Complexity: O(1)
+ this.dequeue = function() {
+ if (this.isEmpty())
+ return "Underflow";
+ return this.items.shift();
+ }
+
+ // Time Complexity: O(N)
+ this.update = function(node, weight) {
+ var idx = getIndex(node, this.items)
+
+ // In this case, we only update the PQ if the new priority
+ // is lower
+ if (idx != -1 && weight < this.items[idx].priority) {
+ this.items.splice(idx, 1)
+ this.enqueue(node, weight)
+ return true
+ }
+ return false
+ }
+
+ // Time Complexity: O(1)
+ this.isEmpty = function() {
+ return this.items.length == 0;
+ }
+
+ // Time Complexity: O(N)
+ this.print = function() {
+ this.items.forEach(function(item) {
+ Console.log(item.element.from().id + " - " + item.element.to().id)
+ Console.log(item.priority)
+ Console.log("----")
+ })
+ }
+
+}
+
+function UnionFind() {
+
+ this.parents = {}
+
+ this.count = 0
+
+ this.init = function(items) {
+ // Construct forest of disjoint elements
+ // Each element is a node in the original
+ // graph
+ for (var i=0; i < items.length; i++) {
+ this.parents[items[i].id] = items[i]
+ }
+
+ this.count = items.length
+ }
+
+ // Unite two disjoint sets
+ this.union = function(a, b) {
+ var root_a = this.find(a)
+ var root_b = this.find(b)
+
+ // Check if the two are already in the same tree
+ if (root_a.id == root_b.id) {
+ return
+ }
+
+ if (root_a.id < root_b.id) {
+ if (this.parents[b.id] != b) {
+ this.union(this.parents[b.id], a)
+ }
+
+ this.parents[b.id] = this.parents[a.id]
+ } else {
+ if (this.parents[a.id] != a) {
+ this.union(this.parents[a.id], b)
+ }
+
+ this.parents[a.id] = this.parents[b.id]
+ }
+ }
+
+ this.find = function(a) {
+ while (this.parents[a.id] != a) {
+ a = this.parents[a.id]
+ }
+
+ return a
+ }
+
+ this.connected = function(a, b) {
+ return this.find(a).id == this.find(b).id
+ }
+}
+
+function kruskal(G) {
+
+ var pq = new PriorityQueue()
+ var uf = new UnionFind()
+
+ var tree = []
+
+ uf.init(G.nodes())
+
+ G.edges().forEach(function(e) {
+ pq.enqueue(e, e.weight)
+ })
+
+ while (!pq.isEmpty()) {
+
+ var edge = pq.dequeue().element
+
+ if (uf.connected(edge.from(), edge.to())) {
+ continue
+ }
+
+ Console.log("Edge: " + edge.from().id + " - " + edge.to().id + " Added!")
+
+ uf.union(edge.from(), edge.to())
+ tree.push(edge)
+ }
+
+ return tree
+}
+
+reset(Document)
+createEdgeWeight(Document)
+Console.log(kruskal(Document).length)
diff --git a/Examples/PrimSpanningTree/journal.html b/Examples/PrimSpanningTree/journal.html
deleted file mode 100644
index 98cb0669..00000000
--- a/Examples/PrimSpanningTree/journal.html
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-This project demonstrates **Prim's algorithm**, a method of finding the minimum spanning tree of a graph.
-
-
-## Story
-
-Maurice runs an online radio station. His original idea was to stream the audio from a single web server. This was easy to set up and initially worked very well.
-
-Unfortunately, as his channel grew, so did the bill from his telco. His rickety server didn't fare much better, as not a week went by before it crashed during peak hour. A flash flood of traffic from Reddit was the last straw. Something had to be done.
-
-The problem was his server sent an individual copy of the podcast to every listener, a strategy that worked with two or three listeners but did not scale to 100. He could have easily upgraded his bandwidth or his computer, but he was already scraping a living and did not want to spend more. So the only viable solution was to share the burden using a peer-to-peer system.
-
-Here's where the **minimum spanning tree** comes in. Given a graph of all the connections, we can help Maurice figure out a way of distributing the load, so that everyone is connected as efficiently as possible. To calculate this, we use – you guessed it – Prim's algorithm.
-
-
-## Algorithm
-
-Not all connections are created equal – some have a higher latency (delay or lag) than others, which is indicated by a number next to each edge. This algorithm takes this into account and chooses the path with minimal cost.
-
-1. Add Maurice's server to the tree. This is our starting point.
-2. Find the edge with the minimum cost that connects a node *inside* the tree to a node *outside*, and add it to the tree.
-3. If all nodes are part of the tree, we're done! Otherwise, repeat from step 2.
\ No newline at end of file
diff --git a/Examples/PrimSpanningTree/prim.js b/Examples/PrimSpanningTree/prim.js
new file mode 100644
index 00000000..e115b468
--- /dev/null
+++ b/Examples/PrimSpanningTree/prim.js
@@ -0,0 +1,159 @@
+/*
+ * Prim's Minimum Spanning Tree
+ *
+ * Written by ctonetti
+ *
+ * (Remember, this algorithm only works correctly in connected graphs.)
+ *
+ * Time Complexity:
+ * (Depends on the data structure used)
+ * - Our Priority Queue: O(V^2)
+ * - Binary Heap: O(|E|log|V|)
+ * - Fibonacci Heap: O(|E|+|V|log|V|)
+ *
+ * This algorithm finds a minimum spanning tree of
+ * a graph. A spanning tree of a graph is a tree
+ * that connects all the nodes of an original graph
+ * by selecting edges.
+ *
+ * A spanning tree is minimal when the sum of the
+ * weights of it's edges is the least among all the
+ * possible spanning tree's of a graph.
+ *
+ */
+
+function reset(G, weight) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function getIndex(node, array) {
+ for (var i = 0; i < array.length; i++) {
+ if (array[i].element.id == node.id) {
+ return i
+ }
+ }
+
+ return -1
+}
+
+// It is possible to create a custom function to generate weights
+function createEdgeWeight(G) {
+ G.edges().forEach(function(edges) {
+ edges.weight = 1
+ })
+}
+
+function Element(e, p) {
+ this.element = e
+ this.priority = p
+}
+
+// Unoptimized data structure
+function PriorityQueue() {
+
+ this.items = []
+
+ // Time Complexity: O(N)
+ this.enqueue = function(node, weight) {
+ var e = new Element(node, weight);
+ var contain = false;
+
+ for (var i = 0; i < this.items.length; i++) {
+ if (this.items[i].priority > e.priority) {
+ this.items.splice(i, 0, e);
+ contain = true;
+ break;
+ }
+ }
+
+ if (!contain) {
+ this.items.push(e);
+ }
+ }
+
+ // Time Complexity: O(1)
+ this.dequeue = function() {
+ if (this.isEmpty())
+ return "Underflow";
+ return this.items.shift();
+ }
+
+ // Time Complexity: O(N)
+ this.update = function(node, weight) {
+ var idx = getIndex(node, this.items)
+
+ // In this case, we only update the PQ if the new priority
+ // is lower
+ if (idx != -1 && weight < this.items[idx].priority) {
+ this.items.splice(idx, 1)
+ this.enqueue(node, weight)
+ return true
+ }
+ return false
+ }
+
+ // Time Complexity: O(1)
+ this.isEmpty = function() {
+ return this.items.length == 0;
+ }
+
+ // Time Complexity: O(N)
+ this.print = function() {
+ this.items.forEach(function(item) {
+ Console.log(item.element.id)
+ Console.log(item.priority)
+ Console.log("----")
+ })
+ }
+
+}
+
+function prim(G, start) {
+
+ var tree = []
+ var pq = new PriorityQueue()
+ var total_weight = 0
+
+ // Initialize Weights
+ G.nodes().forEach(function(node) {
+ if (node.id == start.id) {
+ pq.enqueue(node, 0) // The starting node begin with priority 0
+ } else {
+ pq.enqueue(node, 1000000) // All other nodes begin with priority "Infinity"
+ }
+ })
+
+ while (!pq.isEmpty()) {
+
+ // Get next element from the priority key
+ var val = pq.dequeue()
+ var next = val.element
+
+ tree.push(next)
+ total_weight += val.priority
+
+ // Mark the visisted node
+ mark(next)
+
+ next.edges().forEach(function(edge) {
+ pq.update(edge.to(), edge.weight)
+ pq.update(edge.from(), edge.weight)
+ })
+
+ }
+
+ Console.log("Weight of the MST: " + total_weight)
+}
+
+reset(Document)
+createEdgeWeight(Document)
+prim(Document, Document.nodes()[0])
diff --git a/Examples/PrimSpanningTree/prims.graph b/Examples/PrimSpanningTree/prims.graph
deleted file mode 100644
index f9c8a0ac..00000000
--- a/Examples/PrimSpanningTree/prims.graph
+++ /dev/null
@@ -1,173 +0,0 @@
-[Document Properties]
-top : -207
-bottom : 278.5
-left : -621
-right : 310.5
-DataStructurePlugin : Graph
-
-[DataType 0]
-Name : Client
-IconName : rocs_computer
-Color : #000000
-Properties : name=,visited=false
-
-[DataType 1]
-Name : Server
-IconName : rocs_server2
-Color : #000000
-Properties : name=
-
-[PointerType 0]
-Name : Connection
-Color : #808080
-LineStyle : 3
-Direction : bidirectional
-Properties : weight=0
-
-[PointerType 1]
-Name : Used
-Color : #808080
-LineStyle : 1
-Direction : bidirectional
-Properties : weight=0
-
-[DataStructure 0]
-X-plugin-type : 0
-name : network
-
-[Data 1]
-type : 0
-x : 14
-y : 78
-width : 0.3
-id : 1
-color : #00aa00
-name : E
-visited : true
-
-[Data 2]
-type : 0
-x : -90
-y : 158
-width : 0.3
-id : 2
-color : #00aa00
-name : F
-visited : true
-
-[Data 3]
-type : 0
-x : 87
-y : 169
-width : 0.3
-id : 3
-color : #00aa00
-name : G
-visited : true
-
-[Data 4]
-type : 0
-x : -222
-y : -50
-width : 0.3
-id : 4
-color : #00aa00
-name : A
-visited : true
-
-[Data 5]
-type : 0
-x : -89
-y : -2
-width : 0.3
-id : 5
-color : #00aa00
-name : B
-visited : true
-
-[Data 6]
-type : 0
-x : 32
-y : -50
-width : 0.3
-id : 6
-color : #00aa00
-name : C
-visited : true
-
-[Data 7]
-type : 1
-x : -192
-y : 76
-width : 0.3
-id : 7
-color : #0000ff
-name : D
-visited : true
-
-[Pointer 1->5]
-type : 0
-color : #808080
-width : 1
-weight : 0
-
-[Pointer 1->2]
-type : 0
-color : #808080
-width : 1
-weight : 0
-
-[Pointer 7->2]
-type : 0
-color : #808080
-width : 1
-weight : 0
-
-[Pointer 5->6]
-type : 0
-color : #808080
-width : 1
-weight : 0
-
-[Pointer 7->4]
-type : 0
-color : #808080
-width : 1
-weight : 0
-
-[Pointer 7->1]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
-[Pointer 6->1]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
-[Pointer 5->7]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
-[Pointer 4->5]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
-[Pointer 3->1]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
-[Pointer 2->3]
-type : 1
-color : #808080
-width : 2
-weight : 0
-
diff --git a/Examples/PrimSpanningTree/prims.js b/Examples/PrimSpanningTree/prims.js
deleted file mode 100644
index 777c6fa6..00000000
--- a/Examples/PrimSpanningTree/prims.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/**
- * Prim's algorithm
- *
- * __(')< written by lambda fairy
- * \___)
- */
-
-function reset_graph(g) {
- function reset(thing) {
- thing.set_type(0)
- thing.color = '#000'
- }
- function reset_node(node) {
- reset(node)
- node.width = 0.3
- }
- function reset_edge(edge) {
- reset(edge)
- edge.width = 1
- }
- g.nodes().forEach(reset_node)
- g.edges().forEach(reset_edge)
-}
-
-function add_weights(g) {
- g.edges().forEach(function(edge) {
- edge.weight = randint(1, 10)
- })
-}
-
-function randint(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min
-}
-
-function reset_visited(nodes) {
- nodes.forEach(function(node) {
- node.visited = false
- })
-}
-
-function get_unvisited(edge) {
- var s = edge.from.visited, e = edge.to.visited
- if (s && !e) {
- return edge.to
- } else if (!s && e) {
- return edge.from
- } else {
- return null
- }
-}
-
-function prim(g, start, mark_node, mark_edge) {
- // Mark the starting point
- reset_visited(g.nodes())
- start.visited = true
- var tree = [start]
- mark_node(start); interrupt()
- // Loop until all nodes are included in the tree
- while (tree.length < g.nodes().length) {
- // Go through all the edges connected to the tree ...
- var best_weight = Infinity, best_target = null, best_edge = null
- tree.forEach(function(node) {
- // ... and choose the one with the lowest weight
- node.adj_edges().forEach(function(edge) {
- var outside = get_unvisited(edge)
- if (outside != null && edge.weight < best_weight) {
- best_weight = edge.weight
- best_target = outside
- best_edge = edge
- }
- })
- })
- // Mark the closest node, and add it to the tree
- tree.push(best_target)
- best_target.visited = true
- mark_node(best_target); mark_edge(best_edge); interrupt()
- }
-}
-
-reset_graph(network)
-add_weights(network)
-var server = network.nodes()[0]
-server.set_type(1)
-prim(network, server,
- function(node) {
- if (node.type() == 1) {
- Console.log('Starting server ' + node.name)
- node.color = '#00f'
- node.width = 0.6
- } else {
- Console.log('Connecting ' + node.name)
- node.color = '#0a0'
- }
- },
- function(edge) {
- edge.set_type(1)
- edge.color = '#0a0'
- edge.width = 2
- Console.log(' -- latency: ' + edge.weight)
- })
-
-Console.log('== All clients connected ==')
diff --git a/Examples/PrimSpanningTree/prims.rocs b/Examples/PrimSpanningTree/prims.rocs
deleted file mode 100644
index ee359dde..00000000
--- a/Examples/PrimSpanningTree/prims.rocs
+++ /dev/null
@@ -1,14 +0,0 @@
-[CodeFile1]
-file=./prims.js
-identifier=1
-
-[GraphFile1]
-file=./prims.graph
-identifier=1
-
-[Journal]
-JournalHtml=journal.html
-
-[Project]
-CodeFiles=1
-GraphFiles=1
diff --git a/Examples/TopologicalSort/topologicalSort.js b/Examples/TopologicalSort/topologicalSort.js
new file mode 100644
index 00000000..9a5cc7f9
--- /dev/null
+++ b/Examples/TopologicalSort/topologicalSort.js
@@ -0,0 +1,61 @@
+/*
+ * Topological Sort
+ *
+ * Written by ctonetti
+ *
+ * (A topological order is only valid for Directed Acyclical Graphs)
+ *
+ * Time Complexity: O(|E| + |V|)
+ *
+ * This algorithm finds a topological order of a given graph.
+ * A topological order is an order in which each node appear
+ * before any of the nodes that can be reached by it's out
+ * edges.
+ *
+ */
+
+var order = []
+
+function reset(G) {
+ G.nodes().forEach(unmark)
+ G.edges().forEach(unmark)
+
+ G.nodes().forEach(function(node) {
+ node.seen = false
+ })
+}
+
+function mark(node) {
+ node.color = '#c00'
+}
+
+function unmark(node) {
+ node.color = '#fff'
+}
+
+function printOrder() {
+ order.reverse()
+
+ Console.log("Topological Order:")
+ order.forEach(function(node) {
+ Console.log(node.id)
+ })
+}
+
+// Recursive version of the DFS, modified
+function topologicalSort(G, node) {
+ node.seen = true
+ mark(node)
+
+ node.successors().forEach(function(succ) {
+ if (!succ.seen) {
+ topologicalSort(G, succ)
+ }
+ })
+
+ order.push(node)
+}
+
+reset(Document)
+topologicalSort(Document, Document.nodes()[0])
+printOrder()
diff --git a/libgraphtheory/editorplugins/generategraph/generategraphwidget.cpp b/libgraphtheory/editorplugins/generategraph/generategraphwidget.cpp
index 3c2e2e5e..834b1319 100644
--- a/libgraphtheory/editorplugins/generategraph/generategraphwidget.cpp
+++ b/libgraphtheory/editorplugins/generategraph/generategraphwidget.cpp
@@ -1,706 +1,708 @@
/*
* Copyright 2011-2014 Andreas Cord-Landwehr
* Copyright 2019 Caio Henrique Segawa Tonetti
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3, or any
* later version accepted by the membership of KDE e.V. (or its
* successor approved by the membership of KDE e.V.), which shall
* act as a proxy defined in Section 6 of version 3 of the license.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*/
#include "generategraphwidget.h"
#include "typenames.h"
#include "graphdocument.h"
#include "edge.h"
#include "modifiers/topology.h"
#include "logging_p.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace GraphTheory;
// typedefs used for the boost graph library
typedef boost::adjacency_list < boost::listS, boost::vecS, boost::undirectedS,
boost::property >
Graph;
typedef boost::rectangle_topology<> topology_type;
typedef topology_type::point_type point_type;
typedef std::vector PositionVec;
typedef boost::iterator_property_map < PositionVec::iterator,
boost::property_map::type >
PositionMap;
// handle boost exceptions
namespace boost {
void throw_exception(std::exception const &e) {
qCCritical(GRAPHTHEORY_GENERAL) << "Exception:" << e.what();
}
}
GenerateGraphWidget::GenerateGraphWidget(GraphDocumentPtr document, QWidget *parent)
: QDialog(parent)
, m_document(document)
, m_seed(1)
{
// setup default identifiers for the created graphs
m_defaultIdentifiers.insert(MeshGraph, "MeshGraph");
m_defaultIdentifiers.insert(StarGraph, "StarGraph");
m_defaultIdentifiers.insert(CircleGraph, "CircleGraph");
m_defaultIdentifiers.insert(ErdosRenyiRandomGraph, "RandomGraph");
m_defaultIdentifiers.insert(RandomTree, "RandomTree");
m_defaultIdentifiers.insert(RandomDag, "RandomDag");
m_defaultIdentifiers.insert(PathGraph, "PathGraph");
m_defaultIdentifiers.insert(CompleteGraph, "CompleteGraph");
m_defaultIdentifiers.insert(CompleteBipartiteGraph, "CompleteBipartite");
// set default graph
m_graphGenerator = MeshGraph;
setWindowTitle(i18nc("@title:window", "Generate Graph"));
QVBoxLayout *mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
QWidget *widget = new QWidget(this);
ui = new Ui::GenerateGraphWidget;
ui->setupUi(widget);
mainLayout->addWidget(widget);
ui->buttonShowAdvanced->setIcon(QIcon::fromTheme("rocsadvancedsetup"));
connect(ui->buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(ui->buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(this, &QDialog::accepted, this, &GenerateGraphWidget::generateGraph);
connect(ui->comboGraphGenerator, static_cast(&QComboBox::currentIndexChanged), this, &GenerateGraphWidget::setGraphGenerator);
connect(ui->nodeTypeSelector, static_cast(&QComboBox::currentIndexChanged), this, &GenerateGraphWidget::setNodeType);
connect(ui->edgeTypeSelector, static_cast(&QComboBox::currentIndexChanged), this, &GenerateGraphWidget::setEdgeType);
// set random seeds
qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
uint badRandomSeed = qHash(currentTime) % 99999;
badRandomSeed = (badRandomSeed == 0) ? 1 : badRandomSeed;
ui->randomGeneratorSeed->setValue(badRandomSeed);
ui->GNPGeneratorSeed->setValue(badRandomSeed);
ui->randomTreeGeneratorSeed->setValue(badRandomSeed);
// set visibility for advanced options
// TODO move to containers for easier handling
ui->label_randomGeneratorSeed->setVisible(false);
ui->randomGeneratorSeed->setVisible(false);
ui->label_GNPGeneratorSeed->setVisible(false);
ui->GNPGeneratorSeed->setVisible(false);
ui->label_randomTreeGeneratorSeed->setVisible(false);
ui->randomTreeGeneratorSeed->setVisible(false);
+ ui->label_dagGeneratorSeed->setVisible(false);
+ ui->dagGeneratorSeed->setVisible(false);
for (int i = 0; i < document->edgeTypes().length(); ++i) {
EdgeTypePtr type = document->edgeTypes().at(i);
QString item = i18nc(
"@item:inlistbox",
"%1 (ID %2)",
type->name(),
i);
ui->edgeTypeSelector->addItem(item, QVariant(i));
}
ui->edgeTypeSelector->setCurrentIndex(0);
for (int i = 0; i < document->nodeTypes().length(); ++i) {
NodeTypePtr type = document->nodeTypes().at(i);
QString item = i18nc(
"@item:inlistbox",
"%1 (ID %2)",
type->name(),
i);
ui->nodeTypeSelector->addItem(item, QVariant(i));
}
ui->nodeTypeSelector->setCurrentIndex(0);
}
void GenerateGraphWidget::setGraphGenerator(int index)
{
m_graphGenerator = GraphGenerator(index);
if (m_defaultIdentifiers.contains(m_graphGenerator)) {
ui->identifier->setText(m_defaultIdentifiers[m_graphGenerator]);
} else {
ui->identifier->setText("Graph");
}
}
void GenerateGraphWidget::setGraphIdentifier(const QString &identifier)
{
m_identifier = identifier;
}
void GenerateGraphWidget::setNodeType(int type)
{
if (type >= m_document->nodeTypes().length()) {
qCWarning(GRAPHTHEORY_GENERAL) << "Node type " << type << " does not exist: aborting";
return;
}
m_nodeType = m_document->nodeTypes().at(type);
}
void GenerateGraphWidget::setEdgeType(int type)
{
if (type >= m_document->edgeTypes().length()) {
qCWarning(GRAPHTHEORY_GENERAL) << "Edge type " << type << " does not exist: aborting";
return;
}
m_edgeType = m_document->edgeTypes().at(type);
}
void GenerateGraphWidget::setSeed(int seed)
{
m_seed = seed;
}
void GenerateGraphWidget::generateGraph()
{
setGraphIdentifier(ui->identifier->text());
switch (m_graphGenerator) {
case MeshGraph:
generateMesh(ui->meshRows->value(), ui->meshColumns->value());
break;
case CircleGraph:
generateCircle(ui->circleNodes->value());
break;
case StarGraph:
generateStar(ui->starSatelliteNodes->value());
break;
case RandomEdgeGraph:
setSeed(ui->randomGeneratorSeed->value());
generateRandomGraph(
ui->randomNodes->value(),
ui->randomEdges->value(),
ui->randomAllowSelfedges->isTristate()
);
break;
case ErdosRenyiRandomGraph:
- setSeed(ui->randomGeneratorSeed->value());
+ setSeed(ui->GNPGeneratorSeed->value());
generateErdosRenyiRandomGraph(
ui->GNPNodes->value(),
ui->GNPEdgeProbability->value(),
ui->GNPAllowSelfedges->isTristate()
);
break;
case RandomTree:
setSeed(ui->randomTreeGeneratorSeed->value());
generateRandomTreeGraph(
ui->randomTreeNodes->value()
);
break;
case RandomDag:
- setSeed(ui->randomGeneratorSeed->value());
+ setSeed(ui->dagGeneratorSeed->value());
generateRandomDagGraph(
ui->randomDagNumberOfNodes->value(),
ui->randomDagEdgeProbability->value()
);
break;
case PathGraph:
generatePathGraph(
ui->pathNodes->value()
);
break;
case CompleteGraph:
generateCompleteGraph(
ui->completeNodes->value()
);
break;
case CompleteBipartiteGraph:
generateCompleteBipartiteGraph(
ui->completeBipartiteNodesLeft->value(),
ui->completeBipartiteNodesRight->value()
);
default:
break;
}
close();
deleteLater();
}
GenerateGraphWidget::~GenerateGraphWidget()
{
delete ui;
}
QPointF GenerateGraphWidget::documentCenter() const
{
QPointF center = QPointF(0, 0);
qreal xSum = 0;
qreal ySum = 0;
int number = m_document->nodes().length();
foreach (NodePtr node, m_document->nodes()) {
xSum += node->x();
ySum += node->y();
}
if (number > 0) {
center.setX(xSum / number);
center.setY(ySum / number);
}
return center;
}
/** @brief make sure all @p nodes are on the canvas
*
* Given a collection of @p nodes, make sure they are all visible
* on the canvas (e.g. have a non-negative X and Y coordinate)
* by finding the smallest X and Y and then uniformly translating
* all of the @p nodes by that amount if either are negative.
*/
template
void adjustNodesToCanvas(T& nodes)
{
qreal minX = 0;
qreal minY = 0;
for(auto& n : nodes) {
if (n->x() < minX) {
minX = n->x();
}
if (n->y() < minY) {
minY = n->y();
}
}
if ((minX < 0) || (minY < 0)) {
// If either is non-negative, make it zero to not translate on that axis
minX = qMin(minX, qreal(0));
minY = qMin(minY, qreal(0));
// min* is negative or zero, so subtracting it means **adding** to the coordinate
for(auto& n : nodes) {
n->setX(n->x() - minX);
n->setY(n->y() - minY);
}
}
}
void GenerateGraphWidget::generateMesh(int rows, int columns)
{
QPointF center = documentCenter();
if (rows < 1) {
rows = 1;
}
if (columns < 1) {
columns = 1;
}
// create mesh of NxN points
QMap, NodePtr > meshNodes;
// create mesh nodes, store them in map
for (int i = 0; i < columns; ++i) {
for (int j = 0; j < rows; ++j) {
NodePtr node = Node::create(m_document);
node->setX(i * 50 - (int)25 * columns + center.x());
node->setY(j * 50 - (int)25 * rows + center.y());
node->setType(m_nodeType);
meshNodes[qMakePair(i, j)] = node;
}
}
adjustNodesToCanvas(meshNodes);
// connect mesh nodes
for (int i = 0; i < columns; ++i) {
for (int j = 0; j < rows; ++j) {
if (j < columns - 1) { // horizontal edges
EdgePtr edge = Edge::create(meshNodes[qMakePair(i, j)], meshNodes[qMakePair(i, j + 1)]);
edge->setType(m_edgeType);
if (m_edgeType->direction() == EdgeType::Direction::Unidirectional) {
EdgePtr edge = Edge::create(meshNodes[qMakePair(i, j + 1)], meshNodes[qMakePair(i, j)]);
edge->setType(m_edgeType);
}
}
if (i < rows - 1) { // vertical edges
EdgePtr edge = Edge::create(meshNodes[qMakePair(i, j)], meshNodes[qMakePair(i + 1, j)]);
edge->setType(m_edgeType);
if (m_edgeType->direction() == EdgeType::Direction::Unidirectional) {
EdgePtr edge = Edge::create(meshNodes[qMakePair(i + 1, j)], meshNodes[qMakePair(i, j)]);
edge->setType(m_edgeType);
}
}
}
}
}
void GenerateGraphWidget::generateStar(int satelliteNodes)
{
QPointF center = documentCenter();
// compute radius such that nodes have space ~50 between each other
// circle that border-length of 2*PI*radius
int radius = 50 * satelliteNodes / (2 * boost::math::constants::pi());
NodeList nodes;
for (int i = 1; i <= satelliteNodes; i++) {
NodePtr node = Node::create(m_document);
node->setX(sin(i * 2 * boost::math::constants::pi() / satelliteNodes)*radius + center.x());
node->setY(cos(i * 2 * boost::math::constants::pi() / satelliteNodes)*radius + center.y());
node->setType(m_nodeType);
nodes.append(node);
}
// center
NodePtr node = Node::create(m_document);
node->setX(center.x());
node->setY(center.y());
node->setType(m_nodeType);
nodes.prepend(node);
adjustNodesToCanvas(nodes);
// connect circle nodes
for (int i = 1; i <= satelliteNodes; ++i) {
EdgePtr edge = Edge::create(nodes.at(0), nodes.at(i));
edge->setType(m_edgeType);
}
}
void GenerateGraphWidget::generateCircle(int number)
{
QPointF center = documentCenter();
// compute radius such that nodes have space ~50 between each other
// circle that border-length of 2*PI*radius
int radius = 50 * number / (2 * boost::math::constants::pi());
QList< QPair > circleNodes;
// create mesh nodes, store them in map
NodeList nodes;
for (int i = 1; i <= number; i++) {
NodePtr node = Node::create(m_document);
node->setX(sin(i * 2 * boost::math::constants::pi() / number)*radius + center.x());
node->setY(cos(i * 2 * boost::math::constants::pi() / number)*radius + center.y());
node->setType(m_nodeType);
nodes.append(node);
}
adjustNodesToCanvas(nodes);
// connect circle nodes
for (int i = 0; i < number - 1; i++) {
EdgePtr edge = Edge::create(nodes.at(i), nodes.at(i + 1));
edge->setType(m_edgeType);
}
EdgePtr edge = Edge::create(nodes.at(number - 1), nodes.at(0));
edge->setType(m_edgeType);
}
void GenerateGraphWidget::generateRandomGraph(int nodes, int edges, bool selfEdges)
{
QPointF center = documentCenter();
Graph randomGraph;
std::mt19937 gen;
gen.seed(static_cast(m_seed));
// generate graph
boost::generate_random_graph(
randomGraph,
nodes,
edges,
gen,
selfEdges
);
// generate distribution topology and apply
boost::rectangle_topology< std::mt19937 > topology(gen, center.x() - 20 * nodes, center.y() - 20 * nodes, center.x() + 20 * nodes, center.y() + 20 * nodes);
PositionVec position_vec(boost::num_vertices(randomGraph));
PositionMap positionMap(position_vec.begin(), get(boost::vertex_index, randomGraph));
boost::random_graph_layout(randomGraph, positionMap, topology);
// minimize cuts by Fruchtman-Reingold layout algorithm
boost::fruchterman_reingold_force_directed_layout< boost::rectangle_topology< std::mt19937 >, Graph, PositionMap >
(randomGraph,
positionMap,
topology,
boost::cooling(boost::linear_cooling(100))
);
// put nodes at whiteboard as generated
QMap mapNodes;
boost::graph_traits::vertex_iterator vi, vi_end;
for (boost::tie(vi, vi_end) = boost::vertices(randomGraph); vi != vi_end; ++vi) {
mapNodes[*vi] = Node::create(m_document);
mapNodes[*vi]->setX(positionMap[*vi][0]);
mapNodes[*vi]->setY(positionMap[*vi][1]);
mapNodes[*vi]->setType(m_nodeType);
}
adjustNodesToCanvas(mapNodes);
boost::graph_traits::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(randomGraph); ei != ei_end; ++ei) {
EdgePtr edge = Edge::create(mapNodes[boost::source(*ei, randomGraph)], mapNodes[boost::target(*ei, randomGraph)]);
edge->setType(m_edgeType);
}
}
void GenerateGraphWidget::generateErdosRenyiRandomGraph(int nodes, double edgeProbability, bool selfEdges)
{
QPointF center = documentCenter();
std::mt19937 gen;
gen.seed(static_cast(m_seed));
// generate graph
typedef boost::erdos_renyi_iterator ergen;
Graph randomGraph(ergen(gen, nodes, edgeProbability, selfEdges), ergen(), nodes);
// generate distribution topology and apply
boost::rectangle_topology< std::mt19937 > topology(gen, center.x() - 20 * nodes, center.y() - 20 * nodes, center.x() + 20 * nodes, center.y() + 20 * nodes);
PositionVec position_vec(boost::num_vertices(randomGraph));
PositionMap positionMap(position_vec.begin(), get(boost::vertex_index, randomGraph));
boost::random_graph_layout(randomGraph, positionMap, topology);
// minimize cuts by Fruchtman-Reingold layout algorithm
boost::fruchterman_reingold_force_directed_layout< boost::rectangle_topology< std::mt19937 >, Graph, PositionMap >
(randomGraph,
positionMap,
topology,
boost::cooling(boost::linear_cooling(100))
);
// put nodes at whiteboard as generated
QMap mapNodes;
boost::graph_traits::vertex_iterator vi, vi_end;
for (boost::tie(vi, vi_end) = boost::vertices(randomGraph); vi != vi_end; ++vi) {
mapNodes[*vi] = Node::create(m_document);
mapNodes[*vi]->setX(positionMap[*vi][0]);
mapNodes[*vi]->setY(positionMap[*vi][1]);
mapNodes[*vi]->setType(m_nodeType);
}
adjustNodesToCanvas(mapNodes);
boost::graph_traits::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = boost::edges(randomGraph); ei != ei_end; ++ei) {
EdgePtr edge = Edge::create(mapNodes[boost::source(*ei, randomGraph)], mapNodes[boost::target(*ei, randomGraph)]);
edge->setType(m_edgeType);
}
}
void GenerateGraphWidget::generateRandomTreeGraph(int number)
{
if (EdgeType::Unidirectional == m_edgeType->direction()){
QMessageBox::critical(this, "Incorrect Edge Direction", "Edges in a Tree must be bidirectional.");
return;
}
std::mt19937 gen;
gen.seed(static_cast(m_seed));
NodeList nodes;
QVector notAdded;
QVector added;
for (int i = 0; i < number; i++) {
NodePtr node = Node::create(m_document);
node->setType(m_nodeType);
nodes.append(node);
notAdded.push_back(i);
}
// shuffle
std::shuffle(notAdded.begin(), notAdded.end(), gen);
// add root
added.push_back(notAdded.front());
notAdded.pop_front();
while (!notAdded.empty()) {
boost::random::uniform_int_distribution<> dist(0, added.size()-1);
int randomIdx = dist(gen);
int next = notAdded.front();
notAdded.pop_front();
added.push_back(next);
EdgePtr edge = Edge::create(nodes.at(added[randomIdx]), nodes.at(next));
edge->setType(m_edgeType);
}
Topology::applyCircleAlignment(nodes, 300);
Topology::applyMinCutTreeAlignment(nodes);
adjustNodesToCanvas(nodes);
}
void GenerateGraphWidget::generateRandomDagGraph(int nodes, double edgeProbability)
{
if (EdgeType::Bidirectional == m_edgeType->direction()){
QMessageBox::critical(this, i18n("Incorrect Edge Direction"), i18n("Edges in a Directed Acyclical Graph must be directional."));
return;
}
std::mt19937 gen;
gen.seed(static_cast(m_seed));
boost::random::uniform_real_distribution dist(0, 1);
NodeList nodes_list;
for (int j=0; j < nodes; j++) {
NodePtr node = Node::create(m_document);
node->setType(m_nodeType);
nodes_list.append(node);
}
// Create random edges
for (int i=0; i < nodes - 1; i++) {
for (int j=i+1; j < nodes; j++) {
if (dist(gen)< edgeProbability) {
EdgePtr edge = Edge::create(nodes_list.at(i), nodes_list.at(j));
edge->setType(m_edgeType);
}
}
}
Topology::applyCircleAlignment(nodes_list, 300);
Topology::applyMinCutTreeAlignment(nodes_list);
adjustNodesToCanvas(nodes_list);
}
void GenerateGraphWidget::generatePathGraph(int pathSize)
{
QPointF center = documentCenter();
QList< QPair > pathNodes;
NodeList nodes_list;
for (int i = 1; i <= pathSize; i++) {
NodePtr node = Node::create(m_document);
node->setX(i * 50 + center.x());
node->setY(center.y());
node->setType(m_nodeType);
nodes_list.append(node);
}
adjustNodesToCanvas(nodes_list);
for (int i = 0; i < pathSize - 1; i++) {
EdgePtr edge = Edge::create(nodes_list.at(i), nodes_list.at(i + 1));
edge->setType(m_edgeType);
}
}
void GenerateGraphWidget::generateCompleteGraph(int nodes)
{
QPointF center = documentCenter();
// compute radius such that nodes have space ~100 between each other
// circle that border-length of 2*PI*radius
int radius = 100 * nodes / (2 * boost::math::constants::pi());
QList< QPair > circleNodes;
NodeList node_list;
for (int i = 1; i <= nodes; i++) {
NodePtr node = Node::create(m_document);
node->setX(sin(i * 2 * boost::math::constants::pi() / nodes)*radius + center.x());
node->setY(cos(i * 2 * boost::math::constants::pi() / nodes)*radius + center.y());
node->setType(m_nodeType);
node_list.append(node);
}
adjustNodesToCanvas(node_list);
for (int i = 0; i < nodes - 1; i++) {
for (int j = i + 1; j < nodes; j++) {
EdgePtr edge_lr = Edge::create(node_list.at(i), node_list.at(j));
edge_lr->setType(m_edgeType);
if (m_edgeType->direction() == EdgeType::Direction::Unidirectional) {
EdgePtr edge_rl = Edge::create(node_list.at(j), node_list.at(i));
edge_rl->setType(m_edgeType);
}
}
}
}
void GenerateGraphWidget::generateCompleteBipartiteGraph(int nodes_left, int nodes_right)
{
QPointF center = documentCenter();
int separator = 100;
NodeList node_list;
for (int i = 0; i < nodes_left; i++) {
NodePtr node = Node::create(m_document);
node->setX(center.x());
node->setY(center.y() + i * 50);
node->setType(m_nodeType);
node_list.append(node);
}
for (int i = 0; i < nodes_right; i++) {
NodePtr node = Node::create(m_document);
node->setX(center.x() + separator);
node->setY(center.y() + i * 50);
node->setType(m_nodeType);
node_list.append(node);
}
adjustNodesToCanvas(node_list);
for (int i = 0; i < nodes_left; i++) {
for (int j = 0; j < nodes_right; j++){
EdgePtr edge_lr = Edge::create(node_list.at(i), node_list.at(j + nodes_left));
edge_lr->setType(m_edgeType);
if (m_edgeType->direction() == EdgeType::Direction::Unidirectional) {
EdgePtr edge_rl = Edge::create(node_list.at(j + nodes_left), node_list.at(i));
edge_rl->setType(m_edgeType);
}
}
}
}
diff --git a/libgraphtheory/editorplugins/generategraph/generategraphwidget.ui b/libgraphtheory/editorplugins/generategraph/generategraphwidget.ui
index 462ed17a..7e08548a 100644
--- a/libgraphtheory/editorplugins/generategraph/generategraphwidget.ui
+++ b/libgraphtheory/editorplugins/generategraph/generategraphwidget.ui
@@ -1,735 +1,781 @@
GenerateGraphWidget
0
0
354
423
Generate Graph
-
-
Select the graph generator.
-
Mesh Graph
-
Star Graph
-
Circle Graph
-
Random Graph
-
Erdös-Renyi Graph
-
Random Tree Graph
-
Random Dag Graph
-
Path Graph
-
Complete Graph
-
Complete Bipartite
-
0
0
24
24
Show advanced settings.
true
-
Set the unique identifier of the generated graph.
Graph
-
The node type to create the nodes of the graph with
Node type
-
The identifier of the created graph (used for scripting)
Identifier
-
- 0
+ 3
-
QFormLayout::ExpandingFieldsGrow
-
Number of Columns:
meshColumns
-
1
5
-
Number of Rows:
meshRows
-
1
5
-
-
Satellite Nodes:
-
1
999
10
-
-
Number of Nodes:
-
1
999
10
-
-
Nodes:
-
1
999
10
-
Edges:
-
20
-
Allow self-edges:
-
Generator Seed:
-
1
999999
-
-
-
Nodes (n):
-
Allow self-edges:
-
1
10
-
Edge Probability (p):
-
Generator Seed:
-
1
999999
-
3
0.001000000000000
1.000000000000000
0.250000000000000
-
-
-
Nodes:
-
1
10
-
Generator Seed:
-
1
999999
-
-
Number of Nodes
-
- 10
+ 1
999
-
Edge Probability
-
1.000000000000000
0.100000000000000
0.500000000000000
+ -
+
+
+ Generator Seed
+
+
+
+ -
+
+
+ true
+
+
+
-
-
Number of Nodes:
-
4
-
-
Number of Nodes:
-
5
-
-
Left Set Nodes:
-
4
-
Right Set Nodes:
-
4
-
QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
Select the edge type for connections of the generated graph.
-
Qt::Horizontal
-
The edge type to create the edges of the graph with
Edge type
-
Graph Generator
-
Select the node type for node elements of the generated graph.
KComboBox
QComboBox
buttonShowAdvanced
toggled(bool)
label_randomGeneratorSeed
setVisible(bool)
242
21
46
158
buttonShowAdvanced
toggled(bool)
randomGeneratorSeed
setVisible(bool)
242
21
98
158
buttonShowAdvanced
toggled(bool)
label_GNPGeneratorSeed
setVisible(bool)
242
21
18
158
buttonShowAdvanced
toggled(bool)
GNPGeneratorSeed
setVisible(bool)
242
21
83
158
buttonShowAdvanced
toggled(bool)
label_randomTreeGeneratorSeed
setVisible(bool)
242
21
46
159
buttonShowAdvanced
toggled(bool)
randomTreeGeneratorSeed
setVisible(bool)
242
21
98
159
comboGraphGenerator
currentIndexChanged(int)
stackedWidget
setCurrentIndex(int)
169
16
146
194
+
+ buttonShowAdvanced
+ toggled(bool)
+ label_dagGeneratorSeed
+ setVisible(bool)
+
+
+ 331
+ 23
+
+
+ 70
+ 249
+
+
+
+
+ buttonShowAdvanced
+ toggled(bool)
+ dagGeneratorSeed
+ setVisible(bool)
+
+
+ 331
+ 23
+
+
+ 238
+ 249
+
+
+