File size: 1,897 Bytes
bc20498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// Common distance metrics for clustering algorithms
// https://en.wikipedia.org/wiki/Hierarchical_clustering#Metric

import * as is from '../../is';

let identity = x => x;
let absDiff = ( p, q ) => Math.abs( q - p );
let addAbsDiff = ( total, p, q ) => total + absDiff(p, q);
let addSquaredDiff = ( total, p, q ) => total + Math.pow( q - p, 2 );
let sqrt = x => Math.sqrt(x);
let maxAbsDiff = ( currentMax, p, q ) => Math.max( currentMax, absDiff(p, q) );

let getDistance = function( length, getP, getQ, init, visit, post = identity ){
  let ret = init;
  let p, q;

  for ( let dim = 0; dim < length; dim++ ) {
    p = getP(dim);
    q = getQ(dim);

    ret = visit( ret, p, q );
  }

  return post( ret );
};

let distances = {
  euclidean: function ( length, getP, getQ ) {
    if( length >= 2 ){
      return getDistance( length, getP, getQ, 0, addSquaredDiff, sqrt );
    } else { // for single attr case, more efficient to avoid sqrt
      return getDistance( length, getP, getQ, 0, addAbsDiff );
    }
  },
  squaredEuclidean: function ( length, getP, getQ ) {
    return getDistance( length, getP, getQ, 0, addSquaredDiff );
  },
  manhattan: function ( length, getP, getQ ) {
    return getDistance( length, getP, getQ, 0, addAbsDiff );
  },
  max: function ( length, getP, getQ ) {
    return getDistance( length, getP, getQ, -Infinity, maxAbsDiff );
  }
};

// in case the user accidentally doesn't use camel case
distances['squared-euclidean'] = distances['squaredEuclidean'];
distances['squaredeuclidean'] = distances['squaredEuclidean'];

export default function( method, length, getP, getQ, nodeP, nodeQ ){
  let impl;

  if( is.fn( method ) ){
    impl = method;
  } else {
    impl = distances[ method ] || distances.euclidean;
  }

  if( length === 0 && is.fn( method ) ){
    return impl( nodeP, nodeQ );
  } else {
    return impl( length, getP, getQ, nodeP, nodeQ );
  }
}