View Javadoc
1   package org.djutils.means;
2   
3   import static org.junit.Assert.assertEquals;
4   import static org.junit.Assert.assertTrue;
5   import static org.junit.Assert.fail;
6   
7   import java.util.ArrayList;
8   import java.util.Arrays;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  
13  import org.junit.Test;
14  
15  /**
16   * Test the classes in the means package
17   * <p>
18   * Copyright (c) 2013-2022 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19   * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
20   * <p>
21   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Oct 26, 2018 <br>
22   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
24   */
25  public class MeansTests
26  {
27      /** Some double values. */
28      private static Double[] testValues = {10.0, Math.PI, Math.E, 1234.};
29  
30      /** Some weights. */
31      private static Double[] testWeights = {1.0, 2.0, Math.PI, 1.0};
32  
33      /**
34       * Test the Mean classes using unity weights.
35       */
36      @Test
37      public final void testMeansWithUnityWeights()
38      {
39          ArithmeticMean<Double, Double> am = new ArithmeticMean<Double, Double>();
40          double sum = 0;
41          for (int i = 0; i < testValues.length; i++)
42          {
43              double testValue = testValues[i];
44              assertEquals("add returns object for method chaining", am, am.add(testValue));
45              sum += testValue;
46              assertEquals("sum", sum, am.getSum(), sum / 99999999);
47              assertEquals("arithmetic mean", sum / (i + 1), am.getMean(), sum / (i + 1) / 99999999);
48          }
49          am = new ArithmeticMean<Double, Double>();
50          assertEquals("add returns object for method chaining", am, am.add(testValues));
51          assertEquals("arithmetic mean", sum / testValues.length, am.getMean(), sum / testValues.length / 99999999);
52  
53          am = new ArithmeticMean<Double, Double>();
54          assertEquals("add returns object for method chaining", am, am.add(new ArrayList<Double>(Arrays.asList(testValues))));
55          assertEquals("arithmetic mean", sum / testValues.length, am.getMean(), sum / testValues.length / 99999999);
56      }
57  
58      /**
59       * Test the Mean classes using varying weights.
60       */
61      @SuppressWarnings("checkstyle:methodlength")
62      @Test
63      public final void testMeansWithWeights()
64      {
65          ArithmeticMean<Double, Double> am = new ArithmeticMean<Double, Double>();
66          assertEquals("Initial sum is 0", 0, am.getSum(), 0.00000);
67          assertEquals("Initial sum of weights is 0", 0, am.getSumOfWeights(), 0.00000);
68          assertTrue("Initial mean is NaN", Double.isNaN(am.getMean()));
69          HarmonicMean<Double, Double> hm = new HarmonicMean<Double, Double>();
70          assertEquals("Initial sum is 0", 0, hm.getSum(), 0.00000);
71          assertEquals("Initial sum of weights is 0", 0, hm.getSumOfWeights(), 0.00000);
72          assertTrue("Initial mean is NaN", Double.isNaN(hm.getMean()));
73          GeometricMean<Double, Double> gm = new GeometricMean<Double, Double>();
74          assertEquals("Initial sum is 0", 0, gm.getSum(), 0.00000);
75          assertEquals("Initial sum of weights is 0", 0, gm.getSumOfWeights(), 0.00000);
76          assertTrue("Initial mean is NaN", Double.isNaN(gm.getMean()));
77          double sum = 0;
78          double sumWeights = 0;
79          double recipSum = 0;
80          double product = 1;
81          double geometricMean = 0;
82          Map<Double, Double> map = new HashMap<>();
83          for (int i = 0; i < testValues.length; i++)
84          {
85              double testValue = testValues[i];
86              double testWeight = testWeights[i];
87              map.put(testValue, testWeight); // There are no duplicates in testValues
88              assertEquals("add returns object for method chaining", am, am.add(testValue, testWeight));
89              hm.add(testValue, testWeight);
90              gm.add(testValue, testWeight);
91              sum += testValue * testWeight;
92              recipSum += testWeight / testValue;
93              product *= Math.pow(testValue, testWeight);
94              sumWeights += testWeight;
95              if (0 == i)
96              {
97                  assertEquals("mean of one value equals value", testValue, am.getMean(), testValue / 99999999);
98                  assertEquals("mean of one value equals value", testValue, hm.getMean(), testValue / 99999999);
99                  assertEquals("mean of one value equals value", testValue, gm.getMean(), testValue / 99999999);
100             }
101             assertEquals("sum", sum, am.getSum(), sum / 99999999);
102             assertEquals("sum of weights", sumWeights, am.getSumOfWeights(), sumWeights / 99999999);
103             assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
104             assertEquals("sum", recipSum, hm.getSum(), recipSum / 99999999);
105             assertEquals("sum of weights", sumWeights, hm.getSumOfWeights(), sumWeights / 99999999);
106             assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
107             geometricMean = Math.pow(product, 1 / sumWeights);
108             assertEquals("check with alternative way to compute geometric mean", geometricMean, gm.getMean(),
109                     geometricMean / 99999999);
110         }
111         // System.out.println(
112         // "Mean of test data: arithmetic=" + am.getMean() + ", harmonic=" + hm.getMean() + ", geometric=" + gm.getMean());
113         am = new ArithmeticMean<Double, Double>();
114         hm = new HarmonicMean<Double, Double>();
115         gm = new GeometricMean<Double, Double>();
116         am.add(testValues[0], 123.456);
117         hm.add(testValues[0], 123.456);
118         gm.add(testValues[0], 123.456);
119         assertEquals("One value, any weight has mean equal to value", testValues[0], am.getMean(), testValues[0] / 99999999);
120         assertEquals("One value, any weight has mean equal to value", testValues[0], hm.getMean(), testValues[0] / 99999999);
121         assertEquals("One value, any weight has mean equal to value", testValues[0], gm.getMean(), testValues[0] / 99999999);
122         am = new ArithmeticMean<Double, Double>();
123         hm = new HarmonicMean<Double, Double>();
124         gm = new GeometricMean<Double, Double>();
125         assertEquals("add returns object for method chaining", am, am.add(testValues, testWeights));
126         assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
127         hm.add(testValues, testWeights);
128         assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
129         gm.add(testValues, testWeights);
130         assertEquals("geometric mean", geometricMean, gm.getMean(), geometricMean / 99999999);
131         Double[] shortArray = Arrays.copyOfRange(testValues, 0, 2);
132         try
133         {
134             am.add(shortArray, testWeights);
135             fail("Short array of values should have thrown an IllegalArgumentException");
136         }
137         catch (IllegalArgumentException iae)
138         {
139             // Ignore expected exception
140         }
141         shortArray = Arrays.copyOfRange(testWeights, 0, 2);
142         try
143         {
144             am.add(testValues, shortArray);
145             fail("Short array of weights should have thrown an IllegalArgumentException");
146         }
147         catch (IllegalArgumentException iae)
148         {
149             // Ignore expected exception
150         }
151         am = new ArithmeticMean<Double, Double>();
152         hm = new HarmonicMean<Double, Double>();
153         gm = new GeometricMean<Double, Double>();
154         assertEquals("add returns object for method chaining", am,
155                 am.add(new ArrayList<Double>(Arrays.asList(testValues)), new ArrayList<Double>(Arrays.asList(testWeights))));
156         assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
157         hm.add(new ArrayList<Double>(Arrays.asList(testValues)), new ArrayList<Double>(Arrays.asList(testWeights)));
158         assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
159         gm.add(new ArrayList<Double>(Arrays.asList(testValues)), new ArrayList<Double>(Arrays.asList(testWeights)));
160         assertEquals("geometric mean", geometricMean, gm.getMean(), geometricMean / 99999999);
161 
162         List<Double> shortList = new ArrayList<Double>(Arrays.asList(testValues));
163         shortList.remove(2);
164         try
165         {
166             am.add(shortList, new ArrayList<Double>(Arrays.asList(testWeights)));
167             fail("Short list of values should have thrown an IllegalArgumentException");
168         }
169         catch (IllegalArgumentException iae)
170         {
171             // Ignore expected exception
172         }
173         shortList = new ArrayList<Double>(Arrays.asList(testWeights));
174         shortList.remove(2);
175         try
176         {
177             am.add(new ArrayList<Double>(Arrays.asList(testValues)), shortList);
178             fail("Short list of weights should have thrown an IllegalArgumentException");
179         }
180         catch (IllegalArgumentException iae)
181         {
182             // Ignore expected exception
183         }
184 
185         am = new ArithmeticMean<Double, Double>();
186         hm = new HarmonicMean<Double, Double>();
187         gm = new GeometricMean<Double, Double>();
188         assertEquals("add returns object for method chaining", am, am.add(map));
189         assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
190         hm.add(map);
191         assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
192         gm.add(map);
193         assertEquals("geometric mean", geometricMean, gm.getMean(), geometricMean / 99999999);
194 
195         am = new ArithmeticMean<Double, Double>();
196         hm = new HarmonicMean<Double, Double>();
197         gm = new GeometricMean<Double, Double>();
198         assertEquals("add returns object for method chaining", am,
199                 am.add(new ArrayList<Double>(Arrays.asList(testValues)), (Double v) -> map.get(v)));
200         assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
201         hm.add(new ArrayList<Double>(Arrays.asList(testValues)), (Double v) -> map.get(v));
202         assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
203         gm.add(new ArrayList<Double>(Arrays.asList(testValues)), (Double v) -> map.get(v));
204         assertEquals("geometric mean", geometricMean, gm.getMean(), geometricMean / 99999999);
205 
206         am = new ArithmeticMean<Double, Double>();
207         hm = new HarmonicMean<Double, Double>();
208         gm = new GeometricMean<Double, Double>();
209         Integer[] indices = new Integer[] {0, 1, 2, 3};
210         List<Integer> indexList = new ArrayList<>(Arrays.asList(indices));
211         assertEquals("add returns object for method chaining", am,
212                 am.add(indexList, (Integer i) -> testValues[i], (Integer i) -> testWeights[i]));
213         assertEquals("arithmetic mean", sum / sumWeights, am.getMean(), sum / sumWeights / 99999999);
214         hm.add(indexList, (Integer i) -> testValues[i], (Integer i) -> testWeights[i]);
215         assertEquals("harmonic mean", sumWeights / recipSum, hm.getMean(), sumWeights / recipSum / 999999999);
216         gm.add(indexList, (Integer i) -> testValues[i], (Integer i) -> testWeights[i]);
217         assertEquals("geometric mean", geometricMean, gm.getMean(), geometricMean / 99999999);
218 
219         assertTrue("toString method returns something descriptive", am.toString().contains("ArithmeticMean"));
220         assertTrue("toString method returns something descriptive", hm.toString().contains("HarmonicMean"));
221         assertTrue("toString method returns something descriptive", gm.toString().contains("GeometricMean"));
222     }
223 
224 }