// Generated by CoffeeScript 1.6.3
/* 
  Euclidean distance function
  ----------
  Takes 2 objects, and returns squared Euclidean distance based on the first object's attributes
  e.g. p1 = {a: 1} and p2 = {a: 2, b: 3, c: 5} will ignore extra attributes on p2 and return 1
  All attributes on p1 MUST be present in p2 (otherwise will be inaccurate if ignored)
  Accepts optional third argument, which is an options hash: 
   - [stdv] defines the stdv for each attr
   - [weights] defines the weights for each attr (todo)
*/


(function() {
  var mean;

  exports.distance = function(p1, p2, opts) {
    var attr, dist, distance, val, x, y;
    dist = {
      distance: 0,
      details: {}
    };
    for (attr in p1) {
      val = p1[attr];
      x = val;
      y = p2[attr];
      if ((opts != null ? opts.stdv : void 0) && Object.getOwnPropertyNames(opts.stdv).length > 0 && opts.stdv[attr] !== 0) {
        x /= opts.stdv[attr];
        y /= opts.stdv[attr];
      }
      if ((opts != null ? opts.weights : void 0) && Object.getOwnPropertyNames(opts.weights).length > 0) {
        x *= opts.weights[attr];
        y *= opts.weights[attr];
      }
      distance = Math.pow(x - y, 2);
      dist.details[attr] = distance;
      dist.distance += distance;
    }
    if (opts != null ? opts.debug : void 0) {
      return dist;
    } else {
      return dist.distance;
    }
  };

  /*
    Standard Deviation
    ----------
    Given an array of numbers, returns the stdv
    Given an array of objects, require key parameter identifying the attribute to calculate stdv for
  */


  exports.stdv = function(array, key) {
    var a, arr, m, ssqdiff, x, _i, _len;
    if (typeof array[0] !== 'number' && !key) {
      throw new Error('No key parameter provided');
    }
    arr = [];
    if (key) {
      arr = (function() {
        var _i, _len, _results;
        _results = [];
        for (_i = 0, _len = array.length; _i < _len; _i++) {
          a = array[_i];
          _results.push(a[key]);
        }
        return _results;
      })();
    } else {
      arr = array;
    }
    m = mean(arr);
    ssqdiff = 0;
    for (_i = 0, _len = arr.length; _i < _len; _i++) {
      x = arr[_i];
      ssqdiff += Math.pow(x - m, 2);
    }
    return Math.sqrt(ssqdiff / array.length);
  };

  /*
    Get all Standard Deviations
    ----------
    Given a subject and an array of objects, return an object describing each of subject's key's stdv on objects
  */


  exports.allStdvs = function(subject, objects) {
    var attr, objectsClone, stdvs;
    stdvs = {};
    objectsClone = objects.slice(0);
    objectsClone.push(subject);
    for (attr in subject) {
      stdvs[attr] = exports.stdv(objectsClone, attr);
    }
    return stdvs;
  };

  /*
    Standardize objects
    ---
    Takes an array of objects with numerical attributes, and returns object with standardized values,
    with units in measures of standard deviation from mean. (See www.econ.upf.edu/~michael/stanford/maeb4.pdf)
  */


  exports.standardize = function(array) {
    var m, s, x, _i, _len, _results;
    m = mean(array);
    s = exports.stdv(array);
    _results = [];
    for (_i = 0, _len = array.length; _i < _len; _i++) {
      x = array[_i];
      _results.push((x - m) / s);
    }
    return _results;
  };

  mean = function(array) {
    var sum;
    sum = array.reduce(function(a, b) {
      return a + b;
    });
    return sum / array.length;
  };

}).call(this);
