Home | History | Annotate | Download | only in sort
      1 // sort object
      2 
      3 var manual = 0; // single stepping
      4 var interval_time = 0; // setTimeout interval
      5 var inner_loop_enabled = 1; // should the stepper iterate a little during each step
      6 
      7 // number of elements
      8 var size = 300;
      9 var query = window.location.search.substring(1);
     10 var params = query.split('&');
     11 for (var i = 0; i < params.length; i++) {
     12   var pos = params[i].indexOf('=');
     13   var key = params[i].substring(0, pos);
     14   var val = params[i].substring(pos+1);
     15   if (key == "size") {
     16     var sz = parseInt(val);
     17     size = Math.max(sz, 3);
     18     size = Math.min(1000, size);
     19   }
     20 }
     21 
     22 var log;
     23 function log(msg) {
     24   if (window.console != undefined) {
     25     window.console.log(msg);
     26   }
     27 }
     28 
     29 function Sort(name, func) {
     30   this.name = name;
     31   this.func = func;
     32   this.size = size;
     33   this.compare_x = null;
     34   this.compare_y = null;
     35   this.compares = 0;
     36   this.swap_x = null;
     37   this.swap_y = null;
     38   this.swaps = 0;
     39   this.start_time = 0;
     40   this.stop_time = 0;
     41   this.work_queue = new Array();
     42   this.timer = 0;
     43   this.last_time = 0;
     44   this.num_iterations = 0;
     45   this.num_jobs = 0;
     46   this.overhead_total = 0;
     47   this.overhead_min = 1000000;
     48   this.overhead_max = 0;
     49   this.processing_total = 0;
     50   this.processing_min = 1000000;
     51   this.processing_max = 0;
     52   this.step_min = 1000000;
     53   this.step_max = 0;
     54 
     55   this.setup();
     56 }
     57 
     58 Sort.prototype.setup = function() {
     59   this.size = size;
     60   this.bars = new Array(this.size);
     61   this.numbers = new Array(this.size);
     62   for (i = 0; i < this.size; i++) {
     63     this.numbers[i] = i + 1;
     64   }
     65   for (i = 0; i < this.size; i++) {
     66     var r = Math.floor(Math.random() * this.numbers.length);
     67     if (i != r) {
     68       var tmp = this.numbers[i];
     69       this.numbers[i] = this.numbers[r];
     70       this.numbers[r] = tmp;
     71     }
     72   }
     73 }
     74 
     75 Sort.prototype.status = function(str) {
     76   var label = document.getElementById(this.name + "_label");
     77   label.innerHTML = "<b>" + this.name + " Sort</b><br />" + str;
     78 }
     79 
     80 Sort.prototype.stepper = function() {
     81   if (!manual) {
     82     var sort = this;
     83     this.timer = setTimeout(function(){sort.stepper();},interval_time);
     84   }
     85   var t = new Date();
     86   var overhead = t - this.last_time;
     87   this.overhead_total += overhead;
     88   this.overhead_min = Math.min(this.overhead_min, overhead);
     89   this.overhead_max = Math.max(this.overhead_max, overhead);
     90   this.last_time = t;
     91 
     92   var elapsed = t - this.start_time;
     93   var avg =
     94     Math.floor((elapsed - this.processing_total) / this.num_iterations);
     95   this.status("Overhead: " + avg + "ms");
     96 
     97   var ops = 0;
     98   for (;;) {
     99     var count = this.work_queue.length;
    100     if (count > 0) {
    101       var func = this.work_queue.pop();
    102       if (func.status != undefined) {
    103         //this.status(func.status);
    104       }
    105       ops++;
    106       this.num_jobs++;
    107       func();
    108     } else {
    109       break;
    110     }
    111     if (manual || inner_loop_enabled == 0) {
    112       break;
    113     }
    114     t = new Date();
    115     // If any measurable time has passed, we're good.
    116     // Since the Date has a resolution of 15ms on Windows
    117     // there's no way to differentiate accurately for anything
    118     // less than that.  We don't want to process for longer than
    119     // the timer interval anyway (which is about 10ms), so this
    120     // is fine.
    121     // NOTE: on non-windows platforms, this actually does matter since
    122     // their timer resolution is higher
    123     // TODO(erikkay): make this a parameter
    124     if (t - this.last_time > 10) {
    125       break;
    126     }
    127   }
    128   var processing = t - this.last_time;
    129   this.processing_min = Math.min(this.processing_min, processing);
    130   this.processing_max = Math.max(this.processing_max, processing);
    131   this.processing_total += processing;
    132   var step_time = processing + overhead;
    133   this.step_min = Math.min(this.step_min, step_time);
    134   this.step_max = Math.max(this.processing_max, step_time);
    135   this.num_iterations++;
    136   this.last_time = new Date();
    137 
    138   if (ops == 0) {
    139     this.finished();
    140   }
    141 }
    142 
    143 Sort.prototype.add_work = function(work, name) {
    144   if (name != undefined) {
    145     work.status = name;
    146   }
    147   this.work_queue.push(work);
    148 }
    149 
    150 Sort.prototype.init = function() {
    151   this.print();
    152   this.status("");
    153 }
    154 
    155 Sort.prototype.reset = function() {
    156   this.stop();
    157   this.start_time = 0;
    158   this.stop_time = 0;
    159   this.setup();
    160   this.print();
    161 }
    162 
    163 Sort.prototype.start = function() {
    164   if (this.start_time > 0) {
    165     if (this.stop_time > 0) {
    166       this.shuffle();
    167       this.start_time = 0;
    168       this.stop_time = 0;
    169       this.status("");
    170       return;
    171     } else if (manual) {
    172       this.stepper();
    173       return;
    174     } else {
    175       this.finished();
    176       return;
    177     }
    178   }
    179   if (!manual) {
    180     var t = this;
    181     this.timer = setTimeout(function(){t.stepper();},interval_time);
    182   }
    183   this.compares = 0;
    184   this.swaps = 0;
    185   this.start_time = (new Date()).getTime();
    186   this.last_time = this.start_time;
    187   this.num_jobs = 0;
    188   this.stop_time = 0;
    189   this.overhead_total = 0;
    190   this.overhead_min = 1000000;
    191   this.overhead_max = 0;
    192   this.processing_total = 0;
    193   this.processing_min = 1000000;
    194   this.processing_max = 0;
    195   this.num_iterations = 0;
    196   this.func(this);
    197 }
    198 
    199 Sort.prototype.cleanup = function() {
    200   if (this.compare_x) {
    201     this.compare_x.style.borderColor = "black";
    202     this.compare_y.style.borderColor = "black";
    203   }
    204   if (this.swap_x) {
    205     this.swap_x.style.backgroundColor = "green";
    206     this.swap_y.style.backgroundColor = "green";
    207   }
    208   this.work_queue = new Array();
    209 }
    210 
    211 Sort.prototype.stop = function() {
    212   if (this.timer != 0) {
    213     clearTimeout(this.timer);
    214     this.timer = 0;
    215   }
    216   this.cleanup();
    217 }
    218 
    219 Sort.prototype.finished = function(err) {
    220   this.stop();
    221 
    222   this.stop_time = (new Date()).getTime();
    223 
    224   var total = (this.stop_time - this.start_time);
    225   if (err == null) {
    226     var step_avg = Math.floor(total / this.num_iterations);
    227     var overhead = total - this.processing_total;
    228     var overhead_avg = Math.floor(overhead / this.num_iterations);
    229     var processing_avg = Math.floor(this.processing_total / this.num_iterations);
    230     var table = "<table><tr><td>Times(ms)</td><td>Total</td><td>Avg</td><td>Max</td></tr>"
    231       + "<tr><td>Total</td><td>" + total + "</td><td>" + step_avg + "</td><td>" + this.step_max + "</tr>"
    232       + "<tr><td>Work</td><td>" + this.processing_total + "</td><td>" + processing_avg + "</td><td>" +  this.processing_max + "</tr>"
    233       + "<tr><td>Overhead</td><td>" + overhead + "</td><td>" + overhead_avg + "</td><td>" + this.overhead_max + "</tr>"
    234       + "</table>";
    235     this.status(table);
    236   } else {
    237     this.status(err);
    238     log("error: " + err);
    239   }
    240   log("finished in: " + total);
    241 }
    242 
    243 Sort.prototype.shuffle = function() {
    244   for (i = 0; i < this.size; i++) {
    245     var r = Math.floor(Math.random() * this.size);
    246     if (i != r) {
    247       this.swap(i,r);
    248     }
    249   }
    250   this.cleanup();
    251 }
    252 
    253 Sort.prototype.print = function() {
    254   var graph = document.getElementById(this.name);
    255   if (graph == undefined) {
    256     alert("can't find " + this.name);
    257   }
    258   var text = "<div id='" + this.name + "_label' class='label'>" + this.name + " Sort</div>";
    259   var len = this.numbers.length;
    260   var height_multiple = (graph.clientHeight-20) / len;
    261   var width = Math.max(1,Math.floor((graph.clientWidth-10) / len));
    262   if (width < 3) {
    263     border = 0;
    264   } else {
    265     border = 1;
    266   }
    267   var left_offset = Math.round((graph.clientWidth - (width*len))/2);
    268   for (i = 0; i < len; i++) {
    269     var val = this.numbers[i];
    270     var height = Math.max(1, Math.floor(val * height_multiple));
    271     var left = left_offset + i * width;
    272     text += "<li class='bar' style='border: " + border + "px solid black; height:" + height + "px; left:" + left + "; width:" + width + "' id='" + this.name + val + "' value='" + val + "'></li>";
    273   }
    274   graph.innerHTML = text;
    275   var nodes = document.getElementsByTagName("li");
    276   var j = 0;
    277   for (i = 0; i < nodes.length; i++) {
    278     var name = nodes[i].id;
    279     if (name.indexOf(this.name) == 0) {
    280       this.bars[j] = nodes[i];
    281       j++;
    282     }
    283   }
    284 }
    285 
    286 Sort.prototype.compare = function(x, y) {
    287   var bx = this.bars[x];
    288   var by = this.bars[y];
    289   //log("compare " + x + "(" + bx.value + ")," + y + "(" + by.value + ")");
    290   if (this.compare_x != bx) {
    291     if (this.compare_x) {
    292       this.compare_x.style.borderColor="black";
    293     }
    294     bx.style.borderColor="yellow";
    295     this.compare_x = bx;
    296   }
    297   if (this.compare_y != by) {
    298     if (this.compare_y) {
    299       this.compare_y.style.borderColor="black";
    300     }
    301     by.style.borderColor="white";
    302     this.compare_y = by;
    303   }
    304   this.compares++;
    305   return bx.value - by.value;
    306 }
    307 
    308 Sort.prototype.swap = function(x, y) {
    309   var bx = this.bars[x];
    310   var by = this.bars[y];
    311   //log("swap " + x + "(" + bx.value + ")," + y + "(" + by.value + ")");
    312   if (this.swap_x != x) {
    313     if (this.swap_x) {
    314       this.swap_x.style.backgroundColor="green";
    315     }
    316     bx.style.backgroundColor="blue";
    317     this.swap_x = bx;
    318   }
    319   if (this.swap_y != y) {
    320     if (this.swap_y) {
    321       this.swap_y.style.backgroundColor="green";
    322     }
    323     by.style.backgroundColor="red";
    324     this.swap_y = by;
    325   }
    326   var tmp = bx.style.left;
    327   bx.style.left = by.style.left;
    328   by.style.left = tmp;
    329   this.bars[x] = by;
    330   this.bars[y] = bx;
    331   this.swaps++;
    332 }
    333 
    334 Sort.prototype.insert = function(from, to) {
    335   var bf = this.bars[from];
    336   if (from > to) {
    337     for (i = from; i > to; i--) {
    338       var b1 = this.bars[i];
    339       var b2 = this.bars[i-1];
    340       b2.style.left = b1.style.left;
    341       this.bars[i] = b2;
    342     }
    343     bf.style.left = this.bars[to].style.left;
    344     this.bars[to] = bf;
    345   } else {
    346     // TODO
    347   } 
    348 }
    349