You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							357 lines
						
					
					
						
							6.6 KiB
						
					
					
				
			
		
		
	
	
							357 lines
						
					
					
						
							6.6 KiB
						
					
					
				/**
 | 
						|
 * Mnemonist Set
 | 
						|
 * ==============
 | 
						|
 *
 | 
						|
 * Useful function related to sets such as union, intersection and so on...
 | 
						|
 */
 | 
						|
 | 
						|
// TODO: optimize versions for less variadicities
 | 
						|
 | 
						|
/**
 | 
						|
 * Variadic function computing the intersection of multiple sets.
 | 
						|
 *
 | 
						|
 * @param  {...Set} sets - Sets to intersect.
 | 
						|
 * @return {Set}         - The intesection.
 | 
						|
 */
 | 
						|
exports.intersection = function() {
 | 
						|
  if (arguments.length < 2)
 | 
						|
    throw new Error('mnemonist/Set.intersection: needs at least two arguments.');
 | 
						|
 | 
						|
  var I = new Set();
 | 
						|
 | 
						|
  // First we need to find the smallest set
 | 
						|
  var smallestSize = Infinity,
 | 
						|
      smallestSet = null;
 | 
						|
 | 
						|
  var s, i, l = arguments.length;
 | 
						|
 | 
						|
  for (i = 0; i < l; i++) {
 | 
						|
    s = arguments[i];
 | 
						|
 | 
						|
    // If one of the set has no items, we can stop right there
 | 
						|
    if (s.size === 0)
 | 
						|
      return I;
 | 
						|
 | 
						|
    if (s.size < smallestSize) {
 | 
						|
      smallestSize = s.size;
 | 
						|
      smallestSet = s;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Now we need to intersect this set with the others
 | 
						|
  var iterator = smallestSet.values(),
 | 
						|
      step,
 | 
						|
      item,
 | 
						|
      add,
 | 
						|
      set;
 | 
						|
 | 
						|
  // TODO: we can optimize by iterating each next time over the current intersection
 | 
						|
  // but this probably means more RAM to consume since we'll create n-1 sets rather than
 | 
						|
  // only the one.
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    item = step.value;
 | 
						|
    add = true;
 | 
						|
 | 
						|
    for (i = 0; i < l; i++) {
 | 
						|
      set = arguments[i];
 | 
						|
 | 
						|
      if (set === smallestSet)
 | 
						|
        continue;
 | 
						|
 | 
						|
      if (!set.has(item)) {
 | 
						|
        add = false;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (add)
 | 
						|
      I.add(item);
 | 
						|
  }
 | 
						|
 | 
						|
  return I;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Variadic function computing the union of multiple sets.
 | 
						|
 *
 | 
						|
 * @param  {...Set} sets - Sets to unite.
 | 
						|
 * @return {Set}         - The union.
 | 
						|
 */
 | 
						|
exports.union = function() {
 | 
						|
  if (arguments.length < 2)
 | 
						|
    throw new Error('mnemonist/Set.union: needs at least two arguments.');
 | 
						|
 | 
						|
  var U = new Set();
 | 
						|
 | 
						|
  var i, l = arguments.length;
 | 
						|
 | 
						|
  var iterator,
 | 
						|
      step;
 | 
						|
 | 
						|
  for (i = 0; i < l; i++) {
 | 
						|
    iterator = arguments[i].values();
 | 
						|
 | 
						|
    while ((step = iterator.next(), !step.done))
 | 
						|
      U.add(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  return U;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function computing the difference between two sets.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {Set}   - The difference.
 | 
						|
 */
 | 
						|
exports.difference = function(A, B) {
 | 
						|
 | 
						|
  // If first set is empty
 | 
						|
  if (!A.size)
 | 
						|
    return new Set();
 | 
						|
 | 
						|
  if (!B.size)
 | 
						|
    return new Set(A);
 | 
						|
 | 
						|
  var D = new Set();
 | 
						|
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!B.has(step.value))
 | 
						|
      D.add(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  return D;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function computing the symmetric difference between two sets.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {Set}   - The symmetric difference.
 | 
						|
 */
 | 
						|
exports.symmetricDifference = function(A, B) {
 | 
						|
  var S = new Set();
 | 
						|
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!B.has(step.value))
 | 
						|
      S.add(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  iterator = B.values();
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!A.has(step.value))
 | 
						|
      S.add(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  return S;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning whether A is a subset of B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {boolean}
 | 
						|
 */
 | 
						|
exports.isSubset = function(A, B) {
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  // Shortcuts
 | 
						|
  if (A === B)
 | 
						|
    return true;
 | 
						|
 | 
						|
  if (A.size > B.size)
 | 
						|
    return false;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!B.has(step.value))
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning whether A is a superset of B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {boolean}
 | 
						|
 */
 | 
						|
exports.isSuperset = function(A, B) {
 | 
						|
  return exports.isSubset(B, A);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function adding the items of set B to the set A.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 */
 | 
						|
exports.add = function(A, B) {
 | 
						|
  var iterator = B.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done))
 | 
						|
    A.add(step.value);
 | 
						|
 | 
						|
  return;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function subtracting the items of set B from the set A.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 */
 | 
						|
exports.subtract = function(A, B) {
 | 
						|
  var iterator = B.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done))
 | 
						|
    A.delete(step.value);
 | 
						|
 | 
						|
  return;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function intersecting the items of A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 */
 | 
						|
exports.intersect = function(A, B) {
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!B.has(step.value))
 | 
						|
      A.delete(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  return;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function disjuncting the items of A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 */
 | 
						|
exports.disjunct = function(A, B) {
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  var toRemove = [];
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (B.has(step.value))
 | 
						|
      toRemove.push(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  iterator = B.values();
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (!A.has(step.value))
 | 
						|
      A.add(step.value);
 | 
						|
  }
 | 
						|
 | 
						|
  for (var i = 0, l = toRemove.length; i < l; i++)
 | 
						|
    A.delete(toRemove[i]);
 | 
						|
 | 
						|
  return;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning the size of the intersection of A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {number}
 | 
						|
 */
 | 
						|
exports.intersectionSize = function(A, B) {
 | 
						|
  var tmp;
 | 
						|
 | 
						|
  // We need to know the smallest set
 | 
						|
  if (A.size > B.size) {
 | 
						|
    tmp = A;
 | 
						|
    A = B;
 | 
						|
    B = tmp;
 | 
						|
  }
 | 
						|
 | 
						|
  if (A.size === 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if (A === B)
 | 
						|
    return A.size;
 | 
						|
 | 
						|
  var iterator = A.values(),
 | 
						|
      step;
 | 
						|
 | 
						|
  var I = 0;
 | 
						|
 | 
						|
  while ((step = iterator.next(), !step.done)) {
 | 
						|
    if (B.has(step.value))
 | 
						|
      I++;
 | 
						|
  }
 | 
						|
 | 
						|
  return I;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning the size of the union of A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {number}
 | 
						|
 */
 | 
						|
exports.unionSize = function(A, B) {
 | 
						|
  var I = exports.intersectionSize(A, B);
 | 
						|
 | 
						|
  return A.size + B.size - I;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning the Jaccard similarity between A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {number}
 | 
						|
 */
 | 
						|
exports.jaccard = function(A, B) {
 | 
						|
  var I = exports.intersectionSize(A, B);
 | 
						|
 | 
						|
  if (I === 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  var U = A.size + B.size - I;
 | 
						|
 | 
						|
  return I / U;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Function returning the overlap coefficient between A & B.
 | 
						|
 *
 | 
						|
 * @param  {Set} A - First set.
 | 
						|
 * @param  {Set} B - Second set.
 | 
						|
 * @return {number}
 | 
						|
 */
 | 
						|
exports.overlap = function(A, B) {
 | 
						|
  var I = exports.intersectionSize(A, B);
 | 
						|
 | 
						|
  if (I === 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  return I / Math.min(A.size, B.size);
 | 
						|
};
 |