View Javadoc
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  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus;
24  
25  import org.hipparchus.complex.Complex;
26  import org.hipparchus.complex.ComplexFormat;
27  import org.hipparchus.complex.FieldComplex;
28  import org.hipparchus.distribution.RealDistribution;
29  import org.hipparchus.distribution.continuous.ChiSquaredDistribution;
30  import org.hipparchus.linear.BlockRealMatrix;
31  import org.hipparchus.linear.FieldMatrix;
32  import org.hipparchus.linear.RealMatrix;
33  import org.hipparchus.linear.RealVector;
34  import org.hipparchus.util.Binary64;
35  import org.hipparchus.util.FastMath;
36  import org.hipparchus.util.Precision;
37  
38  import java.io.ByteArrayInputStream;
39  import java.io.ByteArrayOutputStream;
40  import java.io.IOException;
41  import java.io.ObjectInputStream;
42  import java.io.ObjectOutputStream;
43  import java.text.DecimalFormat;
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.HashMap;
47  import java.util.List;
48  import java.util.Map;
49  
50  import static org.junit.jupiter.api.Assertions.assertEquals;
51  import static org.junit.jupiter.api.Assertions.assertNotNull;
52  import static org.junit.jupiter.api.Assertions.assertTrue;
53  import static org.junit.jupiter.api.Assertions.fail;
54  
55  /**
56   */
57  public class UnitTestUtils {
58      /**
59       * Collection of static methods used in math unit tests.
60       */
61      private UnitTestUtils() {
62          super();
63      }
64  
65      /**
66       * Verifies that expected and actual are within delta, or are both NaN or
67       * infinities of the same sign.
68       */
69      public static void customAssertEquals(double expected, double actual, double delta) {
70          assertEquals(expected, actual, delta);
71      }
72  
73      /**
74       * Verifies that expected and actual are within delta, or are both NaN or
75       * infinities of the same sign.
76       */
77      public static void customAssertEquals(String msg, double expected, double actual, double delta) {
78          // check for NaN
79          if(Double.isNaN(expected)){
80              assertTrue(Double.isNaN(actual),
81                  "" + actual + " is not NaN.");
82          } else {
83              assertEquals(expected, actual, delta, msg);
84          }
85      }
86  
87      /**
88       * Verifies that the two arguments are exactly the same, either
89       * both NaN or infinities of same sign, or identical floating point values.
90       */
91      public static void customAssertSame(double expected, double actual) {
92       assertEquals(expected, actual, 0);
93      }
94  
95      /**
96       * Verifies that real and imaginary parts of the two complex arguments
97       * are exactly the same.  Also ensures that NaN / infinite components match.
98       */
99      public static void customAssertSame(Complex expected, Complex actual) {
100         customAssertSame(expected.getRealPart(), actual.getRealPart());
101         customAssertSame(expected.getImaginaryPart(), actual.getImaginaryPart());
102     }
103 
104     /**
105      * Verifies that real and imaginary parts of the two complex arguments
106      * differ by at most delta.  Also ensures that NaN / infinite components match.
107      */
108     public static void customAssertEquals(Complex expected, Complex actual, double delta) {
109         assertEquals(expected.getRealPart(), actual.getRealPart(), delta);
110         assertEquals(expected.getImaginaryPart(), actual.getImaginaryPart(), delta);
111     }
112 
113     /**
114      * Verifies that real and imaginary parts of the two complex arguments
115      * differ by at most delta.  Also ensures that NaN / infinite components match.
116      */
117     public static void customAssertEquals(FieldComplex<Binary64> expected, FieldComplex<Binary64> actual, double delta) {
118         assertEquals(expected.getRealPart().getReal(), actual.getRealPart().getReal(), delta);
119         assertEquals(expected.getImaginaryPart().getReal(), actual.getImaginaryPart().getReal(), delta);
120     }
121 
122     /**
123      * Verifies that real and imaginary parts of the two complex arguments
124      * are exactly the same.  Also ensures that NaN / infinite components match.
125      */
126     public static void customAssertSame(FieldComplex<Binary64> expected, FieldComplex<Binary64> actual) {
127         customAssertSame(expected.getRealPart().getReal(), actual.getRealPart().getReal());
128         customAssertSame(expected.getImaginaryPart().getReal(), actual.getImaginaryPart().getReal());
129     }
130 
131     /**
132      * Verifies that two double arrays have equal entries, up to tolerance
133      */
134     public static void customAssertEquals(double[] expected, double[] observed, double tolerance) {
135         customAssertEquals("Array comparison failure", expected, observed, tolerance);
136     }
137 
138     /**
139      * Serializes an object to a bytes array and then recovers the object from the bytes array.
140      * Returns the deserialized object.
141      *
142      * @param o  object to serialize and recover
143      * @return  the recovered, deserialized object
144      */
145     public static Object serializeAndRecover(Object o) {
146         try {
147             // serialize the Object
148             ByteArrayOutputStream bos = new ByteArrayOutputStream();
149             ObjectOutputStream so = new ObjectOutputStream(bos);
150             so.writeObject(o);
151 
152             // deserialize the Object
153             ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
154             ObjectInputStream si = new ObjectInputStream(bis);
155             return si.readObject();
156         } catch (IOException ioe) {
157             return null;
158         } catch (ClassNotFoundException cnfe) {
159             return null;
160         }
161     }
162 
163     /**
164      * Verifies that serialization preserves equals and hashCode.
165      * Serializes the object, then recovers it and checks equals and hash code.
166      *
167      * @param object  the object to serialize and recover
168      */
169     public static void checkSerializedEquality(Object object) {
170         Object object2 = serializeAndRecover(object);
171         assertEquals(object, object2, "Equals check");
172         assertEquals(object.hashCode(), object2.hashCode(), "HashCode check");
173     }
174 
175     /**
176      * Verifies that the relative error in actual vs. expected is less than or
177      * equal to relativeError.  If expected is infinite or NaN, actual must be
178      * the same (NaN or infinity of the same sign).
179      *
180      * @param expected expected value
181      * @param actual  observed value
182      * @param relativeError  maximum allowable relative error
183      */
184     public static void customAssertRelativelyEquals(double expected, double actual,
185                                                     double relativeError) {
186         customAssertRelativelyEquals(null, expected, actual, relativeError);
187     }
188 
189     /**
190      * Verifies that the relative error in actual vs. expected is less than or
191      * equal to relativeError.  If expected is infinite or NaN, actual must be
192      * the same (NaN or infinity of the same sign).
193      *
194      * @param msg  message to return with failure
195      * @param expected expected value
196      * @param actual  observed value
197      * @param relativeError  maximum allowable relative error
198      */
199     public static void customAssertRelativelyEquals(String msg, double expected,
200                                                     double actual, double relativeError) {
201         if (Double.isNaN(expected)) {
202             assertTrue(Double.isNaN(actual), msg);
203         } else if (Double.isNaN(actual)) {
204             assertTrue(Double.isNaN(expected), msg);
205         } else if (Double.isInfinite(actual) || Double.isInfinite(expected)) {
206             assertEquals(expected, actual, relativeError);
207         } else if (expected == 0.0) {
208             assertEquals(actual, expected, relativeError, msg);
209         } else {
210             double absError = FastMath.abs(expected) * relativeError;
211             assertEquals(expected, actual, absError, msg);
212         }
213     }
214 
215     /**
216      * Fails iff values does not contain a number within epsilon of z.
217      *
218      * @param msg  message to return with failure
219      * @param values complex array to search
220      * @param z  value sought
221      * @param epsilon  tolerance
222      */
223     public static void customAssertContains(String msg, Complex[] values,
224                                             Complex z, double epsilon) {
225         for (Complex value : values) {
226             if (Precision.equals(value.getReal(), z.getReal(), epsilon) &&
227                 Precision.equals(value.getImaginary(), z.getImaginary(), epsilon)) {
228                 return;
229             }
230         }
231         fail(msg + " Unable to find " + (new ComplexFormat()).format(z));
232     }
233 
234     /**
235      * Fails iff values does not contain a number within epsilon of z.
236      *
237      * @param values complex array to search
238      * @param z  value sought
239      * @param epsilon  tolerance
240      */
241     public static void customAssertContains(Complex[] values,
242                                             Complex z, double epsilon) {
243         customAssertContains(null, values, z, epsilon);
244     }
245 
246     /**
247      * Fails iff values does not contain a number within epsilon of x.
248      *
249      * @param msg  message to return with failure
250      * @param values double array to search
251      * @param x value sought
252      * @param epsilon  tolerance
253      */
254     public static void customAssertContains(String msg, double[] values,
255                                             double x, double epsilon) {
256         for (double value : values) {
257             if (Precision.equals(value, x, epsilon)) {
258                 return;
259             }
260         }
261         fail(msg + " Unable to find " + x);
262     }
263 
264     /**
265      * Fails iff values does not contain a number within epsilon of x.
266      *
267      * @param values double array to search
268      * @param x value sought
269      * @param epsilon  tolerance
270      */
271     public static void customAssertContains(double[] values, double x,
272                                             double epsilon) {
273        customAssertContains(null, values, x, epsilon);
274     }
275 
276     /**
277      * Asserts that all entries of the specified vectors are equal to within a
278      * positive {@code delta}.
279      *
280      * @param message the identifying message for the assertion error (can be
281      * {@code null})
282      * @param expected expected value
283      * @param actual actual value
284      * @param delta the maximum difference between the entries of the expected
285      * and actual vectors for which both entries are still considered equal
286      */
287     public static void customAssertEquals(final String message,
288                                           final double[] expected, final RealVector actual, final double delta) {
289         final String msgAndSep = message.equals("") ? "" : message + ", ";
290         assertEquals(expected.length,
291             actual.getDimension(),
292             msgAndSep + "dimension");
293         for (int i = 0; i < expected.length; i++) {
294             assertEquals(expected[i],
295                 actual.getEntry(i), delta, msgAndSep + "entry #" + i);
296         }
297     }
298 
299     /**
300      * Asserts that all entries of the specified vectors are equal to within a
301      * positive {@code delta}.
302      *
303      * @param message the identifying message for the assertion error (can be
304      * {@code null})
305      * @param expected expected value
306      * @param actual actual value
307      * @param delta the maximum difference between the entries of the expected
308      * and actual vectors for which both entries are still considered equal
309      */
310     public static void customAssertEquals(final String message,
311                                           final RealVector expected, final RealVector actual, final double delta) {
312         final String msgAndSep = message.equals("") ? "" : message + ", ";
313         assertEquals(expected.getDimension(),
314             actual.getDimension(),
315             msgAndSep + "dimension");
316         final int dim = expected.getDimension();
317         for (int i = 0; i < dim; i++) {
318             assertEquals(expected.getEntry(i), actual.getEntry(i), delta, msgAndSep + "entry #" + i);
319         }
320     }
321 
322     /** verifies that two matrices are close (1-norm) */
323     public static void customAssertEquals(String msg, RealMatrix expected, RealMatrix observed, double tolerance) {
324 
325         assertNotNull(observed,msg + "\nObserved should not be null");
326 
327         if (expected.getColumnDimension() != observed.getColumnDimension() ||
328                 expected.getRowDimension() != observed.getRowDimension()) {
329             StringBuilder messageBuffer = new StringBuilder(msg);
330             messageBuffer.append("\nObserved has incorrect dimensions.");
331             messageBuffer.append("\nobserved is " + observed.getRowDimension() +
332                     " x " + observed.getColumnDimension());
333             messageBuffer.append("\nexpected " + expected.getRowDimension() +
334                     " x " + expected.getColumnDimension());
335             fail(messageBuffer.toString());
336         }
337 
338         RealMatrix delta = expected.subtract(observed);
339         if (delta.getNorm1() >= tolerance) {
340             StringBuilder messageBuffer = new StringBuilder(msg);
341             messageBuffer.append("\nExpected: " + expected);
342             messageBuffer.append("\nObserved: " + observed);
343             messageBuffer.append("\nexpected - observed: " + delta);
344             fail(messageBuffer.toString());
345         }
346     }
347 
348     /** verifies that two matrices are equal */
349     public static void customAssertEquals(FieldMatrix<? extends FieldElement<?>> expected,
350                                           FieldMatrix<? extends FieldElement<?>> observed) {
351 
352         assertNotNull(observed,"Observed should not be null");
353 
354         if (expected.getColumnDimension() != observed.getColumnDimension() ||
355                 expected.getRowDimension() != observed.getRowDimension()) {
356             StringBuilder messageBuffer = new StringBuilder();
357             messageBuffer.append("Observed has incorrect dimensions.");
358             messageBuffer.append("\nobserved is " + observed.getRowDimension() +
359                     " x " + observed.getColumnDimension());
360             messageBuffer.append("\nexpected " + expected.getRowDimension() +
361                     " x " + expected.getColumnDimension());
362             fail(messageBuffer.toString());
363         }
364 
365         for (int i = 0; i < expected.getRowDimension(); ++i) {
366             for (int j = 0; j < expected.getColumnDimension(); ++j) {
367                 FieldElement<?> eij = expected.getEntry(i, j);
368                 FieldElement<?> oij = observed.getEntry(i, j);
369                 assertEquals(eij, oij);
370             }
371         }
372     }
373 
374     /** verifies that two arrays are close (sup norm) */
375     public static void customAssertEquals(String msg, double[] expected, double[] observed, double tolerance) {
376         StringBuilder out = new StringBuilder(msg);
377         if (expected.length != observed.length) {
378             out.append("\n Arrays not same length. \n");
379             out.append("expected has length ");
380             out.append(expected.length);
381             out.append(" observed length = ");
382             out.append(observed.length);
383             fail(out.toString());
384         }
385         boolean failure = false;
386         for (int i=0; i < expected.length; i++) {
387             if (!Precision.equalsIncludingNaN(expected[i], observed[i], tolerance)) {
388                 failure = true;
389                 out.append("\n Elements at index ");
390                 out.append(i);
391                 out.append(" differ. ");
392                 out.append(" expected = ");
393                 out.append(expected[i]);
394                 out.append(" observed = ");
395                 out.append(observed[i]);
396             }
397         }
398         if (failure) {
399             fail(out.toString());
400         }
401     }
402 
403     /** verifies that two int arrays are equal */
404     public static void customAssertEquals(int[] expected, int[] observed) {
405         StringBuilder out = new StringBuilder();
406         if (expected.length != observed.length) {
407             out.append("\n Arrays not same length. \n");
408             out.append("expected has length ");
409             out.append(expected.length);
410             out.append(" observed length = ");
411             out.append(observed.length);
412             fail(out.toString());
413         }
414         boolean failure = false;
415         for (int i=0; i < expected.length; i++) {
416             if (expected[i] != observed[i]) {
417                 failure = true;
418                 out.append("\n Elements at index ");
419                 out.append(i);
420                 out.append(" differ. ");
421                 out.append(" expected = ");
422                 out.append(expected[i]);
423                 out.append(" observed = ");
424                 out.append(observed[i]);
425             }
426         }
427         if (failure) {
428             fail(out.toString());
429         }
430     }
431 
432     /**
433      * verifies that for i = 0,..., observed.length, observed[i] is within epsilon of one of the values in expected[i]
434      * or observed[i] is NaN and expected[i] contains a NaN.
435      */
436     public static void customAssertContains(double[][] expected, double[] observed, double epsilon) {
437         StringBuilder out = new StringBuilder();
438         if (expected.length != observed.length) {
439             out.append("\n Arrays not same length. \n");
440             out.append("expected has length ");
441             out.append(expected.length);
442             out.append(" observed length = ");
443             out.append(observed.length);
444             fail(out.toString());
445         }
446         boolean failure = false;
447         for (int i = 0; i < expected.length; i++) {
448             boolean found = false;
449             for (int j = 0; j < expected[i].length; j++) {
450                 if (Precision.equalsIncludingNaN(expected[i][j], observed[i], epsilon)) {
451                     found = true;
452                     break;
453                 }
454             }
455             if (!found) {
456                 out.append("\n Observed element at index ");
457                 out.append(i);
458                 out.append(" is not among the expected values. ");
459                 out.append(" expected = " + Arrays.toString(expected[i]));
460                 out.append(" observed = ");
461                 out.append(observed[i]);
462                 failure = true;
463             }
464         }
465         if (failure) {
466             fail(out.toString());
467         }
468     }
469 
470 
471 
472     /** verifies that two int arrays are equal */
473     public static void customAssertEquals(long[] expected, long[] observed) {
474         StringBuilder out = new StringBuilder();
475         if (expected.length != observed.length) {
476             out.append("\n Arrays not same length. \n");
477             out.append("expected has length ");
478             out.append(expected.length);
479             out.append(" observed length = ");
480             out.append(observed.length);
481             fail(out.toString());
482         }
483         boolean failure = false;
484         for (int i=0; i < expected.length; i++) {
485             if (expected[i] != observed[i]) {
486                 failure = true;
487                 out.append("\n Elements at index ");
488                 out.append(i);
489                 out.append(" differ. ");
490                 out.append(" expected = ");
491                 out.append(expected[i]);
492                 out.append(" observed = ");
493                 out.append(observed[i]);
494             }
495         }
496         if (failure) {
497             fail(out.toString());
498         }
499     }
500 
501     /** verifies that two arrays are equal */
502     public static <T extends FieldElement<T>> void customAssertEquals(T[] m, T[] n) {
503         if (m.length != n.length) {
504             fail("vectors not same length");
505         }
506         for (int i = 0; i < m.length; i++) {
507             assertEquals(m[i],n[i]);
508         }
509     }
510 
511     /**
512      * Computes the sum of squared deviations of <values> from <target>
513      * @param values array of deviates
514      * @param target value to compute deviations from
515      *
516      * @return sum of squared deviations
517      */
518     public static double sumSquareDev(double[] values, double target) {
519         double sumsq = 0d;
520         for (int i = 0; i < values.length; i++) {
521             final double dev = values[i] - target;
522             sumsq += (dev * dev);
523         }
524         return sumsq;
525     }
526 
527     /**
528      * Asserts the null hypothesis for a ChiSquare test.  Fails and dumps arguments and test
529      * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
530      *
531      * @param valueLabels labels for the values of the discrete distribution under test
532      * @param expected expected counts
533      * @param observed observed counts
534      * @param alpha significance level of the test
535      */
536     public static void customAssertChiSquareAccept(String[] valueLabels, double[] expected, long[] observed, double alpha) {
537 
538         // Fail if we can reject null hypothesis that distributions are the same
539         if (chiSquareTest(expected, observed) <= alpha) {
540             StringBuilder msgBuffer = new StringBuilder();
541             DecimalFormat df = new DecimalFormat("#.##");
542             msgBuffer.append("Chisquare test failed");
543             msgBuffer.append(" p-value = ");
544             msgBuffer.append(chiSquareTest(expected, observed));
545             msgBuffer.append(" chisquare statistic = ");
546             msgBuffer.append(chiSquare(expected, observed));
547             msgBuffer.append(". \n");
548             msgBuffer.append("value\texpected\tobserved\n");
549             for (int i = 0; i < expected.length; i++) {
550                 msgBuffer.append(valueLabels[i]);
551                 msgBuffer.append("\t");
552                 msgBuffer.append(df.format(expected[i]));
553                 msgBuffer.append("\t\t");
554                 msgBuffer.append(observed[i]);
555                 msgBuffer.append("\n");
556             }
557             msgBuffer.append("This test can fail randomly due to sampling error with probability ");
558             msgBuffer.append(alpha);
559             msgBuffer.append(".");
560             fail(msgBuffer.toString());
561         }
562     }
563 
564     /**
565      * Asserts the null hypothesis for a ChiSquare test.  Fails and dumps arguments and test
566      * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
567      *
568      * @param values integer values whose observed and expected counts are being compared
569      * @param expected expected counts
570      * @param observed observed counts
571      * @param alpha significance level of the test
572      */
573     public static void customAssertChiSquareAccept(int[] values, double[] expected, long[] observed, double alpha) {
574         String[] labels = new String[values.length];
575         for (int i = 0; i < values.length; i++) {
576             labels[i] = Integer.toString(values[i]);
577         }
578         customAssertChiSquareAccept(labels, expected, observed, alpha);
579     }
580 
581     /**
582      * Asserts the null hypothesis for a ChiSquare test.  Fails and dumps arguments and test
583      * statistics if the null hypothesis can be rejected with confidence 100 * (1 - alpha)%
584      *
585      * @param expected expected counts
586      * @param observed observed counts
587      * @param alpha significance level of the test
588      */
589     public static void customAssertChiSquareAccept(double[] expected, long[] observed, double alpha) {
590         String[] labels = new String[expected.length];
591         for (int i = 0; i < labels.length; i++) {
592             labels[i] = Integer.toString(i + 1);
593         }
594         customAssertChiSquareAccept(labels, expected, observed, alpha);
595     }
596 
597     /**
598      * Asserts the null hypothesis that the sample follows the given distribution, using a G-test
599      *
600      * @param expectedDistribution distribution values are supposed to follow
601      * @param values sample data
602      * @param alpha significance level of the test
603      */
604     public static void customAssertGTest(final RealDistribution expectedDistribution, final double[] values, double alpha) {
605         final int numBins = values.length / 30;
606         final double[] breaks = new double[numBins];
607         for (int b = 0; b < breaks.length; b++) {
608             breaks[b] = expectedDistribution.inverseCumulativeProbability((double) b / numBins);
609         }
610 
611         final long[] observed = new long[numBins];
612         for (final double value : values) {
613             int b = 0;
614             do {
615                 b++;
616             } while (b < numBins && value >= breaks[b]);
617 
618             observed[b - 1]++;
619         }
620 
621         final double[] expected = new double[numBins];
622         Arrays.fill(expected, (double) values.length / numBins);
623 
624         customAssertGTest(expected, observed, alpha);
625     }
626 
627     /**
628      * Asserts the null hypothesis that the observed counts follow the given distribution implied by expected,
629      * using a G-test
630      *
631      * @param expected expected counts
632      * @param observed observed counts
633      * @param alpha significance level of the test
634      */
635     public static void customAssertGTest(final double[] expected, long[] observed, double alpha) {
636         if (gTest(expected, observed) <  alpha) {
637             StringBuilder msgBuffer = new StringBuilder();
638             DecimalFormat df = new DecimalFormat("#.##");
639             msgBuffer.append("G test failed");
640             msgBuffer.append(" p-value = ");
641             msgBuffer.append(gTest(expected, observed));
642             msgBuffer.append(". \n");
643             msgBuffer.append("value\texpected\tobserved\n");
644             for (int i = 0; i < expected.length; i++) {
645                 msgBuffer.append(df.format(expected[i]));
646                 msgBuffer.append("\t\t");
647                 msgBuffer.append(observed[i]);
648                 msgBuffer.append("\n");
649             }
650             msgBuffer.append("This test can fail randomly due to sampling error with probability ");
651             msgBuffer.append(alpha);
652             msgBuffer.append(".");
653             fail(msgBuffer.toString());
654         }
655     }
656 
657     /**
658      * Computes the 25th, 50th and 75th percentiles of the given distribution and returns
659      * these values in an array.
660      */
661     public static double[] getDistributionQuartiles(RealDistribution distribution) {
662         double[] quantiles = new double[3];
663         quantiles[0] = distribution.inverseCumulativeProbability(0.25d);
664         quantiles[1] = distribution.inverseCumulativeProbability(0.5d);
665         quantiles[2] = distribution.inverseCumulativeProbability(0.75d);
666         return quantiles;
667     }
668 
669     /**
670      * Updates observed counts of values in quartiles.
671      * counts[0] ↔ 1st quartile ... counts[3] ↔ top quartile
672      */
673     public static void updateCounts(double value, long[] counts, double[] quartiles) {
674         if (value < quartiles[0]) {
675             counts[0]++;
676         } else if (value > quartiles[2]) {
677             counts[3]++;
678         } else if (value > quartiles[1]) {
679             counts[2]++;
680         } else {
681             counts[1]++;
682         }
683     }
684 
685     /**
686      * Eliminates points with zero mass from densityPoints and densityValues parallel
687      * arrays.  Returns the number of positive mass points and collapses the arrays so
688      * that the first <returned value> elements of the input arrays represent the positive
689      * mass points.
690      */
691     public static int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) {
692         int positiveMassCount = 0;
693         for (int i = 0; i < densityValues.length; i++) {
694             if (densityValues[i] > 0) {
695                 positiveMassCount++;
696             }
697         }
698         if (positiveMassCount < densityValues.length) {
699             int[] newPoints = new int[positiveMassCount];
700             double[] newValues = new double[positiveMassCount];
701             int j = 0;
702             for (int i = 0; i < densityValues.length; i++) {
703                 if (densityValues[i] > 0) {
704                     newPoints[j] = densityPoints[i];
705                     newValues[j] = densityValues[i];
706                     j++;
707                 }
708             }
709             System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount);
710             System.arraycopy(newValues,0,densityValues,0,positiveMassCount);
711         }
712         return positiveMassCount;
713     }
714 
715     /*************************************************************************************
716      * Stripped-down implementations of some basic statistics borrowed from hipparchus-stat.
717      * NOTE: These implementations are NOT intended for reuse.  They are neither robust,
718      * nor efficient; nor do they handle NaN, infinity or other corner cases in
719      * a predictable way. They DO NOT CHECK PARAMETERS - the assumption is that incorrect
720      * or meaningless results from bad parameters will trigger test failures in unit
721      * tests using these methods.
722      ************************************************************************************/
723 
724     /**
725      * Returns p-value associated with null hypothesis that observed counts follow
726      * expected distribution.  Will normalize inputs if necessary.
727      *
728      * @param expected expected counts
729      * @param observed observed counts
730      * @return p-value of Chi-square test
731      */
732     public static double chiSquareTest(final double[] expected, final long[] observed) {
733             final org.hipparchus.distribution.continuous.ChiSquaredDistribution distribution =
734                 new ChiSquaredDistribution(expected.length - 1.0);
735             return 1.0 - distribution.cumulativeProbability(chiSquare(expected, observed));
736     }
737 
738     /**
739      * Returns chi-square test statistic for expected and observed arrays. Rescales arrays
740      * if necessary.
741      *
742      * @param expected expected counts
743      * @param observed observed counts
744      * @return chi-square statistic
745      */
746     public static double chiSquare(final double[] expected, final long[] observed) {
747             double sumExpected = 0d;
748             double sumObserved = 0d;
749             for (int i = 0; i < observed.length; i++) {
750                 sumExpected += expected[i];
751                 sumObserved += observed[i];
752             }
753             double ratio = 1.0d;
754             boolean rescale = false;
755             if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
756                 ratio = sumObserved / sumExpected;
757                 rescale = true;
758             }
759             double sumSq = 0.0d;
760             for (int i = 0; i < observed.length; i++) {
761                 if (rescale) {
762                     final double dev = observed[i] - ratio * expected[i];
763                     sumSq += dev * dev / (ratio * expected[i]);
764                 } else {
765                     final double dev = observed[i] - expected[i];
766                     sumSq += dev * dev / expected[i];
767                 }
768             }
769             return sumSq;
770 
771         }
772 
773     /**
774      * Computes G-test statistic for expected, observed counts.
775      * @param expected expected counts
776      * @param observed observed counts
777      * @return G statistic
778      */
779     private static double g(final double[] expected, final long[] observed) {
780         double sumExpected = 0d;
781         double sumObserved = 0d;
782         for (int i = 0; i < observed.length; i++) {
783             sumExpected += expected[i];
784             sumObserved += observed[i];
785         }
786         double ratio = 1d;
787         boolean rescale = false;
788         if (FastMath.abs(sumExpected - sumObserved) > 10E-6) {
789             ratio = sumObserved / sumExpected;
790             rescale = true;
791         }
792         double sum = 0d;
793         for (int i = 0; i < observed.length; i++) {
794             final double dev = rescale ?
795                     FastMath.log(observed[i] / (ratio * expected[i])) :
796                         FastMath.log(observed[i] / expected[i]);
797             sum += (observed[i]) * dev;
798         }
799         return 2d * sum;
800     }
801 
802     /**
803      * Computes p-value for G-test.
804      *
805      * @param expected expected counts
806      * @param observed observed counts
807      * @return p-value
808      */
809     private static double gTest(final double[] expected, final long[] observed) {
810         final ChiSquaredDistribution distribution =
811                 new ChiSquaredDistribution(expected.length - 1.0);
812         return 1.0 - distribution.cumulativeProbability(g(expected, observed));
813     }
814 
815     /**
816      * Computes the mean of the values in the array.
817      *
818      * @param values input values
819      * @return arithmetic mean
820      */
821     public static double mean(final double[] values) {
822         double sum = 0;
823         for (double val : values) {
824             sum += val;
825         }
826         return sum / values.length;
827     }
828 
829     /**
830      * Computes the (bias-adjusted) variance of the values in the input array.
831      *
832      * @param values input values
833      * @return bias-adjusted variance
834      */
835     public static double variance(final double[] values) {
836         final int length = values.length;
837         final double mean = mean(values);
838         double var = Double.NaN;
839         if (length == 1) {
840             var = 0.0;
841         } else if (length > 1) {
842             double accum = 0.0;
843             double dev = 0.0;
844             double accum2 = 0.0;
845             for (int i = 0; i < length; i++) {
846                 dev = values[i] - mean;
847                 accum += dev * dev;
848                 accum2 += dev;
849             }
850             final double len = length;
851             var = (accum - (accum2 * accum2 / len)) / (len - 1.0);
852         }
853         return var;
854     }
855 
856     /**
857      * Computes the standard deviation of the values in the input array.
858      *
859      * @param values input values
860      * @return standard deviation
861      */
862     public static double standardDeviation(final double[] values) {
863         return FastMath.sqrt(variance(values));
864     }
865 
866     /**
867      * Computes the median of the values in the input array.
868      *
869      * @param values input values
870      * @return estimated median
871      */
872     public static double median(final double[] values) {
873         final int len = values.length;
874         final double[] sortedValues = Arrays.copyOf(values, len);
875         Arrays.sort(sortedValues);
876         if (len % 2 == 0) {
877             return ((double)sortedValues[len/2] + (double)sortedValues[len/2 - 1])/2;
878         } else {
879             return (double) sortedValues[len/2];
880         }
881     }
882 
883     /**
884      * Computes the covariance of the two input arrays.
885      *
886      * @param xArray first covariate
887      * @param yArray second covariate
888      * @return covariance
889      */
890     public static double covariance(final double[] xArray, final double[] yArray) {
891         double result = 0d;
892         final int length = xArray.length;
893         final double xMean = mean(xArray);
894         final double yMean = mean(yArray);
895         for (int i = 0; i < length; i++) {
896             final double xDev = xArray[i] - xMean;
897             final double yDev = yArray[i] - yMean;
898             result += (xDev * yDev - result) / (i + 1);
899         }
900         return result * ((double) length / (double)(length - 1));
901     }
902 
903     /**
904      * Computes a covariance matrix from a matrix whose columns represent covariates.
905      *
906      * @param matrix input matrix
907      * @return covariance matrix
908      */
909     public static RealMatrix covarianceMatrix(RealMatrix matrix) {
910         int dimension = matrix.getColumnDimension();
911         final RealMatrix outMatrix = new BlockRealMatrix(dimension, dimension);
912         for (int i = 0; i < dimension; i++) {
913             for (int j = 0; j < i; j++) {
914                 final double cov = covariance(matrix.getColumn(i), matrix.getColumn(j));
915                 outMatrix.setEntry(i, j, cov);
916                 outMatrix.setEntry(j, i, cov);
917             }
918             outMatrix.setEntry(i, i, variance(matrix.getColumn(i)));
919         }
920         return outMatrix;
921     }
922 
923     public static double min(final double[] values) {
924         double min = values[0];
925         for (int i = 1; i < values.length; i++) {
926             if (values[i] < min) {
927                 min = values[i];
928             }
929         }
930         return min;
931     }
932 
933     /**
934      * Computes the maximum of the values in the input array.
935      *
936      * @param values input array
937      * @return the maximum value
938      */
939     public static double max(final double[] values) {
940         double max = values[0];
941         for (int i = 1; i < values.length; i++) {
942             if (values[i] > max) {
943                 max = values[i];
944             }
945         }
946         return max;
947     }
948 
949     /**
950      * Unpacks a list of Doubles into a double[].
951      *
952      * @param values list of Double
953      * @return double array
954      */
955     private static double[] unpack(List<Double> values) {
956         int n = values.size();
957         if (values == null || n == 0) {
958             return new double[] {};
959         }
960         double[] out = new double[n];
961         for (int i = 0; i < n; i++) {
962             out[i] = values.get(i);
963         }
964         return out;
965     }
966 
967     /**
968      * Keeps track of the number of occurrences of distinct T instances
969      * added via {@link #addValue(Object)}.
970      *
971      * @param <T> type of objects being tracked
972      */
973     public static class Frequency<T> {
974         private Map<T, Integer> counts = new HashMap<>();
975         public void addValue(T value) {
976            Integer old = counts.put(value, 0);
977            if (old != null) {
978                counts.put(value, old++);
979            }
980         }
981         public int getCount(T value) {
982            Integer ret = counts.get(value);
983            return ret == null ? 0 : ret;
984         }
985     }
986 
987     /**
988      * Stripped down implementation of StreamingStatistics from o.h.stat.descriptive.
989      * Actually holds all values in memory, so not suitable for very large streams of data.
990      */
991     public static class SimpleStatistics {
992         private final List<Double> values = new ArrayList<>();
993         public void addValue(double value) {
994             values.add(value);
995         }
996         public double getMean() {
997             return mean(unpack(values));
998         }
999         public double getStandardDeviation() {
1000             return standardDeviation(unpack(values));
1001         }
1002         public double getMin() {
1003             return min(unpack(values));
1004         }
1005         public double getMax() {
1006             return max(unpack(values));
1007         }
1008         public double getMedian() {
1009             return median(unpack(values));
1010         }
1011         public double getVariance() {
1012             return variance(unpack(values));
1013         }
1014         public long getN() {
1015             return values.size();
1016         }
1017     }
1018 
1019     /**
1020      * Stripped-down version of the bivariate regression class with the same name
1021      * in o.h.stat.regression.
1022      * Always estimates the model with an intercept term.
1023      */
1024     public static class SimpleRegression {
1025         private double sumX = 0d;
1026         private double sumXX = 0d;
1027         private double sumY = 0d;
1028         private double sumXY = 0d;
1029         private long n = 0;
1030         private double xbar = 0;
1031         private double ybar = 0;
1032 
1033         public void addData(double x, double y) {
1034             if (n == 0) {
1035                 xbar = x;
1036                 ybar = y;
1037             } else {
1038                 final double fact1 = 1.0 + n;
1039                 final double fact2 = n / (1.0 + n);
1040                 final double dx = x - xbar;
1041                 final double dy = y - ybar;
1042                 sumXX += dx * dx * fact2;
1043                 sumXY += dx * dy * fact2;
1044                 xbar += dx / fact1;
1045                 ybar += dy / fact1;
1046             }
1047             sumX += x;
1048             sumY += y;
1049             n++;
1050         }
1051 
1052         public double getSlope() {
1053             if (n < 2) {
1054                 return Double.NaN; //not enough data
1055             }
1056             if (FastMath.abs(sumXX) < 10 * Double.MIN_VALUE) {
1057                 return Double.NaN; //not enough variation in x
1058             }
1059             return sumXY / sumXX;
1060         }
1061 
1062         public double getIntercept() {
1063             return (sumY - getSlope() * sumX) / n;
1064         }
1065     }
1066 }