001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.numbers.combinatorics;
019
020import org.apache.commons.numbers.core.ArithmeticUtils;
021
022/**
023 * Representation of the <a href="http://mathworld.wolfram.com/BinomialCoefficient.html">
024 * binomial coefficient</a>.
025 * It is "{@code n choose k}", the number of {@code k}-element subsets that
026 * can be selected from an {@code n}-element set.
027 */
028public final class BinomialCoefficient {
029    /** The maximum m that can be computed without overflow of a long.
030     * C(68, 34) > 2^63. */
031    private static final int MAX_M = 33;
032    /** The maximum n that can be computed without intermediate overflow for any m.
033     * C(61, 30) * 30 < 2^63. */
034    private static final int SMALL_N = 61;
035    /** The maximum n that can be computed without overflow of a long for any m.
036     * C(66, 33) < 2^63. */
037    private static final int LIMIT_N = 66;
038
039    /** Private constructor. */
040    private BinomialCoefficient() {
041        // intentionally empty.
042    }
043
044    /**
045     * Computes the binomial coefficient.
046     *
047     * <p>The largest value of {@code n} for which <em>all</em> coefficients can
048     * fit into a {@code long} is 66. Larger {@code n} may result in an
049     * {@link ArithmeticException} depending on the value of {@code k}.
050     *
051     * <p>Any {@code min(k, n - k) >= 34} cannot fit into a {@code long}
052     * and will result in an {@link ArithmeticException}.
053     *
054     * @param n Size of the set.
055     * @param k Size of the subsets to be counted.
056     * @return {@code n choose k}.
057     * @throws IllegalArgumentException if {@code n < 0}, {@code k < 0} or {@code k > n}.
058     * @throws ArithmeticException if the result is too large to be
059     * represented by a {@code long}.
060     */
061    public static long value(int n, int k) {
062        final int m = checkBinomial(n, k);
063
064        if (m == 0) {
065            return 1;
066        }
067        if (m == 1) {
068            return n;
069        }
070
071        // We use the formulae:
072        // (n choose m) = n! / (n-m)! / m!
073        // (n choose m) = ((n-m+1)*...*n) / (1*...*m)
074        // which can be written
075        // (n choose m) = (n-1 choose m-1) * n / m
076        long result = 1;
077        if (n <= SMALL_N) {
078            // For n <= 61, the naive implementation cannot overflow.
079            int i = n - m + 1;
080            for (int j = 1; j <= m; j++) {
081                result = result * i / j;
082                i++;
083            }
084        } else if (n <= LIMIT_N) {
085            // For n > 61 but n <= 66, the result cannot overflow,
086            // but we must take care not to overflow intermediate values.
087            int i = n - m + 1;
088            for (int j = 1; j <= m; j++) {
089                // We know that (result * i) is divisible by j,
090                // but (result * i) may overflow, so we split j:
091                // Filter out the gcd, d, so j/d and i/d are integer.
092                // result is divisible by (j/d) because (j/d)
093                // is relative prime to (i/d) and is a divisor of
094                // result * (i/d).
095                final long d = ArithmeticUtils.gcd(i, j);
096                result = (result / (j / d)) * (i / d);
097                ++i;
098            }
099        } else {
100            if (m > MAX_M) {
101                throw new ArithmeticException(n + " choose " + k);
102            }
103
104            // For n > 66, a result overflow might occur, so we check
105            // the multiplication, taking care to not overflow
106            // unnecessary.
107            int i = n - m + 1;
108            for (int j = 1; j <= m; j++) {
109                final long d = ArithmeticUtils.gcd(i, j);
110                result = Math.multiplyExact(result / (j / d), i / d);
111                ++i;
112            }
113        }
114
115        return result;
116    }
117
118    /**
119     * Check binomial preconditions.
120     *
121     * <p>For convenience in implementations this returns the smaller of
122     * {@code k} or {@code n - k} allowing symmetry to be exploited in
123     * computing the binomial coefficient.
124     *
125     * @param n Size of the set.
126     * @param k Size of the subsets to be counted.
127     * @return min(k, n - k)
128     * @throws IllegalArgumentException if {@code n < 0}.
129     * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}.
130     */
131    static int checkBinomial(int n,
132                             int k) {
133        // Combine all checks with a single branch:
134        // 0 <= n; 0 <= k <= n
135        // Note: If n >= 0 && k >= 0 && n - k < 0 then k > n.
136        final int m = n - k;
137        // Bitwise or will detect a negative sign bit in any of the numbers
138        if ((n | k | m) < 0) {
139            // Raise the correct exception
140            if (n < 0) {
141                throw new CombinatoricsException(CombinatoricsException.NEGATIVE, n);
142            }
143            throw new CombinatoricsException(CombinatoricsException.OUT_OF_RANGE, k, 0, n);
144        }
145        return m < k ? m : k;
146    }
147}