CombinatoricsUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.util;

  22. import java.lang.reflect.Array;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Spliterator;
  26. import java.util.Spliterators;
  27. import java.util.concurrent.atomic.AtomicReference;
  28. import java.util.stream.Stream;
  29. import java.util.stream.StreamSupport;

  30. import org.hipparchus.exception.LocalizedCoreFormats;
  31. import org.hipparchus.exception.MathIllegalArgumentException;
  32. import org.hipparchus.exception.MathRuntimeException;
  33. import org.hipparchus.special.Gamma;

  34. /**
  35.  * Combinatorial utilities.
  36.  */
  37. public final class CombinatoricsUtils {

  38.     /** Maximum index of Bell number that fits into a long.
  39.      * @since 2.2
  40.      */
  41.     public static final int MAX_BELL = 25;

  42.     /** All long-representable factorials */
  43.     static final long[] FACTORIALS = {
  44.                        1L,                  1L,                   2L,
  45.                        6L,                 24L,                 120L,
  46.                      720L,               5040L,               40320L,
  47.                   362880L,            3628800L,            39916800L,
  48.                479001600L,         6227020800L,         87178291200L,
  49.            1307674368000L,     20922789888000L,     355687428096000L,
  50.         6402373705728000L, 121645100408832000L, 2432902008176640000L };

  51.     /** Stirling numbers of the second kind. */
  52.     static final AtomicReference<long[][]> STIRLING_S2 = new AtomicReference<> (null);

  53.     /**
  54.      * Default implementation of {@link #factorialLog(int)} method:
  55.      * <ul>
  56.      *  <li>No pre-computation</li>
  57.      *  <li>No cache allocation</li>
  58.      * </ul>
  59.      */
  60.     private static final FactorialLog FACTORIAL_LOG_NO_CACHE = FactorialLog.create();

  61.     /** Bell numbers.
  62.      * @since 2.2
  63.      */
  64.     private static final AtomicReference<long[]> BELL = new AtomicReference<> (null);

  65.     /** Private constructor (class contains only static methods). */
  66.     private CombinatoricsUtils() {}


  67.     /**
  68.      * Returns an exact representation of the <a
  69.      * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
  70.      * Coefficient</a>, "{@code n choose k}", the number of
  71.      * {@code k}-element subsets that can be selected from an
  72.      * {@code n}-element set.
  73.      * <p><Strong>Preconditions</strong>:</p>
  74.      * <ul>
  75.      * <li> {@code 0 <= k <= n } (otherwise
  76.      * {@code MathIllegalArgumentException} is thrown)</li>
  77.      * <li> The result is small enough to fit into a {@code long}. The
  78.      * largest value of {@code n} for which all coefficients are
  79.      * {@code  < Long.MAX_VALUE} is 66. If the computed value exceeds
  80.      * {@code Long.MAX_VALUE} a {@code MathRuntimeException} is
  81.      * thrown.</li>
  82.      * </ul>
  83.      *
  84.      * @param n the size of the set
  85.      * @param k the size of the subsets to be counted
  86.      * @return {@code n choose k}
  87.      * @throws MathIllegalArgumentException if {@code n < 0}.
  88.      * @throws MathIllegalArgumentException if {@code k > n}.
  89.      * @throws MathRuntimeException if the result is too large to be
  90.      * represented by a long integer.
  91.      */
  92.     public static long binomialCoefficient(final int n, final int k)
  93.         throws MathIllegalArgumentException, MathRuntimeException {
  94.         checkBinomial(n, k);
  95.         if ((n == k) || (k == 0)) {
  96.             return 1;
  97.         }
  98.         if ((k == 1) || (k == n - 1)) {
  99.             return n;
  100.         }
  101.         // Use symmetry for large k
  102.         if (k > n / 2) {
  103.             return binomialCoefficient(n, n - k);
  104.         }

  105.         // We use the formula
  106.         // (n choose k) = n! / (n-k)! / k!
  107.         // (n choose k) == ((n-k+1)*...*n) / (1*...*k)
  108.         // which could be written
  109.         // (n choose k) == (n-1 choose k-1) * n / k
  110.         long result = 1;
  111.         if (n <= 61) {
  112.             // For n <= 61, the naive implementation cannot overflow.
  113.             int i = n - k + 1;
  114.             for (int j = 1; j <= k; j++) {
  115.                 result = result * i / j;
  116.                 i++;
  117.             }
  118.         } else if (n <= 66) {
  119.             // For n > 61 but n <= 66, the result cannot overflow,
  120.             // but we must take care not to overflow intermediate values.
  121.             int i = n - k + 1;
  122.             for (int j = 1; j <= k; j++) {
  123.                 // We know that (result * i) is divisible by j,
  124.                 // but (result * i) may overflow, so we split j:
  125.                 // Filter out the gcd, d, so j/d and i/d are integer.
  126.                 // result is divisible by (j/d) because (j/d)
  127.                 // is relative prime to (i/d) and is a divisor of
  128.                 // result * (i/d).
  129.                 final long d = ArithmeticUtils.gcd(i, j);
  130.                 result = (result / (j / d)) * (i / d);
  131.                 i++;
  132.             }
  133.         } else {
  134.             // For n > 66, a result overflow might occur, so we check
  135.             // the multiplication, taking care to not overflow
  136.             // unnecessary.
  137.             int i = n - k + 1;
  138.             for (int j = 1; j <= k; j++) {
  139.                 final long d = ArithmeticUtils.gcd(i, j);
  140.                 result = ArithmeticUtils.mulAndCheck(result / (j / d), i / d);
  141.                 i++;
  142.             }
  143.         }
  144.         return result;
  145.     }

  146.     /**
  147.      * Returns a {@code double} representation of the <a
  148.      * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
  149.      * Coefficient</a>, "{@code n choose k}", the number of
  150.      * {@code k}-element subsets that can be selected from an
  151.      * {@code n}-element set.
  152.      * <p>* <Strong>Preconditions</strong>:</p>
  153.      * <ul>
  154.      * <li> {@code 0 <= k <= n } (otherwise
  155.      * {@code IllegalArgumentException} is thrown)</li>
  156.      * <li> The result is small enough to fit into a {@code double}. The
  157.      * largest value of {@code n} for which all coefficients are &lt;
  158.      * Double.MAX_VALUE is 1029. If the computed value exceeds Double.MAX_VALUE,
  159.      * Double.POSITIVE_INFINITY is returned</li>
  160.      * </ul>
  161.      *
  162.      * @param n the size of the set
  163.      * @param k the size of the subsets to be counted
  164.      * @return {@code n choose k}
  165.      * @throws MathIllegalArgumentException if {@code n < 0}.
  166.      * @throws MathIllegalArgumentException if {@code k > n}.
  167.      * @throws MathRuntimeException if the result is too large to be
  168.      * represented by a long integer.
  169.      */
  170.     public static double binomialCoefficientDouble(final int n, final int k)
  171.         throws MathIllegalArgumentException, MathRuntimeException {
  172.         checkBinomial(n, k);
  173.         if ((n == k) || (k == 0)) {
  174.             return 1d;
  175.         }
  176.         if ((k == 1) || (k == n - 1)) {
  177.             return n;
  178.         }
  179.         if (k > n/2) {
  180.             return binomialCoefficientDouble(n, n - k);
  181.         }
  182.         if (n < 67) {
  183.             return binomialCoefficient(n,k);
  184.         }

  185.         double result = 1d;
  186.         for (int i = 1; i <= k; i++) {
  187.              result *= ((double) (n - k + i)) / i;
  188.         }

  189.         return FastMath.floor(result + 0.5);
  190.     }

  191.     /**
  192.      * Returns the natural {@code log} of the <a
  193.      * href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial
  194.      * Coefficient</a>, "{@code n choose k}", the number of
  195.      * {@code k}-element subsets that can be selected from an
  196.      * {@code n}-element set.
  197.      * <p>* <Strong>Preconditions</strong>:</p>
  198.      * <ul>
  199.      * <li> {@code 0 <= k <= n } (otherwise
  200.      * {@code MathIllegalArgumentException} is thrown)</li>
  201.      * </ul>
  202.      *
  203.      * @param n the size of the set
  204.      * @param k the size of the subsets to be counted
  205.      * @return {@code n choose k}
  206.      * @throws MathIllegalArgumentException if {@code n < 0}.
  207.      * @throws MathIllegalArgumentException if {@code k > n}.
  208.      * @throws MathRuntimeException if the result is too large to be
  209.      * represented by a long integer.
  210.      */
  211.     public static double binomialCoefficientLog(final int n, final int k)
  212.         throws MathIllegalArgumentException, MathRuntimeException {
  213.         checkBinomial(n, k);
  214.         if ((n == k) || (k == 0)) {
  215.             return 0;
  216.         }
  217.         if ((k == 1) || (k == n - 1)) {
  218.             return FastMath.log(n);
  219.         }

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

  227.         /*
  228.          * Return the log of binomialCoefficientDouble for values that will not
  229.          * overflow binomialCoefficientDouble
  230.          */
  231.         if (n < 1030) {
  232.             return FastMath.log(binomialCoefficientDouble(n, k));
  233.         }

  234.         if (k > n / 2) {
  235.             return binomialCoefficientLog(n, n - k);
  236.         }

  237.         /*
  238.          * Sum logs for values that could overflow
  239.          */
  240.         double logSum = 0;

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

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

  249.         return logSum;
  250.     }

  251.     /**
  252.      * Returns n!. Shorthand for {@code n} <a
  253.      * href="http://mathworld.wolfram.com/Factorial.html"> Factorial</a>, the
  254.      * product of the numbers {@code 1,...,n}.
  255.      * <p>* <Strong>Preconditions</strong>:</p>
  256.      * <ul>
  257.      * <li> {@code n >= 0} (otherwise
  258.      * {@code MathIllegalArgumentException} is thrown)</li>
  259.      * <li> The result is small enough to fit into a {@code long}. The
  260.      * largest value of {@code n} for which {@code n!} does not exceed
  261.      * Long.MAX_VALUE} is 20. If the computed value exceeds {@code Long.MAX_VALUE}
  262.      * an {@code MathRuntimeException } is thrown.</li>
  263.      * </ul>
  264.      *
  265.      * @param n argument
  266.      * @return {@code n!}
  267.      * @throws MathRuntimeException if the result is too large to be represented
  268.      * by a {@code long}.
  269.      * @throws MathIllegalArgumentException if {@code n < 0}.
  270.      * @throws MathIllegalArgumentException if {@code n > 20}: The factorial value is too
  271.      * large to fit in a {@code long}.
  272.      */
  273.     public static long factorial(final int n) throws MathIllegalArgumentException {
  274.         if (n < 0) {
  275.             throw new MathIllegalArgumentException(LocalizedCoreFormats.FACTORIAL_NEGATIVE_PARAMETER, n);
  276.         }
  277.         if (n > 20) {
  278.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE, n, 20);
  279.         }
  280.         return FACTORIALS[n];
  281.     }

  282.     /**
  283.      * Compute n!, the<a href="http://mathworld.wolfram.com/Factorial.html">
  284.      * factorial</a> of {@code n} (the product of the numbers 1 to n), as a
  285.      * {@code double}.
  286.      * The result should be small enough to fit into a {@code double}: The
  287.      * largest {@code n} for which {@code n!} does not exceed
  288.      * {@code Double.MAX_VALUE} is 170. If the computed value exceeds
  289.      * {@code Double.MAX_VALUE}, {@code Double.POSITIVE_INFINITY} is returned.
  290.      *
  291.      * @param n Argument.
  292.      * @return {@code n!}
  293.      * @throws MathIllegalArgumentException if {@code n < 0}.
  294.      */
  295.     public static double factorialDouble(final int n) throws MathIllegalArgumentException {
  296.         if (n < 0) {
  297.             throw new MathIllegalArgumentException(LocalizedCoreFormats.FACTORIAL_NEGATIVE_PARAMETER,
  298.                                            n);
  299.         }
  300.         if (n < 21) {
  301.             return FACTORIALS[n];
  302.         }
  303.         return FastMath.floor(FastMath.exp(factorialLog(n)) + 0.5);
  304.     }

  305.     /**
  306.      * Compute the natural logarithm of the factorial of {@code n}.
  307.      *
  308.      * @param n Argument.
  309.      * @return {@code log(n!)}
  310.      * @throws MathIllegalArgumentException if {@code n < 0}.
  311.      */
  312.     public static double factorialLog(final int n) throws MathIllegalArgumentException {
  313.         return FACTORIAL_LOG_NO_CACHE.value(n);
  314.     }

  315.     /**
  316.      * Returns the <a
  317.      * href="http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html">
  318.      * Stirling number of the second kind</a>, "{@code S(n,k)}", the number of
  319.      * ways of partitioning an {@code n}-element set into {@code k} non-empty
  320.      * subsets.
  321.      * <p>
  322.      * The preconditions are {@code 0 <= k <= n } (otherwise
  323.      * {@code MathIllegalArgumentException} is thrown)
  324.      * </p>
  325.      * @param n the size of the set
  326.      * @param k the number of non-empty subsets
  327.      * @return {@code S(n,k)}
  328.      * @throws MathIllegalArgumentException if {@code k < 0}.
  329.      * @throws MathIllegalArgumentException if {@code k > n}.
  330.      * @throws MathRuntimeException if some overflow happens, typically for n exceeding 25 and
  331.      * k between 20 and n-2 (S(n,n-1) is handled specifically and does not overflow)
  332.      */
  333.     public static long stirlingS2(final int n, final int k)
  334.         throws MathIllegalArgumentException, MathRuntimeException {
  335.         if (k < 0) {
  336.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, k, 0);
  337.         }
  338.         if (k > n) {
  339.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE,
  340.                                                    k, n);
  341.         }

  342.         long[][] stirlingS2 = STIRLING_S2.get();

  343.         if (stirlingS2 == null) {
  344.             // the cache has never been initialized, compute the first numbers
  345.             // by direct recurrence relation

  346.             // as S(26,9) = 11201516780955125625 is larger than Long.MAX_VALUE
  347.             // we must stop computation at row 26
  348.             final int maxIndex = 26;
  349.             stirlingS2 = new long[maxIndex][];
  350.             stirlingS2[0] = new long[] { 1l };
  351.             for (int i = 1; i < stirlingS2.length; ++i) {
  352.                 stirlingS2[i] = new long[i + 1];
  353.                 stirlingS2[i][0] = 0;
  354.                 stirlingS2[i][1] = 1;
  355.                 stirlingS2[i][i] = 1;
  356.                 for (int j = 2; j < i; ++j) {
  357.                     stirlingS2[i][j] = j * stirlingS2[i - 1][j] + stirlingS2[i - 1][j - 1];
  358.                 }
  359.             }

  360.             // atomically save the cache
  361.             STIRLING_S2.compareAndSet(null, stirlingS2);

  362.         }

  363.         if (n < stirlingS2.length) {
  364.             // the number is in the small cache
  365.             return stirlingS2[n][k];
  366.         } else {
  367.             // use explicit formula to compute the number without caching it
  368.             if (k == 0) {
  369.                 return 0;
  370.             } else if (k == 1 || k == n) {
  371.                 return 1;
  372.             } else if (k == 2) {
  373.                 return (1l << (n - 1)) - 1l;
  374.             } else if (k == n - 1) {
  375.                 return binomialCoefficient(n, 2);
  376.             } else {
  377.                 // definition formula: note that this may trigger some overflow
  378.                 long sum = 0;
  379.                 long sign = ((k & 0x1) == 0) ? 1 : -1;
  380.                 for (int j = 1; j <= k; ++j) {
  381.                     sign = -sign;
  382.                     sum += sign * binomialCoefficient(k, j) * ArithmeticUtils.pow(j, n);
  383.                     if (sum < 0) {
  384.                         // there was an overflow somewhere
  385.                         throw new MathRuntimeException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
  386.                                                           n, 0, stirlingS2.length - 1);
  387.                     }
  388.                 }
  389.                 return sum / factorial(k);
  390.             }
  391.         }

  392.     }

  393.     /**
  394.      * Returns an iterator whose range is the k-element subsets of {0, ..., n - 1}
  395.      * represented as {@code int[]} arrays.
  396.      * <p>
  397.      * The arrays returned by the iterator are sorted in descending order and
  398.      * they are visited in lexicographic order with significance from right to
  399.      * left. For example, combinationsIterator(4, 2) returns an Iterator that
  400.      * will generate the following sequence of arrays on successive calls to
  401.      * {@code next()}:</p><p>
  402.      * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]}
  403.      * </p><p>
  404.      * If {@code k == 0} an Iterator containing an empty array is returned and
  405.      * if {@code k == n} an Iterator containing [0, ..., n -1] is returned.</p>
  406.      *
  407.      * @param n Size of the set from which subsets are selected.
  408.      * @param k Size of the subsets to be enumerated.
  409.      * @return an {@link Iterator iterator} over the k-sets in n.
  410.      * @throws MathIllegalArgumentException if {@code n < 0}.
  411.      * @throws MathIllegalArgumentException if {@code k > n}.
  412.      */
  413.     public static Iterator<int[]> combinationsIterator(int n, int k) {
  414.         return new Combinations(n, k).iterator();
  415.     }

  416.     /**
  417.      * Check binomial preconditions.
  418.      *
  419.      * @param n Size of the set.
  420.      * @param k Size of the subsets to be counted.
  421.      * @throws MathIllegalArgumentException if {@code n < 0}.
  422.      * @throws MathIllegalArgumentException if {@code k > n}.
  423.      */
  424.     public static void checkBinomial(final int n,
  425.                                      final int k)
  426.         throws MathIllegalArgumentException {
  427.         if (n < k) {
  428.             throw new MathIllegalArgumentException(LocalizedCoreFormats.BINOMIAL_INVALID_PARAMETERS_ORDER,
  429.                                                 k, n, true);
  430.         }
  431.         if (n < 0) {
  432.             throw new MathIllegalArgumentException(LocalizedCoreFormats.BINOMIAL_NEGATIVE_PARAMETER, n);
  433.         }
  434.     }

  435.     /** Compute the Bell number (number of partitions of a set).
  436.      * @param n number of elements of the set
  437.      * @return Bell number Bₙ
  438.      * @since 2.2
  439.      */
  440.     public static long bellNumber(final int n) {

  441.         if (n < 0) {
  442.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, n, 0);
  443.         }
  444.         if (n > MAX_BELL) {
  445.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE, n, MAX_BELL);
  446.         }

  447.         long[] bell = BELL.get();

  448.         if (bell == null) {

  449.             // the cache has never been initialized, compute the numbers using the Bell triangle
  450.             // storage for one line of the Bell triangle
  451.             bell = new long[MAX_BELL];
  452.             bell[0] = 1l;

  453.             final long[] row = new long[bell.length];
  454.             for (int k = 1; k < row.length; ++k) {

  455.                 // first row, with one element
  456.                 row[0] = 1l;

  457.                 // iterative computation of rows
  458.                 for (int i = 1; i < k; ++i) {
  459.                     long previous = row[0];
  460.                     row[0] = row[i - 1];
  461.                     for (int j = 1; j <= i; ++j) {
  462.                         long rj = row[j - 1] + previous;
  463.                         previous = row[j];
  464.                         row[j] = rj;
  465.                     }
  466.                 }

  467.                 bell[k] = row[k - 1];

  468.             }

  469.             BELL.compareAndSet(null, bell);

  470.         }

  471.         return bell[n];

  472.     }

  473.     /** Generate a stream of partitions of a list.
  474.      * <p>
  475.      * This method implements the iterative algorithm described in
  476.      * <a href="https://academic.oup.com/comjnl/article/32/3/281/331557">Short Note:
  477.      * A Fast Iterative Algorithm for Generating Set Partitions</a>
  478.      * by B. Djokić, M. Miyakawa, S. Sekiguchi, I. Semba, and I. Stojmenović
  479.      * (The Computer Journal, Volume 32, Issue 3, 1989, Pages 281–282,
  480.      * <a href="https://doi.org/10.1093/comjnl/32.3.281">https://doi.org/10.1093/comjnl/32.3.281</a>
  481.      * </p>
  482.      * @param <T> type of the list elements
  483.      * @param list list to partition
  484.      * @return stream of partitions of the list, each partition is an array or parts
  485.      * and each part is a list of elements
  486.      * @since 2.2
  487.      */
  488.     public static <T> Stream<List<T>[]> partitions(final List<T> list) {

  489.         // handle special cases of empty and singleton lists
  490.         if (list.size() < 2) {
  491.             @SuppressWarnings("unchecked")
  492.             final List<T>[] partition = (List<T>[]) Array.newInstance(List.class, 1);
  493.             partition[0] = list;
  494.             final Stream.Builder<List<T>[]> builder = Stream.builder();
  495.             return builder.add(partition).build();
  496.         }

  497.         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new PartitionsIterator<>(list),
  498.                                                                         Spliterator.DISTINCT | Spliterator.NONNULL |
  499.                                                                         Spliterator.IMMUTABLE | Spliterator.ORDERED),
  500.                                     false);

  501.     }

  502.     /** Generate a stream of permutations of a list.
  503.      * <p>
  504.      * This method implements the Steinhaus–Johnson–Trotter algorithm
  505.      * with Even's speedup
  506.      * <a href="https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm">Steinhaus–Johnson–Trotter algorithm</a>
  507.      * @param <T> type of the list elements
  508.      * @param list list to permute
  509.      * @return stream of permutations of the list
  510.      * @since 2.2
  511.      */
  512.     public static <T> Stream<List<T>> permutations(final List<T> list) {

  513.         // handle special cases of empty and singleton lists
  514.         if (list.size() < 2) {
  515.             return Stream.of(list);
  516.         }

  517.         return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new PermutationsIterator<>(list),
  518.                                                                         Spliterator.DISTINCT | Spliterator.NONNULL |
  519.                                                                         Spliterator.IMMUTABLE | Spliterator.ORDERED),
  520.                                     false);

  521.     }

  522.     /**
  523.      * Class for computing the natural logarithm of the factorial of {@code n}.
  524.      * It allows to allocate a cache of precomputed values.
  525.      * In case of cache miss, computation is preformed by a call to
  526.      * {@link Gamma#logGamma(double)}.
  527.      */
  528.     public static final class FactorialLog {
  529.         /**
  530.          * Precomputed values of the function:
  531.          * {@code LOG_FACTORIALS[i] = log(i!)}.
  532.          */
  533.         private final double[] LOG_FACTORIALS;

  534.         /**
  535.          * Creates an instance, reusing the already computed values if available.
  536.          *
  537.          * @param numValues Number of values of the function to compute.
  538.          * @param cache Existing cache.
  539.          * @throw MathIllegalArgumentException if {@code n < 0}.
  540.          */
  541.         private FactorialLog(int numValues,
  542.                              double[] cache) {
  543.             if (numValues < 0) {
  544.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, numValues, 0);
  545.             }

  546.             LOG_FACTORIALS = new double[numValues];

  547.             final int beginCopy = 2;
  548.             final int endCopy = cache == null || cache.length <= beginCopy ?
  549.                 beginCopy : cache.length <= numValues ?
  550.                 cache.length : numValues;

  551.             // Copy available values.
  552.             for (int i = beginCopy; i < endCopy; i++) {
  553.                 LOG_FACTORIALS[i] = cache[i];
  554.             }

  555.             // Precompute.
  556.             for (int i = endCopy; i < numValues; i++) {
  557.                 LOG_FACTORIALS[i] = LOG_FACTORIALS[i - 1] + FastMath.log(i);
  558.             }
  559.         }

  560.         /**
  561.          * Creates an instance with no precomputed values.
  562.          * @return instance with no precomputed values
  563.          */
  564.         public static FactorialLog create() {
  565.             return new FactorialLog(0, null);
  566.         }

  567.         /**
  568.          * Creates an instance with the specified cache size.
  569.          *
  570.          * @param cacheSize Number of precomputed values of the function.
  571.          * @return a new instance where {@code cacheSize} values have been
  572.          * precomputed.
  573.          * @throws MathIllegalArgumentException if {@code n < 0}.
  574.          */
  575.         public FactorialLog withCache(final int cacheSize) {
  576.             return new FactorialLog(cacheSize, LOG_FACTORIALS);
  577.         }

  578.         /**
  579.          * Computes {@code log(n!)}.
  580.          *
  581.          * @param n Argument.
  582.          * @return {@code log(n!)}.
  583.          * @throws MathIllegalArgumentException if {@code n < 0}.
  584.          */
  585.         public double value(final int n) {
  586.             if (n < 0) {
  587.                 throw new MathIllegalArgumentException(LocalizedCoreFormats.FACTORIAL_NEGATIVE_PARAMETER,
  588.                                                n);
  589.             }

  590.             // Use cache of precomputed values.
  591.             if (n < LOG_FACTORIALS.length) {
  592.                 return LOG_FACTORIALS[n];
  593.             }

  594.             // Use cache of precomputed factorial values.
  595.             if (n < FACTORIALS.length) {
  596.                 return FastMath.log(FACTORIALS[n]);
  597.             }

  598.             // Delegate.
  599.             return Gamma.logGamma(n + 1);
  600.         }
  601.     }
  602. }