39 lines
1.2 KiB
JavaScript
39 lines
1.2 KiB
JavaScript
import { isInteger } from '../../utils/number.js';
|
|
import { product } from '../../utils/product.js';
|
|
export function combinationsNumber(n, k) {
|
|
if (!isInteger(n) || n < 0) {
|
|
throw new TypeError('Positive integer value expected in function combinations');
|
|
}
|
|
|
|
if (!isInteger(k) || k < 0) {
|
|
throw new TypeError('Positive integer value expected in function combinations');
|
|
}
|
|
|
|
if (k > n) {
|
|
throw new TypeError('k must be less than or equal to n');
|
|
}
|
|
|
|
var nMinusk = n - k;
|
|
var answer = 1;
|
|
var firstnumerator = k < nMinusk ? nMinusk + 1 : k + 1;
|
|
var nextdivisor = 2;
|
|
var lastdivisor = k < nMinusk ? k : nMinusk; // balance multiplications and divisions to try to keep intermediate values
|
|
// in exact-integer range as long as possible
|
|
|
|
for (var nextnumerator = firstnumerator; nextnumerator <= n; ++nextnumerator) {
|
|
answer *= nextnumerator;
|
|
|
|
while (nextdivisor <= lastdivisor && answer % nextdivisor === 0) {
|
|
answer /= nextdivisor;
|
|
++nextdivisor;
|
|
}
|
|
} // for big n, k, floating point may have caused weirdness in remainder
|
|
|
|
|
|
if (nextdivisor <= lastdivisor) {
|
|
answer /= product(nextdivisor, lastdivisor);
|
|
}
|
|
|
|
return answer;
|
|
}
|
|
combinationsNumber.signature = 'number, number'; |