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 */
017package org.apache.commons.statistics.inference;
018
019import java.util.Arrays;
020import java.util.EnumSet;
021import java.util.Objects;
022import org.apache.commons.numbers.core.Sum;
023import org.apache.commons.statistics.distribution.NormalDistribution;
024import org.apache.commons.statistics.ranking.NaNStrategy;
025import org.apache.commons.statistics.ranking.NaturalRanking;
026import org.apache.commons.statistics.ranking.RankingAlgorithm;
027import org.apache.commons.statistics.ranking.TiesStrategy;
028
029/**
030 * Implements the Wilcoxon signed-rank test.
031 *
032 * @see <a href="https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test">Wilcoxon signed-rank test (Wikipedia)</a>
033 * @since 1.1
034 */
035public final class WilcoxonSignedRankTest {
036    /** Limit on sample size for the exact p-value computation. */
037    private static final int EXACT_LIMIT = 1023;
038    /** Limit on sample size for the exact p-value computation for the auto mode. */
039    private static final int AUTO_LIMIT = 50;
040    /** Ranking instance. */
041    private static final RankingAlgorithm RANKING = new NaturalRanking(NaNStrategy.FAILED, TiesStrategy.AVERAGE);
042    /** Default instance. */
043    private static final WilcoxonSignedRankTest DEFAULT = new WilcoxonSignedRankTest(
044        AlternativeHypothesis.TWO_SIDED, PValueMethod.AUTO, true, 0);
045
046    /** Alternative hypothesis. */
047    private final AlternativeHypothesis alternative;
048    /** Method to compute the p-value. */
049    private final PValueMethod pValueMethod;
050    /** Perform continuity correction. */
051    private final boolean continuityCorrection;
052    /** Expected location shift. */
053    private final double mu;
054
055    /**
056     * Result for the Wilcoxon signed-rank test.
057     *
058     * <p>This class is immutable.
059     *
060     * @since 1.1
061     */
062    public static final class Result extends BaseSignificanceResult {
063        /** Flag indicating the data had tied values. */
064        private final boolean tiedValues;
065        /** Flag indicating the data had zero values. */
066        private final boolean zeroValues;
067
068        /**
069         * Create an instance.
070         *
071         * @param statistic Test statistic.
072         * @param tiedValues Flag indicating the data had tied values.
073         * @param zeroValues Flag indicating the data had zero values.
074         * @param p Result p-value.
075         */
076        Result(double statistic, boolean tiedValues, boolean zeroValues, double p) {
077            super(statistic, p);
078            this.tiedValues = tiedValues;
079            this.zeroValues = zeroValues;
080        }
081
082        /**
083         * Return {@code true} if the data had tied values (with equal ranks).
084         *
085         * <p>Note: The exact computation cannot be used when there are tied values.
086         * The p-value uses the asymptotic approximation using a tie correction.
087         *
088         * @return {@code true} if there were tied values
089         */
090        public boolean hasTiedValues() {
091            return tiedValues;
092        }
093
094        /**
095         * Return {@code true} if the data had zero values. This occurs when the differences between
096         * sample values matched the expected location shift: {@code z = x - y == mu}.
097         *
098         * <p>Note: The exact computation cannot be used when there are zero values.
099         * The p-value uses the asymptotic approximation.
100         *
101         * @return {@code true} if there were zero values
102         */
103        public boolean hasZeroValues() {
104            return zeroValues;
105        }
106    }
107
108    /**
109     * @param alternative Alternative hypothesis.
110     * @param method P-value method.
111     * @param continuityCorrection true to perform continuity correction.
112     * @param mu Expected location shift.
113     */
114    private WilcoxonSignedRankTest(AlternativeHypothesis alternative, PValueMethod method,
115        boolean continuityCorrection, double mu) {
116        this.alternative = alternative;
117        this.pValueMethod = method;
118        this.continuityCorrection = continuityCorrection;
119        this.mu = mu;
120    }
121
122    /**
123     * Return an instance using the default options.
124     *
125     * <ul>
126     * <li>{@link AlternativeHypothesis#TWO_SIDED}
127     * <li>{@link PValueMethod#AUTO}
128     * <li>{@link ContinuityCorrection#ENABLED}
129     * <li>{@linkplain #withMu(double) mu = 0}
130     * </ul>
131     *
132     * @return default instance
133     */
134    public static WilcoxonSignedRankTest withDefaults() {
135        return DEFAULT;
136    }
137
138    /**
139     * Return an instance with the configured alternative hypothesis.
140     *
141     * @param v Value.
142     * @return an instance
143     */
144    public WilcoxonSignedRankTest with(AlternativeHypothesis v) {
145        return new WilcoxonSignedRankTest(Objects.requireNonNull(v), pValueMethod, continuityCorrection, mu);
146    }
147
148    /**
149     * Return an instance with the configured p-value method.
150     *
151     * @param v Value.
152     * @return an instance
153     * @throws IllegalArgumentException if the value is not in the allowed options or is null
154     */
155    public WilcoxonSignedRankTest with(PValueMethod v) {
156        return new WilcoxonSignedRankTest(alternative,
157            Arguments.checkOption(v, EnumSet.of(PValueMethod.AUTO, PValueMethod.EXACT, PValueMethod.ASYMPTOTIC)),
158            continuityCorrection, mu);
159    }
160
161    /**
162     * Return an instance with the configured continuity correction.
163     *
164     * <p>If {@code enabled}, adjust the Wilcoxon rank statistic by 0.5 towards the
165     * mean value when computing the z-statistic if a normal approximation is used
166     * to compute the p-value.
167     *
168     * @param v Value.
169     * @return an instance
170     */
171    public WilcoxonSignedRankTest with(ContinuityCorrection v) {
172        return new WilcoxonSignedRankTest(alternative, pValueMethod,
173            Objects.requireNonNull(v) == ContinuityCorrection.ENABLED, mu);
174    }
175
176    /**
177     * Return an instance with the configured expected difference {@code mu}.
178     *
179     * @param v Value.
180     * @return an instance
181     * @throws IllegalArgumentException if the value is not finite
182     */
183    public WilcoxonSignedRankTest withMu(double v) {
184        return new WilcoxonSignedRankTest(alternative, pValueMethod, continuityCorrection, Arguments.checkFinite(v));
185    }
186
187    /**
188     * Computes the Wilcoxon signed ranked statistic comparing the differences between
189     * sample values {@code z = x - y} to {@code mu}.
190     *
191     * <p>This method handles matching samples {@code z[i] == mu} (no difference)
192     * by including them in the ranking of samples but excludes them from the test statistic
193     * (<i>signed-rank zero procedure</i>).
194     *
195     * @param z Signed differences between sample values.
196     * @return Wilcoxon <i>positive-rank sum</i> statistic (W+)
197     * @throws IllegalArgumentException if {@code z} is zero-length; contains NaN values;
198     * or all differences are equal to the expected difference
199     * @see #withMu(double)
200     */
201    public double statistic(double[] z) {
202        return computeStatistic(z, mu);
203    }
204
205    /**
206     * Computes the Wilcoxon signed ranked statistic comparing the differences between two related
207     * samples or repeated measurements on a single sample.
208     *
209     * <p>This method handles matching samples {@code x[i] - mu == y[i]} (no difference)
210     * by including them in the ranking of samples but excludes them from the test statistic
211     * (<i>signed-rank zero procedure</i>).
212     *
213     * <p>This method is functionally equivalent to creating an array of differences
214     * {@code z = x - y} and calling {@link #statistic(double[]) statistic(z)}; the
215     * implementation may use an optimised method to compute the differences and
216     * rank statistic if {@code mu != 0}.
217     *
218     * @param x First sample values.
219     * @param y Second sample values.
220     * @return Wilcoxon <i>positive-rank sum</i> statistic (W+)
221     * @throws IllegalArgumentException if {@code x} or {@code y} are zero-length; are not
222     * the same length; contain NaN values; or {@code x[i] == y[i]} for all data
223     * @see #withMu(double)
224     */
225    public double statistic(double[] x, double[] y) {
226        checkSamples(x, y);
227        // Apply mu before creation of differences
228        final double[] z = calculateDifferences(mu, x, y);
229        return computeStatistic(z, 0);
230    }
231
232    /**
233     * Performs a Wilcoxon signed ranked statistic comparing the differences between
234     * sample values {@code z = x - y} to {@code mu}.
235     *
236     * <p>This method handles matching samples {@code z[i] == mu} (no difference)
237     * by including them in the ranking of samples but excludes them from the test statistic
238     * (<i>signed-rank zero procedure</i>).
239     *
240     * <p>The test is defined by the {@link AlternativeHypothesis}.
241     *
242     * <ul>
243     * <li>'two-sided': the distribution of the difference is not symmetric about {@code mu}.
244     * <li>'greater': the distribution of the difference is stochastically greater than a
245     * distribution symmetric about {@code mu}.
246     * <li>'less': the distribution of the difference is stochastically less than a distribution
247     * symmetric about {@code mu}.
248     * </ul>
249     *
250     * <p>If the p-value method is {@linkplain PValueMethod#AUTO auto} an exact p-value
251     * is computed if the samples contain less than 50 values; otherwise a normal
252     * approximation is used.
253     *
254     * <p>Computation of the exact p-value is only valid if there are no matching
255     * samples {@code z[i] == mu} and no tied ranks in the data; otherwise the
256     * p-value resorts to the asymptotic Cureton approximation using a tie
257     * correction and an optional continuity correction.
258     *
259     * <p><strong>Note: </strong>
260     * Computation of the exact p-value requires the
261     * sample size {@code <= 1023}. Exact computation requires tabulation of values
262     * not exceeding size {@code n(n+1)/2} and computes in Order(n*n/2). Maximum
263     * memory usage is approximately 4 MiB.
264     *
265     * @param z Differences between sample values.
266     * @return test result
267     * @throws IllegalArgumentException if {@code z} is zero-length; contains NaN values;
268     * or all differences are zero
269     * @see #withMu(double)
270     * @see #with(AlternativeHypothesis)
271     * @see #with(ContinuityCorrection)
272     */
273    public Result test(double[] z) {
274        return computeTest(z, mu);
275    }
276
277    /**
278     * Performs a Wilcoxon signed ranked statistic comparing mean for two related
279     * samples or repeated measurements on a single sample.
280     *
281     * <p>This method handles matching samples {@code x[i] - mu == y[i]} (no difference)
282     * by including them in the ranking of samples but excludes them
283     * from the test statistic (<i>signed-rank zero procedure</i>).
284     *
285     * <p>This method is functionally equivalent to creating an array of differences
286     * {@code z = x - y} and calling {@link #test(double[]) test(z)}; the
287     * implementation may use an optimised method to compute the differences and
288     * rank statistic if {@code mu != 0}.
289     *
290     * @param x First sample values.
291     * @param y Second sample values.
292     * @return test result
293     * @throws IllegalArgumentException if {@code x} or {@code y} are zero-length; are not
294     * the same length; contain NaN values; or {@code x[i] - mu == y[i]} for all data
295     * @see #statistic(double[], double[])
296     * @see #test(double[])
297     */
298    public Result test(double[] x, double[] y) {
299        checkSamples(x, y);
300        // Apply mu before creation of differences
301        final double[] z = calculateDifferences(mu, x, y);
302        return computeTest(z, 0);
303    }
304
305    /**
306     * Computes the Wilcoxon signed ranked statistic comparing the differences between
307     * sample values {@code z = x - y} to {@code mu}.
308     *
309     * @param z Signed differences between sample values.
310     * @param mu Expected difference.
311     * @return Wilcoxon <i>positive-rank sum</i> statistic (W+)
312     * @throws IllegalArgumentException if {@code z} is zero-length; contains NaN values;
313     * or all differences are equal to the expected difference
314     * @see #withMu(double)
315     */
316    private static double computeStatistic(double[] z, double mu) {
317        Arguments.checkValuesRequiredSize(z.length, 1);
318        final double[] x = StatisticUtils.subtract(z, mu);
319        // Raises an error if all zeros
320        countZeros(x);
321        final double[] zAbs = calculateAbsoluteDifferences(x);
322        final double[] ranks = RANKING.apply(zAbs);
323        return calculateW(x, ranks);
324    }
325
326    /**
327     * Performs a Wilcoxon signed ranked statistic comparing the differences between
328     * sample values {@code z = x - y} to {@code mu}.
329     *
330     * @param z Differences between sample values.
331     * @param expectedMu Expected difference.
332     * @return test result
333     * @throws IllegalArgumentException if {@code z} is zero-length; contains NaN values;
334     * or all differences are zero
335     */
336    private Result computeTest(double[] z, double expectedMu) {
337        // Computation as above. The ranks are required for tie correction.
338        Arguments.checkValuesRequiredSize(z.length, 1);
339        final double[] x = StatisticUtils.subtract(z, expectedMu);
340        // Raises an error if all zeros
341        final int zeros = countZeros(x);
342        final double[] zAbs = calculateAbsoluteDifferences(x);
343        final double[] ranks = RANKING.apply(zAbs);
344        final double wPlus = calculateW(x, ranks);
345
346        // Exact p has strict requirements for no zeros, no ties
347        final double c = calculateTieCorrection(ranks);
348        final boolean tiedValues = c != 0;
349
350        final int n = z.length;
351        // Exact p requires no ties and no zeros
352        final double p;
353        if (selectMethod(pValueMethod, n) == PValueMethod.EXACT && n <= EXACT_LIMIT && !tiedValues && zeros == 0) {
354            p = calculateExactPValue((int) wPlus, n, alternative);
355        } else {
356            p = calculateAsymptoticPValue(wPlus, n, zeros, c, alternative, continuityCorrection);
357        }
358        return new Result(wPlus, tiedValues, zeros != 0, p);
359    }
360
361    /**
362     * Ensures that the provided arrays fulfil the assumptions.
363     *
364     * @param x First sample.
365     * @param y Second sample.
366     * @throws IllegalArgumentException if {@code x} or {@code y} are zero-length; or do not
367     * have the same length
368     */
369    private static void checkSamples(double[] x, double[] y) {
370        Arguments.checkValuesRequiredSize(x.length, 1);
371        Arguments.checkValuesRequiredSize(y.length, 1);
372        Arguments.checkValuesSizeMatch(x.length, y.length);
373    }
374
375    /**
376     * Calculates x[i] - mu - y[i] for all i.
377     *
378     * @param mu Expected difference.
379     * @param x First sample.
380     * @param y Second sample.
381     * @return z = x - y
382     */
383    private static double[] calculateDifferences(double mu, double[] x, double[] y) {
384        final double[] z = new double[x.length];
385        for (int i = 0; i < x.length; ++i) {
386            z[i] = x[i] - mu - y[i];
387        }
388        return z;
389    }
390
391    /**
392     * Calculates |z[i]| for all i.
393     *
394     * @param z Sample.
395     * @return |z|
396     */
397    private static double[] calculateAbsoluteDifferences(double[] z) {
398        final double[] zAbs = new double[z.length];
399        for (int i = 0; i < z.length; ++i) {
400            zAbs[i] = Math.abs(z[i]);
401        }
402        return zAbs;
403    }
404
405    /**
406     * Calculate the Wilcoxon <i>positive-rank sum</i> statistic.
407     *
408     * @param obs Observed signed value.
409     * @param ranks Ranks (including averages for ties).
410     * @return Wilcoxon <i>positive-rank sum</i> statistic (W+)
411     */
412    private static double calculateW(final double[] obs, final double[] ranks) {
413        final Sum wPlus = Sum.create();
414        for (int i = 0; i < obs.length; ++i) {
415            // Must be positive (excludes zeros)
416            if (obs[i] > 0) {
417                wPlus.add(ranks[i]);
418            }
419        }
420        return wPlus.getAsDouble();
421    }
422
423    /**
424     * Count the number of zeros in the data.
425     *
426     * @param z Input data.
427     * @return the zero count
428     * @throws IllegalArgumentException if the data is all zeros
429     */
430    private static int countZeros(final double[] z) {
431        int c = 0;
432        for (final double v : z) {
433            if (v == 0) {
434                c++;
435            }
436        }
437        if (c == z.length) {
438            throw new InferenceException("All signed differences are zero");
439        }
440        return c;
441    }
442
443    /**
444     * Calculate the tie correction.
445     * Destructively modifies ranks (by sorting).
446     * <pre>
447     * c = sum(t^3 - t)
448     * </pre>
449     * <p>where t is the size of each group of tied observations.
450     *
451     * @param ranks Ranks
452     * @return the tie correction
453     */
454    static double calculateTieCorrection(double[] ranks) {
455        double c = 0;
456        int ties = 1;
457        Arrays.sort(ranks);
458        double last = Double.NaN;
459        for (final double rank : ranks) {
460            // Deliberate use of equals
461            if (last == rank) {
462                // Extend the tied group
463                ties++;
464            } else {
465                if (ties != 1) {
466                    c += Math.pow(ties, 3) - ties;
467                    ties = 1;
468                }
469                last = rank;
470            }
471        }
472        // Final ties count
473        c += Math.pow(ties, 3) - ties;
474        return c;
475    }
476
477    /**
478     * Select the method to compute the p-value.
479     *
480     * @param method P-value method.
481     * @param n Size of the data.
482     * @return p-value method.
483     */
484    private static PValueMethod selectMethod(PValueMethod method, int n) {
485        return method == PValueMethod.AUTO && n <= AUTO_LIMIT ? PValueMethod.EXACT : method;
486    }
487
488    /**
489     * Compute the asymptotic p-value using the Cureton normal approximation. This
490     * corrects for zeros in the signed-rank zero procedure and/or ties corrected using
491     * the average method.
492     *
493     * @param wPlus Wilcoxon signed rank value (W+).
494     * @param n Number of subjects.
495     * @param z Count of number of zeros
496     * @param c Tie-correction
497     * @param alternative Alternative hypothesis.
498     * @param continuityCorrection true to use a continuity correction.
499     * @return two-sided asymptotic p-value
500     */
501    private static double calculateAsymptoticPValue(double wPlus, int n, double z, double c,
502            AlternativeHypothesis alternative, boolean continuityCorrection) {
503        // E[W+] = n * (n + 1) / 4 - z * (z + 1) / 4
504        final double e = (n * (n + 1.0) - z * (z + 1.0)) * 0.25;
505
506        final double variance = ((n * (n + 1.0) * (2 * n + 1.0)) -
507                                (z * (z + 1.0) * (2 * z + 1.0)) - c * 0.5) / 24;
508
509        double x = wPlus - e;
510        if (continuityCorrection) {
511            // +/- 0.5 is a continuity correction towards the expected.
512            if (alternative == AlternativeHypothesis.GREATER_THAN) {
513                x -= 0.5;
514            } else if (alternative == AlternativeHypothesis.LESS_THAN) {
515                x += 0.5;
516            } else {
517                // two-sided. Shift towards the expected of zero.
518                // Use of signum ignores x==0 (i.e. not copySign(0.5, z))
519                x -= Math.signum(x) * 0.5;
520            }
521        }
522        x /= Math.sqrt(variance);
523
524        final NormalDistribution standardNormal = NormalDistribution.of(0, 1);
525        if (alternative == AlternativeHypothesis.GREATER_THAN) {
526            return standardNormal.survivalProbability(x);
527        }
528        if (alternative == AlternativeHypothesis.LESS_THAN) {
529            return standardNormal.cumulativeProbability(x);
530        }
531        // two-sided
532        return 2 * standardNormal.survivalProbability(Math.abs(x));
533    }
534
535    /**
536     * Compute the exact p-value.
537     *
538     * <p>This computation requires that no zeros or ties are found in the data. The input
539     * value n is limited to 1023.
540     *
541     * @param w1 Wilcoxon signed rank value (W+, or W-).
542     * @param n Number of subjects.
543     * @param alternative Alternative hypothesis.
544     * @return exact p-value (two-sided, greater, or less using the options)
545     */
546    private static double calculateExactPValue(int w1, int n, AlternativeHypothesis alternative) {
547        // T+ plus T- equals the sum of the ranks: n(n+1)/2
548        // Compute using the lower half.
549        // No overflow here if n <= 1023.
550        final int sum = n * (n + 1) / 2;
551        final int w2 = sum - w1;
552
553        // Return the correct side:
554        if (alternative == AlternativeHypothesis.GREATER_THAN) {
555            // sf(w1 - 1)
556            return sf(w1 - 1, w2 + 1, n);
557        }
558        if (alternative == AlternativeHypothesis.LESS_THAN) {
559            // cdf(w1)
560            return cdf(w1, w2, n);
561        }
562        // two-sided: 2 * sf(max(w1, w2) - 1) or 2 * cdf(min(w1, w2))
563        final double p = 2 * computeCdf(Math.min(w1, w2), n);
564        // Clip to range: [0, 1]
565        return Math.min(1, p);
566    }
567
568    /**
569     * Compute the cumulative density function of the Wilcoxon signed rank W+ statistic.
570     * The W- statistic is passed for convenience to exploit symmetry in the distribution.
571     *
572     * @param w1 Wilcoxon W+ statistic
573     * @param w2 Wilcoxon W- statistic
574     * @param n Number of subjects.
575     * @return {@code Pr(X <= k)}
576     */
577    private static double cdf(int w1, int w2, int n) {
578        // Exploit symmetry. Note the distribution is discrete thus requiring (w2 - 1).
579        return w2 > w1 ?
580            computeCdf(w1, n) :
581            1 - computeCdf(w2 - 1, n);
582    }
583
584    /**
585     * Compute the survival function of the Wilcoxon signed rank W+ statistic.
586     * The W- statistic is passed for convenience to exploit symmetry in the distribution.
587     *
588     * @param w1 Wilcoxon W+ statistic
589     * @param w2 Wilcoxon W- statistic
590     * @param n Number of subjects.
591     * @return {@code Pr(X <= k)}
592     */
593    private static double sf(int w1, int w2, int n) {
594        // Opposite of the CDF
595        return w2 > w1 ?
596            1 - computeCdf(w1, n) :
597            computeCdf(w2 - 1, n);
598    }
599
600    /**
601     * Compute the cumulative density function for the distribution of the Wilcoxon
602     * signed rank statistic. This is a discrete distribution and is only valid
603     * when no zeros or ties are found in the data.
604     *
605     * <p>This should be called with the lower of W+ or W- for computational efficiency.
606     * The input value n is limited to 1023.
607     *
608     * <p>Uses recursion to compute the density for {@code X <= t} and sums the values.
609     * See: https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test#Computing_the_null_distribution
610     *
611     * @param t Smallest Wilcoxon signed rank value (W+, or W-).
612     * @param n Number of subjects.
613     * @return {@code Pr(T <= t)}
614     */
615    private static double computeCdf(int t, int n) {
616        // Currently limited to n=1023.
617        // Note:
618        // The limit for t is n(n+1)/2.
619        // The highest possible sum is bounded by the normalisation factor 2^n.
620        // n         t              sum          support
621        // 31        [0, 496]       < 2^31       int
622        // 63        [0, 2016]      < 2^63       long
623        // 1023      [0, 523766]    < 2^1023     double
624
625        if (t <= 0) {
626            // No recursion required
627            return t < 0 ? 0 : Math.scalb(1, -n);
628        }
629
630        // Define u_n(t) as the number of sign combinations for T = t
631        // Pr(T == t) = u_n(t) / 2^n
632        // Sum them to create the cumulative probability Pr(T <= t).
633        //
634        // Recursive formula:
635        // u_n(t) = u_{n-1}(t) + u_{n-1}(t-n)
636        // u_0(0) = 1
637        // u_0(t) = 0 : t != 0
638        // u_n(t) = 0 : t < 0 || t > n(n+1)/2
639
640        // Compute all u_n(t) up to t.
641        final double[] u = new double[t + 1];
642        // Initialize u_1(t) using base cases for recursion
643        u[0] = u[1] = 1;
644
645        // Each u_n(t) is created using the current correct values for u_{n-1}(t)
646        for (int nn = 2; nn < n + 1; nn++) {
647            // u[t] holds the correct value for u_{n-1}(t)
648            // u_n(t) = u_{n-1}(t) + u_{n-1}(t-n)
649            for (int tt = t; tt >= nn; tt--) {
650                u[tt] += u[tt - nn];
651            }
652        }
653        final double sum = Arrays.stream(u).sum();
654
655        // Finally divide by the number of possible sums: 2^n
656        return Math.scalb(sum, -n);
657    }
658}