/// <summary>
        /// <para>
        /// Returns the least common multiple of the absolute value of two numbers,
        /// using the formula <c>lcm(a,b) = (a / gcd(a,b)) * b</c>.
        /// </para>
        /// Special cases:
        /// <list type="bullet">
        /// <item>The invocations <c>lcm(Int32.MinValue, n)</c> and
        /// <c>lcm(n, Int32.MinValue)</c>, where <c>abs(n)</c> is a
        /// power of 2, throw an <c>ArithmeticException</c>, because the result
        /// would be 2^31, which is too large for an int value.</item>
        /// <item>The result of <c>lcm(0, x)</c> and <c>lcm(x, 0)</c> is
        /// <c>0</c> for any <c>x</c>.</item>
        /// </list>
        /// </summary>
        /// <param name="a">Number.</param>
        /// <param name="b">Number.</param>
        /// <returns>the least common multiple, never negative.</returns>
        /// <exception cref="MathArithmeticException"> if the result cannot be represented as
        /// a non-negative <c>int</c> value.</exception>
        public static int lcm(int a, int b)
        {
            if (a == 0 || b == 0)
            {
                return(0);
            }
            int lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));

            if (lcm == Int32.MinValue)
            {
                throw new MathArithmeticException(new LocalizedFormats("LCM_OVERFLOW_32_BITS"), a, b);
            }
            return(lcm);
        }
        /// <summary>
        /// <para>
        /// Returns the least common multiple of the absolute value of two numbers,
        /// using the formula <c>lcm(a,b) = (a / gcd(a,b)) * b</c>.
        /// </para>
        /// Special cases:
        /// <list type="bullet">
        /// <item>The invocations <c>lcm(Int64.MinValue, n)</c> and
        /// <c>lcm(n, Int64.MinValue)</c>, where <c>abs(n)</c> is a
        /// power of 2, throw an <c>ArithmeticException</c>, because the result
        /// would be 2^63, which is too large for an int value.</item>
        /// <item>The result of <c>lcm(0L, x)</c> and <c>lcm(x, 0L)</c> is
        /// <c>0L</c> for any <c>x</c>.</item>
        /// </list>
        /// </summary>
        /// <param name="a">Number.</param>
        /// <param name="b">Number.</param>
        /// <returns>the least common multiple, never negative.</returns>
        /// <exception cref="MathArithmeticException"> if the result cannot be represented
        /// as a non-negative <c>long</c> value.</exception>
        public static long lcm(long a, long b)
        {
            if (a == 0 || b == 0)
            {
                return(0);
            }
            long lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b));

            if (lcm == Int64.MinValue)
            {
                throw new MathArithmeticException(new LocalizedFormats("LCM_OVERFLOW_64_BITS"), a, b);
            }
            return(lcm);
        }
        /// <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);
        }