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