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.util;
23  
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  import java.util.Comparator;
28  import java.util.Iterator;
29  import java.util.NoSuchElementException;
30  
31  import org.hipparchus.exception.MathIllegalArgumentException;
32  import org.junit.Assert;
33  import org.junit.Test;
34  
35  /**
36   * Tests for the {@link Combinations} class.
37   */
38  public class CombinationsTest {
39      @Test
40      public void testAccessor1() {
41          final int n = 5;
42          final int k = 3;
43          Assert.assertEquals(n, new Combinations(n, k).getN());
44      }
45      @Test
46      public void testAccessor2() {
47          final int n = 5;
48          final int k = 3;
49          Assert.assertEquals(k, new Combinations(n, k).getK());
50      }
51  
52      @Test
53      public void testLexicographicIterator() {
54          checkLexicographicIterator(new Combinations(5, 3));
55          checkLexicographicIterator(new Combinations(6, 4));
56          checkLexicographicIterator(new Combinations(8, 2));
57          checkLexicographicIterator(new Combinations(6, 1));
58          checkLexicographicIterator(new Combinations(3, 3));
59          checkLexicographicIterator(new Combinations(1, 1));
60          checkLexicographicIterator(new Combinations(1, 0));
61          checkLexicographicIterator(new Combinations(0, 0));
62          checkLexicographicIterator(new Combinations(4, 2));
63          checkLexicographicIterator(new Combinations(123, 2));
64      }
65  
66      @Test(expected=MathIllegalArgumentException.class)
67      public void testLexicographicComparatorWrongIterate1() {
68          final int n = 5;
69          final int k = 3;
70          final Comparator<int[]> comp = new Combinations(n, k).comparator();
71          comp.compare(new int[] {1}, new int[] {0, 1, 2});
72      }
73  
74      @Test(expected=MathIllegalArgumentException.class)
75      public void testLexicographicComparatorWrongIterate2() {
76          final int n = 5;
77          final int k = 3;
78          final Comparator<int[]> comp = new Combinations(n, k).comparator();
79          comp.compare(new int[] {0, 1, 2}, new int[] {0, 1, 2, 3});
80      }
81  
82      @Test(expected=MathIllegalArgumentException.class)
83      public void testLexicographicComparatorWrongIterate3() {
84          final int n = 5;
85          final int k = 3;
86          final Comparator<int[]> comp = new Combinations(n, k).comparator();
87          comp.compare(new int[] {1, 2, 5}, new int[] {0, 1, 2});
88      }
89  
90      @Test(expected=MathIllegalArgumentException.class)
91      public void testLexicographicComparatorWrongIterate4() {
92          final int n = 5;
93          final int k = 3;
94          final Comparator<int[]> comp = new Combinations(n, k).comparator();
95          comp.compare(new int[] {1, 2, 4}, new int[] {-1, 1, 2});
96      }
97  
98      @Test
99      public void testLexicographicComparator() {
100         final int n = 5;
101         final int k = 3;
102         final Comparator<int[]> comp = new Combinations(n, k).comparator();
103         Assert.assertEquals(1, comp.compare(new int[] {1, 2, 4},
104                                             new int[] {1, 2, 3}));
105         Assert.assertEquals(-1, comp.compare(new int[] {0, 1, 4},
106                                              new int[] {0, 2, 4}));
107         Assert.assertEquals(0, comp.compare(new int[] {1, 3, 4},
108                                             new int[] {1, 3, 4}));
109     }
110 
111     /**
112      * Check that iterates can be passed unsorted.
113      */
114     @Test
115     public void testLexicographicComparatorUnsorted() {
116         final int n = 5;
117         final int k = 3;
118         final Comparator<int[]> comp = new Combinations(n, k).comparator();
119         Assert.assertEquals(1, comp.compare(new int[] {1, 4, 2},
120                                             new int[] {1, 3, 2}));
121         Assert.assertEquals(-1, comp.compare(new int[] {0, 4, 1},
122                                              new int[] {0, 4, 2}));
123         Assert.assertEquals(0, comp.compare(new int[] {1, 4, 3},
124                                             new int[] {1, 3, 4}));
125     }
126 
127     @Test
128     public void testEmptyCombination() {
129         final Iterator<int[]> iter = new Combinations(12345, 0).iterator();
130         Assert.assertTrue(iter.hasNext());
131         final int[] c = iter.next();
132         Assert.assertEquals(0, c.length);
133         Assert.assertFalse(iter.hasNext());
134     }
135 
136     @Test
137     public void testFullSetCombination() {
138         final int n = 67;
139         final Iterator<int[]> iter = new Combinations(n, n).iterator();
140         Assert.assertTrue(iter.hasNext());
141         final int[] c = iter.next();
142         Assert.assertEquals(n, c.length);
143 
144         for (int i = 0; i < n; i++) {
145             Assert.assertEquals(i, c[i]);
146         }
147 
148         Assert.assertFalse(iter.hasNext());
149     }
150 
151     /**
152      * Verifies that the iterator generates a lexicographically
153      * increasing sequence of b(n,k) arrays, each having length k
154      * and each array itself increasing.
155      *
156      * @param c Combinations.
157      */
158     private void checkLexicographicIterator(Combinations c) {
159         final Comparator<int[]> comp = c.comparator();
160         final int n = c.getN();
161         final int k = c.getK();
162 
163         int[] lastIterate = null;
164 
165         long numIterates = 0;
166         for (int[] iterate : c) {
167             Assert.assertEquals(k, iterate.length);
168 
169             // Check that the sequence of iterates is ordered.
170             if (lastIterate != null) {
171                 Assert.assertTrue(comp.compare(iterate, lastIterate) == 1);
172             }
173 
174             // Check that each iterate is ordered.
175             for (int i = 1; i < iterate.length; i++) {
176                 Assert.assertTrue(iterate[i] > iterate[i - 1]);
177             }
178 
179             lastIterate = iterate;
180             ++numIterates;
181         }
182 
183         // Check the number of iterates.
184         Assert.assertEquals(CombinatoricsUtils.binomialCoefficient(n, k),
185                             numIterates);
186     }
187 
188     @Test
189     public void testCombinationsIteratorFail() {
190         try {
191             new Combinations(4, 5).iterator();
192             Assert.fail("expecting MathIllegalArgumentException");
193         } catch (MathIllegalArgumentException ex) {
194             // ignored
195         }
196 
197         try {
198             new Combinations(-1, -2).iterator();
199             Assert.fail("expecting MathIllegalArgumentException");
200         } catch (MathIllegalArgumentException ex) {
201             // ignored
202         }
203     }
204 
205     @Test
206     public void testLexicographicIteratorUnreachable() {
207         // this tests things that could really never happen,
208         // as the conditions are tested in the enclosing class before
209         // the lexicographic iterator is built
210         try {
211             Class<?> lexicographicIteratorClass = null;
212             for (Class<?> c : Combinations.class.getDeclaredClasses()) {
213                 if (c.getCanonicalName().endsWith(".LexicographicIterator")) {
214                     lexicographicIteratorClass = c;
215                 }
216             }
217             Constructor<?> ctr = lexicographicIteratorClass.getDeclaredConstructor(Integer.TYPE, Integer.TYPE);
218             Method hasNext = lexicographicIteratorClass.getDeclaredMethod("hasNext");
219             Method next = lexicographicIteratorClass.getDeclaredMethod("next");
220             Method remove = lexicographicIteratorClass.getDeclaredMethod("remove");
221             Assert.assertFalse((Boolean) hasNext.invoke(ctr.newInstance(3, 0)));
222             Assert.assertFalse((Boolean) hasNext.invoke(ctr.newInstance(3, 3)));
223             Assert.assertTrue((Boolean) hasNext.invoke(ctr.newInstance(3, 2)));
224             try {
225                 next.invoke(ctr.newInstance(3, 0));
226                 Assert.fail("an exception should have been thrown");
227             } catch (InvocationTargetException ite) {
228                 Assert.assertTrue(ite.getCause() instanceof NoSuchElementException);
229             }
230             try {
231                 remove.invoke(ctr.newInstance(3, 2));
232                 Assert.fail("an exception should have been thrown");
233             } catch (InvocationTargetException ite) {
234                 Assert.assertTrue(ite.getCause() instanceof UnsupportedOperationException);
235             }
236         } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException |
237                  InvocationTargetException | InstantiationException e) {
238             Assert.fail(e.getLocalizedMessage());
239         }
240     }
241 
242     @Test
243     public void testSingletonIteratorUnreachable() {
244         // this tests things that could really never happen,
245         try {
246             Class<?> singletonIteratorClass = null;
247             for (Class<?> c : Combinations.class.getDeclaredClasses()) {
248                 if (c.getCanonicalName().endsWith(".SingletonIterator")) {
249                     singletonIteratorClass = c;
250                 }
251             }
252             Constructor<?> ctr = singletonIteratorClass.getDeclaredConstructor(Integer.TYPE);
253             Method hasNext = singletonIteratorClass.getDeclaredMethod("hasNext");
254             Method next = singletonIteratorClass.getDeclaredMethod("next");
255             Method remove = singletonIteratorClass.getDeclaredMethod("remove");
256             Object iterator = ctr.newInstance(3);
257             Assert.assertTrue((Boolean) hasNext.invoke(iterator));
258             int[] ret = (int[]) next.invoke(iterator);
259             Assert.assertEquals(3, ret.length);
260             Assert.assertEquals(0, ret[0]);
261             Assert.assertEquals(1, ret[1]);
262             Assert.assertEquals(2, ret[2]);
263             Assert.assertFalse((Boolean) hasNext.invoke(iterator));
264             try {
265                 next.invoke(iterator);
266                 Assert.fail("an exception should have been thrown");
267             } catch (InvocationTargetException ite) {
268                 Assert.assertTrue(ite.getCause() instanceof NoSuchElementException);
269             }
270             try {
271                 remove.invoke(iterator);
272                 Assert.fail("an exception should have been thrown");
273             } catch (InvocationTargetException ite) {
274                 Assert.assertTrue(ite.getCause() instanceof UnsupportedOperationException);
275             }
276         } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException |
277                  InvocationTargetException | InstantiationException e) {
278             Assert.fail(e.getLocalizedMessage());
279         }
280     }
281 
282 }