jsmetric 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +6 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +2 -0
  4. data/README +36 -0
  5. data/Rakefile +37 -0
  6. data/bin/jsmetric +16 -0
  7. data/boot.rb +5 -0
  8. data/build +1 -0
  9. data/features/cyclometric_complexity/boolean_complexity_counting.feature +46 -0
  10. data/features/cyclometric_complexity/case_complexity_counting.feature +117 -0
  11. data/features/cyclometric_complexity/exception_complexity_counting.feature +81 -0
  12. data/features/cyclometric_complexity/function_detection.feature +128 -0
  13. data/features/cyclometric_complexity/if_else_complexity_counting.feature +178 -0
  14. data/features/cyclometric_complexity/loop_complexity_counting.feature +81 -0
  15. data/features/graphing/draw_basic_graph.feature +14 -0
  16. data/features/reporting/report.feature +13 -0
  17. data/features/sample_js_files_for_test/foobar.js +30 -0
  18. data/features/step_definitions/cyclometric_complexity_steps.rb +31 -0
  19. data/features/step_definitions/graph_steps.rb +10 -0
  20. data/features/step_definitions/reporting_steps.rb +14 -0
  21. data/features/support/env.rb +1 -0
  22. data/jsgraphlib/Curry-1.0.1.js +29 -0
  23. data/jsgraphlib/dracula_algorithms.js +599 -0
  24. data/jsgraphlib/dracula_graffle.js +106 -0
  25. data/jsgraphlib/dracula_graph.js +534 -0
  26. data/jsgraphlib/graphtest.html +57 -0
  27. data/jsgraphlib/jquery-1.4.2.min.js +154 -0
  28. data/jsgraphlib/jsgraphsource.js +12 -0
  29. data/jsgraphlib/raphael-min.js +7 -0
  30. data/jsgraphlib/seedrandom.js +266 -0
  31. data/jsmetric.gemspec +26 -0
  32. data/lib/cc_report.rb +20 -0
  33. data/lib/complexity_analyser.rb +90 -0
  34. data/lib/fulljslint.js +6100 -0
  35. data/lib/graphing/graph_analyser.rb +19 -0
  36. data/lib/js_lint.rb +28 -0
  37. data/lib/json2.js +480 -0
  38. data/lib/options.js +24 -0
  39. data/lib/report.rb +9 -0
  40. data/lib/utils.rb +18 -0
  41. data/lib/version.rb +3 -0
  42. data/spec/spec_helper.rb +1 -0
  43. data/tasks/dev.rb +4 -0
  44. data/tasks/run.rb +55 -0
  45. metadata +175 -0
