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 this
4    * work for additional information regarding copyright ownership. The ASF
5    * licenses this file to You under the Apache License, Version 2.0 (the
6    * "License"); you may not use this file except in compliance with the License.
7    * 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, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package org.hipparchus.linear;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.Random;
24  import java.util.Set;
25  
26  import org.hipparchus.analysis.UnivariateFunction;
27  import org.hipparchus.analysis.function.Sin;
28  import org.hipparchus.exception.MathRuntimeException;
29  import org.hipparchus.linear.RealVector.Entry;
30  import org.hipparchus.util.FastMath;
31  import org.junit.Assert;
32  import org.junit.Test;
33  
34  /**
35   * This is an abstract test of the {@link
36   * RealVector#unmodifiableRealVector(RealVector) unmodifiable vector}
37   * implementation. These unmodifiable vectors decorate a (modifiable)
38   * {@link RealVector}; therefore, a new implementation of this abstract
39   * test should be considered for each implementation of
40   * {@link RealVector}.
41   *
42   *
43   */
44  public abstract class UnmodifiableRealVectorAbstractTest {
45      /** The dimension of the randomly generated vectors. */
46      protected static final int DIM = 100;
47      /** Absolute tolerance. */
48      protected static final double EPS = 10 * Math.ulp(1d);
49      /**
50       * The list of methods which are excluded from the general test
51       * {@link #testAllButExcluded()}.
52       */
53      protected static final Set<String> EXCLUDE = new HashSet<String>();
54      /** The random number generator (always initialized with the same seed. */
55      protected static final Random RANDOM;
56  
57      static {
58          EXCLUDE.add("getEntry");
59          EXCLUDE.add("setEntry");
60          EXCLUDE.add("addToEntry");
61          EXCLUDE.add("getSubVector");
62          EXCLUDE.add("setSubVector");
63          EXCLUDE.add("iterator");
64          EXCLUDE.add("sparseIterator");
65          EXCLUDE.add("walkInDefaultOrder");
66          EXCLUDE.add("walkInOptimizedOrder");
67          EXCLUDE.add("ebeDivide");
68          EXCLUDE.add("ebeMultiply");
69  
70          // Excluded because they are inherited from "Object".
71          for (Method m : Object.class.getMethods()) {
72              EXCLUDE.add(m.getName());
73          }
74          RANDOM = new Random(20110813);
75      }
76  
77      /**
78       * Returns {@code true} if the specified {@code double} are equal (within a
79       * given tolerance).
80       *
81       * @param x First {@code double}.
82       * @param y Second {@code double}.
83       * @return {@code true} if {@code x} and {@code y} are equal.
84       */
85      public static boolean equals(final double x, final double y) {
86          if (x == y) {
87              return true;
88          } else if (FastMath.abs(x) <= EPS) {
89              return FastMath.abs(y) <= EPS;
90          } else if (FastMath.abs(y) <= EPS) {
91              return FastMath.abs(x) <= EPS;
92          } else {
93              return FastMath.abs(x - y) <= EPS * FastMath.min(FastMath.abs(x), FastMath.abs(y));
94          }
95      }
96  
97      /**
98       * Returns {@code true} if the specified {@code double} arrays are equal
99       * (within a given tolerance).
100      *
101      * @param x First array.
102      * @param y Second array.
103      * @return {@code true} if {@code x} and {@code y} are equal.
104      */
105     public static boolean equals(final double[] x, final double[] y) {
106         if (x.length != y.length) {
107             return false;
108         }
109         final int n = x.length;
110         for (int i = 0; i < n; i++) {
111             if (!equals(x[i], y[i])) {
112                 return false;
113             }
114         }
115         return true;
116     }
117 
118     /**
119      * Returns {@code true} if the specified {@code RealVector} are equal
120      * (within a given tolerance).
121      *
122      * @param x First vector.
123      * @param y Second vector.
124      * @return {@code true} if {@code x} and {@code y} are equal.
125      */
126     public static boolean equals(final RealVector x, final RealVector y) {
127         if (x.getDimension() != y.getDimension()) {
128             return false;
129         }
130         final int n = x.getDimension();
131         for (int i = 0; i < n; i++) {
132             if (!equals(x.getEntry(i), y.getEntry(i))) {
133                 return false;
134             }
135         }
136         return true;
137     }
138 
139     /**
140      * Returns {@code true} if the specified {@code RealVector} is equal to the
141      * specified {@code double} array (within a given tolerance).
142      *
143      * @param x Vector.
144      * @param y Array.
145      * @return {@code true} if {@code x} and {@code y} are equal.
146      */
147     public static boolean equals(final RealVector x, final double[] y) {
148         if (x.getDimension() != y.length) {
149             return false;
150         }
151         final int n = x.getDimension();
152         for (int i = 0; i < n; i++) {
153             if (!equals(x.getEntry(i), y[i])) {
154                 return false;
155             }
156         }
157         return true;
158     }
159 
160     /**
161      * Returns {@code true} if the specified {@code RealMatrix} are equal
162      * (within a given tolerance).
163      *
164      * @param x First matrix.
165      * @param y Second matrix.
166      * @return {@code true} if {@code x} and {@code y} are equal.
167      */
168     public static boolean equals(final RealMatrix x, final RealMatrix y) {
169         if (x.getRowDimension() != y.getRowDimension()) {
170             return false;
171         }
172         if (x.getColumnDimension() != y.getColumnDimension()) {
173             return false;
174         }
175         final int rows = x.getRowDimension();
176         final int cols = x.getColumnDimension();
177         for (int i = 0; i < rows; i++) {
178             for (int j = 0; j < cols; j++) {
179                 if (!equals(x.getEntry(i, j), y.getEntry(i, j))) {
180                     return false;
181                 }
182             }
183         }
184         return true;
185     }
186 
187     /**
188      * Returns {@code true} if the specified {@code Object} are equal.
189      *
190      * @param x First object.
191      * @param y Second object.
192      * @return {@code true} if {@code x} and {@code y} are equal.
193      * @throws IllegalArgumentException if {@code x} and {@code y} could
194      * not be compared.
195      */
196     public static boolean equals(final Object x, final Object y) {
197         if (x instanceof Boolean) {
198             if (y instanceof Boolean) {
199                 return ((Boolean) x).booleanValue() == ((Boolean) y)
200                         .booleanValue();
201             } else {
202                 return false;
203             }
204         }
205         if (x instanceof Integer) {
206             if (y instanceof Integer) {
207                 return ((Integer) x).intValue() == ((Integer) y).intValue();
208             } else {
209                 return false;
210             }
211         } else if (x instanceof Double) {
212             if (y instanceof Double) {
213                 return equals(((Double) x).doubleValue(),
214                         ((Double) y).doubleValue());
215             } else {
216                 return false;
217             }
218         } else if (x instanceof double[]) {
219             if (y instanceof double[]) {
220                 return equals((double[]) x, (double[]) y);
221             } else if (y instanceof RealVector) {
222                 return equals((RealVector) y, (double[]) x);
223             } else {
224                 return false;
225             }
226         } else if (x instanceof RealVector) {
227             if (y instanceof double[]) {
228                 return equals((RealVector) x, (double[]) y);
229             } else if (y instanceof RealVector) {
230                 return equals((RealVector) x, (RealVector) y);
231             } else {
232                 return false;
233             }
234         } else if (x instanceof RealMatrix) {
235             if (y instanceof RealMatrix) {
236                 return equals((RealMatrix) x, (RealMatrix) y);
237             } else {
238                 return false;
239             }
240         } else {
241             throw new IllegalArgumentException("could not compare " + x + ", "
242                     + y);
243         }
244     }
245 
246     /**
247      * Creates a new random vector of a specified type. This vector is then to
248      * be wrapped in an unmodifiable vector.
249      *
250      * @return a new random vector.
251      */
252     public abstract RealVector createVector();
253 
254     /**
255      * Creates a new random object of the specified type.
256      *
257      * @param c Class of the object to be created.
258      * @return a new random object.
259      * @throws IllegalArgumentException if the specified class is not
260      * recognized by this method.
261      */
262     public Object createParameter(final Class<?> c) {
263         if (c == Integer.TYPE) {
264             return Integer.valueOf(RANDOM.nextInt());
265         } else if (c == Double.TYPE) {
266             return Double.valueOf(RANDOM.nextDouble());
267         } else if (c == double[].class) {
268             final double[] v = new double[DIM];
269             for (int i = 0; i < DIM; i++) {
270                 v[i] = RANDOM.nextDouble();
271             }
272             return v;
273         } else if (c.isAssignableFrom(RealVector.class)) {
274             return createVector();
275         } else if (c.isAssignableFrom(UnivariateFunction.class)) {
276             return new Sin();
277         } else {
278             throw new IllegalArgumentException("could not create " + c);
279         }
280     }
281 
282     /**
283      * This is the general test of most methods in the
284      * {@link RealVector#unmodifiableRealVector(RealVector) unmodifiable vector}.
285      * It works as follows.
286      * First, an unmodifiable view of a copy of the specified random vector
287      * {@code u} is created: this defines {@code v}. Then the <em>same</em>
288      * method {@code m} is invoked on {@code u} and {@code v}, with randomly
289      * generated parameters {@code args}.
290      * If it turns out that {@code u} has changed after the call of method
291      * {@code m}, then this test checks that the call of this method on
292      * {@code v} resulted in a {@link MathRuntimeException}. If
293      * {@code u} was not modified, then this test checks that the results
294      * returned by the call of method {@code m} on {@code u} and {@code v}
295      * returned the same result.
296      *
297      * @param m Method to be tested.
298      * @param u Random vector from which the unmodifiable view is to be
299      *constructed.
300      * @param args Arguments to be passed to method {@code m}.
301      */
302     private void callMethod(final Method m,
303                             final RealVector u,
304                             final Object... args)
305         throws IllegalAccessException,
306                IllegalArgumentException,
307                InvocationTargetException {
308         final RealVector uu = u.copy();
309         final RealVector v = RealVector.unmodifiableRealVector(u.copy());
310         Object exp = m.invoke(u, args);
311         if (equals(uu, u)) {
312             Object act = m.invoke(v, args);
313             Assert.assertTrue(m.toGenericString() + ", unmodifiable vector has changed",
314                               equals(uu, v));
315             Assert.assertTrue(m.toGenericString() + ", wrong result",
316                               equals(exp, act));
317 
318         } else {
319             boolean flag = false;
320             try {
321                 m.invoke(v, args);
322             } catch (InvocationTargetException e) {
323                 if (e.getCause() instanceof MathRuntimeException) {
324                     flag = true;
325                 }
326             }
327             Assert.assertTrue(m.toGenericString()+", exception should have been thrown", flag);
328         }
329     }
330 
331     /**
332      * This test calls {@link #callMethod(Method, RealVector, Object...)} on
333      * every method defined in interface {@link RealVector}. It generates the
334      * appropriate random arguments. Some methods are manually excluded (see
335      * {@link #EXCLUDE}), they must be handled by separate tests.
336      */
337     @Test
338     public void testAllButExcluded()
339         throws IllegalAccessException,
340                IllegalArgumentException,
341                InvocationTargetException {
342         Method[] method = RealVector.class.getMethods();
343         for (int i = 0; i < method.length; i++) {
344             Method m = method[i];
345             if (!EXCLUDE.contains(m.getName())) {
346                 RealVector u = (RealVector) createParameter(RealVector.class);
347                 Class<?>[] paramType = m.getParameterTypes();
348                 Object[] param = new Object[paramType.length];
349                 for (int j = 0; j < paramType.length; j++) {
350                     param[j] = createParameter(paramType[j]);
351                 }
352                 callMethod(m, u, param);
353             }
354         }
355     }
356 
357     @Test
358     public void testGetEntry() {
359         RealVector u = createVector();
360         RealVector v = RealVector.unmodifiableRealVector(u);
361         for (int i = 0; i < DIM; i++) {
362             Assert.assertTrue(equals(u.getEntry(i), v.getEntry(i)));
363         }
364     }
365 
366     @Test(expected = MathRuntimeException.class)
367     public void testSetEntry() {
368         RealVector u = createVector();
369         RealVector v = RealVector.unmodifiableRealVector(u);
370         for (int i = 0; i < DIM; i++) {
371             v.setEntry(i, 0d);
372         }
373     }
374 
375     @Test(expected = MathRuntimeException.class)
376     public void testAddToEntry() {
377         RealVector u = createVector();
378         RealVector v = RealVector.unmodifiableRealVector(u);
379         for (int i = 0; i < DIM; i++) {
380             v.addToEntry(i, 0d);
381         }
382     }
383 
384     @Test
385     public void testGetSubVector() {
386         RealVector u = createVector();
387         RealVector v = RealVector.unmodifiableRealVector(u);
388         for (int i = 0; i < DIM; i++) {
389             for (int n = 1; n < DIM - i; n++) {
390                 RealVector exp = u.getSubVector(i, n);
391                 RealVector act = v.getSubVector(i, n);
392                 Assert.assertTrue(equals(exp, act));
393             }
394         }
395     }
396 
397     @Test(expected = MathRuntimeException.class)
398     public void testSetSubVector() {
399         RealVector u = createVector();
400         RealVector v = RealVector.unmodifiableRealVector(u);
401         v.setSubVector(0, new ArrayRealVector());
402     }
403 
404     @Test
405     public void testIterator() {
406         RealVector u = createVector();
407         Iterator<Entry> i = u.iterator();
408         RealVector v = RealVector.unmodifiableRealVector(u.copy());
409         Iterator<Entry> j = v.iterator();
410         boolean flag;
411         while (i.hasNext()) {
412             Assert.assertTrue(j.hasNext());
413             Entry exp = i.next();
414             Entry act = j.next();
415             Assert.assertTrue(equals(exp.getIndex(), act.getIndex()));
416             Assert.assertTrue(equals(exp.getValue(), act.getValue()));
417             exp.setIndex(RANDOM.nextInt(DIM));
418             act.setIndex(RANDOM.nextInt(DIM));
419             flag = false;
420             try {
421                 act.setValue(RANDOM.nextDouble());
422             } catch (MathRuntimeException e) {
423                 flag = true;
424             }
425             Assert.assertTrue("exception should have been thrown", flag);
426         }
427         Assert.assertFalse(j.hasNext());
428     }
429 
430     @Test
431     public void testSparseIterator() {
432         RealVector u = createVector();
433         Iterator<Entry> i = u.sparseIterator();
434         RealVector v = RealVector.unmodifiableRealVector(u.copy());
435         Iterator<Entry> j = v.sparseIterator();
436         boolean flag;
437         while (i.hasNext()) {
438             Assert.assertTrue(j.hasNext());
439             Entry exp = i.next();
440             Entry act = j.next();
441             Assert.assertTrue(equals(exp.getIndex(), act.getIndex()));
442             Assert.assertTrue(equals(exp.getValue(), act.getValue()));
443             exp.setIndex(RANDOM.nextInt(DIM));
444             act.setIndex(RANDOM.nextInt(DIM));
445             flag = false;
446             try {
447                 act.setValue(RANDOM.nextDouble());
448             } catch (MathRuntimeException e) {
449                 flag = true;
450             }
451             Assert.assertTrue("exception should have been thrown", flag);
452         }
453         Assert.assertFalse(j.hasNext());
454     }
455 }