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 * http://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 package org.apache.commons.rng.sampling.distribution; 19 20 /** 21 * Functions used by some of the samplers. 22 * This class is not part of the public API, as it would be 23 * better to group these utilities in a dedicated components. 24 */ 25 class InternalUtils { // Class is package-private on purpose; do not make it public. 26 /** All long-representable factorials. */ 27 private static final long[] FACTORIALS = new long[] { 28 1L, 1L, 2L, 29 6L, 24L, 120L, 30 720L, 5040L, 40320L, 31 362880L, 3628800L, 39916800L, 32 479001600L, 6227020800L, 87178291200L, 33 1307674368000L, 20922789888000L, 355687428096000L, 34 6402373705728000L, 121645100408832000L, 2432902008176640000L }; 35 36 /** Utility class. */ 37 private InternalUtils() {} 38 39 /** 40 * @param n Argument. 41 * @return {@code n!} 42 * @throws IllegalArgumentException if the result is too large to be represented 43 * by a {@code long} (i.e. if {@code n > 20}). 44 */ 45 public static long factorial(int n) { 46 return FACTORIALS[n]; 47 } 48 49 /** 50 * Class for computing the natural logarithm of the factorial of {@code n}. 51 * It allows to allocate a cache of precomputed values. 52 * In case of cache miss, computation is preformed by a call to 53 * {@link Gamma#logGamma(double)}. 54 */ 55 public static final class FactorialLog { 56 /** 57 * Precomputed values of the function: 58 * {@code LOG_FACTORIALS[i] = log(i!)}. 59 */ 60 private final double[] LOG_FACTORIALS; 61 62 /** 63 * Creates an instance, reusing the already computed values if available. 64 * 65 * @param numValues Number of values of the function to compute. 66 * @param cache Existing cache. 67 * @throw IllegalArgumentException if {@code numValues < 0}. 68 */ 69 private FactorialLog(int numValues, 70 double[] cache) { 71 LOG_FACTORIALS = new double[numValues]; 72 73 final int beginCopy = 2; 74 final int endCopy = cache == null || cache.length <= beginCopy ? 75 beginCopy : cache.length <= numValues ? 76 cache.length : numValues; 77 78 // Copy available values. 79 for (int i = beginCopy; i < endCopy; i++) { 80 LOG_FACTORIALS[i] = cache[i]; 81 } 82 83 // Precompute. 84 for (int i = endCopy; i < numValues; i++) { 85 LOG_FACTORIALS[i] = LOG_FACTORIALS[i - 1] + Math.log(i); 86 } 87 } 88 89 /** 90 * Creates an instance with no precomputed values. 91 * 92 * @return an instance with no precomputed values. 93 */ 94 public static FactorialLog create() { 95 return new FactorialLog(0, null); 96 } 97 98 /** 99 * Creates an instance with the specified cache size. 100 * 101 * @param cacheSize Number of precomputed values of the function. 102 * @return a new instance where {@code cacheSize} values have been 103 * precomputed. 104 * @throws IllegalArgumentException if {@code n < 0}. 105 */ 106 public FactorialLog withCache(final int cacheSize) { 107 return new FactorialLog(cacheSize, LOG_FACTORIALS); 108 } 109 110 /** 111 * Computes {@code log(n!)}. 112 * 113 * @param n Argument. 114 * @return {@code log(n!)}. 115 * @throws IllegalArgumentException if {@code n < 0}. 116 */ 117 public double value(final int n) { 118 // Use cache of precomputed values. 119 if (n < LOG_FACTORIALS.length) { 120 return LOG_FACTORIALS[n]; 121 } 122 123 // Use cache of precomputed factorial values. 124 if (n < FACTORIALS.length) { 125 return Math.log(FACTORIALS[n]); 126 } 127 128 // Delegate. 129 return InternalGamma.logGamma(n + 1); 130 } 131 } 132 }