/// <summary>
 /// Creates an instance whose range is the k-element subsets of
 /// {0, ..., n - 1} represented as <c>int[]</c> arrays.
 /// <para>
 /// If the <c>iterationOrder</c> argument is set to
 /// <see cref="IterationOrder.LEXICOGRAPHIC"/>, the arrays returned by the
 /// <see cref="iterator()"/> are sorted in descending order and
 /// they are visited in lexicographic order with significance from
 /// right to left.
 /// For example, <c>new Combinations(4, 2).iterator()</c> returns
 /// an iterator that will generate the following sequence of arrays
 /// on successive calls to
 /// <c>next()</c>:<para/>
 /// <c>[0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]</c>
 /// </para>
 /// If <c>k == 0</c> an iterator containing an empty array is returned;
 /// if <c>k == n</c> an iterator containing [0, ..., n - 1] is returned.
 ///
 /// </summary>
 /// <param name="n">Size of the set from which subsets are selected.</param>
 /// <param name="k">Size of the subsets to be enumerated.></param>
 /// <param name="iterationOrder">Specifies the <see cref="iterator()"/>.</param>
 /// <exception cref="NotPositiveException"> if <c>n < 0</c>.</exception>
 /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception>
 private Combinations(int n, int k, IterationOrder iterationOrder)
 {
     CombinatoricsUtils.checkBinomial(n, k);
     this.n = n;
     this.k = k;
     this.iterationOrder = iterationOrder;
 }
        /// <summary>
        /// Returns a <c>double</c> representation of the <a
        /// href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
        /// Coefficient</a>, "<c>n choose k</c>", the number of
        /// <c>k</c>-element subsets that can be selected from an
        /// <c>n</c>-element set.
        /// <para>
        /// Preconditions:
        /// <list type="bullet">
        /// <item> <c>0 <= k <= n</c> (otherwise
        /// <c>IllegalArgumentException</c> is thrown)</item>
        /// <item> The result is small enough to fit into a <c>double</c>. The
        /// largest value of <c>n</c> for which all coefficients are <
        /// Double.MaxValue is 1029. If the computed value exceeds Double.MaxValue,
        /// Double.PositiveInvinifty is returned</item>
        /// </list></para>
        /// </summary>
        /// <param name="n">the size of the set</param>
        /// <param name="k">the size of the subsets to be counted</param>
        /// <returns><c>n choose k</c></returns>
        /// <exception cref="NotPositiveException"> if <c>n < 0</c>.</exception>
        /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception>
        /// <exception cref="MathArithmeticException"> if the result is too large to be
        /// represented by a double.</exception>
        public static double binomialCoefficientDouble(int n, int k)
        {
            CombinatoricsUtils.checkBinomial(n, k);
            if ((n == k) || (k == 0))
            {
                return(1d);
            }
            if ((k == 1) || (k == n - 1))
            {
                return(n);
            }
            if (k > n / 2)
            {
                return(binomialCoefficientDouble(n, n - k));
            }
            if (n < 67)
            {
                return(binomialCoefficient(n, k));
            }

            double result = 1d;

            for (int i = 1; i <= k; i++)
            {
                result *= (double)(n - k + i) / (double)i;
            }

            return(FastMath.floor(result + 0.5));
        }
        /// <summary>
        /// Returns the natural <c>log</c> of the <a
        /// href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
        /// Coefficient</a>, "<c>n choose k</c>", the number of
        /// <c>k</c>-element subsets that can be selected from an
        /// <c>n</c>-element set.
        /// <para>
        /// Preconditions:
        /// <list type="bullet">
        /// <item> <c>0 <= k <= n</c> (otherwise
        /// <c>IllegalArgumentException</c> is thrown)</item>
        /// </list></para>
        /// </summary>
        /// <param name="n">the size of the set</param>
        /// <param name="k">the size of the subsets to be counted</param>
        /// <returns><c>n choose k</c></returns>
        /// <exception cref="NotPositiveException"> if <c>n < 0</c>.</exception>
        /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception>
        /// <exception cref="MathArithmeticException"> if the result is too large to be
        /// represented by a double.</exception>
        public static double binomialCoefficientLog(int n, int k)
        {
            CombinatoricsUtils.checkBinomial(n, k);
            if ((n == k) || (k == 0))
            {
                return(0);
            }
            if ((k == 1) || (k == n - 1))
            {
                return(FastMath.log(n));
            }

            /*
             * For values small enough to do exact integer computation,
             * return the log of the exact value
             */
            if (n < 67)
            {
                return(FastMath.log(binomialCoefficient(n, k)));
            }

            /*
             * Return the log of binomialCoefficientDouble for values that will not
             * overflow binomialCoefficientDouble
             */
            if (n < 1030)
            {
                return(FastMath.log(binomialCoefficientDouble(n, k)));
            }

            if (k > n / 2)
            {
                return(binomialCoefficientLog(n, n - k));
            }

            /*
             * Sum logs for values that could overflow
             */
            double logSum = 0;

            // n!/(n-k)!
            for (int i = n - k + 1; i <= n; i++)
            {
                logSum += FastMath.log(i);
            }

            // divide by k!
            for (int i = 2; i <= k; i++)
            {
                logSum -= FastMath.log(i);
            }

            return(logSum);
        }
        /// <summary>
        /// Returns an exact representation of the <a
        /// href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
        /// Coefficient</a>, "<c>n choose k</c>", the number of
        /// <c>k</c>-element subsets that can be selected from an
        /// <c>n</c>-element set.
        /// <para>
        /// Preconditions:
        /// <list type="bullet">
        /// <item> <c>0 <= k <= n</c> (otherwise
        /// <c>MathIllegalArgumentException</c> is thrown)</item>
        /// <item> The result is small enough to fit into a <c>long</c>. The
        /// largest value of <c>n</c> for which all coefficients are
        /// <c> < Int64.MaxValue</c> is 66. If the computed value exceeds
        /// <c>Int64.MaxValue</c> an <c>ArithMeticException</c> is
        /// thrown.</item>
        /// </list></para>
        /// </summary>
        /// <param name="n">the size of the set</param>
        /// <param name="k">the size of the subsets to be counted</param>
        /// <returns><c>n choose k</c></returns>
        /// <exception cref="NotPositiveException"> if <c>n < 0</c>.</exception>
        /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception>
        /// <exception cref="MathArithmeticException"> if the result is too large to be
        /// represented by a long integer.</exception>
        public static long binomialCoefficient(int n, int k)
        {
            CombinatoricsUtils.checkBinomial(n, k);
            if ((n == k) || (k == 0))
            {
                return(1);
            }
            if ((k == 1) || (k == n - 1))
            {
                return(n);
            }
            // Use symmetry for large k
            if (k > n / 2)
            {
                return(binomialCoefficient(n, n - k));
            }

            // 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
            long result = 1;

            if (n <= 61)
            {
                // For n <= 61, the naive implementation cannot overflow.
                int i = n - k + 1;
                for (int j = 1; j <= k; j++)
                {
                    result = result * i / j;
                    i++;
                }
            }
            else if (n <= 66)
            {
                // For n > 61 but n <= 66, the result cannot overflow,
                // but we must take care not to overflow intermediate values.
                int i = n - k + 1;
                for (int j = 1; j <= k; j++)
                {
                    // 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).
                    long d = ArithmeticUtils.gcd(i, j);
                    result = (result / (j / d)) * (i / d);
                    i++;
                }
            }
            else
            {
                // For n > 66, a result overflow might occur, so we check
                // the multiplication, taking care to not overflow
                // unnecessary.
                int i = n - k + 1;
                for (int j = 1; j <= k; j++)
                {
                    long d = ArithmeticUtils.gcd(i, j);
                    result = ArithmeticUtils.mulAndCheck(result / (j / d), i / d);
                    i++;
                }
            }
            return(result);
        }