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     */
017    package org.apache.commons.math3.stat.inference;
018    
019    import org.apache.commons.math3.distribution.ChiSquaredDistribution;
020    import org.apache.commons.math3.exception.DimensionMismatchException;
021    import org.apache.commons.math3.exception.MaxCountExceededException;
022    import org.apache.commons.math3.exception.NotPositiveException;
023    import org.apache.commons.math3.exception.NotStrictlyPositiveException;
024    import org.apache.commons.math3.exception.NullArgumentException;
025    import org.apache.commons.math3.exception.OutOfRangeException;
026    import org.apache.commons.math3.exception.ZeroException;
027    import org.apache.commons.math3.exception.util.LocalizedFormats;
028    import org.apache.commons.math3.util.FastMath;
029    import org.apache.commons.math3.util.MathArrays;
030    
031    /**
032     * Implements Chi-Square test statistics.
033     *
034     * <p>This implementation handles both known and unknown distributions.</p>
035     *
036     * <p>Two samples tests can be used when the distribution is unknown <i>a priori</i>
037     * but provided by one sample, or when the hypothesis under test is that the two
038     * samples come from the same underlying distribution.</p>
039     *
040     * @version $Id: ChiSquareTest.java 1416643 2012-12-03 19:37:14Z tn $
041     */
042    public class ChiSquareTest {
043    
044        /**
045         * Construct a ChiSquareTest
046         */
047        public ChiSquareTest() {
048            super();
049        }
050    
051        /**
052         * Computes the <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
053         * Chi-Square statistic</a> comparing <code>observed</code> and <code>expected</code>
054         * frequency counts.
055         * <p>
056         * This statistic can be used to perform a Chi-Square test evaluating the null
057         * hypothesis that the observed counts follow the expected distribution.</p>
058         * <p>
059         * <strong>Preconditions</strong>: <ul>
060         * <li>Expected counts must all be positive.
061         * </li>
062         * <li>Observed counts must all be &ge; 0.
063         * </li>
064         * <li>The observed and expected arrays must have the same length and
065         * their common length must be at least 2.
066         * </li></ul></p><p>
067         * If any of the preconditions are not met, an
068         * <code>IllegalArgumentException</code> is thrown.</p>
069         * <p><strong>Note: </strong>This implementation rescales the
070         * <code>expected</code> array if necessary to ensure that the sum of the
071         * expected and observed counts are equal.</p>
072         *
073         * @param observed array of observed frequency counts
074         * @param expected array of expected frequency counts
075         * @return chiSquare test statistic
076         * @throws NotPositiveException if <code>observed</code> has negative entries
077         * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
078         * not strictly positive
079         * @throws DimensionMismatchException if the arrays length is less than 2
080         */
081        public double chiSquare(final double[] expected, final long[] observed)
082            throws NotPositiveException, NotStrictlyPositiveException,
083            DimensionMismatchException {
084    
085            if (expected.length < 2) {
086                throw new DimensionMismatchException(expected.length, 2);
087            }
088            if (expected.length != observed.length) {
089                throw new DimensionMismatchException(expected.length, observed.length);
090            }
091            MathArrays.checkPositive(expected);
092            MathArrays.checkNonNegative(observed);
093    
094            double sumExpected = 0d;
095            double sumObserved = 0d;
096            for (int i = 0; i < observed.length; i++) {
097                sumExpected += expected[i];
098                sumObserved += observed[i];
099            }
100            double ratio = 1.0d;
101            boolean rescale = false;
102            if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
103                ratio = sumObserved / sumExpected;
104                rescale = true;
105            }
106            double sumSq = 0.0d;
107            for (int i = 0; i < observed.length; i++) {
108                if (rescale) {
109                    final double dev = observed[i] - ratio * expected[i];
110                    sumSq += dev * dev / (ratio * expected[i]);
111                } else {
112                    final double dev = observed[i] - expected[i];
113                    sumSq += dev * dev / expected[i];
114                }
115            }
116            return sumSq;
117    
118        }
119    
120        /**
121         * Returns the <i>observed significance level</i>, or <a href=
122         * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
123         * p-value</a>, associated with a
124         * <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
125         * Chi-square goodness of fit test</a> comparing the <code>observed</code>
126         * frequency counts to those in the <code>expected</code> array.
127         * <p>
128         * The number returned is the smallest significance level at which one can reject
129         * the null hypothesis that the observed counts conform to the frequency distribution
130         * described by the expected counts.</p>
131         * <p>
132         * <strong>Preconditions</strong>: <ul>
133         * <li>Expected counts must all be positive.
134         * </li>
135         * <li>Observed counts must all be &ge; 0.
136         * </li>
137         * <li>The observed and expected arrays must have the same length and
138         * their common length must be at least 2.
139         * </li></ul></p><p>
140         * If any of the preconditions are not met, an
141         * <code>IllegalArgumentException</code> is thrown.</p>
142         * <p><strong>Note: </strong>This implementation rescales the
143         * <code>expected</code> array if necessary to ensure that the sum of the
144         * expected and observed counts are equal.</p>
145         *
146         * @param observed array of observed frequency counts
147         * @param expected array of expected frequency counts
148         * @return p-value
149         * @throws NotPositiveException if <code>observed</code> has negative entries
150         * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
151         * not strictly positive
152         * @throws DimensionMismatchException if the arrays length is less than 2
153         * @throws MaxCountExceededException if an error occurs computing the p-value
154         */
155        public double chiSquareTest(final double[] expected, final long[] observed)
156            throws NotPositiveException, NotStrictlyPositiveException,
157            DimensionMismatchException, MaxCountExceededException {
158    
159            ChiSquaredDistribution distribution =
160                new ChiSquaredDistribution(expected.length - 1.0);
161            return 1.0 - distribution.cumulativeProbability(chiSquare(expected, observed));
162        }
163    
164        /**
165         * Performs a <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35f.htm">
166         * Chi-square goodness of fit test</a> evaluating the null hypothesis that the
167         * observed counts conform to the frequency distribution described by the expected
168         * counts, with significance level <code>alpha</code>.  Returns true iff the null
169         * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
170         * <p>
171         * <strong>Example:</strong><br>
172         * To test the hypothesis that <code>observed</code> follows
173         * <code>expected</code> at the 99% level, use </p><p>
174         * <code>chiSquareTest(expected, observed, 0.01) </code></p>
175         * <p>
176         * <strong>Preconditions</strong>: <ul>
177         * <li>Expected counts must all be positive.
178         * </li>
179         * <li>Observed counts must all be &ge; 0.
180         * </li>
181         * <li>The observed and expected arrays must have the same length and
182         * their common length must be at least 2.
183         * <li> <code> 0 &lt; alpha &lt; 0.5 </code>
184         * </li></ul></p><p>
185         * If any of the preconditions are not met, an
186         * <code>IllegalArgumentException</code> is thrown.</p>
187         * <p><strong>Note: </strong>This implementation rescales the
188         * <code>expected</code> array if necessary to ensure that the sum of the
189         * expected and observed counts are equal.</p>
190         *
191         * @param observed array of observed frequency counts
192         * @param expected array of expected frequency counts
193         * @param alpha significance level of the test
194         * @return true iff null hypothesis can be rejected with confidence
195         * 1 - alpha
196         * @throws NotPositiveException if <code>observed</code> has negative entries
197         * @throws NotStrictlyPositiveException if <code>expected</code> has entries that are
198         * not strictly positive
199         * @throws DimensionMismatchException if the arrays length is less than 2
200         * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
201         * @throws MaxCountExceededException if an error occurs computing the p-value
202         */
203        public boolean chiSquareTest(final double[] expected, final long[] observed,
204                                     final double alpha)
205            throws NotPositiveException, NotStrictlyPositiveException,
206            DimensionMismatchException, OutOfRangeException, MaxCountExceededException {
207    
208            if ((alpha <= 0) || (alpha > 0.5)) {
209                throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
210                                              alpha, 0, 0.5);
211            }
212            return chiSquareTest(expected, observed) < alpha;
213    
214        }
215    
216        /**
217         *  Computes the Chi-Square statistic associated with a
218         * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
219         *  chi-square test of independence</a> based on the input <code>counts</code>
220         *  array, viewed as a two-way table.
221         * <p>
222         * The rows of the 2-way table are
223         * <code>count[0], ... , count[count.length - 1] </code></p>
224         * <p>
225         * <strong>Preconditions</strong>: <ul>
226         * <li>All counts must be &ge; 0.
227         * </li>
228         * <li>The count array must be rectangular (i.e. all count[i] subarrays
229         *  must have the same length).
230         * </li>
231         * <li>The 2-way table represented by <code>counts</code> must have at
232         *  least 2 columns and at least 2 rows.
233         * </li>
234         * </li></ul></p><p>
235         * If any of the preconditions are not met, an
236         * <code>IllegalArgumentException</code> is thrown.</p>
237         *
238         * @param counts array representation of 2-way table
239         * @return chiSquare test statistic
240         * @throws NullArgumentException if the array is null
241         * @throws DimensionMismatchException if the array is not rectangular
242         * @throws NotPositiveException if {@code counts} has negative entries
243         */
244        public double chiSquare(final long[][] counts)
245            throws NullArgumentException, NotPositiveException,
246            DimensionMismatchException {
247    
248            checkArray(counts);
249            int nRows = counts.length;
250            int nCols = counts[0].length;
251    
252            // compute row, column and total sums
253            double[] rowSum = new double[nRows];
254            double[] colSum = new double[nCols];
255            double total = 0.0d;
256            for (int row = 0; row < nRows; row++) {
257                for (int col = 0; col < nCols; col++) {
258                    rowSum[row] += counts[row][col];
259                    colSum[col] += counts[row][col];
260                    total += counts[row][col];
261                }
262            }
263    
264            // compute expected counts and chi-square
265            double sumSq = 0.0d;
266            double expected = 0.0d;
267            for (int row = 0; row < nRows; row++) {
268                for (int col = 0; col < nCols; col++) {
269                    expected = (rowSum[row] * colSum[col]) / total;
270                    sumSq += ((counts[row][col] - expected) *
271                            (counts[row][col] - expected)) / expected;
272                }
273            }
274            return sumSq;
275    
276        }
277    
278        /**
279         * Returns the <i>observed significance level</i>, or <a href=
280         * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
281         * p-value</a>, associated with a
282         * <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
283         * chi-square test of independence</a> based on the input <code>counts</code>
284         * array, viewed as a two-way table.
285         * <p>
286         * The rows of the 2-way table are
287         * <code>count[0], ... , count[count.length - 1] </code></p>
288         * <p>
289         * <strong>Preconditions</strong>: <ul>
290         * <li>All counts must be &ge; 0.
291         * </li>
292         * <li>The count array must be rectangular (i.e. all count[i] subarrays must have
293         *     the same length).
294         * </li>
295         * <li>The 2-way table represented by <code>counts</code> must have at least 2
296         *     columns and at least 2 rows.
297         * </li>
298         * </li></ul></p><p>
299         * If any of the preconditions are not met, an
300         * <code>IllegalArgumentException</code> is thrown.</p>
301         *
302         * @param counts array representation of 2-way table
303         * @return p-value
304         * @throws NullArgumentException if the array is null
305         * @throws DimensionMismatchException if the array is not rectangular
306         * @throws NotPositiveException if {@code counts} has negative entries
307         * @throws MaxCountExceededException if an error occurs computing the p-value
308         */
309        public double chiSquareTest(final long[][] counts)
310            throws NullArgumentException, DimensionMismatchException,
311            NotPositiveException, MaxCountExceededException {
312    
313            checkArray(counts);
314            double df = ((double) counts.length -1) * ((double) counts[0].length - 1);
315            ChiSquaredDistribution distribution;
316            distribution = new ChiSquaredDistribution(df);
317            return 1 - distribution.cumulativeProbability(chiSquare(counts));
318    
319        }
320    
321        /**
322         * Performs a <a href="http://www.itl.nist.gov/div898/handbook/prc/section4/prc45.htm">
323         * chi-square test of independence</a> evaluating the null hypothesis that the
324         * classifications represented by the counts in the columns of the input 2-way table
325         * are independent of the rows, with significance level <code>alpha</code>.
326         * Returns true iff the null hypothesis can be rejected with 100 * (1 - alpha) percent
327         * confidence.
328         * <p>
329         * The rows of the 2-way table are
330         * <code>count[0], ... , count[count.length - 1] </code></p>
331         * <p>
332         * <strong>Example:</strong><br>
333         * To test the null hypothesis that the counts in
334         * <code>count[0], ... , count[count.length - 1] </code>
335         *  all correspond to the same underlying probability distribution at the 99% level, use</p>
336         * <p><code>chiSquareTest(counts, 0.01)</code></p>
337         * <p>
338         * <strong>Preconditions</strong>: <ul>
339         * <li>All counts must be &ge; 0.
340         * </li>
341         * <li>The count array must be rectangular (i.e. all count[i] subarrays must have the
342         *     same length).</li>
343         * <li>The 2-way table represented by <code>counts</code> must have at least 2 columns and
344         *     at least 2 rows.</li>
345         * </li></ul></p><p>
346         * If any of the preconditions are not met, an
347         * <code>IllegalArgumentException</code> is thrown.</p>
348         *
349         * @param counts array representation of 2-way table
350         * @param alpha significance level of the test
351         * @return true iff null hypothesis can be rejected with confidence
352         * 1 - alpha
353         * @throws NullArgumentException if the array is null
354         * @throws DimensionMismatchException if the array is not rectangular
355         * @throws NotPositiveException if {@code counts} has any negative entries
356         * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
357         * @throws MaxCountExceededException if an error occurs computing the p-value
358         */
359        public boolean chiSquareTest(final long[][] counts, final double alpha)
360            throws NullArgumentException, DimensionMismatchException,
361            NotPositiveException, OutOfRangeException, MaxCountExceededException {
362    
363            if ((alpha <= 0) || (alpha > 0.5)) {
364                throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
365                                              alpha, 0, 0.5);
366            }
367            return chiSquareTest(counts) < alpha;
368    
369        }
370    
371        /**
372         * <p>Computes a
373         * <a href="http://www.itl.nist.gov/div898/software/dataplot/refman1/auxillar/chi2samp.htm">
374         * Chi-Square two sample test statistic</a> comparing bin frequency counts
375         * in <code>observed1</code> and <code>observed2</code>.  The
376         * sums of frequency counts in the two samples are not required to be the
377         * same.  The formula used to compute the test statistic is</p>
378         * <code>
379         * &sum;[(K * observed1[i] - observed2[i]/K)<sup>2</sup> / (observed1[i] + observed2[i])]
380         * </code> where
381         * <br/><code>K = &sqrt;[&sum(observed2 / &sum;(observed1)]</code>
382         * </p>
383         * <p>This statistic can be used to perform a Chi-Square test evaluating the
384         * null hypothesis that both observed counts follow the same distribution.</p>
385         * <p>
386         * <strong>Preconditions</strong>: <ul>
387         * <li>Observed counts must be non-negative.
388         * </li>
389         * <li>Observed counts for a specific bin must not both be zero.
390         * </li>
391         * <li>Observed counts for a specific sample must not all be 0.
392         * </li>
393         * <li>The arrays <code>observed1</code> and <code>observed2</code> must have
394         * the same length and their common length must be at least 2.
395         * </li></ul></p><p>
396         * If any of the preconditions are not met, an
397         * <code>IllegalArgumentException</code> is thrown.</p>
398         *
399         * @param observed1 array of observed frequency counts of the first data set
400         * @param observed2 array of observed frequency counts of the second data set
401         * @return chiSquare test statistic
402         * @throws DimensionMismatchException the the length of the arrays does not match
403         * @throws NotPositiveException if any entries in <code>observed1</code> or
404         * <code>observed2</code> are negative
405         * @throws ZeroException if either all counts of <code>observed1</code> or
406         * <code>observed2</code> are zero, or if the count at some index is zero
407         * for both arrays
408         * @since 1.2
409         */
410        public double chiSquareDataSetsComparison(long[] observed1, long[] observed2)
411            throws DimensionMismatchException, NotPositiveException, ZeroException {
412    
413            // Make sure lengths are same
414            if (observed1.length < 2) {
415                throw new DimensionMismatchException(observed1.length, 2);
416            }
417            if (observed1.length != observed2.length) {
418                throw new DimensionMismatchException(observed1.length, observed2.length);
419            }
420    
421            // Ensure non-negative counts
422            MathArrays.checkNonNegative(observed1);
423            MathArrays.checkNonNegative(observed2);
424    
425            // Compute and compare count sums
426            long countSum1 = 0;
427            long countSum2 = 0;
428            boolean unequalCounts = false;
429            double weight = 0.0;
430            for (int i = 0; i < observed1.length; i++) {
431                countSum1 += observed1[i];
432                countSum2 += observed2[i];
433            }
434            // Ensure neither sample is uniformly 0
435            if (countSum1 == 0 || countSum2 == 0) {
436                throw new ZeroException();
437            }
438            // Compare and compute weight only if different
439            unequalCounts = countSum1 != countSum2;
440            if (unequalCounts) {
441                weight = FastMath.sqrt((double) countSum1 / (double) countSum2);
442            }
443            // Compute ChiSquare statistic
444            double sumSq = 0.0d;
445            double dev = 0.0d;
446            double obs1 = 0.0d;
447            double obs2 = 0.0d;
448            for (int i = 0; i < observed1.length; i++) {
449                if (observed1[i] == 0 && observed2[i] == 0) {
450                    throw new ZeroException(LocalizedFormats.OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY, i);
451                } else {
452                    obs1 = observed1[i];
453                    obs2 = observed2[i];
454                    if (unequalCounts) { // apply weights
455                        dev = obs1/weight - obs2 * weight;
456                    } else {
457                        dev = obs1 - obs2;
458                    }
459                    sumSq += (dev * dev) / (obs1 + obs2);
460                }
461            }
462            return sumSq;
463        }
464    
465        /**
466         * <p>Returns the <i>observed significance level</i>, or <a href=
467         * "http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">
468         * p-value</a>, associated with a Chi-Square two sample test comparing
469         * bin frequency counts in <code>observed1</code> and
470         * <code>observed2</code>.
471         * </p>
472         * <p>The number returned is the smallest significance level at which one
473         * can reject the null hypothesis that the observed counts conform to the
474         * same distribution.
475         * </p>
476         * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for details
477         * on the formula used to compute the test statistic. The degrees of
478         * of freedom used to perform the test is one less than the common length
479         * of the input observed count arrays.
480         * </p>
481         * <strong>Preconditions</strong>: <ul>
482         * <li>Observed counts must be non-negative.
483         * </li>
484         * <li>Observed counts for a specific bin must not both be zero.
485         * </li>
486         * <li>Observed counts for a specific sample must not all be 0.
487         * </li>
488         * <li>The arrays <code>observed1</code> and <code>observed2</code> must
489         * have the same length and
490         * their common length must be at least 2.
491         * </li></ul><p>
492         * If any of the preconditions are not met, an
493         * <code>IllegalArgumentException</code> is thrown.</p>
494         *
495         * @param observed1 array of observed frequency counts of the first data set
496         * @param observed2 array of observed frequency counts of the second data set
497         * @return p-value
498         * @throws DimensionMismatchException the the length of the arrays does not match
499         * @throws NotPositiveException if any entries in <code>observed1</code> or
500         * <code>observed2</code> are negative
501         * @throws ZeroException if either all counts of <code>observed1</code> or
502         * <code>observed2</code> are zero, or if the count at the same index is zero
503         * for both arrays
504         * @throws MaxCountExceededException if an error occurs computing the p-value
505         * @since 1.2
506         */
507        public double chiSquareTestDataSetsComparison(long[] observed1, long[] observed2)
508            throws DimensionMismatchException, NotPositiveException, ZeroException,
509            MaxCountExceededException {
510    
511            ChiSquaredDistribution distribution;
512            distribution = new ChiSquaredDistribution((double) observed1.length - 1);
513            return 1 - distribution.cumulativeProbability(
514                    chiSquareDataSetsComparison(observed1, observed2));
515    
516        }
517    
518        /**
519         * <p>Performs a Chi-Square two sample test comparing two binned data
520         * sets. The test evaluates the null hypothesis that the two lists of
521         * observed counts conform to the same frequency distribution, with
522         * significance level <code>alpha</code>.  Returns true iff the null
523         * hypothesis can be rejected with 100 * (1 - alpha) percent confidence.
524         * </p>
525         * <p>See {@link #chiSquareDataSetsComparison(long[], long[])} for
526         * details on the formula used to compute the Chisquare statistic used
527         * in the test. The degrees of of freedom used to perform the test is
528         * one less than the common length of the input observed count arrays.
529         * </p>
530         * <strong>Preconditions</strong>: <ul>
531         * <li>Observed counts must be non-negative.
532         * </li>
533         * <li>Observed counts for a specific bin must not both be zero.
534         * </li>
535         * <li>Observed counts for a specific sample must not all be 0.
536         * </li>
537         * <li>The arrays <code>observed1</code> and <code>observed2</code> must
538         * have the same length and their common length must be at least 2.
539         * </li>
540         * <li> <code> 0 < alpha < 0.5 </code>
541         * </li></ul><p>
542         * If any of the preconditions are not met, an
543         * <code>IllegalArgumentException</code> is thrown.</p>
544         *
545         * @param observed1 array of observed frequency counts of the first data set
546         * @param observed2 array of observed frequency counts of the second data set
547         * @param alpha significance level of the test
548         * @return true iff null hypothesis can be rejected with confidence
549         * 1 - alpha
550         * @throws DimensionMismatchException the the length of the arrays does not match
551         * @throws NotPositiveException if any entries in <code>observed1</code> or
552         * <code>observed2</code> are negative
553         * @throws ZeroException if either all counts of <code>observed1</code> or
554         * <code>observed2</code> are zero, or if the count at the same index is zero
555         * for both arrays
556         * @throws OutOfRangeException if <code>alpha</code> is not in the range (0, 0.5]
557         * @throws MaxCountExceededException if an error occurs performing the test
558         * @since 1.2
559         */
560        public boolean chiSquareTestDataSetsComparison(final long[] observed1,
561                                                       final long[] observed2,
562                                                       final double alpha)
563            throws DimensionMismatchException, NotPositiveException,
564            ZeroException, OutOfRangeException, MaxCountExceededException {
565    
566            if (alpha <= 0 ||
567                alpha > 0.5) {
568                throw new OutOfRangeException(LocalizedFormats.OUT_OF_BOUND_SIGNIFICANCE_LEVEL,
569                                              alpha, 0, 0.5);
570            }
571            return chiSquareTestDataSetsComparison(observed1, observed2) < alpha;
572    
573        }
574    
575        /**
576         * Checks to make sure that the input long[][] array is rectangular,
577         * has at least 2 rows and 2 columns, and has all non-negative entries.
578         *
579         * @param in input 2-way table to check
580         * @throws NullArgumentException if the array is null
581         * @throws DimensionMismatchException if the array is not valid
582         * @throws NotPositiveException if the array contains any negative entries
583         */
584        private void checkArray(final long[][] in)
585            throws NullArgumentException, DimensionMismatchException,
586            NotPositiveException {
587    
588            if (in.length < 2) {
589                throw new DimensionMismatchException(in.length, 2);
590            }
591    
592            if (in[0].length < 2) {
593                throw new DimensionMismatchException(in[0].length, 2);
594            }
595    
596            MathArrays.checkRectangular(in);
597            MathArrays.checkNonNegative(in);
598    
599        }
600    
601    }