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);
 | |
| };
 |