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  package org.hipparchus.stat.descriptive.rank;
23  
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertFalse;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.util.Arrays;
30  
31  import org.hipparchus.distribution.continuous.NormalDistribution;
32  import org.hipparchus.exception.MathIllegalArgumentException;
33  import org.hipparchus.exception.NullArgumentException;
34  import org.hipparchus.random.RandomDataGenerator;
35  import org.hipparchus.stat.descriptive.UnivariateStatistic;
36  import org.hipparchus.stat.descriptive.UnivariateStatisticAbstractTest;
37  import org.hipparchus.stat.descriptive.rank.Percentile.EstimationType;
38  import org.hipparchus.stat.ranking.NaNStrategy;
39  import org.hipparchus.util.KthSelector;
40  import org.hipparchus.util.PivotingStrategy;
41  import org.junit.Before;
42  import org.junit.Test;
43  
44  /**
45   * Test cases for the {@link Percentile} class.
46   */
47  public class PercentileTest extends UnivariateStatisticAbstractTest{
48  
49      private double quantile;
50  
51      /**
52       * {@link Percentile.EstimationType type}
53       * of estimation to be used while calling {@link #getUnivariateStatistic()}
54       */
55      private Percentile.EstimationType type;
56  
57      /**
58       * {@link NaNStrategy}
59       * of estimation to be used while calling {@link #getUnivariateStatistic()}
60       */
61      private NaNStrategy nanStrategy;
62  
63      /**
64       * kth selector
65       */
66      private KthSelector kthSelector;
67  
68      /**
69       * A default percentile to be used for {@link #getUnivariateStatistic()}
70       */
71      protected final double DEFAULT_PERCENTILE = 95d;
72  
73      /**
74       * Before method to ensure defaults retained
75       */
76      @Before
77      public void setup() {
78          quantile         = 95.0;
79          type             = Percentile.EstimationType.LEGACY;
80          nanStrategy      = NaNStrategy.REMOVED;
81          kthSelector      = new KthSelector(PivotingStrategy.MEDIAN_OF_3);
82      }
83  
84      private void reset(final double p, final Percentile.EstimationType type) {
85          this.quantile = p;
86          this.type     = type;
87          nanStrategy   = (type == Percentile.EstimationType.LEGACY) ? NaNStrategy.FIXED : NaNStrategy.REMOVED;
88      }
89  
90      @Override
91      public Percentile getUnivariateStatistic() {
92          return new Percentile(quantile)
93                  .withEstimationType(type)
94                  .withNaNStrategy(nanStrategy)
95                  .withKthSelector(kthSelector);
96      }
97  
98      @Override
99      public double expectedValue() {
100         return this.percentile95;
101     }
102 
103     @Test
104     public void testHighPercentile(){
105         final double[] d = new double[]{1, 2, 3};
106         final Percentile p = new Percentile(75);
107         assertEquals(3.0, p.evaluate(d), 1.0e-5);
108     }
109 
110     @Test
111     public void testLowPercentile() {
112         final double[] d = new double[] {0, 1};
113         final Percentile p = new Percentile(25);
114         assertEquals(0d, p.evaluate(d), Double.MIN_VALUE);
115     }
116 
117     @Test
118     public void testPercentile() {
119         final double[] d = new double[] {1, 3, 2, 4};
120         final Percentile p = new Percentile(30);
121         assertEquals(1.5, p.evaluate(d), 1.0e-5);
122         p.setQuantile(25);
123         assertEquals(1.25, p.evaluate(d), 1.0e-5);
124         p.setQuantile(75);
125         assertEquals(3.75, p.evaluate(d), 1.0e-5);
126         p.setQuantile(50);
127         assertEquals(2.5, p.evaluate(d), 1.0e-5);
128 
129         // invalid percentiles
130         try {
131             p.evaluate(d, 0, d.length, -1.0);
132             fail();
133         } catch (final MathIllegalArgumentException ex) {
134             // success
135         }
136         try {
137             p.evaluate(d, 0, d.length, 101.0);
138             fail();
139         } catch (final MathIllegalArgumentException ex) {
140             // success
141         }
142     }
143 
144     @Test
145     public void testNISTExample() {
146         final double[] d = new double[] {95.1772, 95.1567, 95.1937, 95.1959,
147                 95.1442, 95.0610,  95.1591, 95.1195, 95.1772, 95.0925, 95.1990, 95.1682
148         };
149         final Percentile p = new Percentile(90);
150         assertEquals(95.1981, p.evaluate(d), 1.0e-4);
151         assertEquals(95.1990, p.evaluate(d,0,d.length, 100d), 0);
152     }
153 
154     @Test
155     public void test5() {
156         final Percentile percentile = new Percentile(5);
157         assertEquals(this.percentile5, percentile.evaluate(testArray), getTolerance());
158     }
159 
160     @Test
161     public void testNullEmpty() {
162         final Percentile percentile = new Percentile(50);
163         final double[] nullArray = null;
164         final double[] emptyArray = new double[] {};
165         try {
166             percentile.evaluate(nullArray);
167             fail("Expecting NullArgumentException for null array");
168         } catch (final NullArgumentException ex) {
169             // expected
170         }
171         assertTrue(Double.isNaN(percentile.evaluate(emptyArray)));
172     }
173 
174     @Test
175     public void testSingleton() {
176         final Percentile percentile = new Percentile(50);
177         final double[] singletonArray = new double[] {1d};
178         assertEquals(1d, percentile.evaluate(singletonArray), 0);
179         assertEquals(1d, percentile.evaluate(singletonArray, 0, 1), 0);
180         assertEquals(1d, percentile.evaluate(singletonArray, 0, 1, 5), 0);
181         assertEquals(1d, percentile.evaluate(singletonArray, 0, 1, 100), 0);
182         assertTrue(Double.isNaN(percentile.evaluate(singletonArray, 0, 0)));
183     }
184 
185     @Test
186     public void testSpecialValues() {
187         final Percentile percentile = new Percentile(50);
188         double[] specialValues = new double[] {0d, 1d, 2d, 3d, 4d,  Double.NaN};
189         assertEquals(/*2.5d*/2d, percentile.evaluate(specialValues), 0);
190         specialValues = new double[] { Double.NEGATIVE_INFINITY, 1d, 2d, 3d, Double.NaN, Double.POSITIVE_INFINITY };
191         assertEquals(/*2.5d*/2d, percentile.evaluate(specialValues), 0);
192         specialValues = new double[] {1d, 1d, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY};
193         assertTrue(Double.isInfinite(percentile.evaluate(specialValues)));
194         specialValues = new double[] {1d, 1d, Double.NaN, Double.NaN};
195         assertTrue(!Double.isNaN(percentile.evaluate(specialValues)));
196         assertTrue(1d==percentile.evaluate(specialValues));
197         specialValues = new double[] {1d, 1d, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY};
198         // Interpolation results in NEGATIVE_INFINITY + POSITIVE_INFINITY
199         assertTrue(Double.isNaN(percentile.evaluate(specialValues)));
200     }
201 
202     @Test
203     public void testSetQuantile() {
204         final Percentile percentile = new Percentile(10);
205         percentile.setQuantile(100); // OK
206         assertEquals(100, percentile.getQuantile(), 0);
207         try {
208             percentile.setQuantile(0);
209             fail("Expecting MathIllegalArgumentException");
210         } catch (final MathIllegalArgumentException ex) {
211             // expected
212         }
213         try {
214             new Percentile(0);
215             fail("Expecting MathIllegalArgumentException");
216         } catch (final MathIllegalArgumentException ex) {
217             // expected
218         }
219     }
220 
221     //Below tests are basically to run for all estimation types.
222     /**
223      * While {@link #testHighPercentile()} checks only for the existing
224      * implementation; this method verifies for all the types including Percentile.Type.CM Percentile.Type.
225      */
226     @Test
227     public void testAllTechniquesHighPercentile() {
228         final double[] d = new double[] { 1, 2, 3 };
229         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 3d }, { Percentile.EstimationType.R_1, 3d },
230                 { Percentile.EstimationType.R_2, 3d }, { Percentile.EstimationType.R_3, 2d }, { Percentile.EstimationType.R_4, 2.25 }, { Percentile.EstimationType.R_5, 2.75 },
231                 { Percentile.EstimationType.R_6, 3d }, { Percentile.EstimationType.R_7, 2.5 },{ Percentile.EstimationType.R_8, 2.83333 }, {Percentile.EstimationType.R_9,2.81250} },
232                 75d, 1.0e-5);
233     }
234 
235     @Test
236     public void testAllTechniquesLowPercentile() {
237         final double[] d = new double[] { 0, 1 };
238         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 0d }, { Percentile.EstimationType.R_1, 0d },
239                 { Percentile.EstimationType.R_2, 0d }, { Percentile.EstimationType.R_3, 0d }, { Percentile.EstimationType.R_4, 0d }, {Percentile.EstimationType.R_5, 0d}, {Percentile.EstimationType.R_6, 0d},
240                 { Percentile.EstimationType.R_7, 0.25 }, { Percentile.EstimationType.R_8, 0d }, {Percentile.EstimationType.R_9, 0d} },
241                 25d, Double.MIN_VALUE);
242     }
243 
244     public void checkAllTechniquesPercentile() {
245         final double[] d = new double[] { 1, 3, 2, 4 };
246 
247         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 1.5d },
248                 { Percentile.EstimationType.R_1, 2d }, { Percentile.EstimationType.R_2, 2d }, { Percentile.EstimationType.R_3, 1d }, { Percentile.EstimationType.R_4, 1.2 }, {Percentile.EstimationType.R_5, 1.7},
249                 { Percentile.EstimationType.R_6, 1.5 },{ Percentile.EstimationType.R_7, 1.9 }, { Percentile.EstimationType.R_8, 1.63333 },{ Percentile.EstimationType.R_9, 1.65 } },
250                 30d, 1.0e-05);
251 
252         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 1.25d },
253                 { Percentile.EstimationType.R_1, 1d }, { Percentile.EstimationType.R_2, 1.5d }, { Percentile.EstimationType.R_3, 1d }, { Percentile.EstimationType.R_4, 1d }, {Percentile.EstimationType.R_5, 1.5},
254                 { Percentile.EstimationType.R_6, 1.25 },{ Percentile.EstimationType.R_7, 1.75 },
255                 { Percentile.EstimationType.R_8, 1.41667 }, { Percentile.EstimationType.R_9, 1.43750 } }, 25d, 1.0e-05);
256 
257         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 3.75d },
258                 { Percentile.EstimationType.R_1, 3d }, { Percentile.EstimationType.R_2, 3.5d }, { Percentile.EstimationType.R_3, 3d }, { Percentile.EstimationType.R_4, 3d },
259                 { Percentile.EstimationType.R_5, 3.5d },{ Percentile.EstimationType.R_6, 3.75d }, { Percentile.EstimationType.R_7, 3.25 },
260                 { Percentile.EstimationType.R_8, 3.58333 },{ Percentile.EstimationType.R_9, 3.56250} }, 75d, 1.0e-05);
261 
262         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 2.5d },
263                 { Percentile.EstimationType.R_1, 2d }, { Percentile.EstimationType.R_2, 2.5d }, { Percentile.EstimationType.R_3, 2d }, { Percentile.EstimationType.R_4, 2d },
264                 { Percentile.EstimationType.R_5, 2.5 },{ Percentile.EstimationType.R_6, 2.5 },{ Percentile.EstimationType.R_7, 2.5 },
265                 { Percentile.EstimationType.R_8, 2.5 },{ Percentile.EstimationType.R_9, 2.5 } }, 50d, 1.0e-05);
266 
267         // invalid percentiles
268         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
269             try {
270                 reset(-1.0, e);
271                 getUnivariateStatistic().evaluate(d, 0, d.length);
272                 fail();
273             } catch (final MathIllegalArgumentException ex) {
274                 // success
275             }
276         }
277 
278         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
279             try {
280                 reset(101.0, e);
281                 getUnivariateStatistic().evaluate(d, 0, d.length);
282                 fail();
283             } catch (final MathIllegalArgumentException ex) {
284                 // success
285             }
286         }
287     }
288 
289     @Test
290     public void testAllTechniquesPercentileUsingMedianOf3Pivoting() {
291         kthSelector = new KthSelector(PivotingStrategy.MEDIAN_OF_3);
292         checkAllTechniquesPercentile();
293     }
294 
295     @Test
296     public void testAllTechniquesPercentileUsingCentralPivoting() {
297         kthSelector = new KthSelector(PivotingStrategy.CENTRAL);
298         checkAllTechniquesPercentile();
299     }
300 
301     @Test
302     public void testAllTechniquesNISTExample() {
303         final double[] d =
304                 new double[] { 95.1772, 95.1567, 95.1937, 95.1959, 95.1442, 95.0610,
305                                95.1591, 95.1195, 95.1772, 95.0925, 95.1990, 95.1682 };
306 
307         testAssertMappedValues(d, new Object[][] { { Percentile.EstimationType.LEGACY, 95.1981 },
308                 { Percentile.EstimationType.R_1, 95.19590 }, { Percentile.EstimationType.R_2, 95.19590 }, { Percentile.EstimationType.R_3, 95.19590 },
309                 { Percentile.EstimationType.R_4, 95.19546 }, { Percentile.EstimationType.R_5, 95.19683 }, { Percentile.EstimationType.R_6, 95.19807 },
310                 { Percentile.EstimationType.R_7, 95.19568 }, { Percentile.EstimationType.R_8, 95.19724 }, { Percentile.EstimationType.R_9, 95.19714 } }, 90d,
311                 1.0e-04);
312 
313         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
314             reset(100.0, e);
315             assertEquals(95.1990, getUnivariateStatistic().evaluate(d), 1.0e-4);
316         }
317     }
318 
319     @Test
320     public void testAllTechniques5() {
321         reset(5, Percentile.EstimationType.LEGACY);
322         final UnivariateStatistic percentile = getUnivariateStatistic();
323         assertEquals(this.percentile5, percentile.evaluate(testArray), getTolerance());
324         testAssertMappedValues(testArray,
325                 new Object[][] { { Percentile.EstimationType.LEGACY, percentile5 }, { Percentile.EstimationType.R_1, 8.8000 },
326                         { Percentile.EstimationType.R_2, 8.8000 }, { Percentile.EstimationType.R_3, 8.2000 }, { Percentile.EstimationType.R_4, 8.2600 },
327                         { Percentile.EstimationType.R_5, 8.5600 }, { Percentile.EstimationType.R_6, 8.2900 },
328                         { Percentile.EstimationType.R_7, 8.8100 }, { Percentile.EstimationType.R_8, 8.4700 },
329                         { Percentile.EstimationType.R_9, 8.4925 }}, 5d, getTolerance());
330     }
331 
332     @Test
333     public void testAllTechniquesNullEmpty() {
334 
335         final double[] nullArray = null;
336         final double[] emptyArray = new double[] {};
337         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
338             reset (50, e);
339             final UnivariateStatistic percentile = getUnivariateStatistic();
340             try {
341                 percentile.evaluate(nullArray);
342                 fail("Expecting NullArgumentException "
343                         + "for null array");
344             } catch (final NullArgumentException ex) {
345                 // expected
346             }
347             assertTrue(Double.isNaN(percentile.evaluate(emptyArray)));
348         }
349 
350     }
351 
352     @Test
353     public void testAllTechniquesSingleton() {
354         final double[] singletonArray = new double[] { 1d };
355         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
356             reset (50, e);
357             final UnivariateStatistic percentile = getUnivariateStatistic();
358             assertEquals(1d, percentile.evaluate(singletonArray), 0);
359             assertEquals(1d, percentile.evaluate(singletonArray, 0, 1), 0);
360             assertEquals(1d, new Percentile().evaluate(singletonArray, 0, 1, 5), 0);
361             assertEquals(1d, new Percentile().evaluate(singletonArray, 0, 1, 100), 0);
362             assertTrue(Double.isNaN(percentile.evaluate(singletonArray, 0, 0)));
363         }
364     }
365 
366     @Test
367     public void testAllTechniquesEmpty() {
368         final double[] singletonArray = new double[] { };
369         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
370             reset (50, e);
371             final UnivariateStatistic percentile = getUnivariateStatistic();
372             assertEquals(Double.NaN, percentile.evaluate(singletonArray), 0);
373             assertEquals(Double.NaN, percentile.evaluate(singletonArray, 0, 0), 0);
374             assertEquals(Double.NaN, new Percentile().evaluate(singletonArray, 0, 0, 5), 0);
375             assertEquals(Double.NaN, new Percentile().evaluate(singletonArray, 0, 0, 100), 0);
376             assertTrue(Double.isNaN(percentile.evaluate(singletonArray, 0, 0)));
377         }
378     }
379 
380     @Test
381     public void testReplaceNanInRange() {
382         final double[] specialValues =
383             new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN, Double.NaN, 5d, 7d, Double.NaN, 8d};
384         assertEquals(/*Double.NaN*/3.5, new Percentile(50d).evaluate(specialValues), 0d);
385         reset (50, Percentile.EstimationType.R_1);
386         assertEquals(3d, getUnivariateStatistic().evaluate(specialValues), 0d);
387         reset (50, Percentile.EstimationType.R_2);
388         assertEquals(3.5d, getUnivariateStatistic().evaluate(specialValues), 0d);
389         assertEquals(Double.POSITIVE_INFINITY,
390                      new Percentile(70).withNaNStrategy(NaNStrategy.MAXIMAL).evaluate(specialValues),
391                      0d);
392     }
393 
394     @Test
395     public void testRemoveNan() {
396         final double[] specialValues = new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN };
397         final double[] expectedValues = new double[] { 0d, 1d, 2d, 3d, 4d };
398         reset (50, Percentile.EstimationType.R_1);
399         assertEquals(2.0, getUnivariateStatistic().evaluate(specialValues), 0d);
400         assertEquals(2.0, getUnivariateStatistic().evaluate(expectedValues),0d);
401         assertTrue(Double.isNaN(getUnivariateStatistic().evaluate(specialValues,5,1)));
402         assertEquals(4d, getUnivariateStatistic().evaluate(specialValues, 4, 2), 0d);
403         assertEquals(3d, getUnivariateStatistic().evaluate(specialValues,3,3),0d);
404         reset(50, Percentile.EstimationType.R_2);
405         assertEquals(3.5d, getUnivariateStatistic().evaluate(specialValues,3,3),0d);
406     }
407 
408     @Test
409     public void testPercentileCopy() {
410        reset(50d, Percentile.EstimationType.LEGACY);
411        final Percentile original = getUnivariateStatistic();
412        final Percentile copy = new Percentile(original);
413        assertEquals(original.getNaNStrategy(),copy.getNaNStrategy());
414        assertEquals(original.getQuantile(), copy.getQuantile(),0d);
415        assertEquals(original.getEstimationType(),copy.getEstimationType());
416        assertEquals(NaNStrategy.FIXED, original.getNaNStrategy());
417     }
418 
419     @Test
420     public void testAllTechniquesSpecialValues() {
421         reset(50d, Percentile.EstimationType.LEGACY);
422         final UnivariateStatistic percentile = getUnivariateStatistic();
423         double[] specialValues = new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN };
424         assertEquals(2.5d, percentile.evaluate(specialValues), 0);
425 
426         testAssertMappedValues(specialValues, new Object[][] {
427                 { Percentile.EstimationType.LEGACY, 2.5d }, { Percentile.EstimationType.R_1, 2.0 }, { Percentile.EstimationType.R_2, 2.0 }, { Percentile.EstimationType.R_3, 1.0 },
428                 { Percentile.EstimationType.R_4, 1.5 }, { Percentile.EstimationType.R_5, 2.0 }, { Percentile.EstimationType.R_6, 2.0 },
429                 { Percentile.EstimationType.R_7, 2.0 }, { Percentile.EstimationType.R_8, 2.0 }, { Percentile.EstimationType.R_9, 2.0 }}, 50d, 0d);
430 
431         specialValues =
432                 new double[] { Double.NEGATIVE_INFINITY, 1d, 2d, 3d, Double.NaN, Double.POSITIVE_INFINITY };
433         assertEquals(2.5d, percentile.evaluate(specialValues), 0);
434 
435         testAssertMappedValues(specialValues, new Object[][] {
436                 { Percentile.EstimationType.LEGACY, 2.5d }, { Percentile.EstimationType.R_1, 2.0 }, { Percentile.EstimationType.R_2, 2.0 }, { Percentile.EstimationType.R_3, 1.0 },
437                 { Percentile.EstimationType.R_4, 1.5 }, { Percentile.EstimationType.R_5, 2.0 }, { Percentile.EstimationType.R_7, 2.0 }, { Percentile.EstimationType.R_7, 2.0 },
438                 { Percentile.EstimationType.R_8, 2.0 }, { Percentile.EstimationType.R_9, 2.0 } }, 50d, 0d);
439 
440         specialValues =
441                 new double[] { 1d, 1d, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY };
442         assertTrue(Double.isInfinite(percentile.evaluate(specialValues)));
443 
444         testAssertMappedValues(specialValues, new Object[][] {
445                 // This is one test not matching with R results.
446                 { Percentile.EstimationType.LEGACY, Double.POSITIVE_INFINITY },
447                 { Percentile.EstimationType.R_1,/* 1.0 */Double.NaN },
448                 { Percentile.EstimationType.R_2, /* Double.POSITIVE_INFINITY */Double.NaN },
449                 { Percentile.EstimationType.R_3, /* 1.0 */Double.NaN }, { Percentile.EstimationType.R_4, /* 1.0 */Double.NaN },
450                 { Percentile.EstimationType.R_5, Double.POSITIVE_INFINITY },
451                 { Percentile.EstimationType.R_6, Double.POSITIVE_INFINITY },
452                 { Percentile.EstimationType.R_7, Double.POSITIVE_INFINITY },
453                 { Percentile.EstimationType.R_8, Double.POSITIVE_INFINITY },
454                 { Percentile.EstimationType.R_9, Double.POSITIVE_INFINITY }, }, 50d, 0d);
455 
456         specialValues = new double[] { 1d, 1d, Double.NaN, Double.NaN };
457         assertTrue(Double.isNaN(percentile.evaluate(specialValues)));
458         testAssertMappedValues(specialValues, new Object[][] {
459                 { Percentile.EstimationType.LEGACY, Double.NaN }, { Percentile.EstimationType.R_1, 1.0 }, { Percentile.EstimationType.R_2, 1.0 }, { Percentile.EstimationType.R_3, 1.0 },
460                 { Percentile.EstimationType.R_4, 1.0 }, { Percentile.EstimationType.R_5, 1.0 },{ Percentile.EstimationType.R_6, 1.0 },{ Percentile.EstimationType.R_7, 1.0 },
461                 { Percentile.EstimationType.R_8, 1.0 }, { Percentile.EstimationType.R_9, 1.0 },}, 50d, 0d);
462 
463         specialValues =
464                 new double[] { 1d, 1d, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY };
465 
466         testAssertMappedValues(specialValues, new Object[][] {
467                 { Percentile.EstimationType.LEGACY, Double.NaN }, { Percentile.EstimationType.R_1, Double.NaN },
468                 { Percentile.EstimationType.R_2, Double.NaN }, { Percentile.EstimationType.R_3, Double.NaN }, { Percentile.EstimationType.R_4, Double.NaN },
469                 { Percentile.EstimationType.R_5, Double.NaN }, { Percentile.EstimationType.R_6, Double.NaN },
470                 { Percentile.EstimationType.R_7, Double.NaN }, { Percentile.EstimationType.R_8, Double.NaN }, { Percentile.EstimationType.R_9, Double.NaN }
471                 }, 50d, 0d);
472     }
473 
474     @Test
475     public void testAllTechniquesSetQuantile() {
476         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
477             reset(10, e);
478             final Percentile percentile = getUnivariateStatistic();
479             percentile.setQuantile(100); // OK
480             assertEquals(100, percentile.getQuantile(), 0);
481             try {
482                 percentile.setQuantile(0);
483                 fail("Expecting MathIllegalArgumentException");
484             } catch (final MathIllegalArgumentException ex) {
485                 // expected
486             }
487             try {
488                 new Percentile(0);
489                 fail("Expecting MathIllegalArgumentException");
490             } catch (final MathIllegalArgumentException ex) {
491                 // expected
492             }
493         }
494     }
495 
496     @Test
497     public void testAllTechniquesEvaluateArraySegmentWeighted() {
498         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
499             reset(quantile, e);
500             testEvaluateArraySegmentWeighted();
501         }
502     }
503 
504     @Test
505     public void testAllTechniquesEvaluateArraySegment() {
506         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
507             reset(quantile, e);
508             testEvaluateArraySegment();
509         }
510     }
511 
512     @Test
513     public void testAllTechniquesWeightedConsistency() {
514         for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
515             reset(quantile, e);
516             testWeightedConsistency();
517         }
518     }
519 
520     @Test
521     public void testAllTechniquesEvaluation() {
522 
523         testAssertMappedValues(testArray, new Object[][] { { Percentile.EstimationType.LEGACY, 20.820 },
524                 { Percentile.EstimationType.R_1, 19.800 }, { Percentile.EstimationType.R_2, 19.800 }, { Percentile.EstimationType.R_3, 19.800 },
525                 { Percentile.EstimationType.R_4, 19.310 }, { Percentile.EstimationType.R_5, 20.280 }, { Percentile.EstimationType.R_6, 20.820 },
526                 { Percentile.EstimationType.R_7, 19.555 }, { Percentile.EstimationType.R_8, 20.460 },{ Percentile.EstimationType.R_9, 20.415} },
527                 DEFAULT_PERCENTILE, tolerance);
528     }
529 
530     @Test
531     public void testPercentileWithTechnique() {
532         reset (50, Percentile.EstimationType.LEGACY);;
533         final Percentile p = getUnivariateStatistic();
534         assertTrue(Percentile.EstimationType.LEGACY.equals(p.getEstimationType()));
535         assertFalse(Percentile.EstimationType.R_1.equals(p.getEstimationType()));
536     }
537 
538     static final int TINY = 10, SMALL = 50, NOMINAL = 100, MEDIUM = 500,
539                      STANDARD = 1000, BIG = 10000, VERY_BIG = 50000, LARGE = 1000000,
540                      VERY_LARGE = 10000000;
541     static final int[] sampleSizes= { TINY, SMALL, NOMINAL, MEDIUM, STANDARD, BIG };
542 
543     @Test
544     public void testStoredVsDirect() {
545         final RandomDataGenerator randomDataGenerator = new RandomDataGenerator(100);
546         final NormalDistribution normalDistribution = new NormalDistribution(4000, 50);
547         for (final int sampleSize:sampleSizes) {
548             final double[] data = randomDataGenerator.nextDeviates(normalDistribution, sampleSize);
549             for (final double p:new double[] {50d,95d}) {
550                 for (final Percentile.EstimationType e : Percentile.EstimationType.values()) {
551                     reset(p, e);
552                     final Percentile pStoredData = getUnivariateStatistic();
553                     pStoredData.setData(data);
554                     final double storedDataResult=pStoredData.evaluate();
555                     pStoredData.setData(null);
556                     final Percentile pDirect = getUnivariateStatistic();
557                     assertEquals("Sample="+sampleSize+",P="+p+" e="+e,
558                                  storedDataResult,
559                                  pDirect.evaluate(data), 0d);
560                 }
561             }
562         }
563     }
564 
565     @Test
566     public void testPercentileWithDataRef() {
567         reset(50.0, Percentile.EstimationType.R_7);
568         final Percentile p = getUnivariateStatistic();
569         p.setData(testArray);
570         assertTrue(Percentile.EstimationType.R_7.equals(p.getEstimationType()));
571         assertFalse(Percentile.EstimationType.R_1.equals(p.getEstimationType()));
572         assertEquals(12d, p.evaluate(), 0d);
573         assertEquals(12.16d, p.evaluate(60d), 0d);
574     }
575 
576     @Test(expected=NullArgumentException.class)
577     public void testNullEstimation() {
578         type = null;
579         getUnivariateStatistic();
580     }
581 
582     @Test
583     public void testAllEstimationTechniquesOnlyLimits() {
584         final int N=testArray.length;
585 
586         final double[] input = testArray.clone();
587         Arrays.sort(input);
588         final double min = input[0];
589         final double max=input[input.length-1];
590         //limits may be ducked by 0.01 to induce the condition of p<pMin
591         final Object[][] map =
592                 new Object[][] { { Percentile.EstimationType.LEGACY, 0d, 1d }, { Percentile.EstimationType.R_1, 0d, 1d },
593                         { Percentile.EstimationType.R_2, 0d,1d }, { Percentile.EstimationType.R_3, 0.5/N,1d },
594                         { Percentile.EstimationType.R_4, 1d/N-0.001,1d },
595                         { Percentile.EstimationType.R_5, 0.5/N-0.001,(N-0.5)/N}, { Percentile.EstimationType.R_6, 0.99d/(N+1),
596                             1.01d*N/(N+1)},
597                         { Percentile.EstimationType.R_7, 0d,1d}, { Percentile.EstimationType.R_8, 1.99d/3/(N+1d/3),
598                             (N-1d/3)/(N+1d/3)},
599                         { Percentile.EstimationType.R_9, 4.99d/8/(N+0.25), (N-3d/8)/(N+0.25)} };
600 
601         for(final Object[] arr:map) {
602             final Percentile.EstimationType t= (Percentile.EstimationType) arr[0];
603             double pMin=(Double)arr[1];
604             final double pMax=(Double)arr[2];
605             assertEquals("Type:"+t,0d, t.index(pMin, N),0d);
606             assertEquals("Type:"+t,N, t.index(pMax, N),0.5d);
607             pMin=pMin==0d?pMin+0.01:pMin;
608             testAssertMappedValues(testArray, new Object[][] { { t, min }}, pMin, 0.01);
609             testAssertMappedValues(testArray, new Object[][] { { t, max }}, pMax * 100, tolerance);
610         }
611     }
612 
613     @Test
614     public void testAllEstimationTechniquesOnly() {
615         assertEquals("Legacy Hipparchus",Percentile.EstimationType.LEGACY.getName());
616         final Object[][] map =
617                 new Object[][] { { Percentile.EstimationType.LEGACY, 20.82 }, { Percentile.EstimationType.R_1, 19.8 },
618                         { Percentile.EstimationType.R_2, 19.8 }, { Percentile.EstimationType.R_3, 19.8 }, { Percentile.EstimationType.R_4, 19.310 },
619                         { Percentile.EstimationType.R_5, 20.280}, { Percentile.EstimationType.R_6, 20.820},
620                         { Percentile.EstimationType.R_7, 19.555 }, { Percentile.EstimationType.R_8, 20.460 },{Percentile.EstimationType.R_9,20.415} };
621         try {
622             Percentile.EstimationType.LEGACY.evaluate(testArray, -1d, new KthSelector(PivotingStrategy.MEDIAN_OF_3));
623         } catch (final MathIllegalArgumentException oore) {
624         }
625         try {
626             Percentile.EstimationType.LEGACY.evaluate(testArray, 101d, new KthSelector());
627         } catch (final MathIllegalArgumentException oore) {
628         }
629         try {
630             Percentile.EstimationType.LEGACY.evaluate(testArray, 50d, new KthSelector());
631         } catch(final MathIllegalArgumentException oore) {
632         }
633         for (final Object[] o : map) {
634             final Percentile.EstimationType e = (Percentile.EstimationType) o[0];
635             final double expected = (Double) o[1];
636             final double result = e.evaluate(testArray, DEFAULT_PERCENTILE, new KthSelector());
637             assertEquals("expected[" + e + "] = " + expected +
638                     " but was = " + result, expected, result, tolerance);
639         }
640     }
641 
642     @Test
643     public void testAllEstimationTechniquesOnlyForAllPivotingStrategies() {
644 
645         assertEquals("Legacy Hipparchus",Percentile.EstimationType.LEGACY.getName());
646 
647         for (final PivotingStrategy strategy : PivotingStrategy.values()) {
648             kthSelector = new KthSelector(strategy);
649             testAllEstimationTechniquesOnly();
650         }
651     }
652 
653     @Test
654     public void testAllEstimationTechniquesOnlyForExtremeIndexes() {
655         final double MAX=100;
656         final Object[][] map =
657                 new Object[][] { { Percentile.EstimationType.LEGACY, 0d, MAX}, { Percentile.EstimationType.R_1, 0d,MAX+0.5 },
658                 { Percentile.EstimationType.R_2, 0d,MAX}, { Percentile.EstimationType.R_3, 0d,MAX }, { Percentile.EstimationType.R_4, 0d,MAX },
659                 { Percentile.EstimationType.R_5, 0d,MAX }, { Percentile.EstimationType.R_6, 0d,MAX },
660                 { Percentile.EstimationType.R_7, 0d,MAX }, { Percentile.EstimationType.R_8, 0d,MAX }, { Percentile.EstimationType.R_9, 0d,MAX }  };
661         for (final Object[] o : map) {
662             final Percentile.EstimationType e = (Percentile.EstimationType) o[0];
663                 assertEquals(((Double)o[1]).doubleValue(), e.index(0d, (int)MAX),0d);
664                 assertEquals("Enum:"+e,((Double)o[2]).doubleValue(), e.index(1.0, (int)MAX),0d);
665             }
666     }
667 
668     @Test
669     public void testAllEstimationTechniquesOnlyForNullsAndOOR() {
670 
671         final Object[][] map =
672                 new Object[][] { { Percentile.EstimationType.LEGACY, 20.82 }, { Percentile.EstimationType.R_1, 19.8 },
673                         { Percentile.EstimationType.R_2, 19.8 }, { Percentile.EstimationType.R_3, 19.8 }, { Percentile.EstimationType.R_4, 19.310 },
674                         { Percentile.EstimationType.R_5, 20.280}, { Percentile.EstimationType.R_6, 20.820},
675                         { Percentile.EstimationType.R_7, 19.555 }, { Percentile.EstimationType.R_8, 20.460 },{ Percentile.EstimationType.R_9, 20.415 } };
676         for (final Object[] o : map) {
677             final Percentile.EstimationType e = (Percentile.EstimationType) o[0];
678             try {
679                 e.evaluate(null, DEFAULT_PERCENTILE, new KthSelector());
680                 fail("Expecting NullArgumentException");
681             } catch (final NullArgumentException nae) {
682                 // expected
683             }
684             try {
685                 e.evaluate(testArray, 120, new KthSelector());
686                 fail("Expecting MathIllegalArgumentException");
687             } catch (final MathIllegalArgumentException oore) {
688                 // expected
689             }
690         }
691     }
692 
693     /**
694      * Simple test assertion utility method assuming {@link NaNStrategy default}
695      * nan handling strategy specific to each {@link EstimationType type}
696      *
697      * @param data input data
698      * @param map of expected result against a {@link EstimationType}
699      * @param p the quantile to compute for
700      * @param tolerance the tolerance of difference allowed
701      */
702     protected void testAssertMappedValues(final double[] data, final Object[][] map,
703             final Double p, final Double tolerance) {
704         for (final Object[] o : map) {
705             final Percentile.EstimationType e = (Percentile.EstimationType) o[0];
706             final double expected = (Double) o[1];
707             try {
708                 reset(p, e);
709                 final double result = getUnivariateStatistic().evaluate(data);
710                 assertEquals("expected[" + e + "] = " + expected +
711                              " but was = " + result, expected, result, tolerance);
712             } catch(final Exception ex) {
713                 fail("Exception occured for estimation type "+e+":"+
714                      ex.getLocalizedMessage());
715             }
716         }
717     }
718 
719     // Some NaNStrategy specific testing
720     @Test
721     public void testNanStrategySpecific() {
722         double[] specialValues = new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN };
723         assertTrue(Double.isNaN(new Percentile(50d).withEstimationType(Percentile.EstimationType.LEGACY).withNaNStrategy(NaNStrategy.MAXIMAL).evaluate(specialValues, 3, 3)));
724         assertEquals(2d,new Percentile(50d).withEstimationType(Percentile.EstimationType.R_1).withNaNStrategy(NaNStrategy.REMOVED).evaluate(specialValues),0d);
725         assertEquals(Double.NaN,new Percentile(50d).withEstimationType(Percentile.EstimationType.R_5).withNaNStrategy(NaNStrategy.REMOVED).evaluate(new double[] {Double.NaN,Double.NaN,Double.NaN}),0d);
726         assertEquals(50d,new Percentile(50d).withEstimationType(Percentile.EstimationType.R_7).withNaNStrategy(NaNStrategy.MINIMAL).evaluate(new double[] {50d,50d,50d},1,2),0d);
727 
728         specialValues = new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN, Double.NaN };
729         assertEquals(3.5,new Percentile().evaluate(specialValues, 3, 4),0d);
730         assertEquals(4d,new Percentile().evaluate(specialValues, 4, 3),0d);
731         assertTrue(Double.isNaN(new Percentile().evaluate(specialValues, 5, 2)));
732 
733         specialValues = new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN, Double.NaN, 5d, 6d };
734         assertEquals(4.5,new Percentile().evaluate(specialValues, 3, 6),0d);
735         assertEquals(5d,new Percentile().evaluate(specialValues, 4, 5),0d);
736         assertTrue(Double.isNaN(new Percentile().evaluate(specialValues, 5, 2)));
737         assertTrue(Double.isNaN(new Percentile().evaluate(specialValues, 5, 1)));
738         assertEquals(5.5,new Percentile().evaluate(specialValues, 5, 4),0d);
739     }
740 
741     // Some NaNStrategy specific testing
742     @Test(expected=MathIllegalArgumentException.class)
743     public void testNanStrategyFailed() {
744         double[] specialValues =
745                 new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN };
746         new Percentile(50d).
747         withEstimationType(Percentile.EstimationType.R_9).
748         withNaNStrategy(NaNStrategy.FAILED).
749         evaluate(specialValues);
750     }
751 
752     @Test
753     public void testAllTechniquesSpecialValuesWithNaNStrategy() {
754         double[] specialValues =
755                 new double[] { 0d, 1d, 2d, 3d, 4d, Double.NaN };
756         try {
757             new Percentile(50d).withEstimationType(Percentile.EstimationType.LEGACY).withNaNStrategy(null);
758             fail("Expecting NullArgumentArgumentException "
759                     + "for null Nan Strategy");
760         } catch (NullArgumentException ex) {
761             // expected
762         }
763         //This is as per each type's default NaNStrategy
764         testAssertMappedValues(specialValues, new Object[][] {
765                 { Percentile.EstimationType.LEGACY, 2.5d }, { Percentile.EstimationType.R_1, 2.0 }, { Percentile.EstimationType.R_2, 2.0 }, { Percentile.EstimationType.R_3, 1.0 },
766                 { Percentile.EstimationType.R_4, 1.5 }, { Percentile.EstimationType.R_5, 2.0 }, { Percentile.EstimationType.R_6, 2.0 },
767                 { Percentile.EstimationType.R_7, 2.0 }, { Percentile.EstimationType.R_8, 2.0 }, { Percentile.EstimationType.R_9, 2.0 }}, 50d, 0d);
768 
769         //This is as per MAXIMAL and hence the values tend a +0.5 upward
770         testAssertMappedValues(specialValues, new Object[][] {
771                 { Percentile.EstimationType.LEGACY, 2.5d }, { Percentile.EstimationType.R_1, 2.0 }, { Percentile.EstimationType.R_2, 2.5 }, { Percentile.EstimationType.R_3, 2.0 },
772                 { Percentile.EstimationType.R_4, 2.0 }, { Percentile.EstimationType.R_5, 2.5 }, { Percentile.EstimationType.R_6, 2.5 },
773                 { Percentile.EstimationType.R_7, 2.5 }, { Percentile.EstimationType.R_8, 2.5 }, { Percentile.EstimationType.R_9, 2.5 }}, 50d, 0d,
774                 NaNStrategy.MAXIMAL);
775 
776         //This is as per MINIMAL and hence the values tend a -0.5 downward
777         testAssertMappedValues(specialValues, new Object[][] {
778                 { Percentile.EstimationType.LEGACY, 1.5d }, { Percentile.EstimationType.R_1, 1.0 }, { Percentile.EstimationType.R_2, 1.5 }, { Percentile.EstimationType.R_3, 1.0 },
779                 { Percentile.EstimationType.R_4, 1.0 }, { Percentile.EstimationType.R_5, 1.5 }, { Percentile.EstimationType.R_6, 1.5 },
780                 { Percentile.EstimationType.R_7, 1.5 }, { Percentile.EstimationType.R_8, 1.5 }, { Percentile.EstimationType.R_9, 1.5 }}, 50d, 0d,
781                 NaNStrategy.MINIMAL);
782 
783         //This is as per REMOVED as here Percentile.Type.CM changed its value from default
784         //while rest of Estimation types were anyways defaulted to REMOVED
785         testAssertMappedValues(specialValues, new Object[][] {
786                 { Percentile.EstimationType.LEGACY, 2.0 }, { Percentile.EstimationType.R_1, 2.0 }, { Percentile.EstimationType.R_2, 2.0 }, { Percentile.EstimationType.R_3, 1.0 },
787                 { Percentile.EstimationType.R_4, 1.5 }, { Percentile.EstimationType.R_5, 2.0 }, { Percentile.EstimationType.R_6, 2.0 },
788                 { Percentile.EstimationType.R_7, 2.0 }, { Percentile.EstimationType.R_8, 2.0 }, { Percentile.EstimationType.R_9, 2.0 }}, 50d, 0d,
789                 NaNStrategy.REMOVED);
790     }
791 
792     /**
793      * Simple test assertion utility method
794      *
795      * @param data input data
796      * @param map of expected result against a {@link EstimationType}
797      * @param p the quantile to compute for
798      * @param tolerance the tolerance of difference allowed
799      * @param nanStrategy NaNStrategy to be passed
800      */
801     protected void testAssertMappedValues(double[] data, Object[][] map,
802                                           Double p, Double tolerance, NaNStrategy nanStrategy) {
803         for (Object[] o : map) {
804             Percentile.EstimationType e = (Percentile.EstimationType) o[0];
805             double expected = (Double) o[1];
806             try {
807                 double result = new Percentile(p).withEstimationType(e).withNaNStrategy(nanStrategy).evaluate(data);
808                 assertEquals("expected[" + e + "] = " + expected + " but was = " + result,
809                                     expected, result, tolerance);
810             } catch(Exception ex) {
811                 fail("Exception occured for estimation type " + e + ":" + ex.getLocalizedMessage());
812             }
813         }
814     }
815 
816 }