PageSourceSearch

https://terrancelaw.github.io/iQuery/vender/scagnostics.min.js

js terrancelaw.github.io collected 2026-10-03 10:05:12 UTC 146,826 bytes, 4,285 lines download raw bytes

1(function () {
2	'use strict';
3
4	var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5
6	function createCommonjsModule(fn, module) {
7		return module = { exports: {} }, fn(module, module.exports), module.exports;
8	}
9
10	var underscore = createCommonjsModule(function (module, exports) {
11	//     Underscore.js 1.9.1
12	//     http://underscorejs.org
13	//     (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
14	//     Underscore may be freely distributed under the MIT license.
15
16	(function() {
17
18	  // Baseline setup
19	  // --------------
20
21	  // Establish the root object, `window` (`self`) in the browser, `global`
22	  // on the server, or `this` in some virtual machines. We use `self`
23	  // instead of `window` for `WebWorker` support.
24	  var root = typeof self == 'object' && self.self === self && self ||
25	            typeof commonjsGlobal == 'object' && commonjsGlobal.global === commonjsGlobal && commonjsGlobal ||
26	            this ||
27	            {};
28
29	  // Save the previous value of the `_` variable.
30	  var previousUnderscore = root._;
31
32	  // Save bytes in the minified (but not gzipped) version:
33	  var ArrayProto = Array.prototype, ObjProto = Object.prototype;
34	  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
35
36	  // Create quick reference variables for speed access to core prototypes.
37	  var push = ArrayProto.push,
38	      slice = ArrayProto.slice,
39	      toString = ObjProto.toString,
40	      hasOwnProperty = ObjProto.hasOwnProperty;
41
42	  // All **ECMAScript 5** native function implementations that we hope to use
43	  // are declared here.
44	  var nativeIsArray = Array.isArray,
45	      nativeKeys = Object.keys,
46	      nativeCreate = Object.create;
47
48	  // Naked function reference for surrogate-prototype-swapping.
49	  var Ctor = function(){};
50
51	  // Create a safe reference to the Underscore object for use below.
52	  var _ = function(obj) {
53	    if (obj instanceof _) return obj;
54	    if (!(this instanceof _)) return new _(obj);
55	    this._wrapped = obj;
56	  };
57
58	  // Export the Underscore object for **Node.js**, with
59	  // backwards-compatibility for their old module API. If we're in
60	  // the browser, add `_` as a global object.
61	  // (`nodeType` is checked to ensure that `module`
62	  // and `exports` are not HTML elements.)
63	  if (!exports.nodeType) {
64	    if (!module.nodeType && module.exports) {
65	      exports = module.exports = _;
66	    }
67	    exports._ = _;
68	  } else {
69	    root._ = _;
70	  }
71
72	  // Current version.
73	  _.VERSION = '1.9.1';
74
75	  // Internal function that returns an efficient (for current engines) version
76	  // of the passed-in callback, to be repeatedly applied in other Underscore
77	  // functions.
78	  var optimizeCb = function(func, context, argCount) {
79	    if (context === void 0) return func;
80	    switch (argCount == null ? 3 : argCount) {
81	      case 1: return function(value) {
82	        return func.call(context, value);
83	      };
84	      // The 2-argument case is omitted because we’re not using it.
85	      case 3: return function(value, index, collection) {
86	        return func.call(context, value, index, collection);
87	      };
88	      case 4: return function(accumulator, value, index, collection) {
89	        return func.call(context, accumulator, value, index, collection);
90	      };
91	    }
92	    return function() {
93	      return func.apply(context, arguments);
94	    };
95	  };
96
97	  var builtinIteratee;
98
99	  // An internal function to generate callbacks that can be applied to each
100	  // element in a collection, returning the desired result — either `identity`,
101	  // an arbitrary callback, a property matcher, or a property accessor.
102	  var cb = function(value, context, argCount) {
103	    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
104	    if (value == null) return _.identity;
105	    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
106	    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
107	    return _.property(value);
108	  };
109
110	  // External wrapper for our callback generator. Users may customize
111	  // `_.iteratee` if they want additional predicate/iteratee shorthand styles.
112	  // This abstraction hides the internal-only argCount argument.
113	  _.iteratee = builtinIteratee = function(value, context) {
114	    return cb(value, context, Infinity);
115	  };
116
117	  // Some functions take a variable number of arguments, or a few expected
118	  // arguments at the beginning and then a variable number of values to operate
119	  // on. This helper accumulates all remaining arguments past the function’s
120	  // argument length (or an explicit `startIndex`), into an array that becomes
121	  // the last argument. Similar to ES6’s "rest parameter".
122	  var restArguments = function(func, startIndex) {
123	    startIndex = startIndex == null ? func.length - 1 : +startIndex;
124	    return function() {
125	      var length = Math.max(arguments.length - startIndex, 0),
126	          rest = Array(length),
127	          index = 0;
128	      for (; index < length; index++) {
129	        rest[index] = arguments[index + startIndex];
130	      }
131	      switch (startIndex) {
132	        case 0: return func.call(this, rest);
133	        case 1: return func.call(this, arguments[0], rest);
134	        case 2: return func.call(this, arguments[0], arguments[1], rest);
135	      }
136	      var args = Array(startIndex + 1);
137	      for (index = 0; index < startIndex; index++) {
138	        args[index] = arguments[index];
139	      }
140	      args[startIndex] = rest;
141	      return func.apply(this, args);
142	    };
143	  };
144
145	  // An internal function for creating a new object that inherits from another.
146	  var baseCreate = function(prototype) {
147	    if (!_.isObject(prototype)) return {};
148	    if (nativeCreate) return nativeCreate(prototype);
149	    Ctor.prototype = prototype;
150	    var result = new Ctor;
151	    Ctor.prototype = null;
152	    return result;
153	  };
154
155	  var shallowProperty = function(key) {
156	    return function(obj) {
157	      return obj == null ? void 0 : obj[key];
158	    };
159	  };
160
161	  var has = function(obj, path) {
162	    return obj != null && hasOwnProperty.call(obj, path);
163	  };
164
165	  var deepGet = function(obj, path) {
166	    var length = path.length;
167	    for (var i = 0; i < length; i++) {
168	      if (obj == null) return void 0;
169	      obj = obj[path[i]];
170	    }
171	    return length ? obj : void 0;
172	  };
173
174	  // Helper for collection methods to determine whether a collection
175	  // should be iterated as an array or as an object.
176	  // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
177	  // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
178	  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
179	  var getLength = shallowProperty('length');
180	  var isArrayLike = function(collection) {
181	    var length = getLength(collection);
182	    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
183	  };
184
185	  // Collection Functions
186	  // --------------------
187
188	  // The cornerstone, an `each` implementation, aka `forEach`.
189	  // Handles raw objects in addition to array-likes. Treats all
190	  // sparse array-likes as if they were dense.
191	  _.each = _.forEach = function(obj, iteratee, context) {
192	    iteratee = optimizeCb(iteratee, context);
193	    var i, length;
194	    if (isArrayLike(obj)) {
195	      for (i = 0, length = obj.length; i < length; i++) {
196	        iteratee(obj[i], i, obj);
197	      }
198	    } else {
199	      var keys = _.keys(obj);
200	      for (i = 0, length = keys.length; i < length; i++) {
201	        iteratee(obj[keys[i]], keys[i], obj);
202	      }
203	    }
204	    return obj;
205	  };
206
207	  // Return the results of applying the iteratee to each element.
208	  _.map = _.collect = function(obj, iteratee, context) {
209	    iteratee = cb(iteratee, context);
210	    var keys = !isArrayLike(obj) && _.keys(obj),
211	        length = (keys || obj).length,
212	        results = Array(length);
213	    for (var index = 0; index < length; index++) {
214	      var currentKey = keys ? keys[index] : index;
215	      results[index] = iteratee(obj[currentKey], currentKey, obj);
216	    }
217	    return results;
218	  };
219
220	  // Create a reducing function iterating left or right.
221	  var createReduce = function(dir) {
222	    // Wrap code that reassigns argument variables in a separate function than
223	    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
224	    var reducer = function(obj, iteratee, memo, initial) {
225	      var keys = !isArrayLike(obj) && _.keys(obj),
226	          length = (keys || obj).length,
227	          index = dir > 0 ? 0 : length - 1;
228	      if (!initial) {
229	        memo = obj[keys ? keys[index] : index];
230	        index += dir;
231	      }
232	      for (; index >= 0 && index < length; index += dir) {
233	        var currentKey = keys ? keys[index] : index;
234	        memo = iteratee(memo, obj[currentKey], currentKey, obj);
235	      }
236	      return memo;
237	    };
238
239	    return function(obj, iteratee, memo, context) {
240	      var initial = arguments.length >= 3;
241	      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
242	    };
243	  };
244
245	  // **Reduce** builds up a single result from a list of values, aka `inject`,
246	  // or `foldl`.
247	  _.reduce = _.foldl = _.inject = createReduce(1);
248
249	  // The right-associative version of reduce, also known as `foldr`.
250	  _.reduceRight = _.foldr = createReduce(-1);
251
252	  // Return the first value which passes a truth test. Aliased as `detect`.
253	  _.find = _.detect = function(obj, predicate, context) {
254	    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
255	    var key = keyFinder(obj, predicate, context);
256	    if (key !== void 0 && key !== -1) return obj[key];
257	  };
258
259	  // Return all the elements that pass a truth test.
260	  // Aliased as `select`.
261	  _.filter = _.select = function(obj, predicate, context) {
262	    var results = [];
263	    predicate = cb(predicate, context);
264	    _.each(obj, function(value, index, list) {
265	      if (predicate(value, index, list)) results.push(value);
266	    });
267	    return results;
268	  };
269
270	  // Return all the elements for which a truth test fails.
271	  _.reject = function(obj, predicate, context) {
272	    return _.filter(obj, _.negate(cb(predicate)), context);
273	  };
274
275	  // Determine whether all of the elements match a truth test.
276	  // Aliased as `all`.
277	  _.every = _.all = function(obj, predicate, context) {
278	    predicate = cb(predicate, context);
279	    var keys = !isArrayLike(obj) && _.keys(obj),
280	        length = (keys || obj).length;
281	    for (var index = 0; index < length; index++) {
282	      var currentKey = keys ? keys[index] : index;
283	      if (!predicate(obj[currentKey], currentKey, obj)) return false;
284	    }
285	    return true;
286	  };
287
288	  // Determine if at least one element in the object matches a truth test.
289	  // Aliased as `any`.
290	  _.some = _.any = function(obj, predicate, context) {
291	    predicate = cb(predicate, context);
292	    var keys = !isArrayLike(obj) && _.keys(obj),
293	        length = (keys || obj).length;
294	    for (var index = 0; index < length; index++) {
295	      var currentKey = keys ? keys[index] : index;
296	      if (predicate(obj[currentKey], currentKey, obj)) return true;
297	    }
298	    return false;
299	  };
300
301	  // Determine if the array or object contains a given item (using `===`).
302	  // Aliased as `includes` and `include`.
303	  _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
304	    if (!isArrayLike(obj)) obj = _.values(obj);
305	    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
306	    return _.indexOf(obj, item, fromIndex) >= 0;
307	  };
308
309	  // Invoke a method (with arguments) on every item in a collection.
310	  _.invoke = restArguments(function(obj, path, args) {
311	    var contextPath, func;
312	    if (_.isFunction(path)) {
313	      func = path;
314	    } else if (_.isArray(path)) {
315	      contextPath = path.slice(0, -1);
316	      path = path[path.length - 1];
317	    }
318	    return _.map(obj, function(context) {
319	      var method = func;
320	      if (!method) {
321	        if (contextPath && contextPath.length) {
322	          context = deepGet(context, contextPath);
323	        }
324	        if (context == null) return void 0;
325	        method = context[path];
326	      }
327	      return method == null ? method : method.apply(context, args);
328	    });
329	  });
330
331	  // Convenience version of a common use case of `map`: fetching a property.
332	  _.pluck = function(obj, key) {
333	    return _.map(obj, _.property(key));
334	  };
335
336	  // Convenience version of a common use case of `filter`: selecting only objects
337	  // containing specific `key:value` pairs.
338	  _.where = function(obj, attrs) {
339	    return _.filter(obj, _.matcher(attrs));
340	  };
341
342	  // Convenience version of a common use case of `find`: getting the first object
343	  // containing specific `key:value` pairs.
344	  _.findWhere = function(obj, attrs) {
345	    return _.find(obj, _.matcher(attrs));
346	  };
347
348	  // Return the maximum element (or element-based computation).
349	  _.max = function(obj, iteratee, context) {
350	    var result = -Infinity, lastComputed = -Infinity,
351	        value, computed;
352	    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
353	      obj = isArrayLike(obj) ? obj : _.values(obj);
354	      for (var i = 0, length = obj.length; i < length; i++) {
355	        value = obj[i];
356	        if (value != null && value > result) {
357	          result = value;
358	        }
359	      }
360	    } else {
361	      iteratee = cb(iteratee, context);
362	      _.each(obj, function(v, index, list) {
363	        computed = iteratee(v, index, list);
364	        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
365	          result = v;
366	          lastComputed = computed;
367	        }
368	      });
369	    }
370	    return result;
371	  };
372
373	  // Return the minimum element (or element-based computation).
374	  _.min = function(obj, iteratee, context) {
375	    var result = Infinity, lastComputed = Infinity,
376	        value, computed;
377	    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
378	      obj = isArrayLike(obj) ? obj : _.values(obj);
379	      for (var i = 0, length = obj.length; i < length; i++) {
380	        value = obj[i];
381	        if (value != null && value < result) {
382	          result = value;
383	        }
384	      }
385	    } else {
386	      iteratee = cb(iteratee, context);
387	      _.each(obj, function(v, index, list) {
388	        computed = iteratee(v, index, list);
389	        if (computed < lastComputed || computed === Infinity && result === Infinity) {
390	          result = v;
391	          lastComputed = computed;
392	        }
393	      });
394	    }
395	    return result;
396	  };
397
398	  // Shuffle a collection.
399	  _.shuffle = function(obj) {
400	    return _.sample(obj, Infinity);
401	  };
402
403	  // Sample **n** random values from a collection using the modern version of the
404	  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
405	  // If **n** is not specified, returns a single random element.
406	  // The internal `guard` argument allows it to work with `map`.
407	  _.sample = function(obj, n, guard) {
408	    if (n == null || guard) {
409	      if (!isArrayLike(obj)) obj = _.values(obj);
410	      return obj[_.random(obj.length - 1)];
411	    }
412	    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
413	    var length = getLength(sample);
414	    n = Math.max(Math.min(n, length), 0);
415	    var last = length - 1;
416	    for (var index = 0; index < n; index++) {
417	      var rand = _.random(index, last);
418	      var temp = sample[index];
419	      sample[index] = sample[rand];
420	      sample[rand] = temp;
421	    }
422	    return sample.slice(0, n);
423	  };
424
425	  // Sort the object's values by a criterion produced by an iteratee.
426	  _.sortBy = function(obj, iteratee, context) {
427	    var index = 0;
428	    iteratee = cb(iteratee, context);
429	    return _.pluck(_.map(obj, function(value, key, list) {
430	      return {
431	        value: value,
432	        index: index++,
433	        criteria: iteratee(value, key, list)
434	      };
435	    }).sort(function(left, right) {
436	      var a = left.criteria;
437	      var b = right.criteria;
438	      if (a !== b) {
439	        if (a > b || a === void 0) return 1;
440	        if (a < b || b === void 0) return -1;
441	      }
442	      return left.index - right.index;
443	    }), 'value');
444	  };
445
446	  // An internal function used for aggregate "group by" operations.
447	  var group = function(behavior, partition) {
448	    return function(obj, iteratee, context) {
449	      var result = partition ? [[], []] : {};
450	      iteratee = cb(iteratee, context);
451	      _.each(obj, function(value, index) {
452	        var key = iteratee(value, index, obj);
453	        behavior(result, value, key);
454	      });
455	      return result;
456	    };
457	  };
458
459	  // Groups the object's values by a criterion. Pass either a string attribute
460	  // to group by, or a function that returns the criterion.
461	  _.groupBy = group(function(result, value, key) {
462	    if (has(result, key)) result[key].push(value); else result[key] = [value];
463	  });
464
465	  // Indexes the object's values by a criterion, similar to `groupBy`, but for
466	  // when you know that your index values will be unique.
467	  _.indexBy = group(function(result, value, key) {
468	    result[key] = value;
469	  });
470
471	  // Counts instances of an object that group by a certain criterion. Pass
472	  // either a string attribute to count by, or a function that returns the
473	  // criterion.
474	  _.countBy = group(function(result, value, key) {
475	    if (has(result, key)) result[key]++; else result[key] = 1;
476	  });
477
478	  var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
479	  // Safely create a real, live array from anything iterable.
480	  _.toArray = function(obj) {
481	    if (!obj) return [];
482	    if (_.isArray(obj)) return slice.call(obj);
483	    if (_.isString(obj)) {
484	      // Keep surrogate pair characters together
485	      return obj.match(reStrSymbol);
486	    }
487	    if (isArrayLike(obj)) return _.map(obj, _.identity);
488	    return _.values(obj);
489	  };
490
491	  // Return the number of elements in an object.
492	  _.size = function(obj) {
493	    if (obj == null) return 0;
494	    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
495	  };
496
497	  // Split a collection into two arrays: one whose elements all satisfy the given
498	  // predicate, and one whose elements all do not satisfy the predicate.
499	  _.partition = group(function(result, value, pass) {
500	    result[pass ? 0 : 1].push(value);
501	  }, true);
502
503	  // Array Functions
504	  // ---------------
505
506	  // Get the first element of an array. Passing **n** will return the first N
507	  // values in the array. Aliased as `head` and `take`. The **guard** check
508	  // allows it to work with `_.map`.
509	  _.first = _.head = _.take = function(array, n, guard) {
510	    if (array == null || array.length < 1) return n == null ? void 0 : [];
511	    if (n == null || guard) return array[0];
512	    return _.initial(array, array.length - n);
513	  };
514
515	  // Returns everything but the last entry of the array. Especially useful on
516	  // the arguments object. Passing **n** will return all the values in
517	  // the array, excluding the last N.
518	  _.initial = function(array, n, guard) {
519	    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
520	  };
521
522	  // Get the last element of an array. Passing **n** will return the last N
523	  // values in the array.
524	  _.last = function(array, n, guard) {
525	    if (array == null || array.length < 1) return n == null ? void 0 : [];
526	    if (n == null || guard) return array[array.length - 1];
527	    return _.rest(array, Math.max(0, array.length - n));
528	  };
529
530	  // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
531	  // Especially useful on the arguments object. Passing an **n** will return
532	  // the rest N values in the array.
533	  _.rest = _.tail = _.drop = function(array, n, guard) {
534	    return slice.call(array, n == null || guard ? 1 : n);
535	  };
536
537	  // Trim out all falsy values from an array.
538	  _.compact = function(array) {
539	    return _.filter(array, Boolean);
540	  };
541
542	  // Internal implementation of a recursive `flatten` function.
543	  var flatten = function(input, shallow, strict, output) {
544	    output = output || [];
545	    var idx = output.length;
546	    for (var i = 0, length = getLength(input); i < length; i++) {
547	      var value = input[i];
548	      if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
549	        // Flatten current level of array or arguments object.
550	        if (shallow) {
551	          var j = 0, len = value.length;
552	          while (j < len) output[idx++] = value[j++];
553	        } else {
554	          flatten(value, shallow, strict, output);
555	          idx = output.length;
556	        }
557	      } else if (!strict) {
558	        output[idx++] = value;
559	      }
560	    }
561	    return output;
562	  };
563
564	  // Flatten out an array, either recursively (by default), or just one level.
565	  _.flatten = function(array, shallow) {
566	    return flatten(array, shallow, false);
567	  };
568
569	  // Return a version of the array that does not contain the specified value(s).
570	  _.without = restArguments(function(array, otherArrays) {
571	    return _.difference(array, otherArrays);
572	  });
573
574	  // Produce a duplicate-free version of the array. If the array has already
575	  // been sorted, you have the option of using a faster algorithm.
576	  // The faster algorithm will not work with an iteratee if the iteratee
577	  // is not a one-to-one function, so providing an iteratee will disable
578	  // the faster algorithm.
579	  // Aliased as `unique`.
580	  _.uniq = _.unique = function(array, isSorted, iteratee, context) {
581	    if (!_.isBoolean(isSorted)) {
582	      context = iteratee;
583	      iteratee = isSorted;
584	      isSorted = false;
585	    }
586	    if (iteratee != null) iteratee = cb(iteratee, context);
587	    var result = [];
588	    var seen = [];
589	    for (var i = 0, length = getLength(array); i < length; i++) {
590	      var value = array[i],
591	          computed = iteratee ? iteratee(value, i, array) : value;
592	      if (isSorted && !iteratee) {
593	        if (!i || seen !== computed) result.push(value);
594	        seen = computed;
595	      } else if (iteratee) {
596	        if (!_.contains(seen, computed)) {
597	          seen.push(computed);
598	          result.push(value);
599	        }
600	      } else if (!_.contains(result, value)) {
601	        result.push(value);
602	      }
603	    }
604	    return result;
605	  };
606
607	  // Produce an array that contains the union: each distinct element from all of
608	  // the passed-in arrays.
609	  _.union = restArguments(function(arrays) {
610	    return _.uniq(flatten(arrays, true, true));
611	  });
612
613	  // Produce an array that contains every item shared between all the
614	  // passed-in arrays.
615	  _.intersection = function(array) {
616	    var result = [];
617	    var argsLength = arguments.length;
618	    for (var i = 0, length = getLength(array); i < length; i++) {
619	      var item = array[i];
620	      if (_.contains(result, item)) continue;
621	      var j;
622	      for (j = 1; j < argsLength; j++) {
623	        if (!_.contains(arguments[j], item)) break;
624	      }
625	      if (j === argsLength) result.push(item);
626	    }
627	    return result;
628	  };
629
630	  // Take the difference between one array and a number of other arrays.
631	  // Only the elements present in just the first array will remain.
632	  _.difference = restArguments(function(array, rest) {
633	    rest = flatten(rest, true, true);
634	    return _.filter(array, function(value){
635	      return !_.contains(rest, value);
636	    });
637	  });
638
639	  // Complement of _.zip. Unzip accepts an array of arrays and groups
640	  // each array's elements on shared indices.
641	  _.unzip = function(array) {
642	    var length = array && _.max(array, getLength).length || 0;
643	    var result = Array(length);
644
645	    for (var index = 0; index < length; index++) {
646	      result[index] = _.pluck(array, index);
647	    }
648	    return result;
649	  };
650
651	  // Zip together multiple lists into a single array -- elements that share
652	  // an index go together.
653	  _.zip = restArguments(_.unzip);
654
655	  // Converts lists into objects. Pass either a single array of `[key, value]`
656	  // pairs, or two parallel arrays of the same length -- one of keys, and one of
657	  // the corresponding values. Passing by pairs is the reverse of _.pairs.
658	  _.object = function(list, values) {
659	    var result = {};
660	    for (var i = 0, length = getLength(list); i < length; i++) {
661	      if (values) {
662	        result[list[i]] = values[i];
663	      } else {
664	        result[list[i][0]] = list[i][1];
665	      }
666	    }
667	    return result;
668	  };
669
670	  // Generator function to create the findIndex and findLastIndex functions.
671	  var createPredicateIndexFinder = function(dir) {
672	    return function(array, predicate, context) {
673	      predicate = cb(predicate, context);
674	      var length = getLength(array);
675	      var index = dir > 0 ? 0 : length - 1;
676	      for (; index >= 0 && index < length; index += dir) {
677	        if (predicate(array[index], index, array)) return index;
678	      }
679	      return -1;
680	    };
681	  };
682
683	  // Returns the first index on an array-like that passes a predicate test.
684	  _.findIndex = createPredicateIndexFinder(1);
685	  _.findLastIndex = createPredicateIndexFinder(-1);
686
687	  // Use a comparator function to figure out the smallest index at which
688	  // an object should be inserted so as to maintain order. Uses binary search.
689	  _.sortedIndex = function(array, obj, iteratee, context) {
690	    iteratee = cb(iteratee, context, 1);
691	    var value = iteratee(obj);
692	    var low = 0, high = getLength(array);
693	    while (low < high) {
694	      var mid = Math.floor((low + high) / 2);
695	      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
696	    }
697	    return low;
698	  };
699
700	  // Generator function to create the indexOf and lastIndexOf functions.
701	  var createIndexFinder = function(dir, predicateFind, sortedIndex) {
702	    return function(array, item, idx) {
703	      var i = 0, length = getLength(array);
704	      if (typeof idx == 'number') {
705	        if (dir > 0) {
706	          i = idx >= 0 ? idx : Math.max(idx + length, i);
707	        } else {
708	          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
709	        }
710	      } else if (sortedIndex && idx && length) {
711	        idx = sortedIndex(array, item);
712	        return array[idx] === item ? idx : -1;
713	      }
714	      if (item !== item) {
715	        idx = predicateFind(slice.call(array, i, length), _.isNaN);
716	        return idx >= 0 ? idx + i : -1;
717	      }
718	      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
719	        if (array[idx] === item) return idx;
720	      }
721	      return -1;
722	    };
723	  };
724
725	  // Return the position of the first occurrence of an item in an array,
726	  // or -1 if the item is not included in the array.
727	  // If the array is large and already in sort order, pass `true`
728	  // for **isSorted** to use binary search.
729	  _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
730	  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
731
732	  // Generate an integer Array containing an arithmetic progression. A port of
733	  // the native Python `range()` function. See
734	  // [the Python documentation](http://docs.python.org/library/functions.html#range).
735	  _.range = function(start, stop, step) {
736	    if (stop == null) {
737	      stop = start || 0;
738	      start = 0;
739	    }
740	    if (!step) {
741	      step = stop < start ? -1 : 1;
742	    }
743
744	    var length = Math.max(Math.ceil((stop - start) / step), 0);
745	    var range = Array(length);
746
747	    for (var idx = 0; idx < length; idx++, start += step) {
748	      range[idx] = start;
749	    }
750
751	    return range;
752	  };
753
754	  // Chunk a single array into multiple arrays, each containing `count` or fewer
755	  // items.
756	  _.chunk = function(array, count) {
757	    if (count == null || count < 1) return [];
758	    var result = [];
759	    var i = 0, length = array.length;
760	    while (i < length) {
761	      result.push(slice.call(array, i, i += count));
762	    }
763	    return result;
764	  };
765
766	  // Function (ahem) Functions
767	  // ------------------
768
769	  // Determines whether to execute a function as a constructor
770	  // or a normal function with the provided arguments.
771	  var executeBound = function(sourceFunc, boundFunc, context, callingContext, args
771) {
772	    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
773	    var self = baseCreate(sourceFunc.prototype);
774	    var result = sourceFunc.apply(self, args);
775	    if (_.isObject(result)) return result;
776	    return self;
777	  };
778
779	  // Create a function bound to a given object (assigning `this`, and arguments,
780	  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
781	  // available.
782	  _.bind = restArguments(function(func, context, args) {
783	    if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
784	    var bound = restArguments(function(callArgs) {
785	      return executeBound(func, bound, context, this, args.concat(callArgs));
786	    });
787	    return bound;
788	  });
789
790	  // Partially apply a function by creating a version that has had some of its
791	  // arguments pre-filled, without changing its dynamic `this` context. _ acts
792	  // as a placeholder by default, allowing any combination of arguments to be
793	  // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
794	  _.partial = restArguments(function(func, boundArgs) {
795	    var placeholder = _.partial.placeholder;
796	    var bound = function() {
797	      var position = 0, length = boundArgs.length;
798	      var args = Array(length);
799	      for (var i = 0; i < length; i++) {
800	        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
801	      }
802	      while (position < arguments.length) args.push(arguments[position++]);
803	      return executeBound(func, bound, this, this, args);
804	    };
805	    return bound;
806	  });
807
808	  _.partial.placeholder = _;
809
810	  // Bind a number of an object's methods to that object. Remaining arguments
811	  // are the method names to be bound. Useful for ensuring that all callbacks
812	  // defined on an object belong to it.
813	  _.bindAll = restArguments(function(obj, keys) {
814	    keys = flatten(keys, false, false);
815	    var index = keys.length;
816	    if (index < 1) throw new Error('bindAll must be passed function names');
817	    while (index--) {
818	      var key = keys[index];
819	      obj[key] = _.bind(obj[key], obj);
820	    }
821	  });
822
823	  // Memoize an expensive function by storing its results.
824	  _.memoize = function(func, hasher) {
825	    var memoize = function(key) {
826	      var cache = memoize.cache;
827	      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
828	      if (!has(cache, address)) cache[address] = func.apply(this, arguments);
829	      return cache[address];
830	    };
831	    memoize.cache = {};
832	    return memoize;
833	  };
834
835	  // Delays a function for the given number of milliseconds, and then calls
836	  // it with the arguments supplied.
837	  _.delay = restArguments(function(func, wait, args) {
838	    return setTimeout(function() {
839	      return func.apply(null, args);
840	    }, wait);
841	  });
842
843	  // Defers a function, scheduling it to run after the current call stack has
844	  // cleared.
845	  _.defer = _.partial(_.delay, _, 1);
846
847	  // Returns a function, that, when invoked, will only be triggered at most once
848	  // during a given window of time. Normally, the throttled function will run
849	  // as much as it can, without ever going more than once per `wait` duration;
850	  // but if you'd like to disable the execution on the leading edge, pass
851	  // `{leading: false}`. To disable execution on the trailing edge, ditto.
852	  _.throttle = function(func, wait, options) {
853	    var timeout, context, args, result;
854	    var previous = 0;
855	    if (!options) options = {};
856
857	    var later = function() {
858	      previous = options.leading === false ? 0 : _.now();
859	      timeout = null;
860	      result = func.apply(context, args);
861	      if (!timeout) context = args = null;
862	    };
863
864	    var throttled = function() {
865	      var now = _.now();
866	      if (!previous && options.leading === false) previous = now;
867	      var remaining = wait - (now - previous);
868	      context = this;
869	      args = arguments;
870	      if (remaining <= 0 || remaining > wait) {
871	        if (timeout) {
872	          clearTimeout(timeout);
873	          timeout = null;
874	        }
875	        previous = now;
876	        result = func.apply(context, args);
877	        if (!timeout) context = args = null;
878	      } else if (!timeout && options.trailing !== false) {
879	        timeout = setTimeout(later, remaining);
880	      }
881	      return result;
882	    };
883
884	    throttled.cancel = function() {
885	      clearTimeout(timeout);
886	      previous = 0;
887	      timeout = context = args = null;
888	    };
889
890	    return throttled;
891	  };
892
893	  // Returns a function, that, as long as it continues to be invoked, will not
894	  // be triggered. The function will be called after it stops being called for
895	  // N milliseconds. If `immediate` is passed, trigger the function on the
896	  // leading edge, instead of the trailing.
897	  _.debounce = function(func, wait, immediate) {
898	    var timeout, result;
899
900	    var later = function(context, args) {
901	      timeout = null;
902	      if (args) result = func.apply(context, args);
903	    };
904
905	    var debounced = restArguments(function(args) {
906	      if (timeout) clearTimeout(timeout);
907	      if (immediate) {
908	        var callNow = !timeout;
909	        timeout = setTimeout(later, wait);
910	        if (callNow) result = func.apply(this, args);
911	      } else {
912	        timeout = _.delay(later, wait, this, args);
913	      }
914
915	      return result;
916	    });
917
918	    debounced.cancel = function() {
919	      clearTimeout(timeout);
920	      timeout = null;
921	    };
922
923	    return debounced;
924	  };
925
926	  // Returns the first function passed as an argument to the second,
927	  // allowing you to adjust arguments, run code before and after, and
928	  // conditionally execute the original function.
929	  _.wrap = function(func, wrapper) {
930	    return _.partial(wrapper, func);
931	  };
932
933	  // Returns a negated version of the passed-in predicate.
934	  _.negate = function(predicate) {
935	    return function() {
936	      return !predicate.apply(this, arguments);
937	    };
938	  };
939
940	  // Returns a function that is the composition of a list of functions, each
941	  // consuming the return value of the function that follows.
942	  _.compose = function() {
943	    var args = arguments;
944	    var start = args.length - 1;
945	    return function() {
946	      var i = start;
947	      var result = args[start].apply(this, arguments);
948	      while (i--) result = args[i].call(this, result);
949	      return result;
950	    };
951	  };
952
953	  // Returns a function that will only be executed on and after the Nth call.
954	  _.after = function(times, func) {
955	    return function() {
956	      if (--times < 1) {
957	        return func.apply(this, arguments);
958	      }
959	    };
960	  };
961
962	  // Returns a function that will only be executed up to (but not including) the Nth call.
963	  _.before = function(times, func) {
964	    var memo;
965	    return function() {
966	      if (--times > 0) {
967	        memo = func.apply(this, arguments);
968	      }
969	      if (times <= 1) func = null;
970	      return memo;
971	    };
972	  };
973
974	  // Returns a function that will be executed at most one time, no matter how
975	  // often you call it. Useful for lazy initialization.
976	  _.once = _.partial(_.before, 2);
977
978	  _.restArguments = restArguments;
979
980	  // Object Functions
981	  // ----------------
982
983	  // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
984	  var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
985	  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
986	    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
987
988	  var collectNonEnumProps = function(obj, keys) {
989	    var nonEnumIdx = nonEnumerableProps.length;
990	    var constructor = obj.constructor;
991	    var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
992
993	    // Constructor is a special case.
994	    var prop = 'constructor';
995	    if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
996
997	    while (nonEnumIdx--) {
998	      prop = nonEnumerableProps[nonEnumIdx];
999	      if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
1000	        keys.push(prop);
1001	      }
1002	    }
1003	  };
1004
1005	  // Retrieve the names of an object's own properties.
1006	  // Delegates to **ECMAScript 5**'s native `Object.keys`.
1007	  _.keys = function(obj) {
1008	    if (!_.isObject(obj)) return [];
1009	    if (nativeKeys) return nativeKeys(obj);
1010	    var keys = [];
1011	    for (var key in obj) if (has(obj, key)) keys.push(key);
1012	    // Ahem, IE < 9.
1013	    if (hasEnumBug) collectNonEnumProps(obj, keys);
1014	    return keys;
1015	  };
1016
1017	  // Retrieve all the property names of an object.
1018	  _.allKeys = function(obj) {
1019	    if (!_.isObject(obj)) return [];
1020	    var keys = [];
1021	    for (var key in obj) keys.push(key);
1022	    // Ahem, IE < 9.
1023	    if (hasEnumBug) collectNonEnumProps(obj, keys);
1024	    return keys;
1025	  };
1026
1027	  // Retrieve the values of an object's properties.
1028	  _.values = function(obj) {
1029	    var keys = _.keys(obj);
1030	    var length = keys.length;
1031	    var values = Array(length);
1032	    for (var i = 0; i < length; i++) {
1033	      values[i] = obj[keys[i]];
1034	    }
1035	    return values;
1036	  };
1037
1038	  // Returns the results of applying the iteratee to each element of the object.
1039	  // In contrast to _.map it returns an object.
1040	  _.mapObject = function(obj, iteratee, context) {
1041	    iteratee = cb(iteratee, context);
1042	    var keys = _.keys(obj),
1043	        length = keys.length,
1044	        results = {};
1045	    for (var index = 0; index < length; index++) {
1046	      var currentKey = keys[index];
1047	      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
1048	    }
1049	    return results;
1050	  };
1051
1052	  // Convert an object into a list of `[key, value]` pairs.
1053	  // The opposite of _.object.
1054	  _.pairs = function(obj) {
1055	    var keys = _.keys(obj);
1056	    var length = keys.length;
1057	    var pairs = Array(length);
1058	    for (var i = 0; i < length; i++) {
1059	      pairs[i] = [keys[i], obj[keys[i]]];
1060	    }
1061	    return pairs;
1062	  };
1063
1064	  // Invert the keys and values of an object. The values must be serializable.
1065	  _.invert = function(obj) {
1066	    var result = {};
1067	    var keys = _.keys(obj);
1068	    for (var i = 0, length = keys.length; i < length; i++) {
1069	      result[obj[keys[i]]] = keys[i];
1070	    }
1071	    return result;
1072	  };
1073
1074	  // Return a sorted list of the function names available on the object.
1075	  // Aliased as `methods`.
1076	  _.functions = _.methods = function(obj) {
1077	    var names = [];
1078	    for (var key in obj) {
1079	      if (_.isFunction(obj[key])) names.push(key);
1080	    }
1081	    return names.sort();
1082	  };
1083
1084	  // An internal function for creating assigner functions.
1085	  var createAssigner = function(keysFunc, defaults) {
1086	    return function(obj) {
1087	      var length = arguments.length;
1088	      if (defaults) obj = Object(obj);
1089	      if (length < 2 || obj == null) return obj;
1090	      for (var index = 1; index < length; index++) {
1091	        var source = arguments[index],
1092	            keys = keysFunc(source),
1093	            l = keys.length;
1094	        for (var i = 0; i < l; i++) {
1095	          var key = keys[i];
1096	          if (!defaults || obj[key] === void 0) obj[key] = source[key];
1097	        }
1098	      }
1099	      return obj;
1100	    };
1101	  };
1102
1103	  // Extend a given object with all the properties in passed-in object(s).
1104	  _.extend = createAssigner(_.allKeys);
1105
1106	  // Assigns a given object with all the own properties in the passed-in object(s).
1107	  // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
1108	  _.extendOwn = _.assign = createAssigner(_.keys);
1109
1110	  // Returns the first key on an object that passes a predicate test.
1111	  _.findKey = function(obj, predicate, context) {
1112	    predicate = cb(predicate, context);
1113	    var keys = _.keys(obj), key;
1114	    for (var i = 0, length = keys.length; i < length; i++) {
1115	      key = keys[i];
1116	      if (predicate(obj[key], key, obj)) return key;
1117	    }
1118	  };
1119
1120	  // Internal pick helper function to determine if `obj` has key `key`.
1121	  var keyInObj = function(value, key, obj) {
1122	    return key in obj;
1123	  };
1124
1125	  // Return a copy of the object only containing the whitelisted properties.
1126	  _.pick = restArguments(function(obj, keys) {
1127	    var result = {}
1127, iteratee = keys[0];
1128	    if (obj == null) return result;
1129	    if (_.isFunction(iteratee)) {
1130	      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
1131	      keys = _.allKeys(obj);
1132	    } else {
1133	      iteratee = keyInObj;
1134	      keys = flatten(keys, false, false);
1135	      obj = Object(obj);
1136	    }
1137	    for (var i = 0, length = keys.length; i < length; i++) {
1138	      var key = keys[i];
1139	      var value = obj[key];
1140	      if (iteratee(value, key, obj)) result[key] = value;
1141	    }
1142	    return result;
1143	  });
1144
1145	  // Return a copy of the object without the blacklisted properties.
1146	  _.omit = restArguments(function(obj, keys) {
1147	    var iteratee = keys[0], context;
1148	    if (_.isFunction(iteratee)) {
1149	      iteratee = _.negate(iteratee);
1150	      if (keys.length > 1) context = keys[1];
1151	    } else {
1152	      keys = _.map(flatten(keys, false, false), String);
1153	      iteratee = function(value, key) {
1154	        return !_.contains(keys, key);
1155	      };
1156	    }
1157	    return _.pick(obj, iteratee, context);
1158	  });
1159
1160	  // Fill in a given object with default properties.
1161	  _.defaults = createAssigner(_.allKeys, true);
1162
1163	  // Creates an object that inherits from the given prototype object.
1164	  // If additional properties are provided then they will be added to the
1165	  // created object.
1166	  _.create = function(prototype, props) {
1167	    var result = baseCreate(prototype);
1168	    if (props) _.extendOwn(result, props);
1169	    return result;
1170	  };
1171
1172	  // Create a (shallow-cloned) duplicate of an object.
1173	  _.clone = function(obj) {
1174	    if (!_.isObject(obj)) return obj;
1175	    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
1176	  };
1177
1178	  // Invokes interceptor with the obj, and then returns obj.
1179	  // The primary purpose of this method is to "tap into" a method chain, in
1180	  // order to perform operations on intermediate results within the chain.
1181	  _.tap = function(obj, interceptor) {
1182	    interceptor(obj);
1183	    return obj;
1184	  };
1185
1186	  // Returns whether an object has a given set of `key:value` pairs.
1187	  _.isMatch = function(object, attrs) {
1188	    var keys = _.keys(attrs), length = keys.length;
1189	    if (object == null) return !length;
1190	    var obj = Object(object);
1191	    for (var i = 0; i < length; i++) {
1192	      var key = keys[i];
1193	      if (attrs[key] !== obj[key] || !(key in obj)) return false;
1194	    }
1195	    return true;
1196	  };
1197
1198
1199	  // Internal recursive comparison function for `isEqual`.
1200	  var eq, deepEq;
1201	  eq = function(a, b, aStack, bStack) {
1202	    // Identical objects are equal. `0 === -0`, but they aren't identical.
1203	    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
1204	    if (a === b) return a !== 0 || 1 / a === 1 / b;
1205	    // `null` or `undefined` only equal to itself (strict comparison).
1206	    if (a == null || b == null) return false;
1207	    // `NaN`s are equivalent, but non-reflexive.
1208	    if (a !== a) return b !== b;
1209	    // Exhaust primitive checks
1210	    var type = typeof a;
1211	    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
1212	    return deepEq(a, b, aStack, bStack);
1213	  };
1214
1215	  // Internal recursive comparison function for `isEqual`.
1216	  deepEq = function(a, b, aStack, bStack) {
1217	    // Unwrap any wrapped objects.
1218	    if (a instanceof _) a = a._wrapped;
1219	    if (b instanceof _) b = b._wrapped;
1220	    // Compare `[[Class]]` names.
1221	    var className = toString.call(a);
1222	    if (className !== toString.call(b)) return false;
1223	    switch (className) {
1224	      // Strings, numbers, regular expressions, dates, and booleans are compared by value.
1225	      case '[object RegExp]':
1226	      // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
1227	      case '[object String]':
1228	        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
1229	        // equivalent to `new String("5")`.
1230	        return '' + a === '' + b;
1231	      case '[object Number]':
1232	        // `NaN`s are equivalent, but non-reflexive.
1233	        // Object(NaN) is equivalent to NaN.
1234	        if (+a !== +a) return +b !== +b;
1235	        // An `egal` comparison is performed for other numeric values.
1236	        return +a === 0 ? 1 / +a === 1 / b : +a === +b;
1237	      case '[object Date]':
1238	      case '[object Boolean]':
1239	        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
1240	        // millisecond representations. Note that invalid dates with millisecond representations
1241	        // of `NaN` are not equivalent.
1242	        return +a === +b;
1243	      case '[object Symbol]':
1244	        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
1245	    }
1246
1247	    var areArrays = className === '[object Array]';
1248	    if (!areArrays) {
1249	      if (typeof a != 'object' || typeof b != 'object') return false;
vendor: 5,169 bytes, lines 1250-1394
1250
1251	      // Objects with different constructors are not equivalent, but `Object`s or `Array`s
1252	      // from different frames are.
1253	      var aCtor = a.constructor, bCtor = b.constructor;
1254	      if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
1255	                               _.isFunction(bCtor) && bCtor instanceof bCtor)
1256	                          && ('constructor' in a && 'constructor' in b)) {
1257	        return false;
1258	      }
1259	    }
1260	    // Assume equality for cyclic structures. The algorithm for detecting cyclic
1261	    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
1262
1263	    // Initializing stack of traversed objects.
1264	    // It's done here since we only need them for objects and arrays comparison.
1265	    aStack = aStack || [];
1266	    bStack = bStack || [];
1267	    var length = aStack.length;
1268	    while (length--) {
1269	      // Linear search. Performance is inversely proportional to the number of
1270	      // unique nested structures.
1271	      if (aStack[length] === a) return bStack[length] === b;
1272	    }
1273
1274	    // Add the first object to the stack of traversed objects.
1275	    aStack.push(a);
1276	    bStack.push(b);
1277
1278	    // Recursively compare objects and arrays.
1279	    if (areArrays) {
1280	      // Compare array lengths to determine if a deep comparison is necessary.
1281	      length = a.length;
1282	      if (length !== b.length) return false;
1283	      // Deep compare the contents, ignoring non-numeric properties.
1284	      while (length--) {
1285	        if (!eq(a[length], b[length], aStack, bStack)) return false;
1286	      }
1287	    } else {
1288	      // Deep compare objects.
1289	      var keys = _.keys(a), key;
1290	      length = keys.length;
1291	      // Ensure that both objects contain the same number of properties before comparing deep equality.
1292	      if (_.keys(b).length !== length) return false;
1293	      while (length--) {
1294	        // Deep compare each member
1295	        key = keys[length];
1296	        if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
1297	      }
1298	    }
1299	    // Remove the first object from the stack of traversed objects.
1300	    aStack.pop();
1301	    bStack.pop();
1302	    return true;
1303	  };
1304
1305	  // Perform a deep comparison to check if two objects are equal.
1306	  _.isEqual = function(a, b) {
1307	    return eq(a, b);
1308	  };
1309
1310	  // Is a given array, string, or object empty?
1311	  // An "empty" object has no enumerable own-properties.
1312	  _.isEmpty = function(obj) {
1313	    if (obj == null) return true;
1314	    if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
1315	    return _.keys(obj).length === 0;
1316	  };
1317
1318	  // Is a given value a DOM element?
1319	  _.isElement = function(obj) {
1320	    return !!(obj && obj.nodeType === 1);
1321	  };
1322
1323	  // Is a given value an array?
1324	  // Delegates to ECMA5's native Array.isArray
1325	  _.isArray = nativeIsArray || function(obj) {
1326	    return toString.call(obj) === '[object Array]';
1327	  };
1328
1329	  // Is a given variable an object?
1330	  _.isObject = function(obj) {
1331	    var type = typeof obj;
1332	    return type === 'function' || type === 'object' && !!obj;
1333	  };
1334
1335	  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
1336	  _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
1337	    _['is' + name] = function(obj) {
1338	      return toString.call(obj) === '[object ' + name + ']';
1339	    };
1340	  });
1341
1342	  // Define a fallback version of the method in browsers (ahem, IE < 9), where
1343	  // there isn't any inspectable "Arguments" type.
1344	  if (!_.isArguments(arguments)) {
1345	    _.isArguments = function(obj) {
1346	      return has(obj, 'callee');
1347	    };
1348	  }
1349
1350	  // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
1351	  // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
1352	  var nodelist = root.document && root.document.childNodes;
1353	  if (typeof Int8Array != 'object' && typeof nodelist != 'function') {
1354	    _.isFunction = function(obj) {
1355	      return typeof obj == 'function' || false;
1356	    };
1357	  }
1358
1359	  // Is a given object a finite number?
1360	  _.isFinite = function(obj) {
1361	    return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
1362	  };
1363
1364	  // Is the given value `NaN`?
1365	  _.isNaN = function(obj) {
1366	    return _.isNumber(obj) && isNaN(obj);
1367	  };
1368
1369	  // Is a given value a boolean?
1370	  _.isBoolean = function(obj) {
1371	    return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
1372	  };
1373
1374	  // Is a given value equal to null?
1375	  _.isNull = function(obj) {
1376	    return obj === null;
1377	  };
1378
1379	  // Is a given variable undefined?
1380	  _.isUndefined = function(obj) {
1381	    return obj === void 0;
1382	  };
1383
1384	  // Shortcut function for checking if an object has a given property directly
1385	  // on itself (in other words, not on a prototype).
1386	  _.has = function(obj, path) {
1387	    if (!_.isArray(path)) {
1388	      return has(obj, path);
1389	    }
1390	    var length = path.length;
1391	    for (var i = 0; i < length; i++) {
1392	      var key = path[i];
1393	      if (obj == null || !hasOwnProperty.call(obj, key)) {
1394	        return false;
1395	      }
1396	      obj = obj[key];
1397	    }
1398	    return !!length;
1399	  };
1400
1401	  // Utility Functions
1402	  // -----------------
1403
1404	  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
1405	  // previous owner. Returns a reference to the Underscore object.
1406	  _.noConflict = function() {
1407	    root._ = previousUnderscore;
1408	    return this;
1409	  };
1410
1411	  // Keep the identity function around for default iteratees.
1412	  _.identity = function(value) {
1413	    return value;
1414	  };
1415
1416	  // Predicate-generating functions. Often useful outside of Underscore.
1417	  _.constant = function(value) {
1418	    return function() {
1419	      return value;
1420	    };
1421	  };
1422
1423	  _.noop = function(){};
1424
1425	  // Creates a function that, when passed an object, will traverse that object’s
1426	  // properties down the given `path`, specified as an array of keys or indexes.
1427	  _.property = function(path) {
1428	    if (!_.isArray(path)) {
1429	      return shallowProperty(path);
1430	    }
1431	    return function(obj) {
1432	      return deepGet(obj, path);
1433	    };
1434	  };
1435
1436	  // Generates a function for a given object that returns a given property.
1437	  _.propertyOf = function(obj) {
1438	    if (obj == null) {
1439	      return function(){};
1440	    }
1441	    return function(path) {
1442	      return !_.isArray(path) ? obj[path] : deepGet(obj, path);
1443	    };
1444	  };
1445
1446	  // Returns a predicate for checking whether an object has a given set of
1447	  // `key:value` pairs.
1448	  _.matcher = _.matches = function(attrs) {
1449	    attrs = _.extendOwn({}, attrs);
1450	    return function(obj) {
1451	      return _.isMatch(obj, attrs);
1452	    };
1453	  };
1454
1455	  // Run a function **n** times.
1456	  _.times = function(n, iteratee, context) {
1457	    var accum = Array(Math.max(0, n));
1458	    iteratee = optimizeCb(iteratee, context, 1);
1459	    for (var i = 0; i < n; i++) accum[i] = iteratee(i);
1460	    return accum;
1461	  };
1462
1463	  // Return a random integer between min and max (inclusive).
1464	  _.random = function(min, max) {
1465	    if (max == null) {
1466	      max = min;
1467	      min = 0;
1468	    }
1469	    return min + Math.floor(Math.random() * (max - min + 1));
1470	  };
1471
1472	  // A (possibly faster) way to get the current timestamp as an integer.
1473	  _.now = Date.now || function() {
1474	    return new Date().getTime();
1475	  };
1476
1477	  // List of HTML entities for escaping.
1478	  var escapeMap = {
1479	    '&': '&amp;',
1480	    '<': '&lt;',
1481	    '>': '&gt;',
1482	    '"': '&quot;',
1483	    "'": '&#x27;',
1484	    '`': '&#x60;'
1485	  };
1486	  var unescapeMap = _.invert(escapeMap);
1487
1488	  // Functions for escaping and unescaping strings to/from HTML interpolation.
1489	  var createEscaper = function(map) {
1490	    var escaper = function(match) {
1491	      return map[match];
1492	    };
1493	    // Regexes for identifying a key that needs to be escaped.
1494	    var source = '(?:' + _.keys(map).join('|') + ')';
1495	    var testRegexp = RegExp(source);
1496	    var replaceRegexp = RegExp(source, 'g');
1497	    return function(string) {
1498	      string = string == null ? '' : '' + string;
1499	      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
1500	    };
1501	  };
1502	  _.escape = createEscaper(escapeMap);
1503	  _.unescape = createEscaper(unescapeMap);
1504
1505	  // Traverses the children of `obj` along `path`. If a child is a function, it
1506	  // is invoked with its parent as context. Returns the value of the final
1507	  // child, or `fallback` if any child is undefined.
1508	  _.result = function(obj, path, fallback) {
1509	    if (!_.isArray(path)) path = [path];
1510	    var length = path.length;
1511	    if (!length) {
1512	      return _.isFunction(fallback) ? fallback.call(obj) : fallback;
1513	    }
1514	    for (var i = 0; i < length; i++) {
1515	      var prop = obj == null ? void 0 : obj[path[i]];
1516	      if (prop === void 0) {
1517	        prop = fallback;
1518	        i = length; // Ensure we don't continue iterating.
1519	      }
1520	      obj = _.isFunction(prop) ? prop.call(obj) : prop;
1521	    }
1522	    return obj;
1523	  };
1524
1525	  // Generate a unique integer id (unique within the entire client session).
1526	  // Useful for temporary DOM ids.
1527	  var idCounter = 0;
1528	  _.uniqueId = function(prefix) {
1529	    var id = ++idCounter + '';
1530	    return prefix ? prefix + id : id;
1531	  };
1532
1533	  // By default, Underscore uses ERB-style template delimiters, change the
1534	  // following template settings to use alternative delimiters.
1535	  _.templateSettings = {
1536	    evaluate: /<%([\s\S]+?)%>/g,
1537	    interpolate: /<%=([\s\S]+?)%>/g,
1538	    escape: /<%-([\s\S]+?)%>/g
1539	  };
1540
1541	  // When customizing `templateSettings`, if you don't want to define an
1542	  // interpolation, evaluation or escaping regex, we need one that is
1543	  // guaranteed not to match.
1544	  var noMatch = /(.)^/;
1545
1546	  // Certain characters need to be escaped so that they can be put into a
1547	  // string literal.
1548	  var escapes = {
1549	    "'": "'",
1550	    '\\': '\\',
1551	    '\r': 'r',
1552	    '\n': 'n',
1553	    '\u2028': 'u2028',
1554	    '\u2029': 'u2029'
1555	  };
1556
1557	  var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
1558
1559	  var escapeChar = function(match) {
1560	    return '\\' + escapes[match];
1561	  };
1562
1563	  // JavaScript micro-templating, similar to John Resig's implementation.
1564	  // Underscore templating handles arbitrary delimiters, preserves whitespace,
1565	  // and correctly escapes quotes within interpolated code.
1566	  // NB: `oldSettings` only exists for backwards compatibility.
1567	  _.template = function(text, settings, oldSettings) {
1568	    if (!settings && oldSettings) settings = oldSettings;
1569	    settings = _.defaults({}, settings, _.templateSettings);
1570
1571	    // Combine delimiters into one regular expression via alternation.
1572	    var matcher = RegExp([
1573	      (settings.escape || noMatch).source,
1574	      (settings.interpolate || noMatch).source,
1575	      (settings.evaluate || noMatch).source
1576	    ].join('|') + '|$', 'g');
1577
1578	    // Compile the template source, escaping string literals appropriately.
1579	    var index = 0;
1580	    var source = "__p+='";
1581	    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
1582	      source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
1583	      index = offset + match.length;
1584
1585	      if (escape) {
1586	        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
1587	      } else if (interpolate) {
1588	        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
1589	      } else if (evaluate) {
1590	        source += "';\n" + evaluate + "\n__p+='";
1591	      }
1592
1593	      // Adobe VMs need the match returned to produce the correct offset.
1594	      return match;
1595	    });
1596	    source += "';\n";
1597
1598	    // If a variable is not specified, place data values in local scope.
1599	    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
1600
1601	    source = "var __t,__p='',__j=Array.prototype.join," +
1602	      "print=function(){__p+=__j.call(arguments,'');};\n" +
1603	      source + 'return __p;\n';
1604
1605	    var render;
1606	    try {
1607	      render = new Function(settings.variable || 'obj', '_', source);
1608	    } catch (e) {
1609	      e.source = source;
1610	      throw e;
1611	    }
1612
1613	    var template = function(data) {
1614	      return render.call(this, data, _);
1615	    };
1616
1617	    // Provide the compiled source as a convenience for precompilation.
1618	    var argument = settings.variable || 'obj';
1619	    template.source = 'function(' + argument + '){\n' + source + '}';
1620
1621	    return template;
1622	  };
1623
1624	  // Add a "chain" function. Start chaining a wrapped Underscore object.
1625	  _.chain = function(obj) {
1626	    var instance = _(obj);
1627	    instance._chain = true;
1628	    return instance;
1629	  };
1630
1631	  // OOP
1632	  // ---------------
1633	  // If Underscore is called as a function, it returns a wrapped object that
1634	  // can be used OO-style. This wrapper holds altered versions of all the
1635	  // underscore functions. Wrapped objects may be chained.
1636
1637	  // Helper function to continue chaining intermediate results.
1638	  var chainResult = function(instance, obj) {
1639	    return instance._chain ? _(obj).chain() : obj;
1640	  };
1641
1642	  // Add your own custom functions to the Underscore object.
1643	  _.mixin = function(obj) {
1644	    _.each(_.functions(obj), function(name) {
1645	      var func = _[name] = obj[name];
1646	      _.prototype[name] = function() {
1647	        var args = [this._wrapped];
1648	        push.apply(args, arguments);
1649	        return chainResult(this, func.apply(_, args));
1650	      };
1651	    });
1652	    return _;
1653	  };
1654
1655	  // Add all of the Underscore functions to the wrapper object.
1656	  _.mixin(_);
1657
1658	  // Add all mutator Array functions to the wrapper.
1659	  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1660	    var method = ArrayProto[name];
1661	    _.prototype[name] = function() {
1662	      var obj = this._wrapped;
1663	      method.apply(obj, arguments);
1664	      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
1665	      return chainResult(this, obj);
1666	    };
1667	  });
1668
1669	  // Add all accessor Array functions to the wrapper.
1670	  _.each(['concat', 'join', 'slice'], function(name) {
1671	    var method = ArrayProto[name];
1672	    _.prototype[name] = function() {
1673	      return chainResult(this, method.apply(this._wrapped, arguments));
1674	    };
1675	  });
1676
1677	  // Extracts the result from a wrapped and chained object.
1678	  _.prototype.value = function() {
1679	    return this._wrapped;
1680	  };
1681
1682	  // Provide unwrapping proxy for some methods used in engine operations
1683	  // such as arithmetic and JSON stringification.
1684	  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
1685
1686	  _.prototype.toString = function() {
1687	    return String(this._wrapped);
1688	  };
1689	}());
1690	});
1691	var underscore_1 = underscore._;
1692
1693	// https://gist.github.com/bmershon/25a74f7b1c7cbd07e7456af1d2c07da1
1694
1695	/**
1696	 * This function create the pairs between node and its links.
1697	 *
1698	 * @param links
1699	 * @returns [["nodeX,nodeY", Array(numberOfLinksRelatedToTheNodes)]]
1700	 */
1701	function pairNodeLinks(links) {
1702	    let nestedByNodes = {};
1703	    links.forEach(l => {
1704	        let sourceKey = l.source.join(',');
1705	        if (!nestedByNodes[sourceKey]) {
1706	            nestedByNodes[sourceKey] = [];
1707	        }
1708	        nestedByNodes[sourceKey].push(l);
1709	        let targetKey = l.target.join(',');
1710	        if (!nestedByNodes[targetKey]) {
1711	            nestedByNodes[targetKey] = [];
1712	        }
1713	        nestedByNodes[targetKey].push(l);
1714	    });
1715	    //Pair the results
1716	    let pairedResults = underscore.pairs(nestedByNodes);
1717	    return pairedResults;
1718	}
1719
1720	/**
1721	 * This function returns corners (three vertices) of vertices of degree two in the for mat of
1722	 * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)).
1723	 * @param tree
1724	 * @returns {*}
1725	 */
1726	function getAllV2CornersFromTree(tree) {
1727	    let pairedResults = pairNodeLinks(tree.links);
1728	    //Get all pairs with length = 2 (V2)
1729	    let allV2 = pairedResults.filter(p => p[1].length == 2);
1730
1731	    let allCorners = allV2.map(v2 => {
1732	        let corner = [];
1733	        //First point is the common vertice
1734	        corner.push(v2[0].split(',').map(d => +d));//map(d=>+d) is to convert the strings into digits
1735	        //Push the source or target if they are not the common vertices of the two edges
1736	        v2[1].forEach(link => {
1737	            if (link.source.join(',') != v2[0]) {
1738	                corner.push(link.source);
1739	            } else {
1740	                corner.push(link.target);
1741	            }
1742	        });
1743	        return corner;
1744	    });
1745	    return allCorners;
1746	}
1747
1748	/**
1749	 * This function returns all single degree vertices from a tree
1750	 * @param tree
1751	 */
1752	function getAllV1sFromTree(tree){
1753	    let pairedResults = pairNodeLinks(tree.links);
1754	    //Get all pairs with length = 2 (V2)
1755	    let allV1 = pairedResults.filter(p => p[1].length == 1);
1756	    return allV1.map(v1=>v1[0].split(',').map(Number));
1757	}
1758	/**
1759	 * Create a graph from mesh
1760	 * @param triangles is inform of set of triangles as the result from delaunay triangulations
1761	 */
1762	function createGraph (triangles) {
1763
1764	    function makeLink(sourceId, targetId, weight) {
1765	        return {"source": sourceId, "target": targetId, "weight": weight};
1766	    }
1767
1768	    let graph = {};
1769	    graph.nodes = [];
1770	    graph.links = [];
1771	    //Creating nodes
1772	    triangles.forEach(t => {
1773	        for (let i = 0; i < 3; i++) {
1774	            let id = t[i];
1775	            if (!idExists(graph.nodes, id)) {
1776	                graph.nodes.push(makeNode(id));
1777	            }
1778	        }
1779	    });
1780
1781	    //Creating links
1782	    triangles.forEach(t => {
1783	        for (let i = 0; i < 3; i++) {
1784	            let p1 = t[i];
1785	            let p2 = t[(i + 1) % 3];
1786	            let id1 = p1;
1787	            let id2 = p2;
1788	            let dist = distance(p1, p2);
1789	            let link = makeLink(id1, id2, dist);
1790	            if (!linkExists(graph.links, link)) {
1791	                graph.links.push(link);
1792	            }
1793	        }
1794	    });
1795
1796
1797	    //TODO: may sort the id alphabetically => when creating => so we can just check 1 condition only.
1798	    function linkExists(links, link) {
1799	        let length = links.length;
1800	        for (let i = length-1; i >= 0; --i) {
1801	            if (equalLinks(link, links[i])) {
1802	                return true;
1803	            }
1804	        }
1805	        return false;
1806	    }
1807
1808	    return graph;
1809	}
1810
1811	function distance(a, b) {
1812	    let dx = a[0] - b[0],
1813	        dy = a[1] - b[1];
1814	    //For computer storage issue, some coordinates of the same distance may return different distances if we use long floating point
1815	    //So take only 10 digits after the floating points=> this is precise enough and still have the same values for two different lines of the same distance
1816	    return Math.round(Math.sqrt((dx * dx) + (dy * dy))*Math.pow(10, 10))/Math.pow(10, 10);
1817	}
1818
1819	function equalPoints(id1, id2) {
1820	    return (id1[0] === id2[0] && id1[1] === id2[1]);
1821	}
1822	function equalLinks(l1, l2) {
1823	    return (equalPoints(l1.source, l2.source) && equalPoints(l1.target, l2.target)) ||
1824	        (equalPoints(l1.source, l2.target) && equalPoints(l1.target, l2.source));
1825	}
1826	function idExists(nodes, id) {
1827	    let length = nodes.length;
1828	    for (let i = length-1; i >= 0; --i) {
1829	        let node = nodes[i];
1830	        if (equalPoints(node.id, id)) {
1831	            return true;
1832	        }
1833	    }
1834	    return false;
1835	}
1836	function makeNode(id) {
1837	    return {"id": id};
1838	}
1839	/**
1840	 * create the mst
1841	 * @param graph: in form of nodes and links
1842	 * @returns {{nodes: (selection_nodes|nodes), links: Array}}
1843	 */
1844	function mst (graph) {
1845	    let vertices = graph.nodes,
1846	        edges = graph.links.slice(0),
1847	        selectedEdges = [],
1848	        forest = new DisjointSet();
1849
1850	    // Each vertex begins "disconnected" and isolated from all the others.
1851	    vertices.forEach((vertex) => {
1852	        forest.makeSet(vertex.id);
1853	    });
1854
1855	    // Sort edges in descending order of weight. We will pop edges beginning
1856	    // from the end of the array.
1857	    edges.sort((a, b) => {
1858	        return -(a.weight - b.weight);
1859	    });
1860
1861	    while (edges.length && forest.size() > 1) {
1862	        let edge = edges.pop();
1863
1864	        if (forest.find(edge.source) !== forest.find(edge.target)) {
1865	            forest.union(edge.source, edge.target);
1866	            selectedEdges.push(edge);
1867	        }
1868	    }
1869
1870	    return {
1871	        nodes: vertices,
1872	        links: selectedEdges
1873	    }
1874	}
1875
1876
1877	function DisjointSet() {
1878	    this.index_ = {};
1879	}
1880
1881	function Node(id) {
1882	    this.id_ = id;
1883	    this.parent_ = this;
1884	    this.rank_ = 0;
1885	}
1886
1887	DisjointSet.prototype.makeSet = function (id) {
1888	    if (!this.index_[id]) {
1889	        let created = new Node(id);
1890	        this.index_[id] = created;
1891	    }
1892	};
1893
1894	// Returns the id of the representative element of this set that (id)
1895	// belongs to.
1896	DisjointSet.prototype.find = function (id) {
1897	    if (this.index_[id] === undefined) {
1898	        return undefined;
1899	    }
1900
1901	    let current = this.index_[id].parent_;
1902	    while (current !== current.parent_) {
1903	        current = current.parent_;
1904	    }
1905	    return current.id_;
1906	};
1907
1908	DisjointSet.prototype.union = function (x, y) {
1909	    let xRoot = this.index_[this.find(x)];
1910	    let yRoot = this.index_[this.find(y)];
1911
1912	    if (xRoot === undefined || yRoot === undefined || xRoot === yRoot) {
1913	        // x and y already belong to the same set.
1914	        return;
1915	    }
1916
1917	    if (xRoot.rank < yRoot.rank) { // Move x into the set y is a member of.
1918	        xRoot.parent_ = yRoot;
1919	    } else if (yRoot.rank_ < xRoot.rank_) { // Move y into the set x is a member of.
1920	        yRoot.parent_ = xRoot;
1921	    } else { // Arbitrarily choose to move y into the set x is a member of.
1922	        yRoot.parent_ = xRoot;
1923	        xRoot.rank_++;
1924	    }
1925	};
1926
1927	// Returns the current number of disjoint sets.
1928	DisjointSet.prototype.size = function () {
1929	    let uniqueIndices = {};
1930	    Object.keys(this.index_).forEach((id) => {
1931	        uniqueIndices[id] = true;
1932	    });
1933	    return Object.keys(uniqueIndices).length;
1934	};
1935
1936	/* @flow */
1937
1938	/* @flow */
1939
1940	/**
1941	 * This computes the maximum number in an array.
1942	 *
1943	 * This runs on `O(n)`, linear time in respect to the array
1944	 *
1945	 * @param {Array<number>} x sample of one or more data points
1946	 * @returns {number} maximum value
1947	 * @throws {Error} if the the length of x is less than one
1948	 * @example
1949	 * max([1, 2, 3, 4]);
1950	 * // => 4
1951	 */
1952	function max(x /*: Array<number> */) /*:number*/ {
1953
1954	    if (x.length === 0) {
1955	        throw new Error('max requires at least one data point');
1956	    }
1957
1958	    var value = x[0];
1959	    for (var i = 1; i < x.length; i++) {
1960	        // On the first iteration of this loop, max is
1961	        // undefined and is thus made the maximum element in the array
1962	        if (x[i] > value) {
1963	            value = x[i];
1964	        }
1965	    }
1966	    return value;
1967	}
1968
1969	/* @flow */
1970
1971	/**
1972	 * This is the internal implementation of quantiles: when you know
1973	 * that the order is sorted, you don't need to re-sort it, and the computations
1974	 * are faster.
1975	 *
1976	 * @param {Array<number>} x sample of one or more data points
1977	 * @param {number} p desired quantile: a number between 0 to 1, inclusive
1978	 * @returns {number} quantile value
1979	 * @throws {Error} if p ix outside of the range from 0 to 1
1980	 * @throws {Error} if x is empty
1981	 * @example
1982	 * quantileSorted([3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 0.5); // => 9
1983	 */
1984	function quantileSorted(x /*: Array<number> */, p /*: number */)/*: number */ {
1985	    var idx = x.length * p;
1986	    if (x.length === 0) {
1987	        throw new Error('quantile requires at least one data point.');
1988	    } else if (p < 0 || p > 1) {
1989	        throw new Error('quantiles must be between 0 and 1');
1990	    } else if (p === 1) {
1991	        // If p is 1, directly return the last element
1992	        return x[x.length - 1];
1993	    } else if (p === 0) {
1994	        // If p is 0, directly return the first element
1995	        return x[0];
1996	    } else if (idx % 1 !== 0) {
1997	        // If p is not integer, return the next element in array
1998	        return x[Math.ceil(idx) - 1];
1999	    } else if (x.length % 2 === 0) {
2000	        // If the list has even-length, we'll take the average of this number
2001	        // and the next value, if there is one
2002	        return (x[idx - 1] + x[idx]) / 2;
2003	    } else {
2004	        // Finally, in the simple case of an integer value
2005	        // with an odd-length list, return the x value at the index.
2006	        return x[idx];
2007	    }
2008	}
2009
2010	/* @flow */
2011
2012	/**
2013	 * Rearrange items in `arr` so that all items in `[left, k]` range are the smallest.
2014	 * The `k`-th element will have the `(k - left + 1)`-th smallest value in `[left, right]`.
2015	 *
2016	 * Implements Floyd-Rivest selection algorithm https://en.wikipedia.org/wiki/Floyd-Rivest_algorithm
2017	 *
2018	 * @param {Array<number>} arr input array
2019	 * @param {number} k pivot index
2020	 * @param {number} [left] left index
2021	 * @param {number} [right] right index
2022	 * @returns {void} mutates input array
2023	 * @example
2024	 * var arr = [65, 28, 59, 33, 21, 56, 22, 95, 50, 12, 90, 53, 28, 77, 39];
2025	 * quickselect(arr, 8);
2026	 * // = [39, 28, 28, 33, 21, 12, 22, 50, 53, 56, 59, 65, 90, 77, 95]
2027	 */
2028	function quickselect(arr/*: Array<number> */, k/*: number */, left/*: ?number */, right/*: ?number */)/*: void */ {
2029	    left = left || 0;
2030	    right = right || (arr.length - 1);
2031
2032	    while (right > left) {
2033	        // 600 and 0.5 are arbitrary constants chosen in the original paper to minimize execution time
2034	        if (right - left > 600) {
2035	            var n = right - left + 1;
2036	            var m = k - left + 1;
2037	            var z = Math.log(n);
2038	            var s = 0.5 * Math.exp(2 * z / 3);
2039	            var sd = 0.5 * Math.sqrt(z * s * (n - s) / n);
2040	            if (m - n / 2 < 0) sd *= -1;
2041	            var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
2042	            var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
2043	            quickselect(arr, k, newLeft, newRight);
2044	        }
2045
2046	        var t = arr[k];
2047	        var i = left;
2048	        var j = right;
2049
2050	        swap(arr, left, k);
2051	        if (arr[right] > t) swap(arr, left, right);
2052
2053	        while (i < j) {
2054	            swap(arr, i, j);
2055	            i++;
2056	            j--;
2057	            while (arr[i] < t) i++;
2058	            while (arr[j] > t) j--;
2059	        }
2060
2061	        if (arr[left] === t) swap(arr, left, j);
2062	        else {
2063	            j++;
2064	            swap(arr, j, right);
2065	        }
2066
2067	        if (j <= k) left = j + 1;
2068	        if (k <= j) right = j - 1;
2069	    }
2070	}
2071
2072	function swap(arr, i, j) {
2073	    var tmp = arr[i];
2074	    arr[i] = arr[j];
2075	    arr[j] = tmp;
2076	}
2077
2078	/* @flow */
2079
2080	/**
2081	 * The [quantile](https://en.wikipedia.org/wiki/Quantile):
2082	 * this is a population quantile, since we assume to know the entire
2083	 * dataset in this library. This is an implementation of the
2084	 * [Quantiles of a Population](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population)
2085	 * algorithm from wikipedia.
2086	 *
2087	 * Sample is a one-dimensional array of numbers,
2088	 * and p is either a decimal number from 0 to 1 or an array of decimal
2089	 * numbers from 0 to 1.
2090	 * In terms of a k/q quantile, p = k/q - it's just dealing with fractions or dealing
2091	 * with decimal values.
2092	 * When p is an array, the result of the function is also an array containing the appropriate
2093	 * quantiles in input order
2094	 *
2095	 * @param {Array<number>} x sample of one or more numbers
2096	 * @param {Array<number> | number} p the desired quantile, as a number between 0 and 1
2097	 * @returns {number} quantile
2098	 * @example
2099	 * quantile([3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 0.5); // => 9
2100	 */
2101	function quantile(x/*: Array<number> */, p/*: Array<number> | number */)/*: Array<number> | number */ {
2102	    var copy = x.slice();
2103
2104	    if (Array.isArray(p)) {
2105	        // rearrange elements so that each element corresponding to a requested
2106	        // quantile is on a place it would be if the array was fully sorted
2107	        multiQuantileSelect(copy, p);
2108	        // Initialize the result array
2109	        var results = [];
2110	        // For each requested quantile
2111	        for (var i = 0; i < p.length; i++) {
2112	            results[i] = quantileSorted(copy, p[i]);
2113	        }
2114	        return results;
2115	    } else {
2116	        var idx = quantileIndex(copy.length, p);
2117	        quantileSelect(copy, idx, 0, copy.length - 1);
2118	        return quantileSorted(copy, p);
2119	    }
2120	}
2121
2122	function quantileSelect(arr, k, left, right) {
2123	    if (k % 1 === 0) {
2124	        quickselect(arr, k, left, right);
2125	    } else {
2126	        k = Math.floor(k);
2127	        quickselect(arr, k, left, right);
2128	        quickselect(arr, k + 1, k + 1, right);
2129	    }
2130	}
2131
2132	function multiQuantileSelect(arr, p) {
2133	    var indices = [0];
2134	    for (var i = 0; i < p.length; i++) {
2135	        indices.push(quantileIndex(arr.length, p[i]));
2136	    }
2137	    indices.push(arr.length - 1);
2138	    indices.sort(compare);
2139
2140	    var stack = [0, indices.length - 1];
2141
2142	    while (stack.length) {
2143	        var r = Math.ceil(stack.pop());
2144	        var l = Math.floor(stack.pop());
2145	        if (r - l <= 1) continue;
2146
2147	        var m = Math.floor((l + r) / 2);
2148	        quantileSelect(arr, indices[m], indices[l], indices[r]);
2149
2150	        stack.push(l, m, m, r);
2151	    }
2152	}
2153
2154	function compare(a, b) {
2155	    return a - b;
2156	}
2157
2158	function quantileIndex(len /*: number */, p /*: number */)/*:number*/ {
2159	    var idx = len * p;
2160	    if (p === 1) {
2161	        // If p is 1, directly return the last index
2162	        return len - 1;
2163	    } else if (p === 0) {
2164	        // If p is 0, directly return the first index
2165	        return 0;
2166	    } else if (idx % 1 !== 0) {
2167	        // If index is not integer, return the next index in array
2168	        return Math.ceil(idx) - 1;
2169	    } else if (len % 2 === 0) {
2170	        // If the list has even-length, we'll return the middle of two indices
2171	        // around quantile to indicate that we need an average value of the two
2172	        return idx - 0.5;
2173	    } else {
2174	        // Finally, in the simple case of an integer index
2175	        // with an odd-length list, return the index
2176	        return idx;
2177	    }
2178	}
2179	var LOGSQRT2PI = Math.log(Math.sqrt(2 * Math.PI));
2180
2181	/* @flow */
2182
2183	var SQRT_2PI = Math.sqrt(2 * Math.PI);
2184
2185	/* @flow */
2186
2187	var SQRT_2PI$1 = Math.sqrt(2 * Math.PI);
2188
2189	class Clumpy {
2190	    constructor(tree) {
2191	        //Clone the tree to avoid modifying it
2192	        this.tree = JSON.parse(JSON.stringify(tree));
2193	    }
2194
2195	    /**
2196	     * Returns clumpy score
2197	     * @returns {number}
2198	     */
2199	    score() {
2200	        let allRuntRatios = [];
2201	        this.tree.links.forEach(link =>{
2202	            let rg = this.runtGraph(link);
2203	            if(rg.length>0){
2204	                allRuntRatios.push(this.maxLength(rg)/link.weight);
2205	            }
2206	        });
2207	        if(allRuntRatios.length>0){
2208	            //Only if there are some runt graphs
2209	            return max(allRuntRatios.map(rr=>1-rr));
2210	        }else{
2211	            //In case all lengths are equal => then the score is 0
2212	            return 0;
2213	        }
2214	    }
2215	    // score() {
2216	    //     return max(this.tree.links.map(link=>1-this.maxLength(this.runtGraph(link))/link.weight));
2217	    // }
2218	    // runtGraph(link){
2219	    //     let greaterOrEqualLinks = this.tree.links.filter(l=>l.weight >= link.weight);
2220	    //     //Remove the currently checking link.
2221	    //     greaterOrEqualLinks = greaterOrEqualLinks.filter(l=>!equalLinks(l, link));
2222	    //     let pairedResults = pairNodeLinks(greaterOrEqualLinks);
2223	    //
2224	    //     //Process the source side.
2225	    //     let sourceConnectedNodes = [link.source];
2226	    //     let sourceConnectedLinks = this.getConnectedLinks(sourceConnectedNodes, pairedResults);
2227	    //
2228	    //     let targetConnectedNodes = [link.target];
2229	    //     let targetConnectedLinks = this.getConnectedLinks(targetConnectedNodes, pairedResults);
2230	    //
2231	    //     return sourceConnectedLinks.length < targetConnectedLinks.length?sourceConnectedLinks:targetConnectedLinks;
2232	    // }
2233	    runtGraph(link){
2234	        let greaterOrEqualLinks = this.tree.links.filter(l=>l.weight < link.weight);
2235	        //Remove the currently checking link.
2236	        greaterOrEqualLinks = greaterOrEqualLinks.filter(l=>!equalLinks(l, link));
2237	        let pairedResults = pairNodeLinks(greaterOrEqualLinks);
2238
2239	        //Process the source side.
2240	        let sourceConnectedNodes = [link.source];
2241	        let sourceConnectedLinks = this.getConnectedLinks(sourceConnectedNodes, pairedResults);
2242
2243	        let targetConnectedNodes = [link.target];
2244	        let targetConnectedLinks = this.getConnectedLinks(targetConnectedNodes, pairedResults);
2245
2246	        return sourceConnectedLinks.length < targetConnectedLinks.length?sourceConnectedLinks:targetConnectedLinks;
2247	    }
2248
2249
2250	    getConnectedLinks(connectedNodes, pairedResults) {
2251	        let processedNodes = [];
2252	        let connectedLinks = [];
2253	        while (connectedNodes.length > 0) {
2254	            //Can stop earlier if this is having more than half of the links in the whole tree.
2255	            if(connectedLinks.length > this.tree.links.length + 1){
2256	                break;
2257	            }
2258	            let firstNode = underscore.first(connectedNodes);
2259	            //Removed the processed nodes
2260	            connectedNodes = underscore.without(connectedNodes, firstNode);
2261	            //Add it to the processed node
2262	            processedNodes.push(firstNode);
2263	            //Find the edges connected to that node.
2264	            let result = pairedResults.find(p => p[0] === firstNode.join(","));
2265	            let links = result?result[1]:[];
2266	            connectedLinks = connectedLinks.concat(links);
2267	            //Add new nodes to be processed
2268	            links.forEach(l => {
2269	                //If the node in the connected link is not processed => then add it to be processed (to expand later on).
2270	                if (!pointExists(processedNodes, l.source)) {
2271	                    connectedNodes.push(l.source);
2272	                }
2273	                if(!pointExists(processedNodes, l.target)) {
2274	                    connectedNodes.push(l.target);
2275	                }
2276	            });
2277	        }
2278	        return connectedLinks;
2279	    }
2280
2281	    maxLength(runtGraph){
2282	        if(runtGraph.length===0){
2283	            return 0;
2284	        }
2285	        return max(runtGraph.map(l=>l.weight));
2286	    }
2287	}
2288	function pointExists(points, point){
2289	    for (let i = 0; i < points.length; i++) {
2290	        let point1 = points[i];
2291	        if (equalPoints(point1, point)) {
2292	            return true;
2293	        }
2294	    }
2295	    return false;
2296	}
2297
2298	const EPSILON = Math.pow(2, -52);
2299
2300	class Delaunator {
2301
2302	    static from(points, getX, getY) {
2303	        if (!getX) getX = defaultGetX;
2304	        if (!getY) getY = defaultGetY;
2305
2306	        const n = points.length;
2307	        const coords = new Float64Array(n * 2);
2308
2309	        for (let i = 0; i < n; i++) {
2310	            const p = points[i];
2311	            coords[2 * i] = getX(p);
2312	            coords[2 * i + 1] = getY(p);
2313	        }
2314
2315	        return new Delaunator(coords);
2316	    }
2317
2318	    constructor(coords) {
2319	        let minX = Infinity;
2320	        let minY = Infinity;
2321	        let maxX = -Infinity;
2322	        let maxY = -Infinity;
2323
2324	        const n = coords.length >> 1;
2325	        const ids = this.ids = new Uint32Array(n);
2326
2327	        if (n > 0 && typeof coords[0] !== 'number') throw new Error('Expected coords to contain numbers.');
2328
2329	        this.coords = coords;
2330
2331	        for (let i = 0; i < n; i++) {
2332	            const x = coords[2 * i];
2333	            const y = coords[2 * i + 1];
2334	            if (x < minX) minX = x;
2335	            if (y < minY) minY = y;
2336	            if (x > maxX) maxX = x;
2337	            if (y > maxY) maxY = y;
2338	            ids[i] = i;
2339	        }
2340
2341	        const cx = (minX + maxX) / 2;
2342	        const cy = (minY + maxY) / 2;
2343
2344	        let minDist = Infinity;
2345	        let i0, i1, i2;
2346
2347	        // pick a seed point close to the centroid
2348	        for (let i = 0; i < n; i++) {
2349	            const d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]);
2350	            if (d < minDist) {
2351	                i0 = i;
2352	                minDist = d;
2353	            }
2354	        }
2355	        const i0x = coords[2 * i0];
2356	        const i0y = coords[2 * i0 + 1];
2357
2358	        minDist = Infinity;
2359
2360	        // find the point closest to the seed
2361	        for (let i = 0; i < n; i++) {
2362	            if (i === i0) continue;
2363	            const d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]);
2364	            if (d < minDist && d > 0) {
2365	                i1 = i;
2366	                minDist = d;
2367	            }
2368	        }
2369	        let i1x = coords[2 * i1];
2370	        let i1y = coords[2 * i1 + 1];
2371
2372	        let minRadius = Infinity;
2373
2374	        // find the third point which forms the smallest circumcircle with the first two
2375	        for (let i = 0; i < n; i++) {
2376	            if (i === i0 || i === i1) continue;
2377	            const r = circumradius(i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]);
2378	            if (r < minRadius) {
2379	                i2 = i;
2380	                minRadius = r;
2381	            }
2382	        }
2383	        let i2x = coords[2 * i2];
2384	        let i2y = coords[2 * i2 + 1];
2385
2386	        if (minRadius === Infinity) {
2387	            throw new Error('No Delaunay triangulation exists for this input.');
2388	        }
2389
2390	        // swap the order of the seed points for counter-clockwise orientation
2391	        if (orient(i0x, i0y, i1x, i1y, i2x, i2y)) {
2392	            const i = i1;
2393	            const x = i1x;
2394	            const y = i1y;
2395	            i1 = i2;
2396	            i1x = i2x;
2397	            i1y = i2y;
2398	            i2 = i;
2399	            i2x = x;
2400	            i2y = y;
2401	        }
2402
2403	        const center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
2404	        this._cx = center.x;
2405	        this._cy = center.y;
2406
2407	        // sort the points by distance from the seed triangle circumcenter
2408	        quicksort(ids, coords, 0, ids.length - 1, center.x, center.y);
2409
2410	        // initialize a hash table for storing edges of the advancing convex hull
2411	        this._hashSize = Math.ceil(Math.sqrt(n));
2412	        this._hash = new Array(this._hashSize);
2413
2414	        // initialize a circular doubly-linked list that will hold an advancing convex hull
2415	        let e = this.hull = insertNode(coords, i0);
2416	        this._hashEdge(e);
2417	        e.t = 0;
2418	        e = insertNode(coords, i1, e);
2419	        this._hashEdge(e);
2420	        e.t = 1;
2421	        e = insertNode(coords, i2, e);
2422	        this._hashEdge(e);
2423	        e.t = 2;
2424
2425	        const maxTriangles = 2 * n - 5;
2426	        const triangles = this.triangles = new Uint32Array(maxTriangles * 3);
2427	        const halfedges = this.halfedges = new Int32Array(maxTriangles * 3);
2428
2429	        this.trianglesLen = 0;
2430
2431	        this._addTriangle(i0, i1, i2, -1, -1, -1);
2432
2433	        for (let k = 0, xp, yp; k < ids.length; k++) {
2434	            const i = ids[k];
2435	            const x = coords[2 * i];
2436	            const y = coords[2 * i + 1];
2437
2438	            // skip near-duplicate points
2439	            if (k > 0 && Math.abs(x - xp) <= EPSILON && Math.abs(y - yp) <= EPSILON) continue;
2440	            xp = x;
2441	            yp = y;
2442
2443	            // skip seed triangle points
2444	            if (i === i0 || i === i1 || i === i2) continue;
2445
2446	            // find a visible edge on the convex hull using edge hash
2447	            const startKey = this._hashKey(x, y);
2448	            let key = startKey;
2449	            let start;
2450	            do {
2451	                start = this._hash[key];
2452	                key = (key + 1) % this._hashSize;
2453	            } while ((!start || start.removed) && key !== startKey);
2454
2455	            start = start.prev;
2456	            e = start;
2457	            while (!orient(x, y, e.x, e.y, e.next.x, e.next.y)) {
2458	                e = e.next;
2459	                if (e === start) {
2460	                    e = null;
2461	                    break;
2462	                }
2463	            }
2464	            // likely a near-duplicate point; skip it
2465	            if (!e) continue;
2466
2467	            const walkBack = e === start;
2468
2469	            // add the first triangle from the point
2470	            let t = this._addTriangle(e.i, i, e.next.i, -1, -1, e.t);
2471
2472	            e.t = t; // keep track of boundary triangles on the hull
2473	            e = insertNode(coords, i, e);
2474
2475	            // recursively flip triangles from the point until they satisfy the Delaunay condition
2476	            e.t = this._legalize(t + 2);
2477
2478	            // walk forward through the hull, adding more triangles and flipping recursively
2479	            let q = e.next;
2480	            while (orient(x, y, q.x, q.y, q.next.x, q.next.y)) {
2481	                t = this._addTriangle(q.i, i, q.next.i, q.prev.t, -1, q.t);
2482	                q.prev.t = this._legalize(t + 2);
2483	                this.hull = removeNode(q);
2484	                q = q.next;
2485	            }
2486
2487	            if (walkBack) {
2488	                // walk backward from the other side, adding more triangles and flipping
2489	                q = e.prev;
2490	                while (orient(x, y, q.prev.x, q.prev.y, q.x, q.y)) {
2491	                    t = this._addTriangle(q.prev.i, i, q.i, -1, q.t, q.prev.t);
2492	                    this._legalize(t + 2);
2493	                    q.prev.t = t;
2494	                    this.hull = removeNode(q);
2495	                    q = q.prev;
2496	                }
2497	            }
2498
2499	            // save the two new edges in the hash table
2500	            this._hashEdge(e);
2501	            this._hashEdge(e.prev);
2502	        }
2503
2504	        // trim typed triangle mesh arrays
2505	        this.triangles = triangles.subarray(0, this.trianglesLen);
2506	        this.halfedges = halfedges.subarray(0, this.trianglesLen);
2507	    }
2508
2509	    _hashEdge(e) {
2510	        this._hash[this._hashKey(e.x, e.y)] = e;
2511	    }
2512
2513	    _hashKey(x, y) {
2514	        return Math.floor(pseudoAngle(x - this._cx, y - this._cy) * this._hashSize) % this._hashSize;
2515	    }
2516
2517	    _legalize(a) {
2518	        const {triangles, coords, halfedges} = this;
2519
2520	        const b = halfedges[a];
2521
2522	        /* if the pair of triangles doesn't satisfy the Delaunay condition
2523	         * (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
2524	         * then do the same check/flip recursively for the new pair of triangles
2525	         *
2526	         *           pl                    pl
2527	         *          /||\                  /  \
2528	         *       al/ || \bl            al/    \a
2529	         *        /  ||  \              /      \
2530	         *       /  a||b  \    flip    /___ar___\
2531	         *     p0\   ||   /p1   =>   p0\---bl---/p1
2532	         *        \  ||  /              \      /
2533	         *       ar\ || /br             b\    /br
2534	         *          \||/                  \  /
2535	         *           pr                    pr
2536	         */
2537	        const a0 = a - a % 3;
2538	        const b0 = b - b % 3;
2539
2540	        const al = a0 + (a + 1) % 3;
2541	        const ar = a0 + (a + 2) % 3;
2542	        const bl = b0 + (b + 2) % 3;
2543
2544	        if (b === -1) return ar;
2545
2546	        const p0 = triangles[ar];
2547	        const pr = triangles[a];
2548	        const pl = triangles[al];
2549	        const p1 = triangles[bl];
2550
2551	        const illegal = inCircle(
2552	            coords[2 * p0], coords[2 * p0 + 1],
2553	            coords[2 * pr], coords[2 * pr + 1],
2554	            coords[2 * pl], coords[2 * pl + 1],
2555	            coords[2 * p1], coords[2 * p1 + 1]);
2556
2557	        if (illegal) {
2558	            triangles[a] = p1;
2559	            triangles[b] = p0;
2560
2561	            const hbl = halfedges[bl];
2562
2563	            // edge swapped on the other side of the hull (rare); fix the halfedge reference
2564	            if (hbl === -1) {
2565	                let e = this.hull;
2566	                do {
2567	                    if (e.t === bl) {
2568	                        e.t = a;
2569	                        break;
2570	                    }
2571	                    e = e.next;
2572	                } while (e !== this.hull);
2573	            }
2574	            this._link(a, hbl);
2575	            this._link(b, halfedges[ar]);
2576	            this._link(ar, bl);
2577
2578	            const br = b0 + (b + 1) % 3;
2579
2580	            this._legalize(a);
2581	            return this._legalize(br);
2582	        }
2583
2584	        return ar;
2585	    }
2586
2587	    _link(a, b) {
2588	        this.halfedges[a] = b;
2589	        if (b !== -1) this.halfedges[b] = a;
2590	    }
2591
2592	    // add a new triangle given vertex indices and adjacent half-edge ids
2593	    _addTriangle(i0, i1, i2, a, b, c) {
2594	        const t = this.trianglesLen;
2595
2596	        this.triangles[t] = i0;
2597	        this.triangles[t + 1] = i1;
2598	        this.triangles[t + 2] = i2;
2599
2600	        this._link(t, a);
2601	        this._link(t + 1, b);
2602	        this._link(t + 2, c);
2603
2604	        this.trianglesLen += 3;
2605
2606	        return t;
2607	    }
2608	}
2609
2610	// monotonically increases with real angle, but doesn't need expensive trigonometry
2611	function pseudoAngle(dx, dy) {
2612	    const p = dx / (Math.abs(dx) + Math.abs(dy));
2613	    return (dy > 0 ? 3 - p : 1 + p) / 4; // [0..1]
2614	}
2615
2616	function dist(ax, ay, bx, by) {
2617	    const dx = ax - bx;
2618	    const dy = ay - by;
2619	    return dx * dx + dy * dy;
2620	}
2621
2622	function orient(px, py, qx, qy, rx, ry) {
2623	    return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0;
2624	}
2625
2626	function inCircle(ax, ay, bx, by, cx, cy, px, py) {
2627	    const dx = ax - px;
2628	    const dy = ay - py;
2629	    const ex = bx - px;
2630	    const ey = by - py;
2631	    const fx = cx - px;
2632	    const fy = cy - py;
2633
2634	    const ap = dx * dx + dy * dy;
2635	    const bp = ex * ex + ey * ey;
2636	    const cp = fx * fx + fy * fy;
2637
2638	    return dx * (ey * cp - bp * fy) -
2639	           dy * (ex * cp - bp * fx) +
2640	           ap * (ex * fy - ey * fx) < 0;
2641	}
2642
2643	function circumradius(ax, ay, bx, by, cx, cy) {
2644	    const dx = bx - ax;
2645	    const dy = by - ay;
2646	    const ex = cx - ax;
2647	    const ey = cy - ay;
2648
2649	    const bl = dx * dx + dy * dy;
2650	    const cl = ex * ex + ey * ey;
2651	    const d = dx * ey - dy * ex;
2652
2653	    const x = (ey * bl - dy * cl) * 0.5 / d;
2654	    const y = (dx * cl - ex * bl) * 0.5 / d;
2655
2656	    return bl && cl && d && (x * x + y * y) || Infinity;
2657	}
2658
2659	function circumcenter(ax, ay, bx, by, cx, cy) {
2660	    const dx = bx - ax;
2661	    const dy = by - ay;
2662	    const ex = cx - ax;
2663	    const ey = cy - ay;
2664
2665	    const bl = dx * dx + dy * dy;
2666	    const cl = ex * ex + ey * ey;
2667	    const d = dx * ey - dy * ex;
2668
2669	    const x = ax + (ey * bl - dy * cl) * 0.5 / d;
2670	    const y = ay + (dx * cl - ex * bl) * 0.5 / d;
2671
2672	    return {x, y};
2673	}
2674
2675	// create a new node in a doubly linked list
2676	function insertNode(coords, i, prev) {
2677	    const node = {
2678	        i,
2679	        x: coords[2 * i],
2680	        y: coords[2 * i + 1],
2681	        t: 0,
2682	        prev: null,
2683	        next: null,
2684	        removed: false
2685	    };
2686
2687	    if (!prev) {
2688	        node.prev = node;
2689	        node.next = node;
2690
2691	    } else {
2692	        node.next = prev.next;
2693	        node.prev = prev;
2694	        prev.next.prev = node;
2695	        prev.next = node;
2696	    }
2697	    return node;
2698	}
2699
2700	function removeNode(node) {
2701	    node.prev.next = node.next;
2702	    node.next.prev = node.prev;
2703	    node.removed = true;
2704	    return node.prev;
2705	}
2706
2707	function quicksort(ids, coords, left, right, cx, cy) {
2708	    let i, j, temp;
2709
2710	    if (right - left <= 20) {
2711	        for (i = left + 1; i <= right; i++) {
2712	            temp = ids[i];
2713	            j = i - 1;
2714	            while (j >= left && compare$1(coords, ids[j], temp, cx, cy) > 0) ids[j + 1] = ids[j--];
2715	            ids[j + 1] = temp;
2716	        }
2717	    } else {
2718	        const median = (left + right) >> 1;
2719	        i = left + 1;
2720	        j = right;
2721	        swap$1(ids, median, i);
2722	        if (compare$1(coords, ids[left], ids[right], cx, cy) > 0) swap$1(ids, left, right);
2723	        if (compare$1(coords, ids[i], ids[right], cx, cy) > 0) swap$1(ids, i, right);
2724	        if (compare$1(coords, ids[left], ids[i], cx, cy) > 0) swap$1(ids, left, i);
2725
2726	        temp = ids[i];
2727	        while (true) {
2728	            do i++; while (compare$1(coords, ids[i], temp, cx, cy) < 0);
2729	            do j--; while (compare$1(coords, ids[j], temp, cx, cy) > 0);
2730	            if (j < i) break;
2731	            swap$1(ids, i, j);
2732	        }
2733	        ids[left + 1] = ids[j];
2734	        ids[j] = temp;
2735
2736	        if (right - i + 1 >= j - left) {
2737	            quicksort(ids, coords, i, right, cx, cy);
2738	            quicksort(ids, coords, left, j - 1, cx, cy);
2739	        } else {
2740	            quicksort(ids, coords, left, j - 1, cx, cy);
2741	            quicksort(ids, coords, i, right, cx, cy);
2742	        }
2743	    }
2744	}
2745
2746	function compare$1(coords, i, j, cx, cy) {
2747	    const d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy);
2748	    const d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy);
2749	    return (d1 - d2) || (coords[2 * i] - coords[2 * j]) || (coords[2 * i + 1] - coords[2 * j + 1]);
2750	}
2751
2752	function swap$1(arr, i, j) {
2753	    const tmp = arr[i];
2754	    arr[i] = arr[j];
2755	    arr[j] = tmp;
2756	}
2757
2758	function defaultGetX(p) {
2759	    return p[0];
2760	}
2761	function defaultGetY(p) {
2762	    return p[1];
2763	}
2764
2765	const epsilon$1 = 1e-6;
2766
2767	class Path {
2768	  constructor() {
2769	    this._x0 = this._y0 = // start of current subpath
2770	    this._x1 = this._y1 = null; // end of current subpath
2771	    this._ = "";
2772	  }
2773	  moveTo(x, y) {
2774	    this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}`;
2775	  }
2776	  closePath() {
2777	    if (this._x1 !== null) {
2778	      this._x1 = this._x0, this._y1 = this._y0;
2779	      this._ += "Z";
2780	    }
2781	  }
2782	  lineTo(x, y) {
2783	    this._ += `L${this._x1 = +x},${this._y1 = +y}`;
2784	  }
2785	  arc(x, y, r) {
2786	    x = +x, y = +y, r = +r;
2787	    const x0 = x + r;
2788	    const y0 = y;
2789	    if (r < 0) throw new Error("negative radius");
2790	    if (this._x1 === null) this._ += `M${x0},${y0}`;
2791	    else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) this._ += "L" + x0 + "," + y0;
2792	    if (!r) return;
2793	    this._ += `A${r},${r},0,1,1,${x - r},${y}A${r},${r},0,1,1,${this._x1 = x0},${this._y1 = y0}`;
2794	  }
2795	  rect(x, y, w, h) {
2796	    this._ += `M${this._x0 = this._x1 = +x},${this._y0 = this._y1 = +y}h${+w}v${+h}h${-w}Z`;
2797	  }
2798	  value() {
2799	    return this._ || null;
2800	  }
2801	}
2802
2803	class Polygon {
2804	  constructor() {
2805	    this._ = [];
2806	  }
2807	  moveTo(x, y) {
2808	    this._.push([x, y]);
2809	  }
2810	  closePath() {
2811	    this._.push(this._[0].slice());
2812	  }
2813	  lineTo(x, y) {
2814	    this._.push([x, y]);
2815	  }
2816	  value() {
2817	    return this._.length ? this._ : null;
2818	  }
2819	}
2820
2821	class Voronoi {
2822	  constructor(delaunay, [xmin, ymin, xmax, ymax] = [0, 0, 960, 500]) {
2823	    if (!((xmax = +xmax) >= (xmin = +xmin)) || !((ymax = +ymax) >= (ymin = +ymin))) throw new Error("invalid bounds");
2824	    const {points, hull, triangles} = this.delaunay = delaunay;
2825	    const circumcenters = this.circumcenters = new Float64Array(triangles.length / 3 * 2);
2826	    const vectors = this.vectors = new Float64Array(points.length * 2);
vendor: 6,957 bytes, lines 2827-3014
2827	    this.xmax = xmax, this.xmin = xmin;
2828	    this.ymax = ymax, this.ymin = ymin;
2829
2830	    // Compute circumcenters.
2831	    for (let i = 0, j = 0, n = triangles.length; i < n; i += 3, j += 2) {
2832	      const t1 = triangles[i] * 2;
2833	      const t2 = triangles[i + 1] * 2;
2834	      const t3 = triangles[i + 2] * 2;
2835	      const x1 = points[t1];
2836	      const y1 = points[t1 + 1];
2837	      const x2 = points[t2];
2838	      const y2 = points[t2 + 1];
2839	      const x3 = points[t3];
2840	      const y3 = points[t3 + 1];
2841	      const a2 = x1 - x2;
2842	      const a3 = x1 - x3;
2843	      const b2 = y1 - y2;
2844	      const b3 = y1 - y3;
2845	      const d1 = x1 * x1 + y1 * y1;
2846	      const d2 = d1 - x2 * x2 - y2 * y2;
2847	      const d3 = d1 - x3 * x3 - y3 * y3;
2848	      const ab = (a3 * b2 - a2 * b3) * 2;
2849	      circumcenters[j] = (b2 * d3 - b3 * d2) / ab;
2850	      circumcenters[j + 1] = (a3 * d2 - a2 * d3) / ab;
2851	    }
2852
2853	    // Compute exterior cell rays.
2854	    let node = hull;
2855	    let p0, p1 = node.i * 4;
2856	    let x0, x1 = node.x;
2857	    let y0, y1 = node.y;
2858	    do {
2859	      node = node.next, p0 = p1, x0 = x1, y0 = y1, p1 = node.i * 4, x1 = node.x, y1 = node.y;
2860	      vectors[p0 + 2] = vectors[p1] = y0 - y1;
2861	      vectors[p0 + 3] = vectors[p1 + 1] = x1 - x0;
2862	    } while (node !== hull);
2863	  }
2864	  render(context) {
2865	    const buffer = context == null ? context = new Path : undefined;
2866	    const {delaunay: {halfedges, hull}, circumcenters, vectors} = this;
2867	    for (let i = 0, n = halfedges.length; i < n; ++i) {
2868	      const j = halfedges[i];
2869	      if (j < i) continue;
2870	      const ti = Math.floor(i / 3) * 2;
2871	      const tj = Math.floor(j / 3) * 2;
2872	      const xi = circumcenters[ti];
2873	      const yi = circumcenters[ti + 1];
2874	      const xj = circumcenters[tj];
2875	      const yj = circumcenters[tj + 1];
2876	      this._renderSegment(xi, yi, xj, yj, context);
2877	    }
2878	    let node = hull;
2879	    do {
2880	      node = node.next;
2881	      const t = Math.floor(node.t / 3) * 2;
2882	      const x = circumcenters[t];
2883	      const y = circumcenters[t + 1];
2884	      const v = node.i * 4;
2885	      const p = this._project(x, y, vectors[v + 2], vectors[v + 3]);
2886	      if (p) this._renderSegment(x, y, p[0], p[1], context);
2887	    } while (node !== hull);
2888	    return buffer && buffer.value();
2889	  }
2890	  renderBounds(context) {
2891	    const buffer = context == null ? context = new Path : undefined;
2892	    context.rect(this.xmin, this.ymin, this.xmax - this.xmin, this.ymax - this.ymin);
2893	    return buffer && buffer.value();
2894	  }
2895	  renderCell(i, context) {
2896	    const buffer = context == null ? context = new Path : undefined;
2897	    const points = this._clip(i);
2898	    if (points === null) return;
2899	    context.moveTo(points[0], points[1]);
2900	    for (let i = 2, n = points.length; i < n; i += 2) {
2901	      context.lineTo(points[i], points[i + 1]);
2902	    }
2903	    context.closePath();
2904	    return buffer && buffer.value();
2905	  }
2906	  *cellPolygons() {
2907	    const {delaunay: {points}} = this;
2908	    for (let i = 0, n = points.length / 2; i < n; ++i) {
2909	      const cell = this.cellPolygon(i);
2910	      if (cell) yield cell;
2911	    }
2912	  }
2913	  cellPolygon(i) {
2914	    const polygon = new Polygon;
2915	    this.renderCell(i, polygon);
2916	    return polygon.value();
2917	  }
2918	  _renderSegment(x0, y0, x1, y1, context) {
2919	    let S;
2920	    const c0 = this._regioncode(x0, y0);
2921	    const c1 = this._regioncode(x1, y1);
2922	    if (c0 === 0 && c1 === 0) {
2923	      context.moveTo(x0, y0);
2924	      context.lineTo(x1, y1);
2925	    } else if (S = this._clipSegment(x0, y0, x1, y1, c0, c1)) {
2926	      context.moveTo(S[0], S[1]);
2927	      context.lineTo(S[2], S[3]);
2928	    }
2929	  }
2930	  contains(i, x, y) {
2931	    if ((x = +x, x !== x) || (y = +y, y !== y)) return false;
2932	    return this.delaunay._step(i, x, y) === i;
2933	  }
2934	  _cell(i) {
2935	    const {circumcenters, delaunay: {inedges, halfedges, triangles}} = this;
2936	    const e0 = inedges[i];
2937	    if (e0 === -1) return null; // coincident point
2938	    const points = [];
2939	    let e = e0;
2940	    do {
2941	      const t = Math.floor(e / 3);
2942	      points.push(circumcenters[t * 2], circumcenters[t * 2 + 1]);
2943	      e = e % 3 === 2 ? e - 2 : e + 1;
2944	      if (triangles[e] !== i) break; // bad triangulation
2945	      e = halfedges[e];
2946	    } while (e !== e0 && e !== -1);
2947	    return points;
2948	  }
2949	  _clip(i) {
2950	    const points = this._cell(i);
2951	    if (points === null) return null;
2952	    const {vectors: V} = this;
2953	    const v = i * 4;
2954	    return V[v] || V[v + 1]
2955	        ? this._clipInfinite(i, points, V[v], V[v + 1], V[v + 2], V[v + 3])
2956	        : this._clipFinite(i, points);
2957	  }
2958	  _clipFinite(i, points) {
2959	    const n = points.length;
2960	    let P = null;
2961	    let x0, y0, x1 = points[n - 2], y1 = points[n - 1];
2962	    let c0, c1 = this._regioncode(x1, y1);
2963	    let e0, e1;
2964	    for (let j = 0; j < n; j += 2) {
2965	      x0 = x1, y0 = y1, x1 = points[j], y1 = points[j + 1];
2966	      c0 = c1, c1 = this._regioncode(x1, y1);
2967	      if (c0 === 0 && c1 === 0) {
2968	        e0 = e1, e1 = 0;
2969	        if (P) P.push(x1, y1);
2970	        else P = [x1, y1];
2971	      } else {
2972	        let S, sx0, sy0, sx1, sy1;
2973	        if (c0 === 0) {
2974	          if ((S = this._clipSegment(x0, y0, x1, y1, c0, c1)) === null) continue;
2975	          [sx0, sy0, sx1, sy1] = S;
2976	        } else {
2977	          if ((S = this._clipSegment(x1, y1, x0, y0, c1, c0)) === null) continue;
2978	          [sx1, sy1, sx0, sy0] = S;
2979	          e0 = e1, e1 = this._edgecode(sx0, sy0);
2980	          if (e0 && e1) this._edge(i, e0, e1, P, P.length);
2981	          if (P) P.push(sx0, sy0);
2982	          else P = [sx0, sy0];
2983	        }
2984	        e0 = e1, e1 = this._edgecode(sx1, sy1);
2985	        if (e0 && e1) this._edge(i, e0, e1, P, P.length);
2986	        if (P) P.push(sx1, sy1);
2987	        else P = [sx1, sy1];
2988	      }
2989	    }
2990	    if (P) {
2991	      e0 = e1, e1 = this._edgecode(P[0], P[1]);
2992	      if (e0 && e1) this._edge(i, e0, e1, P, P.length);
2993	    } else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) {
2994	      return [this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax, this.xmin, this.ymin];
2995	    }
2996	    return P;
2997	  }
2998	  _clipSegment(x0, y0, x1, y1, c0, c1) {
2999	    while (true) {
3000	      if (c0 === 0 && c1 === 0) return [x0, y0, x1, y1];
3001	      if (c0 & c1) return null;
3002	      let x, y, c = c0 || c1;
3003	      if (c & 0b1000) x = x0 + (x1 - x0) * (this.ymax - y0) / (y1 - y0), y = this.ymax;
3004	      else if (c & 0b0100) x = x0 + (x1 - x0) * (this.ymin - y0) / (y1 - y0), y = this.ymin;
3005	      else if (c & 0b0010) y = y0 + (y1 - y0) * (this.xmax - x0) / (x1 - x0), x = this.xmax;
3006	      else y = y0 + (y1 - y0) * (this.xmin - x0) / (x1 - x0), x = this.xmin;
3007	      if (c0) x0 = x, y0 = y, c0 = this._regioncode(x0, y0);
3008	      else x1 = x, y1 = y, c1 = this._regioncode(x1, y1);
3009	    }
3010	  }
3011	  _clipInfinite(i, points, vx0, vy0, vxn, vyn) {
3012	    let P = Array.from(points), p;
3013	    if (p = this._project(P[0], P[1], vx0, vy0)) P.unshift(p[0], p[1]);
3014	    if (p = this._project(P[P.length - 2], P[P.length - 1], vxn, vyn)) P.push(p[
30140], p[1]);
3015	    if (P = this._clipFinite(i, P)) {
3016	      for (let j = 0, n = P.length, c0, c1 = this._edgecode(P[n - 2], P[n - 1]); j < n; j += 2) {
3017	        c0 = c1, c1 = this._edgecode(P[j], P[j + 1]);
3018	        if (c0 && c1) j = this._edge(i, c0, c1, P, j), n = P.length;
3019	      }
3020	    } else if (this.contains(i, (this.xmin + this.xmax) / 2, (this.ymin + this.ymax) / 2)) {
3021	      P = [this.xmin, this.ymin, this.xmax, this.ymin, this.xmax, this.ymax, this.xmin, this.ymax];
3022	    }
3023	    return P;
3024	  }
3025	  _edge(i, e0, e1, P, j) {
3026	    while (e0 !== e1) {
3027	      let x, y;
3028	      switch (e0) {
3029	        case 0b0101: e0 = 0b0100; continue; // top-left
3030	        case 0b0100: e0 = 0b0110, x = this.xmax, y = this.ymin; break; // top
3031	        case 0b0110: e0 = 0b0010; continue; // top-right
3032	        case 0b0010: e0 = 0b1010, x = this.xmax, y = this.ymax; break; // right
3033	        case 0b1010: e0 = 0b1000; continue; // bottom-right
3034	        case 0b1000: e0 = 0b1001, x = this.xmin, y = this.ymax; break; // bottom
3035	        case 0b1001: e0 = 0b0001; continue; // bottom-left
3036	        case 0b0001: e0 = 0b0101, x = this.xmin, y = this.ymin; break; // left
3037	      }
3038	      if ((P[j] !== x || P[j + 1] !== y) && this.contains(i, x, y)) {
3039	        P.splice(j, 0, x, y), j += 2;
3040	      }
3041	    }
3042	    return j;
3043	  }
3044	  _project(x0, y0, vx, vy) {
3045	    let t = Infinity, c, x, y;
3046	    if (vy < 0) { // top
3047	      if (y0 <= this.ymin) return null;
3048	      if ((c = (this.ymin - y0) / vy) < t) y = this.ymin, x = x0 + (t = c) * vx;
3049	    } else if (vy > 0) { // bottom
3050	      if (y0 >= this.ymax) return null;
3051	      if ((c = (this.ymax - y0) / vy) < t) y = this.ymax, x = x0 + (t = c) * vx;
3052	    }
3053	    if (vx > 0) { // right
3054	      if (x0 >= this.xmax) return null;
3055	      if ((c = (this.xmax - x0) / vx) < t) x = this.xmax, y = y0 + (t = c) * vy;
3056	    } else if (vx < 0) { // left
3057	      if (x0 <= this.xmin) return null;
3058	      if ((c = (this.xmin - x0) / vx) < t) x = this.xmin, y = y0 + (t = c) * vy;
3059	    }
3060	    return [x, y];
3061	  }
3062	  _edgecode(x, y) {
3063	    return (x === this.xmin ? 0b0001
3064	        : x === this.xmax ? 0b0010 : 0b0000)
3065	        | (y === this.ymin ? 0b0100
3066	        : y === this.ymax ? 0b1000 : 0b0000);
3067	  }
3068	  _regioncode(x, y) {
3069	    return (x < this.xmin ? 0b0001
3070	        : x > this.xmax ? 0b0010 : 0b0000)
3071	        | (y < this.ymin ? 0b0100
3072	        : y > this.ymax ? 0b1000 : 0b0000);
3073	  }
3074	}
3075
3076	const tau = 2 * Math.PI;
3077
3078	function pointX(p) {
3079	  return p[0];
3080	}
3081
3082	function pointY(p) {
3083	  return p[1];
3084	}
3085
3086	class Delaunay {
3087	  constructor(points) {
3088	    const {halfedges, hull, triangles} = new Delaunator(points);
3089	    this.points = points;
3090	    this.halfedges = halfedges;
3091	    this.hull = hull;
3092	    this.triangles = triangles;
3093	    const inedges = this.inedges = new Int32Array(points.length / 2).fill(-1);
3094	    const outedges = this.outedges = new Int32Array(points.length / 2).fill(-1);
3095
3096	    // Compute an index from each point to an (arbitrary) incoming halfedge.
3097	    for (let e = 0, n = halfedges.length; e < n; ++e) {
3098	      inedges[triangles[e % 3 === 2 ? e - 2 : e + 1]] = e;
3099	    }
3100
3101	    // For points on the hull, index both the incoming and outgoing halfedges.
3102	    let node0, node1 = hull;
3103	    do {
3104	      node0 = node1, node1 = node1.next;
3105	      inedges[node1.i] = node0.t;
3106	      outedges[node0.i] = node1.t;
3107	    } while (node1 !== hull);
3108	  }
3109	  voronoi(bounds) {
3110	    return new Voronoi(this, bounds);
3111	  }
3112	  *neighbors(i) {
3113	    const {inedges, outedges, halfedges, triangles} = this;
3114	    const e0 = inedges[i];
3115	    if (e0 === -1) return; // coincident point
3116	    let e = e0;
3117	    do {
3118	      yield triangles[e];
3119	      e = e % 3 === 2 ? e - 2 : e + 1;
3120	      if (triangles[e] !== i) return; // bad triangulation
3121	      e = halfedges[e];
3122	      if (e === -1) return yield triangles[outedges[i]];
3123	    } while (e !== e0);
3124	  }
3125	  find(x, y, i = 0) {
3126	    if ((x = +x, x !== x) || (y = +y, y !== y)) return -1;
3127	    let c;
3128	    while ((c = this._step(i, x, y)) >= 0 && c !== i) i = c;
3129	    return c;
3130	  }
3131	  _step(i, x, y) {
3132	    const {inedges, points} = this;
3133	    if (inedges[i] === -1) return -1; // coincident point
3134	    let c = i;
3135	    let dc = (x - points[i * 2]) ** 2 + (y - points[i * 2 + 1]) ** 2;
3136	    for (const t of this.neighbors(i)) {
3137	      const dt = (x - points[t * 2]) ** 2 + (y - points[t * 2 + 1]) ** 2;
3138	      if (dt < dc) dc = dt, c = t;
3139	    }
3140	    return c;
3141	  }
3142	  render(context) {
3143	    const buffer = context == null ? context = new Path : undefined;
3144	    const {points, halfedges, triangles} = this;
3145	    for (let i = 0, n = halfedges.length; i < n; ++i) {
3146	      const j = halfedges[i];
3147	      if (j < i) continue;
3148	      const ti = triangles[i] * 2;
3149	      const tj = triangles[j] * 2;
3150	      context.moveTo(points[ti], points[ti + 1]);
3151	      context.lineTo(points[tj], points[tj + 1]);
3152	    }
3153	    this.renderHull(context);
3154	    return buffer && buffer.value();
3155	  }
3156	  renderPoints(context, r = 2) {
3157	    const buffer = context == null ? context = new Path : undefined;
3158	    const {points} = this;
3159	    for (let i = 0, n = points.length; i < n; i += 2) {
3160	      const x = points[i], y = points[i + 1];
3161	      context.moveTo(x + r, y);
3162	      context.arc(x, y, r, 0, tau);
3163	    }
3164	    return buffer && buffer.value();
3165	  }
3166	  renderHull(context) {
3167	    const buffer = context == null ? context = new Path : undefined;
3168	    const {hull} = this;
3169	    let node = hull;
3170	    context.moveTo(node.x, node.y);
3171	    while (node = node.next, node !== hull) context.lineTo(node.x, node.y);
3172	    context.closePath();
3173	    return buffer && buffer.value();
3174	  }
3175	  hullPolygon() {
3176	    const polygon = new Polygon;
3177	    this.renderHull(polygon);
3178	    return polygon.value();
3179	  }
3180	  renderTriangle(i, context) {
3181	    const buffer = context == null ? context = new Path : undefined;
3182	    const {points, triangles} = this;
3183	    const t0 = triangles[i *= 3] * 2;
3184	    const t1 = triangles[i + 1] * 2;
3185	    const t2 = triangles[i + 2] * 2;
3186	    context.moveTo(points[t0], points[t0 + 1]);
3187	    context.lineTo(points[t1], points[t1 + 1]);
3188	    context.lineTo(points[t2], points[t2 + 1]);
3189	    context.closePath();
3190	    return buffer && buffer.value();
3191	  }
3192	  *trianglePolygons() {
3193	    const {triangles} = this;
3194	    for (let i = 0, n = triangles.length / 3; i < n; ++i) {
3195	      yield this.trianglePolygon(i);
3196	    }
3197	  }
3198	  trianglePolygon(i) {
3199	    const polygon = new Polygon;
3200	    this.renderTriangle(i, polygon);
3201	    return polygon.value();
3202	  }
3203	}
3204
3205	Delaunay.from = function(points, fx = pointX, fy = pointY, that) {
3206	  return new Delaunay("length" in points
3207	      ? flatArray(points, fx, fy, that)
3208	      : Float64Array.from(flatIterable(points, fx, fy, that)));
3209	};
3210
3211	function flatArray(points, fx, fy, that) {
3212	  const n = points.length;
3213	  const array = new Float64Array(n * 2);
3214	  for (let i = 0; i < n; ++i) {
3215	    const p = points[i];
3216	    array[i * 2] = fx.call(that, p, i, points);
3217	    array[i * 2 + 1] = fy.call(that, p, i, points);
3218	  }
3219	  return array;
3220	}
3221
3222	function* flatIterable(points, fx, fy, that) {
3223	  let i = 0;
3224	  for (const p of points) {
3225	    yield fx.call(that, p, i, points);
3226	    yield fy.call(that, p, i, points);
3227	    ++i;
3228	  }
3229	}
3230
3231	class Outlying {
3232	    constructor(tree, upperBound) {
3233	        //Clone the tree to avoid modifying it
3234	        this.tree = JSON.parse(JSON.stringify(tree));
3235	        this.upperBound = upperBound;
3236	    }
3237
3238	    /**
3239	     * Returns outlying score
3240	     * @returns {number}
3241	     */
3242	    score() {
3243	        let tree = this.tree,
3244	            totalLengths = 0,
3245	            totalOutlyingLengths = 0;
3246	        let upperBound = this.upperBound;
3247	        if(!upperBound){
3248	            let allLengths = tree.links.map(l => l.weight),
3249	                q1 = quantile(allLengths, 0.25),
3250	                q3 = quantile(allLengths, 0.75),
3251	                iqr = q3 - q1;
3252	            // upperBound = q3+1.5*iqr;
3253	            upperBound = q3+3*iqr;
3254	            //Save it for displaying purpose.
3255	            this.upperBound = upperBound;
3256	        }
3257	        tree.links.forEach(l => {
3258	            totalLengths += l.weight;
3259	            if (l.weight > upperBound) {
3260	                totalOutlyingLengths += l.weight;
3261	                l.isOutlying = true;
3262	            }
3263	        });
3264	        return totalOutlyingLengths / totalLengths;
3265	    }
3266
3267	    /**
3268	     * Returns outlying links
3269	     */
3270	    links() {
3271	        return this.tree.links.filter(l => l.isOutlying);
3272	    }
3273
3274	    /**
3275	     * Remove outlying links and nodes and return a new tree without outlying points/edges
3276	     */
3277	    removeOutlying() {
3278	        var newTree = JSON.parse(JSON.stringify(this.tree));
3279	        //Remove outlying links
3280	        newTree.links = newTree.links.filter(l => !l.isOutlying);
3281	        //Remove outlying nodes (nodes are not in any none-outlying links
3282	        let allNodesWithLinks = [];
3283	        newTree.links.forEach(l => {
3284	            allNodesWithLinks.push(l.source);
3285	            allNodesWithLinks.push(l.target);
3286	        });
3287	        allNodesWithLinks = underscore.uniq(allNodesWithLinks, false, d => d.join(','));
3288	        //TODO: May need to reuse the existing result instead of calculating all again.
3289	        //Triangulate again
3290	        let delaunay = Delaunay.from(allNodesWithLinks);
3291	        delaunay.points = allNodesWithLinks;
3292	        delaunay.triangleCoordinates = function(){
3293	            let triangles = this.triangles;
3294	            let tc = [];
3295	            for (let i = 0; i < triangles.length; i += 3) {
3296	                tc.push([
3297	                    this.points[triangles[i]],
3298	                    this.points[triangles[i + 1]],
3299	                    this.points[triangles[i + 2]]
3300	                ]);
3301	            }
3302	            return tc;
3303	        };
3304	        let graph = createGraph(delaunay.triangleCoordinates());
3305	        newTree = mst(graph);
3306	        return newTree;
3307	    }
3308
3309	    /**
3310	     * Returns the outlying points (in form of points, not node object).
3311	     * @returns {Array}
3312	     */
3313	    points(){
3314	        let newTree = this.removeOutlying();
3315	        let newNodes = newTree.nodes;
3316	        let oldNodes = this.tree.nodes;
3317	        let ops = [];
3318	        oldNodes.forEach(n=>{
3319	            //.id since we are accessing to points and the node is in form of {id: thePoint}
3320	            if(!pointExists(newNodes.map(n=>n.id), n.id)){
3321	                ops.push(n.id);
3322	            }
3323	        });
3324	        return ops;
3325	    }
3326	}
3327
3328	class Skewed {
3329	    constructor(tree) {
3330	        //Clone the tree to avoid modifying it
3331	        this.tree = JSON.parse(JSON.stringify(tree));
3332	    }
3333
3334	    /**
3335	     * Returns skewed score
3336	     * @returns {number}
3337	     */
3338	    score() {
3339	        let allLengths = this.tree.links.map(l=>l.weight),
3340	        q90 = quantile(allLengths, .9),
3341	        q50 = quantile(allLengths, .5),
3342	        q10 = quantile(allLengths, .1);
3343	        if(q90!=q10){
3344	            return (q90-q50)/(q90-q10);
3345	        }else{
3346	            return 0;
3347	        }
3348
3349	    }
3350	}
3351
3352	class Sparse {
3353	    constructor(tree) {
3354	        //Clone the tree to avoid modifying it
3355	        this.tree = JSON.parse(JSON.stringify(tree));
3356	    }
3357
3358	    /**
3359	     * Returns sparse score
3360	     * @returns {number}
3361	     */
3362	    score() {
3363	        let allLengths = this.tree.links.map(l=>l.weight),
3364	            q90 = quantile(allLengths, .9);
3365	        return q90;
3366	    }
3367	}
3368
3369	class Striated {
3370	    constructor(tree) {
3371	        //Clone the tree to avoid modifying it
3372	        this.tree = JSON.parse(JSON.stringify(tree));
3373	    }
3374
3375	    /**
3376	     * Returns striated score
3377	     * @returns {number}
3378	     */
3379	    score() {
3380	        //Loop through the nodes.
3381	        let verticesCount = this.tree.nodes.length;
3382	        let allObtuseV2CornersCount = this.getAllObtuseV2Corners().length;
3383	        return allObtuseV2CornersCount / verticesCount;
3384	    }
3385
3386	    getAllObtuseV2Corners() {
3387	        let allV2Corners = this.getAllV2Corners();
3388	        let allObtuseV2Corners = [];
3389	        allV2Corners.forEach(corner => {
3390	            let cs = cosine(corner[0], corner[1], corner[2]);
3391	            if (cs < -.75) {
3392	                allObtuseV2Corners.push(corner);
3393	            }
3394	        });
3395	        return allObtuseV2Corners;
3396
3397	        function cosine(p1, p2, p3) {
3398	            let p12 = distance(p1, p2),
3399	                p13 = distance(p1, p3),
3400	                p23 = distance(p2, p3);
3401	            return ((Math.pow(p12, 2)) + (Math.pow(p13, 2)) - (Math.pow(p23, 2))) / (2 * p12 * p13);
3402	        }
3403	    }
3404
3405	    /**
3406	     * This function returns corners (three vertices) of vertices of degree two in the for mat of
3407	     * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)).
3408	     */
3409	    getAllV2Corners() {
3410	        return getAllV2CornersFromTree(this.tree);
3411	    }
3412	}
3413
3414	function area(polygon) {
3415	  var i = -1,
3416	      n = polygon.length,
3417	      a,
3418	      b = polygon[n - 1],
3419	      area = 0;
3420
3421	  while (++i < n) {
3422	    a = b;
3423	    b = polygon[i];
3424	    area += a[1] * b[0] - a[0] * b[1];
3425	  }
3426
3427	  return area / 2;
3428	}
3429
3430	// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
3431
3432	function contains(polygon, point) {
3433	  var n = polygon.length,
3434	      p = polygon[n - 1],
3435	      x = point[0], y = point[1],
3436	      x0 = p[0], y0 = p[1],
3437	      x1, y1,
3438	      inside = false;
3439
3440	  for (var i = 0; i < n; ++i) {
3441	    p = polygon[i], x1 = p[0], y1 = p[1];
3442	    if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;
3443	    x0 = x1, y0 = y1;
3444	  }
3445
3446	  return inside;
3447	}
3448
3449	function length(polygon) {
3450	  var i = -1,
3451	      n = polygon.length,
3452	      b = polygon[n - 1],
3453	      xa,
3454	      ya,
3455	      xb = b[0],
3456	      yb = b[1],
3457	      perimeter = 0;
3458
3459	  while (++i < n) {
3460	    xa = xb;
3461	    ya = yb;
3462	    b = polygon[i];
3463	    xb = b[0];
3464	    yb = b[1];
3465	    xa -= xb;
3466	    ya -= yb;
3467	    perimeter += Math.sqrt(xa * xa + ya * ya);
3468	  }
3469
3470	  return perimeter;
3471	}
3472
3473	class ConcaveHull{
3474	    constructor(distance$$1){
3475	        if(!distance$$1){
3476	            this.calculateDistance = this.stdevDistance;
3477	        }
3478	        else if(typeof distance$$1 === "function"){
3479	            this.calculateDistance = distance$$1;
3480	        }else if(typeof distance$$1 === "number"){
3481	            this.calculateDistance = function () {return distance$$1;};
3482	        }
3483	        this.padding = 0;
3484	    }
3485	    coveringConcaveHull(delaunay){
3486	        let theMst = mst(createGraph(delaunay));
3487	        let allLengths = theMst.links.map(l=>l.weight);
3488	        let allPoints = theMst.nodes.map(n=>n.id);
3489	        //We will start with q7
3490	        let qx = 0.5,
3491	            increment = 0.01;
3492	        let theHull = null;
3493	        do{
3494	            qx = qx + increment;
3495	            let longEdge = quantile(allLengths, qx);
3496	            theHull = this.concaveHull(delaunay, longEdge);
3497	        }while(!coverAllPoints(theHull, allPoints));
3498
3499	        return theHull;
3500
3501	        function coverAllPoints(hull$$1, points) {
3502	            for (let i = 0; i < points.length; i++) {
3503	                let point = points[i];
3504	                let pointIsCovered = false;
3505	                for (let j = 0; j < hull$$1.length; j++) {
3506	                    if(contains(hull$$1[j], point)){
3507	                        pointIsCovered = true;
3508	                        break;
3509	                    }
3510	                }
3511	                if(!pointIsCovered){
3512	                    return false;
3513	                }
3514	            }
3515	            return true;
3516	        }
3517	    }
3518	    /**
3519	     * generate the concave hull.
3520	     * @param delaunay is inform of coordinates of triangulated triangles (3 points per triangle)
3521	     * @returns {Array}
3522	     */
3523	    concaveHull(delaunay, longEdge) {
3524	        if(!longEdge){
3525	            longEdge = this.calculateDistance(delaunay);
3526	        }
3527	        let mesh = delaunay.filter(function (d) {
3528	            return distance(d[0], d[1]) < longEdge && distance(d[0], d[2]) < longEdge && distance(d[1], d[2]) < longEdge
3529	        });
3530
3531	        var counts = {},
3532	            edges = {},
3533	            r,
3534	            result = [];
3535	        // Traverse the edges of all triangles and discard any edges that appear twice.
3536	        mesh.forEach(function (triangle) {
3537	            for (var i = 0; i < 3; i++) {
3538	                var edge = [triangle[i], triangle[(i + 1) % 3]].sort(ascendingCoords).map(String);
3539	                (edges[edge[0]] = (edges[edge[0]] || [])).push(edge[1]);
3540	                (edges[edge[1]] = (edges[edge[1]] || [])).push(edge[0]);
3541	                var k = edge.join(":");
3542	                if (counts[k]) delete counts[k];
3543	                else counts[k] = 1;
3544	            }
3545	        });
3546
3547	        while (1) {
3548	            var k = null;
3549	            // Pick an arbitrary starting point on a boundary.
3550	            for (k in counts) break;
3551	            if (k == null) break;
3552	            result.push(r = k.split(":").map(function (d) {
3553	                return d.split(",").map(Number);
3554	            }));
3555	            delete counts[k];
3556	            var q = r[1];
3557	            while (q[0] !== r[0][0] || q[1] !== r[0][1]) {
3558	                var p = q,
3559	                    qs = edges[p.join(",")],
3560	                    n = qs.length;
3561	                for (var i = 0; i < n; i++) {
3562	                    q = qs[i].split(",").map(Number);
3563	                    var edge = [p, q].sort(ascendingCoords).join(":");
3564	                    if (counts[edge]) {
3565	                        delete counts[edge];
3566	                        r.push(q);
3567	                        break;
3568	                    }
3569	                }
3570	            }
3571	        }
3572
3573	        if (this.padding !== 0) {
3574	            result = pad(result, this.padding);
3575	        }
3576
3577	        return result;
3578
3579	        function pad(bounds, amount) {
3580	            var result = [];
3581	            bounds.forEach(function (bound) {
3582	                var padded = [];
3583
3584	                var area$$1 = 0;
3585	                bound.forEach(function (p, i) {
3586	                    // http://forums.esri.com/Thread.asp?c=2&f=1718&t=174277
3587	                    // Area = Area + (X2 - X1) * (Y2 + Y1) / 2
3588
3589	                    var im1 = i - 1;
3590	                    if (i == 0) {
3591	                        im1 = bound.length - 1;
3592	                    }
3593	                    var pm = bound[im1];
3594	                    area$$1 += (p[0] - pm[0]) * (p[1] + pm[1]) / 2;
3595	                });
3596	                var handedness = 1;
3597	                if (area$$1 > 0) handedness = -1;
3598	                bound.forEach(function (p, i) {
3599	                    // average the tangent between
3600	                    var im1 = i - 1;
3601	                    if (i == 0) {
3602	                        im1 = bound.length - 2;
3603	                    }
3604	                    //var tp = getTangent(p, bound[ip1]);
3605	                    var tm = getTangent(p, bound[im1]);
3606	                    //var avg = { x: (tp.x + tm.x)/2, y: (tp.y + tm.y)/2 };
3607	                    //var normal = rotate2d(avg, 90);
3608	                    var normal = rotate2d(tm, 90 * handedness);
3609	                    padded.push([p[0] + normal.x * amount, p[1] + normal.y * amount]);
3610	                });
3611	                result.push(padded);
3612	            });
3613	            return result
3614	        }
3615
3616	        function getTangent(a, b) {
3617	            var vector = {x: b[0] - a[0], y: b[1] - a[1]};
3618	            var magnitude = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
3619	            vector.x /= magnitude;
3620	            vector.y /= magnitude;
3621	            return vector
3622	        }
3623
3624	        function rotate2d(vector, angle) {
3625	            //rotate a vector
3626	            angle *= Math.PI / 180; //convert to radians
3627	            return {
3628	                x: vector.x * Math.cos(angle) - vector.y * Math.sin(angle),
3629	                y: vector.x * Math.sin(angle) + vector.y * Math.cos(angle)
3630	            }
3631	        }
3632
3633	        function ascendingCoords(a, b) {
3634	            return a[0] === b[0] ? b[1] - a[1] : b[0] - a[0];
3635	        }
3636	    }
3637	    stdevDistance(delaunay) {
3638	        var sides = [];
3639	        delaunay.forEach(function (d) {
3640	            sides.push(distance(d[0], d[1]));
3641	            sides.push(distance(d[0], d[2]));
3642	            sides.push(distance(d[1], d[2]));
3643	        });
3644
3645	        var dev = d3.deviation(sides);
3646	        var mean$$1 = d3.mean(sides);
3647
3648	        return mean$$1 + dev;
3649	    }
3650	}
3651	function concaveHullArea(concaveHull) {
3652	    let concaveArea = 0;
3653	    concaveHull.forEach(hull$$1 => {
3654	        let area$$1 = area(hull$$1);
3655	        concaveArea += area$$1>0?area$$1:-area$$1;
3656	    });
3657	    return concaveArea;
3658	}
3659	function concaveHullLength(concaveHull) {
3660	    let concaveLength = 0;
3661	    concaveHull.forEach(hull$$1 => {
3662	        concaveLength += length(hull$$1);
3663	    });
3664	    return concaveLength;
3665	}
3666
3667	class Convex {
3668	    constructor(tree) {
3669	        //Clone the tree to avoid modifying it
3670	        this.tree = JSON.parse(JSON.stringify(tree));
3671	        let sites = this.tree.nodes.map(d => d.id);
3672	        this.delaunay = Delaunay.from(sites);
3673	        this.delaunay.points = sites;
3674	        this.delaunay.triangleCoordinates = function(){
3675	            let triangles = this.triangles;
3676	            let tc = [];
3677	            for (let i = 0; i < triangles.length; i += 3) {
3678	                tc.push([
3679	                    this.points[triangles[i]],
3680	                    this.points[triangles[i + 1]],
3681	                    this.points[triangles[i + 2]]
3682	                ]);
3683	            }
3684	            return tc;
3685	        };
3686	    }
3687	    /**
3688	     * Returns convex score
3689	     * @returns {number}
3690	     */
3691	    score() {
3692	        let concaveArea = concaveHullArea(this.concaveHull());
3693	        return concaveArea/area(this.convexHull());
3694	    }
3695	    noOutlyingTriangleCoordinates(){
3696
3697	        return this.delaunay.triangleCoordinates();
3698	    }
3699	    concaveHull() {
3700	        //Use quantile as cutoff values.
3701	        let allLengths = this.tree.links.map(l => l.weight),
3702	            concaveHull = new ConcaveHull(quantile(allLengths, 0.999)).concaveHull(this.noOutlyingTriangleCoordinates());
3703	            // concaveHull = new ConcaveHull().coveringConcaveHull(this.noOutlyingTriangleCoordinates());
3704	        return concaveHull;
3705	    }
3706	    convexHull() {
3707	        let hull$$1 = this.delaunay.hull;
3708	        let convexHull = [];
3709	        let e = hull$$1;
3710	        do {
3711	            convexHull.push([e.x, e.y]);
3712	            e = e.next;
3713	        } while (e != hull$$1);
3714	        return convexHull;
3715	    }
3716	}
3717
3718	class Skinny {
3719	    constructor(alphaHull) {
3720	        //Clone it in order to avoid modifying it.
3721	        this.alphaHull = alphaHull.slice(0);
3722	    }
3723	    /**
3724	     * Returns skinny score
3725	     * @returns {number}
3726	     */
3727	    score() {
3728	        return 1 - Math.sqrt(4*Math.PI*concaveHullArea(this.alphaHull))/concaveHullLength(this.alphaHull);
3729	    }
3730	}
3731
3732	class Stringy {
3733	    constructor(tree) {
3734	        //Clone the tree to avoid modifying it
3735	        this.tree = JSON.parse(JSON.stringify(tree));
3736	    }
3737
3738	    /**
3739	     * Returns striated score
3740	     * @returns {number}
3741	     */
3742	    score() {
3743	        //Loop through the nodes.
3744	        let verticesCount = this.tree.nodes.length;
3745	        let v2Count = this.getAllV2Corners().length;
3746	        let v1Count = this.getAllV1s().length;
3747	        return v2Count/(verticesCount-v1Count);
3748	    }
3749
3750	    /**
3751	     * This function returns corners (three vertices) of vertices of degree two in the for mat of
3752	     * point1, point2, point3 => point1 is the the vertex with degree two (two edges connected to it are [point1, point2] and [point1, point3] (order of the points in each edge is not important)).
3753	     * @returns {Array}
3754	     */
3755	    getAllV2Corners(){
3756	        return getAllV2CornersFromTree(this.tree);
3757	    }
3758
3759	    /**
3760	     * This function returns
3761	     * @returns {Array}
3762	     */
3763	    getAllV1s(){
3764	        return getAllV1sFromTree(this.tree);
3765	    }
3766	}
3767
3768	class Monotonic {
3769	    constructor(points) {
3770	        //Clone it in order to avoid modifying it.
3771	        this.points = points.slice(0);
3772	    }
3773	    /**
3774	     * Returns monotonic score
3775	     * @returns {number}
3776	     */
3777	    score() {
3778	        let xArr = [];
3779	        let yArr = [];
3780	        this.points.forEach(p=>{
3781	            xArr.push(p[0]);
3782	            yArr.push(p[1]);
3783	        });
3784	        let r = computeSpearmans(xArr, yArr);
3785	        return Math.pow(r, 2);
3786
3787	        /**Adopted from: https://bl.ocks.org/nkullman/f65d5619843dc22e061d957249121408**/
3788	        function computeSpearmans(arrX, arrY) {
3789	            // simple error handling for input arrays of nonequal lengths
3790	            if (arrX.length !== arrY.length) { return null; }
3791
3792	            // number of observations
3793	            let n = arrX.length;
3794
3795	            // rank datasets
3796	            let xRanked = rankArray(arrX),
3797	                yRanked = rankArray(arrY);
3798
3799	            // sum of distances between ranks
3800	            let dsq = 0;
3801	            for (let i = 0; i < n; i++) {
3802	                dsq += Math.pow(xRanked[i] - yRanked[i], 2);
3803	            }
3804
3805	            // compute correction for ties
3806	            let xTies = countTies(arrX),
3807	                yTies = countTies(arrY);
3808	            let xCorrection = 0,
3809	                yCorrection = 0;
3810	            for (let tieLength in xTies) {
3811	                xCorrection += xTies[tieLength] * tieLength * (Math.pow(tieLength, 2) - 1);
3812	            }
3813	            xCorrection /= 12.0;
3814	            for (let tieLength in yTies) {
3815	                yCorrection += yTies[tieLength] * tieLength * (Math.pow(tieLength, 2) - 1);
3816	            }
3817	            yCorrection /= 12.0;
3818
3819	            // denominator
3820	            let denominator = n * (Math.pow(n, 2) - 1) / 6.0;
3821
3822	            // compute rho
3823	            let rho = denominator - dsq - xCorrection - yCorrection;
3824	            rho /= Math.sqrt((denominator - 2 * xCorrection) * (denominator - 2 * yCorrection));
3825
3826	            return rho;
3827	        }
3828	        /** Computes the rank array for arr, where each entry in arr is
3829	         * assigned a value 1 thru n, where n is arr.length.
3830	         *
3831	         * Tied entries in arr are each given the average rank of the ties.
3832	         * Lower ranks are not increased
3833	         */
3834	        function rankArray(arr) {
3835
3836	            // ranking without averaging
3837	            let sorted = arr.slice().sort(function (a, b) { return b - a });
3838	            let ranks = arr.slice().map(function (v) { return sorted.indexOf(v) + 1 });
3839
3840	            // counts of each rank
3841	            let counts = {};
3842	            ranks.forEach(function (x) { counts[x] = (counts[x] || 0) + 1; });
3843
3844	            // average duplicates
3845	            ranks = ranks.map(function (x) { return x + 0.5 * ((counts[x] || 0) - 1); });
3846
3847	            return ranks;
3848	        }
3849	        /** Counts the number of ties in arr, and returns
3850	         * an object with
3851	         * a key for each tie length (an entry n for each n-way tie) and
3852	         * a value corresponding to the number of key-way (n-way) ties
3853	         */
3854	         function countTies(arr) {
3855	            let ties = {},
3856	                arrSorted = arr.slice().sort(),
3857	                currValue = arrSorted[0],
3858	                tieLength = 1;
3859
3860	            for (let i = 1; i < arrSorted.length; i++) {
3861	                if (arrSorted[i] === currValue) {
3862	                    tieLength++;
3863	                } else {
3864	                    if (tieLength > 1) {
3865	                        if (ties[tieLength] === undefined) ties[tieLength] = 0;
3866	                        ties[tieLength]++;
3867	                    }
3868	                    currValue = arrSorted[i];
3869	                    tieLength = 1;
3870	                }
3871	            }
3872	            if (tieLength > 1) {
3873	                if (ties[tieLength] === undefined) ties[tieLength] = 0;
3874	                ties[tieLength]++;
3875	            }
3876	            return ties;
3877	        }
3878	    }
3879	}
3880
3881	class Normalizer {
3882	    constructor(points) {
3883	        this.points = points.slice(0);
3884	        let [xArr, yArr] = [this.xArr, this.yArr] = underscore.unzip(this.points),
3885	            maxX = this.maxX = underscore.max(xArr),
3886	            minX = this.minX = underscore.min(xArr),
3887	            maxY = this.maxY = underscore.max(yArr),
3888	            minY = this.minY = underscore.min(yArr),
3889	            rangeX = this.rangeX = (maxX!=minX) ? maxX - minX: 1,
3890	            rangeY = this.rangeY = (maxY!=minY) ? maxY - minY: 1,
3891	            normalizedXArr = this.normalizedXArr = xArr.map(x => (x - minX) / rangeX),
3892	            normalizedYArr = this.normalizedYArr = yArr.map(y => (y - minY) / rangeY);
3893	        this.normalizedPoints = underscore.zip(normalizedXArr, normalizedYArr);
3894	        //Add one step to pass the data over if there is.
3895	        let length = this.points.length;
3896	        for (let i = 0; i < length; i++) {
3897	            this.normalizedPoints[i].data = this.points[i].data;
3898	        }
3899	    }
3900
3901	    /**
3902	     * Input a set of points in this scale range [0, 1] and will be scaled back to
3903	     * - Original scale ([minX, maxX], [minY, maxY])
3904	     * @param points
3905	     */
3906	    scaleBackPoints(points) {
3907	        return points.map(point=>{
3908	            return this.scaleBackPoint(point);
3909	        });
3910	    }
3911	    /**
3912	     * Input a single point in this scale range [0, 1] and will be scaled back to
3913	     * - Original scale ([minX, maxX], [minY, maxY])
3914	     * @param points
3915	     */
3916	    scaleBackPoint(point) {
3917	        let xs = point[0],
3918	            x = this.rangeX * xs + this.minX,
3919	            ys = point[1],
3920	            y = this.rangeY * ys + this.minY;
3921	        return [x, y];
3922	    }
3923	}
3924
3925	class LeaderBinner{
3926	    constructor(points, radius){
3927	        //TODO: Should check if there are more than 3 unique values here or even after the binning.
3928	        //TODO: May need to clone the points to avoid modifying it, but we don't do to reserve other data or to make the process faster
3929	        // //Clone these to avoid modifying them
3930	        // this.points = points.map(p=>p.slice(0));
3931	        this.points = points;
3932	        this.radius = radius;
3933	    }
3934	    get leaders(){
3935	        let self = this;
3936	        let theLeaders = [];
3937	        //find all the leaders
3938	        this.points.forEach(point=>{
3939	            let leader = closestLeader(theLeaders, point);
3940	            if(!leader){
3941	                let newLeader = [];
3942	                newLeader.x = point[0];
3943	                newLeader.y = point[1];
3944	                theLeaders.push(newLeader);
3945	            }
3946	        });
3947	        //now do this again to set the closest leader.
3948	        this.points.forEach(point=>{
3949	           let leader = closestLeader(theLeaders, point);
3950	           leader.push(point);
3951	        });
3952	        return theLeaders;
3953	        function closestLeader(leaders, point){
3954	            let length = leaders.length;
3955	            let minDistance = 2;//select 2 since normalized distance can't  be greater than 2.
3956	            let theLeader = null;
3957	            for (let i = 0; i < length; ++i) {
3958	                let l = leaders[i];
3959	                let d = distance([l.x, l.y], point);
3960	                if(d< self.radius){
3961	                    if(d<minDistance){
3962	                        minDistance = d;
3963	                        theLeader = l;
3964	                    }
3965	                }
3966	            }
3967	            return theLeader;
3968
3969	            // let distances = leaders.map(l=>distance([l.x, l.y], point));
3970	            // //Filter the distance to be <= the radius
3971	            // let copiedDistances = distances.filter(d=>d<self.radius);
3972	            // if(copiedDistances.length===0){
3973	            //     return null;
3974	            // }
3975	            // let theDistance = _.min(distances);
3976	            // let theLeader = leaders[distances.indexOf(theDistance)];
3977	            // return theLeader;
3978	        }
3979	    }
3980	}
3981
3982	//Inspired from https://github.com/d3/d3-hexbin/blob/master/src/hexbin.js
3983	function Binner(){
3984	    let thirdPi = Math.PI/3,
3985	    angles = [0, thirdPi, 2*thirdPi, 3*thirdPi, 4*thirdPi, 5*thirdPi];
3986
3987	    function pointX(d){
3988	        return d[0];
3989	    }
3990	    function pointY(d){
3991	        return d[1];
3992	    }
3993
3994	    var x0 = 0,
3995	        y0 = 0,
3996	        x1 = 1,
3997	        y1 = 1,
3998	        x = pointX,
3999	        y = pointY,
4000	        r,
4001	        dx,
4002	        dy;
4003
4004	    function hexbin(points){
4005	        var binsById = {}, bins = [], i, n = points.length;
4006
4007	        for (i = 0; i < n; ++i) {
4008	            if (isNaN(px = +x.call(null, point = points[i], i, points))
4009	                || isNaN(py = +y.call(null, point, i, points))) continue;
4010
4011	            var point,
4012	                px,
4013	                py,
4014	                pj = Math.round(py = py / dy),
4015	                pi = Math.round(px = px / dx - (pj & 1) / 2),
4016	                py1 = py - pj;
4017
4018	            if (Math.abs(py1) * 3 > 1) {
4019	                var px1 = px - pi,
4020	                    pi2 = pi + (px < pi ? -1 : 1) / 2,
4021	                    pj2 = pj + (py < pj ? -1 : 1),
4022	                    px2 = px - pi2,
4023	                    py2 = py - pj2;
4024	                if (px1 * px1 + py1 * py1 > px2 * px2 + py2 * py2) pi = pi2 + (pj & 1 ? 1 : -1) / 2, pj = pj2;
4025	            }
4026
4027	            var id = pi + "-" + pj, bin = binsById[id];
4028	            if (bin) bin.push(point);
4029	            else {
4030	                bins.push(bin = binsById[id] = [point]);
4031	                bin.x = (pi + (pj & 1) / 2) * dx;
4032	                bin.y = pj * dy;
4033	                //TODO: fix this since there are results with bin.x, bin.y > 1 => we may need to do the rounding.
4034	                // if(bin.x>1){bin.x = 1;}
4035	                // if(bin.y>1){bin.y =1;}
4036	            }
4037	        }
4038
4039	        return bins;
4040	    }
4041	    function hexagon(radius){
4042	        var x0 = 0, y0 = 0;
4043	        return angles.map(function(angle) {
4044	            var x1 = Math.sin(angle) * radius,
4045	                y1 = -Math.cos(angle) * radius,
4046	                dx = x1 - x0,
4047	                dy = y1 - y0;
4048	            x0 = x1, y0 = y1;
4049
4050	            return [dx, dy];
4051	        });
4052	    }
4053
4054	    //Export it
4055	    this.hexbin = hexbin;
4056	    this.hexagon = function(radius){
4057	        return "m" + hexagon(radius == null ? r : +radius).join("l") + "z";
4058	    };
4059	    this.centers = function(){
4060	        var centers = [],
4061	            j = Math.round(y0 / dy),
4062	            i = Math.round(x0 / dx);
4063	        for (var y = j * dy; y < y1 + r; y += dy, ++j) {
4064	            for (var x = i * dx + (j & 1) * dx / 2; x < x1 + dx / 2; x += dx) {
4065	                centers.push([x, y]);
4066	            }
4067	        }
4068	        return centers;
4069	    };
4070	    this.mesh = function(){
4071	        var fragment = hexagon(r).slice(0, 4).join("l");
4072	        return hexbin.centers().map(function(p) { return "M" + p + "m" + fragment; }).join("");
4073	    };
4074	    //Setters/getters
4075	    this.x = function(_){
4076	        return arguments.length ? (x = _, this) : x;
4077	    };
4078	    this.y = function(_){
4079	        return arguments.length ? (y = _, this) : y;
4080	    };
4081	    this.radius = function(_){
4082	        return arguments.length ? (r= +_, dx = r * 2 * Math.sin(thirdPi), dy = r*1.5, this): r;
4083	    };
4084	    this.size = function(_) {
4085	        return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], this) : [x1 - x0, y1 - y0];
4086	    };
4087	    this.extent = function(_){
4088	        return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], this): [[x0, y0], [x1, y1]];
4089	    };
4090
4091	    return this.radius(1);
4092	}
4093
4094	(function(window){
4095	    /**
4096	     * initialize a scagnostic object
4097	     * @param inputPoints   {*[][]} set of points from the scatter plot
4098	     * @returns {*[][]}
4099	     */
4100	    window.scagnostics = function(inputPoints, binType,startBinGridSize, isNormalized, isBinned, outlyingUpperBound, minBins, maxBins) {
4101	        //Clone it to avoid modifying it.
4102	        let points = inputPoints.slice(0);
4103	        let normalizedPoints = points;
4104	        /******This section is about normalizing the data******/
4105	        if(!isNormalized){
4106	            let normalizer = new Normalizer(points);
4107	            normalizedPoints = normalizer.normalizedPoints;
4108	        }
4109	        outputValue("normalizedPoints", normalizedPoints);
4110	        /******This section is about finding number of bins and binners******/
4111	        let sites = null;
4112	        let bins = null;
4113	        let binner = null;
4114	        let binSize = null;
4115	        let binRadius = 0;
4116
4117	        if(!isBinned){//Only do the binning if needed.
4118	            if(!startBinGridSize){
4119	                startBinGridSize = 40;
4120	            }
4121	            bins = [];
4122	            //Default number of bins
4123	            let minNumOfBins = 50;
4124	            let maxNumOfBins = 250;
4125	            if(minBins){
4126	                minNumOfBins =minBins;
4127	            }
4128	            if(maxBins){
4129	                maxNumOfBins = maxBins;
4130	            }
4131	            //Don't do the binning if the unique set of values are less than 50. Just return the unique set.
4132	            let uniqueKeys = underscore.uniq(normalizedPoints.map(p=>p.join(',')));
4133	            let groups = underscore.groupBy(normalizedPoints, p=>p.join(','));
4134	            if(uniqueKeys.length<minNumOfBins){
4135	                uniqueKeys.forEach(key=>{
4136	                    let bin = groups[key];
4137	                    //Take the coordinate of the first point in the group to be the bin leader (they should have the same points actually=> so just take the first one.
4138	                    bin.x = bin[0][0];
4139	                    bin.y = bin[0][1];
4140	                    bin.binRadius = 0;
4141	                    bins.push(bin);
4142	                });
4143	            }else{
4144	                do{
4145	                    //Start with 40x40 bins, and divided by 2 every time there are more than maxNumberofBins none empty cells, increase 5 (+5) if less than minNumberOfBins
4146	                    if(binSize===null){
4147	                        binSize = startBinGridSize;
4148	                    }else if(bins.length>maxNumOfBins){
4149	                        binSize = binSize/2;
4150	                    }else if(bins.length<minNumOfBins){
4151	                        binSize = binSize + 5;
4152	                    }
4153	                    if(binType==="hexagon"){
4154	                        // This section uses hexagon binning
4155	                        let shortDiagonal = 1/binSize;
4156	                        binRadius = Math.sqrt(3)*shortDiagonal/2;
4157	                        binner = new Binner().radius(binRadius).extent([[0, 0], [1, 1]]);//extent from [0, 0] to [1, 1] since we already normalized data.
4158	                        bins = binner.hexbin(normalizedPoints);
4159	                    }else if(!binType || binType==="leader"){
4160	                        // This section uses leader binner
4161	                        binRadius = 1/(binSize*2);
4162	                        binner = new LeaderBinner(normalizedPoints, binRadius);
4163	                        bins = binner.leaders;
4164	                    }
4165	                }while(bins.length > maxNumOfBins || bins.length < minNumOfBins);
4166	            }
4167	            sites = bins.map(d => [d.x, d.y]); //=>sites are the set of centers of all bins
4168	            /******This section is about the binning and binning results******/
4169	            outputValue("binner", binner);
4170	            outputValue("bins", bins);
4171	            outputValue("binSize", binSize);
4172	            outputValue("binRadius", binRadius);
4173	        }else{
4174	            sites = normalizedPoints;
4175	        }
4176
4177	        outputValue("binnedSites", sites);
4178
4179	        /******This section is about the triangulating and triangulating results******/
4180	        //Triangulation calculation
4181	        let delaunay = Delaunay.from(sites);
4182	        //TODO: There are many placed we need the triangleCoordinates function => we should build it as a prototype instead of copy/paste this function in many different places.
4183	        delaunay.points = sites;
4184	        let triangles = delaunay.triangles;
4185	        delaunay.triangleCoordinates = function(){
4186	            let triangles = this.triangles;
4187	            let tc = [];
4188	            for (let i = 0; i < triangles.length; i += 3) {
4189	                tc.push([
4190	                    this.points[triangles[i]],
4191	                    this.points[triangles[i + 1]],
4192	                    this.points[triangles[i + 2]]
4193	                ]);
4194	            }
4195	            return tc;
4196	        };
4197	        let triangleCoordinates = delaunay.triangleCoordinates();
4198	        //Assigning output values
4199	        outputValue("delaunay", delaunay);
4200	        outputValue("triangles", triangles);
4201	        outputValue("triangleCoordinates", triangleCoordinates);
4202
4203	        /******This section is about the spanning tree and spanning tree results******/
4204	        //Spanning tree calculation
4205	        let graph = createGraph(triangleCoordinates);
4206	        let mstree = mst(graph);
4207	        //Assigning the output values
4208	        outputValue("graph", graph);
4209	        outputValue("mst", mstree);
4210
4211	        /******This section is about the outlying score and outlying score results******/
4212	        let outlying = new Outlying(mstree);
4213	        let outlyingScore = outlying.score();
4214	            outlyingUpperBound = outlying.upperBound;
4215	        let outlyingLinks = outlying.links();
4216	        let outlyingPoints = outlying.points();
4217	        let noOutlyingTree = outlying.removeOutlying();
4218	        outputValue("outlyingScore", outlyingScore);
4219	        outputValue("outlyingUpperBound", outlyingUpperBound);
4220	        outputValue("outlyingLinks", outlyingLinks);
4221	        outputValue("outlyingPoints", outlyingPoints);
4222	        outputValue("noOutlyingTree", noOutlyingTree);
4223
4224	        /******This section is about the skewed score and skewed score results******/
4225	        let skewed = new Skewed(noOutlyingTree);
4226	        outputValue("skewedScore", skewed.score());
4227
4228	        /******This section is about the sparse score and sparse score results******/
4229	        let sparse = new Sparse(noOutlyingTree);
4230	        outputValue("sparseScore", sparse.score());
4231
4232	        /******This section is about the clumpy score and clumpy score results******/
4233	        let clumpy = new Clumpy(noOutlyingTree);
4234	        outputValue("clumpy", clumpy);
4235	        outputValue("clumpyScore", clumpy.score());
4236
4237	        /******This section is about the striated score and striated score results******/
4238	        let striated = new Striated(noOutlyingTree);
4239	        let v2Corners = striated.getAllV2Corners();
4240	        let obtuseV2Corners = striated.getAllObtuseV2Corners();
4241	        outputValue("striatedScore", striated.score());
4242	        outputValue("v2Corners", v2Corners);
4243	        outputValue("obtuseV2Corners", obtuseV2Corners);
4244
4245	        /******This section is about the convex hull and convex hull results******/
4246	        let convex = new Convex(noOutlyingTree);
4247	        let convexHull = convex.convexHull();
4248	        let noOutlyingTriangleCoordinates = convex.noOutlyingTriangleCoordinates();
4249	        outputValue("noOutlyingTriangleCoordinates", noOutlyingTriangleCoordinates);
4250	        outputValue("convexHull", convexHull);
4251
4252	        /******This section is about the concave hull and concave hull results******/
4253	        let concaveHull = convex.concaveHull();
4254	        outputValue("concaveHull", concaveHull);
4255
4256	        /******This section is about the convex score and convex score results******/
4257	        let convexScore = convex.score();
4258	        outputValue("convexScore", convexScore);
4259
4260	        /******This section is about the skinny score and skinny score results******/
4261	        let skinny = new Skinny(concaveHull);
4262	        let skinnyScore = skinny.score();
4263	        outputValue("skinnyScore", skinnyScore);
4264
4265	        /******This section is about the stringy score and stringy score results******/
4266	        let stringy = new Stringy(noOutlyingTree);
4267	        let v1s = stringy.getAllV1s();
4268	        let stringyScore = stringy.score();
4269	        outputValue("v1s", v1s);
4270	        outputValue("stringyScore", stringyScore);
4271
4272	        /******This section is about the monotonic score and monotonic score results******/
4273	        let monotonic = new Monotonic(noOutlyingTree.nodes.map(n=>n.id));
4274	        let monotonicScore = monotonic.score();
4275	        outputValue("monotonicScore", monotonicScore);
4276
4277	        return window.scagnostics;
4278	        function outputValue(name, value){
4279	            window.scagnostics[name] = value;
4280	        }
4281	    };
4282
4283	})(window);
4284
4285}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.