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
			| 
											3 years ago
										 | /** | ||
|  |  * 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); | ||
|  | }; |