1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS 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    *   http://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  package org.orekit.time;
18  
19  import org.hipparchus.Field;
20  import org.hipparchus.util.Binary64;
21  import org.hipparchus.util.Binary64Field;
22  import org.junit.jupiter.api.Assertions;
23  import org.junit.jupiter.api.DisplayName;
24  import org.junit.jupiter.api.RepeatedTest;
25  import org.junit.jupiter.api.Test;
26  
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.ExecutorService;
31  import java.util.concurrent.Executors;
32  import java.util.concurrent.TimeUnit;
33  import java.util.concurrent.atomic.AtomicInteger;
34  
35  class TimeStampedFieldHermiteInterpolatorTest {
36      @Test
37      @DisplayName("Test default constructor")
38      void testDefaultConstructor() {
39          // When
40          final TimeStampedFieldHermiteInterpolator<Binary64> interpolator = new TimeStampedFieldHermiteInterpolator<>();
41  
42          // Then
43          Assertions.assertEquals(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS,
44                                  interpolator.getNbInterpolationPoints());
45          Assertions.assertEquals(AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC,
46                                  interpolator.getExtrapolationThreshold());
47      }
48  
49      @RepeatedTest(10)
50      @DisplayName("test interpolator in multi-threaded environment")
51      void testIssue1164() throws InterruptedException {
52          // GIVEN
53          // Create field instance
54          final Field<Binary64> field = Binary64Field.getInstance();
55  
56          // Create interpolator
57          final FieldTimeInterpolator<TimeStampedField<Binary64>, Binary64> interpolator =
58                  new TimeStampedFieldHermiteInterpolator<>();
59  
60          // Create sample and interpolation dates
61          final int                               sampleSize  = 100;
62          final FieldAbsoluteDate<Binary64>       initialDate = new FieldAbsoluteDate<>(field);
63          final List<TimeStampedField<Binary64>>  sample      = new ArrayList<>();
64          final List<FieldAbsoluteDate<Binary64>> dates       = new ArrayList<>();
65          for (int i = 0; i < sampleSize + 1; i++) {
66              sample.add(new TimeStampedField<>(new Binary64(i * i), initialDate.shiftedBy(i * 60)));
67              dates.add(initialDate.shiftedBy(i * 60));
68          }
69  
70          // Create multithreading environment
71          ExecutorService service = Executors.newFixedThreadPool(sampleSize);
72  
73          final AtomicInteger           sum   = new AtomicInteger(0);
74          final List<Callable<Integer>> tasks = new ArrayList<>();
75          for (final FieldAbsoluteDate<Binary64> date : dates) {
76              tasks.add(new ParallelTask(interpolator, sum, sample, date));
77          }
78  
79          // WHEN
80          service.invokeAll(tasks);
81  
82          // THEN
83          // Sum of 1*1 + 2*2 + 3*3 + ...
84          final int expectedSum = sampleSize * (sampleSize + 1) * (2 * sampleSize + 1) / 6;
85          Assertions.assertEquals(expectedSum, sum.get());
86          try {
87              // wait for proper ending
88              service.shutdown();
89              service.awaitTermination(5, TimeUnit.SECONDS);
90          } catch (InterruptedException ie) {
91              // Restore interrupted state...
92              Thread.currentThread().interrupt();
93          }
94      }
95  
96      /** Custom class for multi threading testing purpose */
97      private static class ParallelTask implements Callable<Integer> {
98  
99          private final FieldTimeInterpolator<TimeStampedField<Binary64>, Binary64> interpolator;
100 
101         private final List<TimeStampedField<Binary64>> sample;
102 
103         private final AtomicInteger sum;
104 
105         private final FieldAbsoluteDate<Binary64> interpolationDate;
106 
107         private ParallelTask(final FieldTimeInterpolator<TimeStampedField<Binary64>, Binary64> interpolator,
108                              final AtomicInteger sum, final List<TimeStampedField<Binary64>> sample,
109                              final FieldAbsoluteDate<Binary64> interpolationDate) {
110             // Store interpolator
111             this.interpolator      = interpolator;
112             this.sum               = sum;
113             this.interpolationDate = interpolationDate;
114             this.sample            = sample;
115         }
116 
117         @Override
118         public Integer call() {
119             // Add result to sum
120             final int valueToAdd = (int) interpolator.interpolate(interpolationDate, sample).getValue().getReal();
121             sum.getAndAdd(valueToAdd);
122             return 1;
123         }
124     }
125 
126 }