Last active
August 29, 2015 14:13
-
-
Save mashingan/d317ea7dfc831bb138d0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* The call in console: | |
* $ node digitcombinatorial.js [maxnumber] [digitpattern] | |
* | |
* If maxnumber and digitpattern is not supplied in command line, it will | |
* fallback to the default 10000 and 14 respectively | |
*/ | |
// Initial parameter definition. Since it is parameter, it's expected won't | |
// be changed during runtime. | |
var assert = require('assert'); | |
var MAXNUM, | |
DIGITS; | |
if(process.argv[2]) { | |
MAXNUM = (parseInt(process.argv[2])-1).toString().length; | |
} else { | |
MAXNUM = (10000-1).toString().length; | |
} | |
if(process.argv[3]) { | |
DIGITS = process.argv[3].length; | |
} else { | |
DIGITS = '14'.length; | |
} | |
// Combinatorial mechanics which consist of factorial, combination, | |
// and case-specific combinatorial e.g. Cn and Ck. | |
// The use of function instead of object because of immutability input | |
// and always of return the result as using object requires immutable | |
// and restricts the generality functions cascading. | |
function factorial(n, start) { | |
start = start || 1; | |
var sum = 1; | |
if(n === 0) return 1; | |
for(var i=start+1; i<=n; i++) | |
sum *= i; | |
return sum; | |
} | |
function permutation(n, r, label) { | |
label = label || 'repeat'; | |
denom = (label === 'repeat') ? 1 : (n - r); | |
return (label === 'repeat') ? Math.pow(n, r) : factorial(n, r); | |
} | |
function combination(n, r, label) { | |
label = label || 'repeat'; | |
var num, denom; | |
if(label === 'repeat') { | |
num = r - 1; | |
denom = 1; | |
} else { | |
num = 0; | |
denom = r; | |
} | |
return factorial(n+num) / (factorial(r) * factorial(n-denom)); | |
} | |
function Cn(n, r) { | |
return combination(n, r) * Math.pow(10, r); | |
} | |
/* | |
* From recursively combination, we got | |
* result = Ck(n, r) = Cn(n, r) - (Ck(n+1, r-digits)) while r >= 0 | |
* where | |
* Cn = C(n, r) . 10^r; and | |
* digits = total length of pattern (e.g. 14 == 2, 115 == 3 etc) | |
* <==> | |
* if digits == 2 | |
* result = Cn(n, r) - (Cn(n+1, r-2) - (Cn(n+2, r-4) - (Cn(n+3, r-6) - ...))) | |
* <==> | |
* result = Cn(n, r) - Cn(n+1, r-2) + Cn(n+2, r-4) - Cn(n+3, r-6) + .... | |
* <==> | |
* result = Cn0(n, r-digits) - (-1)^(i-1) * Cni(n+1, r-(digits*i)) | |
*/ | |
function Ck(n, r){ | |
var sum = Cn(n, r); | |
for(var i=1, iter = r - DIGITS; iter >= 0; i++, iter -= DIGITS) { | |
sum -= (Math.pow(-1, i-1) * Cn(n+i, iter)); | |
} | |
return sum; | |
} | |
/* | |
* testmaker(Object ParamTest, failinput1, failinput2, ...) | |
* Function which return testing function template based on | |
* ParamTest object. | |
* This is simple test framework which specifically targeted for positive | |
* integer hence using assert.strictEqual. | |
* The inputs for supplying failing test callback are using variable | |
* arguments, (e.g. failinput1, failinput2 ....) | |
*/ | |
function testmaker(param) { | |
var failinputs = []; | |
for(var i=1; i<arguments.length; i++) | |
failinputs.push(arguments[i]); | |
if(param.values.length !== param.expected.length) { | |
throw new Error('Supplied values and expected are not same'); | |
} | |
return function() { | |
var success = 0; | |
var totaltest = param.values.length + failinputs.length; | |
function equaltest(value, expected) { | |
try { | |
assert.strictEqual(value, expected); | |
success++; | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
function failtest(inputs) { | |
var catching = false; | |
try { | |
if(inputs instanceof Array){ | |
assert.fail(param.callback.apply(inputs), 'error', | |
'Invalid input', param.operator); | |
} else { | |
assert.fail(param.callback.call(inputs), 'error', | |
'Invalid input', param.operator); | |
} | |
} catch (e) { | |
catching = !catching; | |
} finally { | |
if(catching) { | |
console.log('Catching success'); | |
success++; | |
} else { | |
console.log('Catching failed'); | |
} | |
} | |
} | |
for(var i=0; i<param.values.length; i++) { | |
var value = param.values[i]; | |
var expected = param.expected[i]; | |
equaltest(value, expected); | |
} | |
for(var i=0; i<failinputs.length; i++) { | |
var input = failinputs[i]; | |
failtest(input); | |
} | |
if(success === totaltest) { | |
console.log('All ' + param.label + ' tests success'); | |
} else { | |
console.error('Failed: ' + (totaltest-success) + ' from ' + | |
totaltest + ' tests'); | |
} | |
console.log('----------'); | |
}; | |
} | |
/* | |
* ParamTest object | |
* The datatype (or struct or record etc) for supplying the testmaker. | |
* Note that this object only acts as a data packs and not a mutable one | |
* hence the lack of methods in the definition. | |
*/ | |
function ParamTest(label, values, expected, callback) { | |
this.label = label; | |
this.values = values; | |
this.expected = expected; | |
this.callback = callback; | |
} | |
var tests = [ | |
testmaker(new ParamTest( | |
'factorial', | |
[factorial(5), factorial(0), factorial(5, 4), factorial(5, 3)], | |
[120, 1, 5, 20], | |
factorial), | |
'a', -1), | |
testmaker(new ParamTest( | |
'Ck', | |
[Ck(2, 0), Ck(2, 1), Ck(2, 2)], | |
[1, 20, 299], | |
Ck), | |
[2, -1], 2, ['a', 2]) | |
]; | |
for(var i=0; i<tests.length; i++){ | |
tests[i](); | |
} | |
var inputs = [10000, 10000000, 10000000000] | |
console.log('\n----------'); | |
console.log('Iteration from command line'); | |
console.log('Iteration: C(' +2+ ', ' +(MAXNUM-DIGITS) + ')'); | |
console.log(Ck(2, MAXNUM-DIGITS)); | |
console.log(); | |
console.log('Iteration with predetermined inputs'); | |
for(var i=0; i<inputs.length; i++) { | |
console.log('----------'); | |
var input = inputs[i]; | |
var supply = (input-1).toString().length - 2; | |
console.log('Input ' + input + ': ' + Ck(2, supply)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment