function KMeans(points, k, distance) {
  this.centroids = [];
  Math.random.seeded.setSeed(66613);

};
function mulberry32(a) {
  var out = function() {
    var t = a += 0x6D2B79F5;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  }

  out.getSeed = function() {
    return a;
  };
  out.setSeed = function(A) {
    a = A;
  };
  out.setStringSeed = function(str) {
    str = str.replace(/[^0-9a-z]/g,'').substr(0,12);
    if(str.length === 0)str = '1';
    a = parseInt(str,36);
  };
  out.getStringSeed = function() {
    return a.toString(36);
  };
  return out;
}

Math.random.seeded = mulberry32(Math.floor(Math.random()*4294967296));
KMeans.prototype = {
  randomCentroids: function( points, k ){
    var centroids = points.slice( 0 ); // copy
    centroids.sort( function(){
      return ( Math.round( Math.random.seeded() ) - 0.5 );
    } );
    return centroids.slice( 0, k );
  },

  classify: function( point, distance ){
    var min = Infinity,
      index = 0;

    for( var i = 0; i < this.centroids.length; i++ ){
      var dist = distance( point, this.centroids[ i ] );
      if( dist < min ){
        min = dist;
        index = i;
      }
    }

    return index;
  },

  cluster: function( points, k, distance ){
    k = k || Math.max( 2, Math.ceil( Math.sqrt( points.length / 2 ) ) );

    this.centroids = this.randomCentroids( points, k );

    var assignment = new Array( points.length );
    var clusters = new Array( k );

    var iterations = 0;
    var movement = true;
    while( movement ){
      iterations++;
      if(iterations>500)
        break;
      // update point-to-centroid assignments
      for( var i = 0; i < points.length; i++ ){
        assignment[ i ] = this.classify( points[ i ], distance );
      }

      // update location of each centroid
      movement = false;
      for( var j = 0; j < k; j++ ){
        var assigned = [];
        for( var i = 0; i < assignment.length; i++ ){
          if( assignment[ i ] == j ){
            assigned.push( points[ i ] );
          }
        }

        if( !assigned.length ){
          continue;
        }

        var centroid = this.centroids[ j ];
        var newCentroid = new Array( centroid.length );

        for( var g = 0; g < centroid.length; g++ ){
          var sum = 0;
          for( var i = 0; i < assigned.length; i++ ){
            sum += assigned[ i ][ g ];
          }
          newCentroid[ g ] = sum / assigned.length;

          if( newCentroid[ g ] != centroid[ g ] ){
            movement = true;
          }
        }

        this.centroids[ j ] = newCentroid;
        clusters[ j ] = assigned;
      }

    }

    return clusters;
  }
};