<commit_before><commit_msg>Add Javascript implementation of Hungarian algorithm<commit_after>"use strict";

// This algorithm was written in Coffeescript for another one of my projects:
// https://github.com/skishore/tesseract/blob/master/coffee/hungarian.coffee 

var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };

this.Hungarian = (function() {
  function Hungarian(cost_matrix) {
    var i, j, last_matched, len, ref, ref1, results, row, x, y;
    this.cost_matrix = cost_matrix;
    this.get_final_score = bind(this.get_final_score, this);
    this.update_labels = bind(this.update_labels, this);
    this.find_root_and_slacks = bind(this.find_root_and_slacks, this);
    this.augment = bind(this.augment, this);
    this.match = bind(this.match, this);
    this.cost = bind(this.cost, this);
    this.find_greedy_solution = bind(this.find_greedy_solution, this);
    this.reduce_cost_matrix = bind(this.reduce_cost_matrix, this);
    this.n = this.cost_matrix.length;
    ref = this.cost_matrix;
    for (i = 0, len = ref.length; i < len; i++) {
      row = ref[i];
      if (row.length !== this.n) {
        throw new Error("Malforrmed cost_matrix: " + this.cost_matrix);
      }
    }
    this.range = (function() {
      results = [];
      for (var j = 0, ref1 = this.n; 0 <= ref1 ? j < ref1 : j > ref1; 0 <= ref1 ? j++ : j--){ results.push(j); }
      return results;
    }).apply(this);
    this.matched = 0;
    this.x_label = (function() {
      var k, len1, ref2, results1;
      ref2 = this.range;
      results1 = [];
      for (k = 0, len1 = ref2.length; k < len1; k++) {
        x = ref2[k];
        results1.push(0);
      }
      return results1;
    }).call(this);
    this.y_label = (function() {
      var k, len1, ref2, results1;
      ref2 = this.range;
      results1 = [];
      for (k = 0, len1 = ref2.length; k < len1; k++) {
        y = ref2[k];
        results1.push(0);
      }
      return results1;
    }).call(this);
    this.x_match = (function() {
      var k, len1, ref2, results1;
      ref2 = this.range;
      results1 = [];
      for (k = 0, len1 = ref2.length; k < len1; k++) {
        x = ref2[k];
        results1.push(-1);
      }
      return results1;
    }).call(this);
    this.y_match = (function() {
      var k, len1, ref2, results1;
      ref2 = this.range;
      results1 = [];
      for (k = 0, len1 = ref2.length; k < len1; k++) {
        y = ref2[k];
        results1.push(-1);
      }
      return results1;
    }).call(this);
    this.reduce_cost_matrix();
    this.find_greedy_solution();
    while (this.matched < this.n) {
      last_matched = this.matched;
      this.augment();
      if (this.matched <= last_matched) {
        throw new Error("Augmentation round did not increase matched!");
      }
    }
  }

  Hungarian.prototype.reduce_cost_matrix = function() {
    var i, j, k, l, len, len1, len2, len3, max_cost, ref, ref1, ref2, ref3, row, x, y;
    this.cost_matrix = (function() {
      var i, len, ref, results;
      ref = this.cost_matrix;
      results = [];
      for (i = 0, len = ref.length; i < len; i++) {
        row = ref[i];
        results.push(row.slice());
      }
      return results;
    }).call(this);
    ref = this.range;
    for (i = 0, len = ref.length; i < len; i++) {
      x = ref[i];
      max_cost = Math.max.apply(0, (function() {
        var j, len1, ref1, results;
        ref1 = this.range;
        results = [];
        for (j = 0, len1 = ref1.length; j < len1; j++) {
          y = ref1[j];
          results.push(this.cost_matrix[x][y]);
        }
        return results;
      }).call(this));
      ref1 = this.range;
      for (j = 0, len1 = ref1.length; j < len1; j++) {
        y = ref1[j];
        this.cost_matrix[x][y] -= max_cost;
      }
      this.x_label[x] = 0;
    }
    ref2 = this.range;
    for (k = 0, len2 = ref2.length; k < len2; k++) {
      y = ref2[k];
      max_cost = Math.max.apply(0, (function() {
        var l, len3, ref3, results;
        ref3 = this.range;
        results = [];
        for (l = 0, len3 = ref3.length; l < len3; l++) {
          x = ref3[l];
          results.push(this.cost_matrix[x][y]);
        }
        return results;
      }).call(this));
      ref3 = this.range;
      for (l = 0, len3 = ref3.length; l < len3; l++) {
        x = ref3[l];
        this.cost_matrix[x][y] -= max_cost;
      }
      this.y_label[y] = 0;
    }
  };

  Hungarian.prototype.find_greedy_solution = function() {
    var i, len, ref, results, x, y;
    ref = this.range;
    results = [];
    for (i = 0, len = ref.length; i < len; i++) {
      x = ref[i];
      results.push((function() {
        var j, len1, ref1, results1;
        ref1 = this.range;
        results1 = [];
        for (j = 0, len1 = ref1.length; j < len1; j++) {
          y = ref1[j];
          if (this.x_match[x] === -1 && this.y_match[y] === -1 && (this.cost(x, y)) === 0) {
            this.match(x, y);
            results1.push(this.matched += 1);
          } else {
            results1.push(void 0);
          }
        }
        return results1;
      }).call(this));
    }
    return results;
  };

  Hungarian.prototype.cost = function(x, y) {
    return this.cost_matrix[x][y] - this.x_label[x] - this.y_label[y];
  };

  Hungarian.prototype.match = function(x, y) {
    this.x_match[x] = y;
    return this.y_match[y] = x;
  };

  Hungarian.prototype.augment = function() {
    var cur_x, cur_y, delta, delta_x, delta_y, i, j, len, len1, new_slack, next_y, ref, ref1, ref2, root, slack, slack_x, x, x_in_tree, y, y_parent;
    x_in_tree = (function() {
      var i, len, ref, results;
      ref = this.range;
      results = [];
      for (i = 0, len = ref.length; i < len; i++) {
        x = ref[i];
        results.push(false);
      }
      return results;
    }).call(this);
    y_parent = (function() {
      var i, len, ref, results;
      ref = this.range;
      results = [];
      for (i = 0, len = ref.length; i < len; i++) {
        y = ref[i];
        results.push(-1);
      }
      return results;
    }).call(this);
    ref = this.find_root_and_slacks(), root = ref[0], slack = ref[1], slack_x = ref[2];
    x_in_tree[root] = true;
    while (true) {
      delta = Infinity;
      ref1 = this.range;
      for (i = 0, len = ref1.length; i < len; i++) {
        y = ref1[i];
        if (y_parent[y] < 0 && slack[y] < delta) {
          delta = slack[y];
          delta_x = slack_x[y];
          delta_y = y;
        }
      }
      this.update_labels(delta, x_in_tree, y_parent, slack);
      y_parent[delta_y] = delta_x;
      if (this.y_match[delta_y] < 0) {
        cur_y = delta_y;
        while (cur_y >= 0) {
          cur_x = y_parent[cur_y];
          next_y = this.x_match[cur_x];
          this.match(cur_x, cur_y);
          cur_y = next_y;
        }
        this.matched += 1;
        return;
      }
      x = this.y_match[delta_y];
      x_in_tree[x] = true;
      ref2 = this.range;
      for (j = 0, len1 = ref2.length; j < len1; j++) {
        y = ref2[j];
        if (y_parent[y] < 0) {
          new_slack = -(this.cost(x, y));
          if (slack[y] > new_slack) {
            slack[y] = new_slack;
            slack_x[y] = x;
          }
        }
      }
    }
  };

  Hungarian.prototype.find_root_and_slacks = function() {
    var i, len, ref, x, y;
    ref = this.range;
    for (i = 0, len = ref.length; i < len; i++) {
      x = ref[i];
      if (this.x_match[x] < 0) {
        return [
          x, (function() {
            var j, len1, ref1, results;
            ref1 = this.range;
            results = [];
            for (j = 0, len1 = ref1.length; j < len1; j++) {
              y = ref1[j];
              results.push(-(this.cost(x, y)));
            }
            return results;
          }).call(this), (function() {
            var j, len1, ref1, results;
            ref1 = this.range;
            results = [];
            for (j = 0, len1 = ref1.length; j < len1; j++) {
              y = ref1[j];
              results.push(x);
            }
            return results;
          }).call(this)
        ];
      }
    }
  };

  Hungarian.prototype.update_labels = function(delta, x_in_tree, y_parent, slack) {
    var i, j, len, len1, ref, ref1, results, x, y;
    ref = this.range;
    for (i = 0, len = ref.length; i < len; i++) {
      x = ref[i];
      if (x_in_tree[x]) {
        this.x_label[x] -= delta;
      }
    }
    ref1 = this.range;
    results = [];
    for (j = 0, len1 = ref1.length; j < len1; j++) {
      y = ref1[j];
      if (y_parent[y] < 0) {
        results.push(slack[y] -= delta);
      } else {
        results.push(this.y_label[y] += delta);
      }
    }
    return results;
  };

  Hungarian.prototype.get_final_score = function(original_matrix) {
    var x;
    return Util.sum((function() {
      var i, len, ref, results;
      ref = this.range;
      results = [];
      for (i = 0, len = ref.length; i < len; i++) {
        x = ref[i];
        results.push(original_matrix[x][this.x_match[x]]);
      }
      return results;
    }).call(this));
  };

  return Hungarian;

})();

{'fraction_non_alphanumeric': 0.16303514726814847, 'fraction_numerical': 0.019818241541662104, 'mean_word_length': 1.866018198933166, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 6, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7266198', 'n_tokens_mistral': 3377, 'n_tokens_neox': 3301, 'n_words': 776}