in core/src/main/scala/com/spotify/featran/transformers/PolynomialExpansion.scala [141:198]
def binomialCoefficient(n: Int, k: Int): Long = {
checkBinomial(n, k)
if (n == k || k == 0) {
1L
} else if (k == 1 || k == n - 1) {
n.toLong
} else if (k > n / 2) {
// Use symmetry for large k
binomialCoefficient(n, n - k)
} else {
// We use the formula
// (n choose k) = n! / (n-k)! / k!
// (n choose k) == ((n-k+1)*...*n) / (1*...*k)
// which could be written
// (n choose k) == (n-1 choose k-1) * n / k
var result = 1L
if (n <= 61) {
// For n <= 61, the naive implementation cannot overflow.
var i = n - k + 1
var j = 1
while (j <= k) {
result = result * i / j
i += 1
j += 1
}
} else if (n <= 66) {
// For n > 61 but n <= 66, the result cannot overflow,
// but we must take care not to overflow intermediate values.
var i = n - k + 1
var j = 1
while (j <= k) {
// We know that (result * i) is divisible by j,
// but (result * i) may overflow, so we split j:
// Filter out the gcd, d, so j/d and i/d are integer.
// result is divisible by (j/d) because (j/d)
// is relative prime to (i/d) and is a divisor of
// result * (i/d).
val d = gcd(i, j)
result = (result / (j / d)) * (i / d)
i += 1
j += 1
}
} else {
// For n > 66, a result overflow might occur, so we check
// the multiplication, taking care to not overflow
// unnecessary.
var i = n - k + 1
var j = 1
while (j <= k) {
val d = gcd(i, j)
result = mulAndCheck(result / (j / d), (i / d).toLong)
i += 1
j += 1
}
}
result
}
}