Move packages up in directory hierarchy
[feed/routing.git] / luci-app-bmx6 / files / www / luci-static / resources / bmx6 / js / dracula_algorithms.js
1 /*
2 * Various algorithms and data structures, licensed under the MIT-license.
3 * (c) 2010 by Johann Philipp Strathausen <strathausen@gmail.com>
4 * http://strathausen.eu
5 *
6 */
7
8
9
10 /*
11 Bellman-Ford
12
13 Path-finding algorithm, finds the shortest paths from one node to all nodes.
14
15
16 Complexity
17
18 O( |E| · |V| ), where E = edges and V = vertices (nodes)
19
20
21 Constraints
22
23 Can run on graphs with negative edge weights as long as they do not have
24 any negative weight cycles.
25
26 */
27 function bellman_ford(g, source) {
28
29 /* STEP 1: initialisation */
30 for(var n in g.nodes)
31 g.nodes[n].distance = Infinity;
32 /* predecessors are implicitly null */
33 source.distance = 0;
34
35 step("Initially, all distances are infinite and all predecessors are null.");
36
37 /* STEP 2: relax each edge (this is at the heart of Bellman-Ford) */
38 /* repeat this for the number of nodes minus one */
39 for(var i = 1; i < g.nodes.length; i++)
40 /* for each edge */
41 for(var e in g.edges) {
42 var edge = g.edges[e];
43 if(edge.source.distance + edge.weight < edge.target.distance) {
44 step("Relax edge between " + edge.source.id + " and " + edge.target.id + ".");
45 edge.target.distance = edge.source.distance + edge.weight;
46 edge.target.predecessor = edge.source;
47 }
48 //Added by Jake Stothard (Needs to be tested)
49 if(!edge.style.directed) {
50 if(edge.target.distance + edge.weight < edge.source.distance) {
51 g.snapShot("Relax edge between "+edge.target.id+" and "+edge.source.id+".");
52 edge.source.distance = edge.target.distance + edge.weight;
53 edge.source.predecessor = edge.target;
54 }
55 }
56 }
57 step("Ready.");
58
59 /* STEP 3: TODO Check for negative cycles */
60 /* For now we assume here that the graph does not contain any negative
61 weights cycles. (this is left as an excercise to the reader[tm]) */
62 }
63
64
65
66 /*
67 Path-finding algorithm Dijkstra
68
69 - worst-case running time is O((|E| + |V|) · log |V| ) thus better than
70 Bellman-Ford for sparse graphs (with less edges), but cannot handle
71 negative edge weights
72 */
73 function dijkstra(g, source) {
74
75 /* initially, all distances are infinite and all predecessors are null */
76 for(var n in g.nodes)
77 g.nodes[n].distance = Infinity;
78 /* predecessors are implicitly null */
79
80 g.snapShot("Initially, all distances are infinite and all predecessors are null.");
81
82 source.distance = 0;
83 /* set of unoptimized nodes, sorted by their distance (but a Fibonacci heap
84 would be better) */
85 var q = new BinaryMinHeap(g.nodes, "distance");
86
87 /* pointer to the node in focus */
88 var node;
89
90 /* get the node with the smallest distance
91 as long as we have unoptimized nodes. q.min() can have O(log n). */
92 while(q.min() != undefined) {
93 /* remove the latest */
94 node = q.extractMin();
95 node.optimized = true;
96
97 /* no nodes accessible from this one, should not happen */
98 if(node.distance == Infinity)
99 throw "Orphaned node!";
100
101 /* for each neighbour of node */
102 for(e in node.edges) {
103 var other = (node == node.edges[e].target) ? node.edges[e].source : node.edges[e].target;
104
105 if(other.optimized)
106 continue;
107
108 /* look for an alternative route */
109 var alt = node.distance + node.edges[e].weight;
110
111 /* update distance and route if a better one has been found */
112 if (alt < other.distance) {
113
114 /* update distance of neighbour */
115 other.distance = alt;
116
117 /* update priority queue */
118 q.heapify();
119
120 /* update path */
121 other.predecessor = node;
122 g.snapShot("Enhancing node.")
123 }
124 }
125 }
126 }
127
128
129 /* All-Pairs-Shortest-Paths */
130 /* Runs at worst in O(|V|³) and at best in Omega(|V|³) :-)
131 complexity Sigma(|V|²) */
132 /* This implementation is not yet ready for general use, but works with the
133 Dracula graph library. */
134 function floyd_warshall(g, source) {
135
136 /* Step 1: initialising empty path matrix (second dimension is implicit) */
137 var path = [];
138 var next = [];
139 var n = g.nodes.length;
140
141 /* construct path matrix, initialize with Infinity */
142 for(j in g.nodes) {
143 path[j] = [];
144 next[j] = [];
145 for(i in g.nodes)
146 path[j][i] = j == i ? 0 : Infinity;
147 }
148
149 /* initialize path with edge weights */
150 for(e in g.edges)
151 path[g.edges[e].source.id][g.edges[e].target.id] = g.edges[e].weight;
152
153 /* Note: Usually, the initialisation is done by getting the edge weights
154 from a node matrix representation of the graph, not by iterating through
155 a list of edges as done here. */
156
157 /* Step 2: find best distances (the heart of Floyd-Warshall) */
158 for(k in g.nodes){
159 for(i in g.nodes) {
160 for(j in g.nodes)
161 if(path[i][j] > path[i][k] + path[k][j]) {
162 path[i][j] = path[i][k] + path[k][j];
163 /* Step 2.b: remember the path */
164 next[i][j] = k;
165 }
166 }
167 }
168
169 /* Step 3: Path reconstruction, get shortest path */
170 function getPath(i, j) {
171 if(path[i][j] == Infinity)
172 throw "There is no path.";
173 var intermediate = next[i][j];
174 if(intermediate == undefined)
175 return null;
176 else
177 return getPath(i, intermediate)
178 .concat([intermediate])
179 .concat(getPath(intermediate, j));
180 }
181
182 /* TODO use the knowledge, e.g. mark path in graph */
183 }
184
185 /*
186 Ford-Fulkerson
187
188 Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
189 graph from source to sink.
190
191
192 Complexity
193
194 O(E * max(f)), max(f) being the maximum flow
195
196
197 Description
198
199 As long as there is an open path through the residual graph, send the
200 minimum of the residual capacities on the path.
201
202
203 Constraints
204
205 The algorithm works only if all weights are integers. Otherwise it is
206 possible that the Ford–Fulkerson algorithm will not converge to the maximum
207 value.
208
209
210 Input
211
212 g - Graph object
213 s - Source ID
214 t - Target (sink) ID
215
216
217 Output
218
219 Maximum flow from Source s to Target t
220
221 */
222 /*
223 Edmonds-Karp
224
225 Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
226 graph from source to sink. An implementation of the Ford-Fulkerson
227 algorithm.
228
229
230 Complexity
231
232 O(|V|*|E|²)
233
234
235 Input
236
237 g - Graph object (with node and edge lists, capacity is a property of edge)
238 s - source ID
239 t - sink ID
240
241 */
242 function edmonds_karp(g, s, t) {
243
244 }
245
246 /*
247 A simple binary min-heap serving as a priority queue
248 - takes an array as the input, with elements having a key property
249 - elements will look like this:
250 {
251 key: "... key property ...",
252 value: "... element content ..."
253 }
254 - provides insert(), min(), extractMin() and heapify()
255 - example usage (e.g. via the Firebug or Chromium console):
256 var x = {foo: 20, hui: "bla"};
257 var a = new BinaryMinHeap([x,{foo:3},{foo:10},{foo:20},{foo:30},{foo:6},{foo:1},{foo:3}],"foo");
258 console.log(a.extractMin());
259 console.log(a.extractMin());
260 x.foo = 0; // update key
261 a.heapify(); // call this always after having a key updated
262 console.log(a.extractMin());
263 console.log(a.extractMin());
264 - can also be used on a simple array, like [9,7,8,5]
265 */
266 function BinaryMinHeap(array, key) {
267
268 /* Binary tree stored in an array, no need for a complicated data structure */
269 var tree = [];
270
271 var key = key || 'key';
272
273 /* Calculate the index of the parent or a child */
274 var parent = function(index) { return Math.floor((index - 1)/2); };
275 var right = function(index) { return 2 * index + 2; };
276 var left = function(index) { return 2 * index + 1; };
277
278 /* Helper function to swap elements with their parent
279 as long as the parent is bigger */
280 function bubble_up(i) {
281 var p = parent(i);
282 while((p >= 0) && (tree[i][key] < tree[p][key])) {
283 /* swap with parent */
284 tree[i] = tree.splice(p, 1, tree[i])[0];
285 /* go up one level */
286 i = p;
287 p = parent(i);
288 }
289 }
290
291 /* Helper function to swap elements with the smaller of their children
292 as long as there is one */
293 function bubble_down(i) {
294 var l = left(i);
295 var r = right(i);
296
297 /* as long as there are smaller children */
298 while(tree[l] && (tree[i][key] > tree[l][key]) || tree[r] && (tree[i][key] > tree[r][key])) {
299
300 /* find smaller child */
301 var child = tree[l] ? tree[r] ? tree[l][key] > tree[r][key] ? r : l : l : l;
302
303 /* swap with smaller child with current element */
304 tree[i] = tree.splice(child, 1, tree[i])[0];
305
306 /* go up one level */
307 i = child;
308 l = left(i);
309 r = right(i);
310 }
311 }
312
313 /* Insert a new element with respect to the heap property
314 1. Insert the element at the end
315 2. Bubble it up until it is smaller than its parent */
316 this.insert = function(element) {
317
318 /* make sure there's a key property */
319 (element[key] == undefined) && (element = {key:element});
320
321 /* insert element at the end */
322 tree.push(element);
323
324 /* bubble up the element */
325 bubble_up(tree.length - 1);
326 }
327
328 /* Only show us the minimum */
329 this.min = function() {
330 return tree.length == 1 ? undefined : tree[0];
331 }
332
333 /* Return and remove the minimum
334 1. Take the root as the minimum that we are looking for
335 2. Move the last element to the root (thereby deleting the root)
336 3. Compare the new root with both of its children, swap it with the
337 smaller child and then check again from there (bubble down)
338 */
339 this.extractMin = function() {
340 var result = this.min();
341
342 /* move the last element to the root or empty the tree completely */
343 /* bubble down the new root if necessary */
344 (tree.length == 1) && (tree = []) || (tree[0] = tree.pop()) && bubble_down(0);
345
346 return result;
347 }
348
349 /* currently unused, TODO implement */
350 this.changeKey = function(index, key) {
351 throw "function not implemented";
352 }
353
354 this.heapify = function() {
355 for(var start = Math.floor((tree.length - 2) / 2); start >= 0; start--) {
356 bubble_down(start);
357 }
358 }
359
360 /* insert the input elements one by one only when we don't have a key property (TODO can be done more elegant) */
361 for(i in (array || []))
362 this.insert(array[i]);
363 }
364
365
366
367 /*
368 Quick Sort:
369 1. Select some random value from the array, the median.
370 2. Divide the array in three smaller arrays according to the elements
371 being less, equal or greater than the median.
372 3. Recursively sort the array containg the elements less than the
373 median and the one containing elements greater than the median.
374 4. Concatenate the three arrays (less, equal and greater).
375 5. One or no element is always sorted.
376 TODO: This could be implemented more efficiently by using only one array object and several pointers.
377 */
378 function quickSort(arr) {
379 /* recursion anchor: one element is always sorted */
380 if(arr.length <= 1) return arr;
381 /* randomly selecting some value */
382 var median = arr[Math.floor(Math.random() * arr.length)];
383 var arr1 = [], arr2 = [], arr3 = [];
384 for(var i in arr) {
385 arr[i] < median && arr1.push(arr[i]);
386 arr[i] == median && arr2.push(arr[i]);
387 arr[i] > median && arr3.push(arr[i]);
388 }
389 /* recursive sorting and assembling final result */
390 return quickSort(arr1).concat(arr2).concat(quickSort(arr3));
391 }
392
393 /*
394 Selection Sort:
395 1. Select the minimum and remove it from the array
396 2. Sort the rest recursively
397 3. Return the minimum plus the sorted rest
398 4. An array with only one element is already sorted
399 */
400 function selectionSort(arr) {
401 /* recursion anchor: one element is always sorted */
402 if(arr.length == 1) return arr;
403 var minimum = Infinity;
404 var index;
405 for(var i in arr) {
406 if(arr[i] < minimum) {
407 minimum = arr[i];
408 index = i; /* remember the minimum index for later removal */
409 }
410 }
411 /* remove the minimum */
412 arr.splice(index, 1);
413 /* assemble result and sort recursively (could be easily done iteratively as well)*/
414 return [minimum].concat(selectionSort(arr));
415 }
416
417 /*
418 Merge Sort:
419 1. Cut the array in half
420 2. Sort each of them recursively
421 3. Merge the two sorted arrays
422 4. An array with only one element is considered sorted
423
424 */
425 function mergeSort(arr) {
426 /* merges two sorted arrays into one sorted array */
427 function merge(a, b) {
428 /* result set */
429 var c = [];
430 /* as long as there are elements in the arrays to be merged */
431 while(a.length > 0 || b.length > 0){
432 /* are there elements to be merged, if yes, compare them and merge */
433 var n = a.length > 0 && b.length > 0 ? a[0] < b[0] ? a.shift() : b.shift() : b.length > 0 ? b.shift() : a.length > 0 ? a.shift() : null;
434 /* always push the smaller one onto the result set */
435 n != null && c.push(n);
436 }
437 return c;
438 }
439 /* this mergeSort implementation cuts the array in half, wich should be fine with randomized arrays, but introduces the risk of a worst-case scenario */
440 median = Math.floor(arr.length / 2);
441 var part1 = arr.slice(0, median); /* for some reason it doesn't work if inserted directly in the return statement (tried so with firefox) */
442 var part2 = arr.slice(median - arr.length);
443 return arr.length <= 1 ? arr : merge(
444 mergeSort(part1), /* first half */
445 mergeSort(part2) /* second half */
446 );
447 }
448
449 /* Balanced Red-Black-Tree */
450 function RedBlackTree(arr) {
451
452 }
453
454 function BTree(arr) {
455
456 }
457
458 function NaryTree(n, arr) {
459
460 }
461
462 /**
463 * Knuth-Morris-Pratt string matching algorithm - finds a pattern in a text.
464 * FIXME: Doesn't work correctly yet.
465 */
466 function kmp(p, t) {
467
468 /**
469 * PREFIX, OVERLAP or FALIURE function for KMP. Computes how many iterations
470 * the algorithm can skip after a mismatch.
471 *
472 * @input p - pattern (string)
473 * @result array of skippable iterations
474 */
475 function prefix(p) {
476 /* pi contains the computed skip marks */
477 var pi = [0], k = 0;
478 for(q = 1; q < p.length; q++) {
479 while(k > 0 && (p.charAt(k) != p.charAt(q)))
480 k = pi[k-1];
481
482 (p.charAt(k) == p.charAt(q)) && k++;
483
484 pi[q] = k;
485 }
486 return pi;
487 }
488
489 /* The actual KMP algorithm starts here. */
490
491 var pi = prefix(p), q = 0, result = [];
492
493 for(var i = 0; i < t.length; i++) {
494 /* jump forward as long as the character doesn't match */
495 while((q > 0) && (p.charAt(q) != t.charAt(i)))
496 q = pi[q];
497
498 (p.charAt(q) == t.charAt(i)) && q++;
499
500 (q == p.length) && result.push(i - p.length) && (q = pi[q]);
501 }
502
503 return result;
504 }
505
506 /* step for algorithm visualisation */
507 function step(comment, funct) {
508 //wait for input
509 //display comment (before or after waiting)
510 // next.wait();
511 /* execute callback function */
512 funct();
513 }
514
515 /**
516 * Curry - Function currying
517 * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
518 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
519 * Date: 10/4/2008
520 *
521 * @author Ariel Flesler
522 * @version 1.0.1
523 */
524 function curry( fn ){
525 return function(){
526 var args = curry.args(arguments),
527 master = arguments.callee,
528 self = this;
529
530 return args.length >= fn.length ? fn.apply(self,args) : function(){
531 return master.apply( self, args.concat(curry.args(arguments)) );
532 };
533 };
534 };
535
536 curry.args = function( args ){
537 return Array.prototype.slice.call(args);
538 };
539
540 Function.prototype.curry = function(){
541 return curry(this);
542 };
543
544 /**
545 * Topological Sort
546 *
547 * Sort a directed graph based on incoming edges
548 *
549 * Coded by Jake Stothard
550 */
551 function topological_sort(g) {
552 //Mark nodes as "deleted" instead of actually deleting them
553 //That way we don't have to copy g
554
555 for(i in g.nodes)
556 g.nodes[i].deleted = false;
557
558 var ret = topological_sort_helper(g);
559
560 //Cleanup: Remove the deleted property
561 for(i in g.nodes)
562 delete g.nodes[i].deleted
563
564 return ret;
565 }
566 function topological_sort_helper(g) {
567 //Find node with no incoming edges
568 var node;
569 for(i in g.nodes) {
570 if(g.nodes[i].deleted)
571 continue; //Bad style, meh
572
573 var incoming = false;
574 for(j in g.nodes[i].edges) {
575 if(g.nodes[i].edges[j].target == g.nodes[i]
576 && g.nodes[i].edges[j].source.deleted == false) {
577 incoming = true;
578 break;
579 }
580 }
581 if(!incoming) {
582 node = g.nodes[i];
583 break;
584 }
585 }
586
587 // Either unsortable or done. Either way, GTFO
588 if(node == undefined)
589 return [];
590
591 //"Delete" node from g
592 node.deleted = true;
593
594 var tail = topological_sort_helper(g);
595
596 tail.unshift(node);
597
598 return tail;
599 }