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    * https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
9    * or agreed to in writing, software distributed under the License is
10   * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11   * KIND, either express or implied. See the License for the specific language
12   * governing permissions and limitations under the License.
13   */
14  package org.hipparchus.util;
15  
16  import org.hipparchus.exception.MathIllegalArgumentException;
17  import org.hipparchus.exception.NullArgumentException;
18  import org.hipparchus.random.RandomDataGenerator;
19  import org.hipparchus.util.ResizableDoubleArray.ExpansionMode;
20  import org.junit.jupiter.api.AfterEach;
21  import org.junit.jupiter.api.BeforeEach;
22  import org.junit.jupiter.api.Test;
23  
24  import java.util.Arrays;
25  
26  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
27  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
28  import static org.junit.jupiter.api.Assertions.assertEquals;
29  import static org.junit.jupiter.api.Assertions.assertFalse;
30  import static org.junit.jupiter.api.Assertions.assertNotEquals;
31  import static org.junit.jupiter.api.Assertions.fail;
32  
33  /**
34   * This class contains test cases for the ResizableDoubleArray.
35   */
36  class ResizableDoubleArrayTest {
37  
38      protected ResizableDoubleArray da = null;
39  
40      // Array used to test rolling
41      protected ResizableDoubleArray ra = null;
42  
43      @AfterEach
44      void tearDown()
45          throws Exception {
46          da = null;
47          ra = null;
48      }
49  
50      @BeforeEach
51      void setUp()
52          throws Exception {
53          da = new ResizableDoubleArray();
54          ra = new ResizableDoubleArray();
55      }
56  
57      @Test
58      void testConstructors() {
59          float defaultExpansionFactor = 2.0f;
60          double defaultContractionCriteria = 2.5;
61          ExpansionMode defaultMode = ResizableDoubleArray.ExpansionMode.MULTIPLICATIVE;
62  
63          ResizableDoubleArray testDa = new ResizableDoubleArray(2);
64          assertEquals(0, testDa.getNumElements());
65          assertEquals(2, testDa.getCapacity());
66          assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
67          assertEquals(defaultContractionCriteria, testDa.getContractionCriterion(), 0);
68          assertEquals(defaultMode, testDa.getExpansionMode());
69          try {
70              da = new ResizableDoubleArray(-1);
71              fail("Expecting MathIllegalArgumentException");
72          } catch (MathIllegalArgumentException ex) {
73              // expected
74          }
75  
76          testDa = new ResizableDoubleArray((double[]) null);
77          assertEquals(0, testDa.getNumElements());
78  
79          double[] initialArray = new double[] { 0, 1, 2 };
80  
81          testDa = new ResizableDoubleArray(initialArray);
82          assertEquals(3, testDa.getNumElements());
83  
84          testDa = new ResizableDoubleArray(2, 2.0);
85          assertEquals(0, testDa.getNumElements());
86          assertEquals(2, testDa.getCapacity());
87          assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
88          assertEquals(defaultContractionCriteria, testDa.getContractionCriterion(), 0);
89          assertEquals(defaultMode, testDa.getExpansionMode());
90  
91          try {
92              da = new ResizableDoubleArray(2, 0.5);
93              fail("Expecting MathIllegalArgumentException");
94          } catch (MathIllegalArgumentException ex) {
95              // expected
96          }
97  
98          testDa = new ResizableDoubleArray(2, 3.0);
99          assertEquals(3.0f, testDa.getExpansionFactor(), 0);
100         assertEquals(3.5f, testDa.getContractionCriterion(), 0);
101 
102         testDa = new ResizableDoubleArray(2, 2.0, 3.0);
103         assertEquals(0, testDa.getNumElements());
104         assertEquals(2, testDa.getCapacity());
105         assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
106         assertEquals(3.0f, testDa.getContractionCriterion(), 0);
107         assertEquals(defaultMode, testDa.getExpansionMode());
108 
109         try {
110             da = new ResizableDoubleArray(2, 2.0, 1.5);
111             fail("Expecting MathIllegalArgumentException");
112         } catch (MathIllegalArgumentException ex) {
113             // expected
114         }
115 
116         testDa = new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
117         assertEquals(0, testDa.getNumElements());
118         assertEquals(2, testDa.getCapacity());
119         assertEquals(defaultExpansionFactor, testDa.getExpansionFactor(), 0);
120         assertEquals(3.0f, testDa.getContractionCriterion(), 0);
121         assertEquals(ResizableDoubleArray.ExpansionMode.ADDITIVE, testDa.getExpansionMode());
122 
123         try {
124             da = new ResizableDoubleArray(2, 2.0d, 2.5d, null);
125             fail("Expecting NullArgumentException");
126         } catch (NullArgumentException ex) {
127             // expected
128         }
129 
130         // Copy constructor
131         testDa = new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
132         testDa.addElement(2.0);
133         testDa.addElement(3.2);
134         ResizableDoubleArray copyDa = new ResizableDoubleArray(testDa);
135         assertEquals(copyDa, testDa);
136         assertEquals(testDa, copyDa);
137 
138         // JIRA: MATH-1252
139         final double[] values = { 1 };
140         testDa = new ResizableDoubleArray(values);
141         assertArrayEquals(values, testDa.getElements(), 0);
142         assertEquals(1, testDa.getNumElements());
143         assertEquals(1, testDa.getElement(0), 0);
144     }
145 
146     @Test
147     void testGetValues() {
148         double[] controlArray = {
149             2.0, 4.0, 6.0
150         };
151 
152         da.addElement(2.0);
153         da.addElement(4.0);
154         da.addElement(6.0);
155 
156         double[] testArray = da.getElements();
157 
158         for (int i = 0; i < da.getNumElements(); i++) {
159             assertEquals(testArray[i],
160                          controlArray[i], Double.MIN_VALUE, "The testArray values should equal the controlArray values, " +
161                          "index i: " + i + " does not match");
162         }
163     }
164 
165     @Test
166     void testMinMax() {
167         da.addElement(2.0);
168         da.addElement(22.0);
169         da.addElement(-2.0);
170         da.addElement(21.0);
171         da.addElement(22.0);
172         da.addElement(42.0);
173         da.addElement(62.0);
174         da.addElement(22.0);
175         da.addElement(122.0);
176         da.addElement(1212.0);
177 
178         assertEquals(-2.0,
179                      Arrays.stream(da.getElements()).min().getAsDouble(),
180                      Double.MIN_VALUE,
181                      "Min should be -2.0");
182 
183         assertEquals(1212.0,
184                      Arrays.stream(da.getElements()).max().getAsDouble(),
185                      Double.MIN_VALUE,
186                      "Max should be 1212.0");
187     }
188 
189     @Test
190     void testSetElementArbitraryExpansion1() {
191 
192         // MULTIPLICATIVE_MODE
193         da.addElement(2.0);
194         da.addElement(4.0);
195         da.addElement(6.0);
196         da.setElement(1, 3.0);
197 
198         // Expand the array arbitrarily to 1000 items
199         da.setElement(1000, 3.4);
200 
201         assertEquals(1001, da.getNumElements(), "The number of elements should now be 1001, it isn't");
202 
203         assertEquals(0.0, da.getElement(760), Double.MIN_VALUE, "Uninitialized Elements are default value of 0.0, index 766 wasn't");
204 
205         assertEquals(3.4,
206                      da.getElement(1000), Double.MIN_VALUE, "The 1000th index should be 3.4, it isn't");
207         assertEquals(2.0,
208                      da.getElement(0), Double.MIN_VALUE, "The 0th index should be 2.0, it isn't");
209     }
210 
211     @Test
212     void testSetElementArbitraryExpansion2() {
213         // Make sure numElements and expansion work correctly for expansion
214         // boundary cases
215         da.addElement(2.0);
216         da.addElement(4.0);
217         da.addElement(6.0);
218         assertEquals(16, da.getCapacity());
219         assertEquals(3, da.getNumElements());
220         da.setElement(3, 7.0);
221         assertEquals(16, da.getCapacity());
222         assertEquals(4, da.getNumElements());
223         da.setElement(10, 10.0);
224         assertEquals(16, da.getCapacity());
225         assertEquals(11, da.getNumElements());
226         da.setElement(9, 10.0);
227         assertEquals(16, da.getCapacity());
228         assertEquals(11, da.getNumElements());
229 
230         try {
231             da.setElement(-2, 3);
232             fail("Expecting ArrayIndexOutOfBoundsException for negative index");
233         } catch (ArrayIndexOutOfBoundsException ex) {
234             // expected
235         }
236 
237         // ADDITIVE_MODE
238 
239         ResizableDoubleArray testDa =
240                 new ResizableDoubleArray(2, 2.0, 3.0, ResizableDoubleArray.ExpansionMode.ADDITIVE);
241         assertEquals(2, testDa.getCapacity());
242         testDa.addElement(1d);
243         testDa.addElement(1d);
244         assertEquals(2, testDa.getCapacity());
245         testDa.addElement(1d);
246         assertEquals(4, testDa.getCapacity());
247     }
248 
249     @Test
250     void testAdd1000() {
251         for (int i = 0; i < 1000; i++) {
252             da.addElement(i);
253         }
254 
255         assertEquals(1000, da.getNumElements(), "Number of elements should be equal to 1000 after adding 1000 values");
256 
257         assertEquals(56.0,
258                      da.getElement(56), Double.MIN_VALUE, "The element at the 56th index should be 56");
259 
260         assertEquals(1024,
261                      da.getCapacity(),
262                      "Internal Storage length should be 1024 if we started out with initial capacity of " +
263                      "16 and an expansion factor of 2.0");
264     }
265 
266     @Test
267     void testAddElements() {
268         ResizableDoubleArray testDa = new ResizableDoubleArray();
269 
270         // MULTIPLICATIVE_MODE
271         testDa.addElements(new double[] { 4, 5, 6 });
272         assertEquals(3, testDa.getNumElements(), 0);
273         assertEquals(4, testDa.getElement(0), 0);
274         assertEquals(5, testDa.getElement(1), 0);
275         assertEquals(6, testDa.getElement(2), 0);
276 
277         testDa.addElements(new double[] { 4, 5, 6 });
278         assertEquals(6, testDa.getNumElements());
279 
280         // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
281         testDa = new ResizableDoubleArray(2, 2.0, 2.5, ResizableDoubleArray.ExpansionMode.ADDITIVE);
282         assertEquals(2, testDa.getCapacity());
283         testDa.addElements(new double[] { 1d }); // x,0
284         testDa.addElements(new double[] { 2d }); // x,x
285         testDa.addElements(new double[] { 3d }); // x,x,x,0 -- expanded
286         assertEquals(1d, testDa.getElement(0), 0);
287         assertEquals(2d, testDa.getElement(1), 0);
288         assertEquals(3d, testDa.getElement(2), 0);
289         assertEquals(4, testDa.getCapacity()); // x,x,x,0
290         assertEquals(3, testDa.getNumElements());
291     }
292 
293     @Test
294     void testAddElementRolling() {
295         ra.addElement(0.5);
296         ra.addElement(1.0);
297         ra.addElement(1.0);
298         ra.addElement(1.0);
299         ra.addElement(1.0);
300         ra.addElement(1.0);
301         ra.addElementRolling(2.0);
302 
303         assertEquals(6,
304                      ra.getNumElements(),
305                      "There should be 6 elements in the eda");
306         assertEquals(2.0,
307                      Arrays.stream(ra.getElements()).max().getAsDouble(),
308                      Double.MIN_VALUE,
309                      "The max element should be 2.0");
310         assertEquals(1.0,
311                      Arrays.stream(ra.getElements()).min().getAsDouble(),
312                      Double.MIN_VALUE,
313                      "The min element should be 1.0");
314 
315         for (int i = 0; i < 1024; i++) {
316             ra.addElementRolling(i);
317         }
318 
319         assertEquals(6, ra.getNumElements(), "We just inserted 1024 rolling elements, num elements should still be 6");
320 
321         // MULTIPLICATIVE_MODE
322         da.clear();
323         da.addElement(1);
324         da.addElement(2);
325         da.addElementRolling(3);
326         assertEquals(3, da.getElement(1), 0);
327         da.addElementRolling(4);
328         assertEquals(3, da.getElement(0), 0);
329         assertEquals(4, da.getElement(1), 0);
330         da.addElement(5);
331         assertEquals(5, da.getElement(2), 0);
332         da.addElementRolling(6);
333         assertEquals(4, da.getElement(0), 0);
334         assertEquals(5, da.getElement(1), 0);
335         assertEquals(6, da.getElement(2), 0);
336 
337         // ADDITIVE_MODE (x's are occupied storage locations, 0's are open)
338         ResizableDoubleArray testDa =
339                 new ResizableDoubleArray(2, 2.0, 2.5, ResizableDoubleArray.ExpansionMode.ADDITIVE);
340         assertEquals(2, testDa.getCapacity());
341         testDa.addElement(1d); // x,0
342         testDa.addElement(2d); // x,x
343         testDa.addElement(3d); // x,x,x,0 -- expanded
344         assertEquals(1d, testDa.getElement(0), 0);
345         assertEquals(2d, testDa.getElement(1), 0);
346         assertEquals(3d, testDa.getElement(2), 0);
347         assertEquals(4, testDa.getCapacity()); // x,x,x,0
348         assertEquals(3, testDa.getNumElements());
349         testDa.addElementRolling(4d);
350         assertEquals(2d, testDa.getElement(0), 0);
351         assertEquals(3d, testDa.getElement(1), 0);
352         assertEquals(4d, testDa.getElement(2), 0);
353         assertEquals(4, testDa.getCapacity()); // 0,x,x,x
354         assertEquals(3, testDa.getNumElements());
355         testDa.addElementRolling(5d); // 0,0,x,x,x,0 -- time to contract
356         assertEquals(3d, testDa.getElement(0), 0);
357         assertEquals(4d, testDa.getElement(1), 0);
358         assertEquals(5d, testDa.getElement(2), 0);
359         assertEquals(4, testDa.getCapacity()); // contracted -- x,x,x,0
360         assertEquals(3, testDa.getNumElements());
361         try {
362             testDa.getElement(4);
363             fail("Expecting ArrayIndexOutOfBoundsException");
364         } catch (ArrayIndexOutOfBoundsException ex) {
365             // expected
366         }
367         try {
368             testDa.getElement(-1);
369             fail("Expecting ArrayIndexOutOfBoundsException");
370         } catch (ArrayIndexOutOfBoundsException ex) {
371             // expected
372         }
373     }
374 
375     @Test
376     void testSetNumberOfElements() {
377         da.addElement(1.0);
378         da.addElement(1.0);
379         da.addElement(1.0);
380         da.addElement(1.0);
381         da.addElement(1.0);
382         da.addElement(1.0);
383         assertEquals(6, da.getNumElements(), "Number of elements should equal 6");
384 
385         da.setNumElements(3);
386         assertEquals(3, da.getNumElements(), "Number of elements should equal 3");
387 
388         try {
389             da.setNumElements(-3);
390             fail("Setting number of elements to negative should've thrown an exception");
391         } catch (MathIllegalArgumentException iae) {
392         }
393 
394         da.setNumElements(1024);
395         assertEquals(1024, da.getNumElements(), "Number of elements should now be 1024");
396         assertEquals(0.0, da.getElement(453), Double.MIN_VALUE, "Element 453 should be a default double");
397     }
398 
399     @Test
400     void testWithInitialCapacity() {
401 
402         ResizableDoubleArray eDA2 = new ResizableDoubleArray(2);
403         assertEquals(0, eDA2.getNumElements(), "Initial number of elements should be 0");
404 
405         final RandomDataGenerator gen = new RandomDataGenerator(1000);
406         final int iterations = gen.nextInt(100, 1000);
407 
408         for (int i = 0; i < iterations; i++) {
409             eDA2.addElement(i);
410         }
411 
412         assertEquals(iterations, eDA2.getNumElements(), "Number of elements should be equal to " + iterations);
413 
414         eDA2.addElement(2.0);
415 
416         assertEquals(iterations + 1, eDA2.getNumElements(), "Number of elements should be equals to " +
417                      (iterations + 1));
418     }
419 
420     @Test
421     void testWithInitialCapacityAndExpansionFactor() {
422 
423         ResizableDoubleArray eDA3 = new ResizableDoubleArray(3, 3.0, 3.5);
424         assertEquals(0, eDA3.getNumElements(), "Initial number of elements should be 0");
425 
426         final RandomDataGenerator gen = new RandomDataGenerator(1000);
427         final int iterations = gen.nextInt(100, 1000);
428 
429         for (int i = 0; i < iterations; i++) {
430             eDA3.addElement(i);
431         }
432 
433         assertEquals(iterations, eDA3.getNumElements(), "Number of elements should be equal to " + iterations);
434 
435         eDA3.addElement(2.0);
436 
437         assertEquals(iterations + 1, eDA3.getNumElements(), "Number of elements should be equals to " +
438                      (iterations + 1));
439 
440         assertEquals(3.0f,
441                      eDA3.getExpansionFactor(), Double.MIN_VALUE, "Expansion factor should equal 3.0");
442     }
443 
444     @Test
445     void testDiscard() {
446         da.addElement(2.0);
447         da.addElement(2.0);
448         da.addElement(2.0);
449         da.addElement(2.0);
450         da.addElement(2.0);
451         da.addElement(2.0);
452         da.addElement(2.0);
453         da.addElement(2.0);
454         da.addElement(2.0);
455         da.addElement(2.0);
456         da.addElement(2.0);
457         assertEquals(11, da.getNumElements(), "Number of elements should be 11");
458 
459         da.discardFrontElements(5);
460         assertEquals(6, da.getNumElements(), "Number of elements should be 6");
461 
462         da.addElement(2.0);
463         da.addElement(2.0);
464         da.addElement(2.0);
465         da.addElement(2.0);
466         assertEquals(10, da.getNumElements(), "Number of elements should be 10");
467 
468         da.discardMostRecentElements(2);
469         assertEquals(8, da.getNumElements(), "Number of elements should be 8");
470 
471         try {
472             da.discardFrontElements(-1);
473             fail("Trying to discard a negative number of element is not allowed");
474         } catch (Exception e) {
475         }
476 
477         try {
478             da.discardMostRecentElements(-1);
479             fail("Trying to discard a negative number of element is not allowed");
480         } catch (Exception e) {
481         }
482 
483         try {
484             da.discardFrontElements(10000);
485             fail("You can't discard more elements than the array contains");
486         } catch (Exception e) {
487         }
488 
489         try {
490             da.discardMostRecentElements(10000);
491             fail("You can't discard more elements than the array contains");
492         } catch (Exception e) {
493         }
494 
495     }
496 
497     @Test
498     void testSubstitute() {
499 
500         da.addElement(2.0);
501         da.addElement(2.0);
502         da.addElement(2.0);
503         da.addElement(2.0);
504         da.addElement(2.0);
505         da.addElement(2.0);
506         da.addElement(2.0);
507         da.addElement(2.0);
508         da.addElement(2.0);
509         da.addElement(2.0);
510         da.addElement(2.0);
511         assertEquals(11, da.getNumElements(), "Number of elements should be 11");
512 
513         da.substituteMostRecentElement(24);
514 
515         assertEquals(11, da.getNumElements(), "Number of elements should be 11");
516 
517         assertDoesNotThrow(() -> {
518             da.discardMostRecentElements(10);
519         }, "Trying to discard a negative number of element is not allowed");
520 
521         da.substituteMostRecentElement(24);
522 
523         assertEquals(1, da.getNumElements(), "Number of elements should be 1");
524 
525     }
526 
527     @SuppressWarnings("unlikely-arg-type")
528     @Test
529     void testEqualsAndHashCode()
530         throws Exception {
531 
532         // Wrong type
533         ResizableDoubleArray first = new ResizableDoubleArray();
534         Double other = Double.valueOf(2);
535         assertNotEquals(first, other);
536 
537         // Null
538         other = null;
539         assertNotEquals(first, other);
540 
541         // Reflexive
542         assertEquals(first, first);
543 
544         // Argumentless constructor
545         ResizableDoubleArray second = new ResizableDoubleArray();
546         verifyEquality(first, second);
547 
548         // Equals iff same data, same properties
549         ResizableDoubleArray third = new ResizableDoubleArray(3, 2.0, 2.0);
550         verifyInequality(third, first);
551         ResizableDoubleArray fourth = new ResizableDoubleArray(3, 2.0, 2.0);
552         ResizableDoubleArray fifth = new ResizableDoubleArray(2, 2.0, 2.0);
553         verifyEquality(third, fourth);
554         verifyInequality(third, fifth);
555         third.addElement(4.1);
556         third.addElement(4.2);
557         third.addElement(4.3);
558         fourth.addElement(4.1);
559         fourth.addElement(4.2);
560         fourth.addElement(4.3);
561         verifyEquality(third, fourth);
562 
563         // expand
564         fourth.addElement(4.4);
565         verifyInequality(third, fourth);
566         third.addElement(4.4);
567         verifyEquality(third, fourth);
568         fourth.addElement(4.4);
569         verifyInequality(third, fourth);
570         third.addElement(4.4);
571         verifyEquality(third, fourth);
572         fourth.addElementRolling(4.5);
573         third.addElementRolling(4.5);
574         verifyEquality(third, fourth);
575 
576         // discard
577         third.discardFrontElements(1);
578         verifyInequality(third, fourth);
579         fourth.discardFrontElements(1);
580         verifyEquality(third, fourth);
581 
582         // discard recent
583         third.discardMostRecentElements(2);
584         fourth.discardMostRecentElements(2);
585         verifyEquality(third, fourth);
586 
587         // wrong order
588         third.addElement(18);
589         fourth.addElement(17);
590         third.addElement(17);
591         fourth.addElement(18);
592         verifyInequality(third, fourth);
593 
594         // Copy constructor
595         verifyEquality(fourth, new ResizableDoubleArray(fourth));
596 
597         // Instance copy
598         verifyEquality(fourth, fourth.copy());
599     }
600 
601     @Test
602     void testGetArrayRef() {
603         final ResizableDoubleArray a = new ResizableDoubleArray();
604 
605         // Modify "a" through the public API.
606         final int index = 20;
607         final double v1 = 1.2;
608         a.setElement(index, v1);
609 
610         // Modify the internal storage through the protected API.
611         final double v2 = v1 + 3.4;
612         final double[] aInternalArray = a.getArrayRef();
613         aInternalArray[a.getStartIndex() + index] = v2;
614 
615         assertEquals(v2, a.getElement(index), 0d);
616     }
617 
618     @Test
619     void testCompute() {
620         final ResizableDoubleArray a = new ResizableDoubleArray();
621         final int max = 20;
622         for (int i = 1; i <= max; i++) {
623             a.setElement(i, i);
624         }
625 
626         final MathArrays.Function add = new MathArrays.Function() {
627 
628             @Override
629             public double evaluate(double[] a, int index, int num) {
630                 double sum = 0;
631                 final int max = index + num;
632                 for (int i = index; i < max; i++) {
633                     sum += a[i];
634                 }
635                 return sum;
636             }
637 
638             @Override
639             public double evaluate(double[] a) {
640                 return evaluate(a, 0, a.length);
641             }
642         };
643 
644         final double sum = a.compute(add);
645         assertEquals(0.5 * max * (max + 1), sum, 0);
646     }
647 
648     private void verifyEquality(ResizableDoubleArray a,
649                                 ResizableDoubleArray b) {
650         assertEquals(b, a);
651         assertEquals(a, b);
652         assertEquals(a.hashCode(), b.hashCode());
653     }
654 
655     private void verifyInequality(ResizableDoubleArray a,
656                                   ResizableDoubleArray b) {
657         assertNotEquals(b, a);
658         assertNotEquals(a, b);
659         assertFalse(a.hashCode() == b.hashCode());
660     }
661 
662 }