@@ -0,0 +1,178 @@
1
+ @ifelse
2
+ Feature: Calculate IF/ELSE complexity for a stand alone Javascript function
3
+
4
+ Scenario: No branches
5
+ Given javascript code as:
6
+ """
7
+ function foo() { };
8
+ """
9
+ When I run the complexity analysis on it
10
+ Then the complexity is reported as "1"
11
+
12
+ Scenario: Just one IF statement
13
+ Given javascript code as:
14
+ """
15
+ function foo() {
16
+ if (true) {
17
+ // do something
18
+ }
19
+ }
20
+ """
21
+ When I run the complexity analysis on it
22
+ Then the complexity is reported as "2"
23
+
24
+ Scenario: 2 sequential IF statements
25
+ Given javascript code as:
26
+ """
27
+ function foo() {
28
+ if (true) {
29
+ // do something
30
+ }
31
+ if (true) {
32
+ // do something else
33
+ }
34
+ }
35
+ """
36
+ When I run the complexity analysis on it
37
+ Then the complexity is reported as "3"
38
+
39
+ Scenario: 2 nested IF statements
40
+ Given javascript code as:
41
+ """
42
+ function foo() {
43
+ if (true) {
44
+ // do something
45
+ if (true) {
46
+ // do something else
47
+ }
48
+ }
49
+ }
50
+ """
51
+ When I run the complexity analysis on it
52
+ Then the complexity is reported as "3"
53
+
54
+ Scenario: A single IF with Else statement
55
+ Given javascript code as:
56
+ """
57
+ function foo() {
58
+ if (true) {
59
+ // do something
60
+ }
61
+ else {
62
+ // do something else
63
+ }
64
+ }
65
+ """
66
+ When I run the complexity analysis on it
67
+ Then the complexity is reported as "2"
68
+
69
+ Scenario: 2 nested IF/Else statements
70
+ Given javascript code as:
71
+ """
72
+ function foo() {
73
+ if (true) {
74
+ // do something
75
+ if (true) {
76
+ // do something else
77
+ }
78
+ else {
79
+ // do this
80
+ }
81
+ }
82
+ else {
83
+ // do that
84
+ }
85
+ }
86
+ """
87
+ When I run the complexity analysis on it
88
+ Then the complexity is reported as "3"
89
+
90
+ Scenario: An IF-ELSE-IF type scenario
91
+ Given javascript code as:
92
+ """
93
+ function foo() {
94
+ if (true) {
95
+ }
96
+ else if(true) {
97
+ // do that
98
+ }
99
+ }
100
+ """
101
+ When I run the complexity analysis on it
102
+ Then the complexity is reported as "3"
103
+
104
+ Scenario: An IF-ELSE-IF and then ELSE type scenario
105
+ Given javascript code as:
106
+ """
107
+ function foo() {
108
+ if (true) {
109
+ // do something
110
+ }
111
+ else if(true) {
112
+ // do this
113
+ }
114
+ else {
115
+ // do that
116
+ }
117
+ }
118
+ """
119
+ When I run the complexity analysis on it
120
+ Then the complexity is reported as "3"
121
+
122
+
123
+ Scenario: A nested IF-ELSE-IF type scenario
124
+ Given javascript code as:
125
+ """
126
+ function foo() {
127
+ if (true) {
128
+ if (true) {
129
+ }
130
+ else if(true) {
131
+ // do that
132
+ }
133
+ }
134
+ }
135
+ """
136
+ When I run the complexity analysis on it
137
+ Then the complexity is reported as "4"
138
+
139
+ Scenario: Multiple nested IF-ELSE-IF type scenario
140
+ Given javascript code as:
141
+ """
142
+ function foo() {
143
+ if (true) {
144
+ if (true) {
145
+ // do this
146
+ }
147
+ else if(true) {
148
+ // do that
149
+ }
150
+ }
151
+ else if(true) {
152
+ // do something
153
+ }
154
+ }
155
+ """
156
+ When I run the complexity analysis on it
157
+ Then the complexity is reported as "5"
158
+
159
+ Scenario: IF statement implied with ?
160
+ Given javascript code as:
161
+ """
162
+ function foo() {
163
+ (true)? true : false;
164
+ }
165
+ """
166
+ When I run the complexity analysis on it
167
+ Then the complexity is reported as "2"
168
+
169
+
170
+ Scenario: IF statement nested inside a ternary operator
171
+ Given javascript code as:
172
+ """
173
+ function foo() {
174
+ (true)? "" : (false) ? true :"" ;
175
+ }
176
+ """
177
+ When I run the complexity analysis on it
178
+ Then the complexity is reported as "3"
@@ -0,0 +1,81 @@
1
+ @loops
2
+ Feature: Calculate LOOP complexity for a stand alone Javascript function
3
+
4
+ Scenario: Single FOR loop
5
+ Given javascript code as:
6
+ """
7
+ function foo() {
8
+ var i;
9
+ for(i=1;i<3;i++) {}
10
+ };
11
+ """
12
+ When I run the complexity analysis on it
13
+ Then the complexity is reported as "2"
14
+
15
+ Scenario: Single WHILE loop
16
+ Given javascript code as:
17
+ """
18
+ function foo() {
19
+ while(true) {}
20
+ };
21
+ """
22
+ When I run the complexity analysis on it
23
+ Then the complexity is reported as "2"
24
+
25
+ Scenario: Single DO/WHILE loop
26
+ Given javascript code as:
27
+ """
28
+ function foo() {
29
+ do { }
30
+ while (true);
31
+ }
32
+ """
33
+ When I run the complexity analysis on it
34
+ Then the complexity is reported as "2"
35
+
36
+ Scenario: Multiple WHILE/DO loops in sequence
37
+ Given javascript code as:
38
+ """
39
+ function foo() {
40
+ while (true)
41
+ {
42
+ // do something
43
+ }
44
+
45
+ do {
46
+ // something else
47
+ }
48
+ while (true);
49
+ }
50
+ """
51
+ When I run the complexity analysis on it
52
+ Then the complexity is reported as "3"
53
+
54
+ Scenario: Nested FOR loops
55
+ Given javascript code as:
56
+ """
57
+ function foo() {
58
+ var i,j;
59
+ for(i=1;i<3;i++) {
60
+ for(j=1;j<3;j++) {
61
+ }
62
+ }
63
+ };
64
+ """
65
+ When I run the complexity analysis on it
66
+ Then the complexity is reported as "3"
67
+
68
+ Scenario: Nested FOR loops
69
+ Given javascript code as:
70
+ """
71
+ function foo() {
72
+ while(true) {
73
+ do {
74
+
75
+ }
76
+ while(true)
77
+ }
78
+ };
79
+ """
80
+ When I run the complexity analysis on it
81
+ Then the complexity is reported as "3"
@@ -0,0 +1,14 @@
1
+ Feature: Output JSON to describe calls within a class
2
+
3
+ Scenario: Empty graph data when there is no JavaScript
4
+ Given javascript code as:
5
+ """
6
+
7
+ """
8
+ When I run the graph analysis on it
9
+ Then the JSON object returned is:
10
+ """
11
+ {
12
+ "graphdata" : {}
13
+ }
14
+ """
@@ -0,0 +1,13 @@
1
+ @reports
2
+ Feature: Generate a report for a given JS file
3
+
4
+ Scenario: CC and Function name report generated for single sample JS file
5
+ Given a sample JS file called "foobar"
6
+ When the CC report target in run on it
7
+ Then the contents of the report are as follows
8
+ """
9
+ Name, CC
10
+ Klass,3
11
+ constructor,2
12
+ Annonymous,2
13
+ """
@@ -0,0 +1,30 @@
1
+ // A Sample Test function
2
+ function Klass(definition)
3
+ {
4
+ var prototype = definition;
5
+
6
+ if (definition['_extends'] !== undefined)
7
+ {
8
+ $.each(definition['_extends'].prototype, function(k, v){
9
+
10
+ if (prototype[k] === undefined) {
11
+ prototype[k] = v;
12
+ } else {
13
+ prototype[k+'_super'] = v;
14
+ }
15
+ });
16
+ }
17
+
18
+ var constructor = definition['_init'];
19
+ if (constructor === undefined)
20
+ {
21
+ constructor = function()
22
+ {
23
+ if (this.prototype('_init_super')) {
24
+ this._init_super();
25
+ }
26
+ }
27
+ }
28
+ constructor.prototype = prototype;
29
+ return constructor
30
+ }
@@ -0,0 +1,31 @@
1
+ #TODO: rename this file
2
+
3
+ Given /^javascript code as:$/ do |string|
4
+ @code = string
5
+ end
6
+
7
+ When /^I run the complexity analysis on it$/ do
8
+ @analyser = ComplexityAnalyser.new
9
+ @analyser.parse(@code)
10
+ end
11
+
12
+ Then /^the number of functions is reported as "([^"]*)"$/ do |num_funcs|
13
+ @analyser.functions.count.should eql num_funcs.to_i
14
+ end
15
+
16
+ Then /^the complexity is reported as "([^"]*)"$/ do |complexity|
17
+ @analyser.functions.first[:complexity].should eql complexity.to_i
18
+ end
19
+
20
+ And /^the function name is "([^"]*)"$/ do |func_name|
21
+ @analyser.functions.first[:name].should eql func_name
22
+ end
23
+
24
+ And /^the function names are:$/ do |table|
25
+ table.hashes.each do |function|
26
+ match = @analyser.functions.find {|func| func[:name].eql?(function["Name"])}
27
+ match.should_not be_nil, "Could not find function with name '#{function["Name"]}' in #{@analyser.functions}"
28
+ end
29
+
30
+ end
31
+
@@ -0,0 +1,10 @@
1
+ #specific to graph analysis
2
+
3
+ When /^I run the graph analysis on it$/ do
4
+ @analyser = GraphAnalyser.new
5
+ @analyser.parse(@code)
6
+ end
7
+
8
+ Then /^the JSON object returned is:$/ do |json|
9
+ JSON.parse(@analyser.json).should eql JSON.parse(json)
10
+ end
@@ -0,0 +1,14 @@
1
+ Given /^a sample JS file called "([^"]*)"$/ do |filename|
2
+ @filename = filename
3
+ end
4
+
5
+ When /^the CC report target in run on it$/ do
6
+ path = File.join(File.dirname(__FILE__),'..', "sample_js_files_for_test", @filename + ".js")
7
+ contents = File.open(path, 'r') { |f| f.read }
8
+ @results = CCReport.generate_for contents
9
+ end
10
+
11
+ Then /^the contents of the report are as follows$/ do |expected_report|
12
+ @results.should == expected_report
13
+ end
14
+
@@ -0,0 +1 @@
1
+ require(File.join(File.dirname(__FILE__),'..' ,'..','boot'))
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Curry - Function currying
3
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
4
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
5
+ * Date: 10/4/2008
6
+ *
7
+ * @author Ariel Flesler
8
+ * @version 1.0.1
9
+ */
10
+
11
+ function curry( fn ){
12
+ return function(){
13
+ var args = curry.args(arguments),
14
+ master = arguments.callee,
15
+ self = this;
16
+
17
+ return args.length >= fn.length ? fn.apply(self,args) : function(){
18
+ return master.apply( self, args.concat(curry.args(arguments)) );
19
+ };
20
+ };
21
+ };
22
+
23
+ curry.args = function( args ){
24
+ return Array.prototype.slice.call(args);
25
+ };
26
+
27
+ Function.prototype.curry = function(){
28
+ return curry(this);
29
+ };
@@ -0,0 +1,599 @@
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
+ }