cluedin-widget
Version:
This is the project for creating and managing widgets in CluedIn.
398 lines (395 loc) • 12.8 kB
JavaScript
/*
* $Id: combinatorics.js,v 0.25 2013/03/11 15:42:14 dankogai Exp dankogai $
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* References:
* http://www.ruby-doc.org/core-2.0/Array.html#method-i-combination
* http://www.ruby-doc.org/core-2.0/Array.html#method-i-permutation
* http://en.wikipedia.org/wiki/Factorial_number_system
*/
(function( root, factory ) {
if ( typeof define === 'function' && define.amd ) {
define( [], factory );
} else if ( typeof exports === 'object' ) {
module.exports = factory();
} else {
root.Combinatorics = factory();
}
}( this, function() {
'use strict';
var version = "0.5.0";
/* combinatory arithmetics */
var P = function( m, n ) {
var t, p = 1;
if ( m < n ) {
t = m;
m = n;
n = t;
}
while( n-- ) p *= m--;
return p;
};
var C = function( m, n ) {
return P( m, n ) / P( n, n );
};
var factorial = function( n ) {
return P( n, n );
};
var factoradic = function( n, d ) {
var f = 1;
if ( !d ) {
for( d = 1; f < n; f *= ++d );
if ( f > n ) f /= d--;
} else {
f = factorial( d );
}
var result = [ 0 ];
for( ; d; f /= d-- ) {
result[ d ] = Math.floor( n / f );
n %= f;
}
return result;
};
/* common methods */
var addProperties = function( dst, src ) {
Object.keys( src ).forEach( function( p ) {
Object.defineProperty( dst, p, {
value: src[ p ]
} );
} );
};
var hideProperty = function( o, p ) {
Object.defineProperty( o, p, {
writable: true
} );
};
var toArray = function( f ) {
var e, result = [];
this.init();
while( e = this.next() ) result.push( f ? f( e ) : e );
this.init();
return result;
};
var common = {
toArray: toArray,
map: toArray,
forEach: function( f ) {
var e;
this.init();
while( e = this.next() ) f( e );
this.init();
},
filter: function( f ) {
var e, result = [];
this.init();
while( e = this.next() ) if ( f( e ) ) result.push( e );
this.init();
return result;
}
};
/* power set */
var power = function( ary, fun ) {
var size = 1 << ary.length,
sizeOf = function() {
return size;
},
that = Object.create( ary.slice(), {
length: {
get: sizeOf
}
} );
hideProperty( that, 'index' );
addProperties( that, {
valueOf: sizeOf,
init: function() {
that.index = 0;
},
nth: function( n ) {
if ( n >= size ) return;
var i = 0,
result = [];
for( ; n; n >>>= 1, i++ ) if ( n & 1 ) result.push( this[ i ] );
return result;
},
next: function() {
return this.nth( this.index++ );
}
} );
addProperties( that, common );
that.init();
return (typeof (fun) === 'function') ? that.map( fun ) : that;
};
/* combination */
var nextIndex = function( n ) {
var smallest = n & -n,
ripple = n + smallest,
new_smallest = ripple & -ripple,
ones = ((new_smallest / smallest) >> 1) - 1;
return ripple | ones;
};
var combination = function( ary, nelem, fun ) {
if ( !nelem ) nelem = ary.length;
if ( nelem < 1 ) throw new RangeError;
if ( nelem > ary.length ) throw new RangeError;
var first = (1 << nelem) - 1,
size = C( ary.length, nelem ),
maxIndex = 1 << ary.length,
sizeOf = function() {
return size;
},
that = Object.create( ary.slice(), {
length: {
get: sizeOf
}
} );
hideProperty( that, 'index' );
addProperties( that, {
valueOf: sizeOf,
init: function() {
this.index = first;
},
next: function() {
if ( this.index >= maxIndex ) return;
var i = 0,
n = this.index,
result = [];
for( ; n; n >>>= 1, i++ ) if ( n & 1 ) result.push( this[ i ] );
this.index = nextIndex( this.index );
return result;
}
} );
addProperties( that, common );
that.init();
return (typeof (fun) === 'function') ? that.map( fun ) : that;
};
/* permutation */
var _permutation = function( ary ) {
var that = ary.slice(),
size = factorial( that.length );
that.index = 0;
that.next = function() {
if ( this.index >= size ) return;
var copy = this.slice(),
digits = factoradic( this.index, this.length ),
result = [],
i = this.length - 1;
for( ; i >= 0; --i ) result.push( copy.splice( digits[ i ], 1 )[ 0 ] );
this.index++;
return result;
};
return that;
};
// which is really a permutation of combination
var permutation = function( ary, nelem, fun ) {
if ( !nelem ) nelem = ary.length;
if ( nelem < 1 ) throw new RangeError;
if ( nelem > ary.length ) throw new RangeError;
var size = P( ary.length, nelem ),
sizeOf = function() {
return size;
},
that = Object.create( ary.slice(), {
length: {
get: sizeOf
}
} );
hideProperty( that, 'cmb' );
hideProperty( that, 'per' );
addProperties( that, {
valueOf: function() {
return size;
},
init: function() {
this.cmb = combination( ary, nelem );
this.per = _permutation( this.cmb.next() );
},
next: function() {
var result = this.per.next();
if ( !result ) {
var cmb = this.cmb.next();
if ( !cmb ) return;
this.per = _permutation( cmb );
return this.next();
}
return result;
}
} );
addProperties( that, common );
that.init();
return (typeof (fun) === 'function') ? that.map( fun ) : that;
};
var PC = function( m ) {
var total = 0;
for( var n = 1; n <= m; n++ ) {
var p = P( m, n );
total += p;
}
;
return total;
};
// which is really a permutation of combination
var permutationCombination = function( ary, fun ) {
// if (!nelem) nelem = ary.length;
// if (nelem < 1) throw new RangeError;
// if (nelem > ary.length) throw new RangeError;
var size = PC( ary.length ),
sizeOf = function() {
return size;
},
that = Object.create( ary.slice(), {
length: {
get: sizeOf
}
} );
hideProperty( that, 'cmb' );
hideProperty( that, 'per' );
hideProperty( that, 'nelem' );
addProperties( that, {
valueOf: function() {
return size;
},
init: function() {
this.nelem = 1;
// console.log("Starting nelem: " + this.nelem);
this.cmb = combination( ary, this.nelem );
this.per = _permutation( this.cmb.next() );
},
next: function() {
var result = this.per.next();
if ( !result ) {
var cmb = this.cmb.next();
if ( !cmb ) {
this.nelem++;
// console.log("increment nelem: " + this.nelem + " vs " + ary.length);
if ( this.nelem > ary.length ) return;
this.cmb = combination( ary, this.nelem );
cmb = this.cmb.next();
if ( !cmb ) return;
}
this.per = _permutation( cmb );
return this.next();
}
return result;
}
} );
addProperties( that, common );
that.init();
return (typeof (fun) === 'function') ? that.map( fun ) : that;
};
/* Cartesian Product */
var arraySlice = Array.prototype.slice;
var cartesianProduct = function() {
if ( !arguments.length ) throw new RangeError;
var args = arraySlice.call( arguments ),
size = args.reduce( function( p, a ) {
return p * a.length;
}, 1 ),
sizeOf = function() {
return size;
},
dim = args.length,
that = Object.create( args, {
length: {
get: sizeOf
}
} );
if ( !size ) throw new RangeError;
hideProperty( that, 'index' );
addProperties( that, {
valueOf: sizeOf,
dim: dim,
init: function() {
this.index = 0;
},
get: function() {
if ( arguments.length !== this.length ) return;
var result = [],
d = 0;
for( ; d < dim; d++ ) {
var i = arguments[ d ];
if ( i >= this[ d ].length ) return;
result.push( this[ d ][ i ] );
}
return result;
},
nth: function( n ) {
var result = [],
d = 0;
for( ; d < dim; d++ ) {
var l = this[ d ].length;
var i = n % l;
result.push( this[ d ][ i ] );
n -= i;
n /= l;
}
return result;
},
next: function() {
if ( this.index >= size ) return;
var result = this.nth( this.index );
this.index++;
return result;
}
} );
addProperties( that, common );
that.init();
return that;
};
/* baseN */
var baseN = function( ary, nelem, fun ) {
if ( !nelem ) nelem = ary.length;
if ( nelem < 1 ) throw new RangeError;
var base = ary.length,
size = Math.pow( base, nelem );
var sizeOf = function() {
return size;
},
that = Object.create( ary.slice(), {
length: {
get: sizeOf
}
} );
hideProperty( that, 'index' );
addProperties( that, {
valueOf: sizeOf,
init: function() {
that.index = 0;
},
nth: function( n ) {
if ( n >= size ) return;
var result = [];
for( var i = 0; i < nelem; i++ ) {
var d = n % base;
result.push( ary[ d ] )
n -= d;
n /= base
}
return result;
},
next: function() {
return this.nth( this.index++ );
}
} );
addProperties( that, common );
that.init();
return (typeof (fun) === 'function') ? that.map( fun ) : that;
};
/* export */
var Combinatorics = Object.create( null );
addProperties( Combinatorics, {
C: C,
P: P,
factorial: factorial,
factoradic: factoradic,
cartesianProduct: cartesianProduct,
combination: combination,
permutation: permutation,
permutationCombination: permutationCombination,
power: power,
baseN: baseN,
VERSION: version
} );
return Combinatorics;
} ));