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.utils;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.util.Binary64;
21  import org.hipparchus.util.Binary64Field;
22  import org.junit.jupiter.api.AfterEach;
23  import org.junit.jupiter.api.Assertions;
24  import org.junit.jupiter.api.BeforeEach;
25  import org.junit.jupiter.api.Test;
26  import org.orekit.Utils;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.utils.FieldTimeSpanMap.Span;
32  import org.orekit.utils.FieldTimeSpanMap.Transition;
33  
34  import java.util.SortedSet;
35  import java.util.function.Consumer;
36  
37  public class FieldTimeSpanMapTest {
38  
39      private FieldAbsoluteDate<Binary64> arbitraryEpoch;
40  
41      @Test
42      public void testSingleEntry() {
43          String                             single = "single";
44          final Binary64Field                field  = Binary64Field.getInstance();
45          FieldTimeSpanMap<String, Binary64> map    = new FieldTimeSpanMap<>(single, field);
46          checkCountConsistency(map);
47          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getCCSDSEpoch(field)));
48          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getFiftiesEpoch(field)));
49          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getFutureInfinity(field)));
50          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getGalileoEpoch(field)));
51          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getGPSEpoch(field)));
52          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getJ2000Epoch(field)));
53          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getJavaEpoch(field)));
54          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getJulianEpoch(field)));
55          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getModifiedJulianEpoch(field)));
56          Assertions.assertSame(single, map.get(FieldAbsoluteDate.getPastInfinity(field)));
57          Assertions.assertEquals(1, map.getSpansNumber());
58          Assertions.assertSame(single, map.getFirstNonNullSpan().getData());
59          Assertions.assertSame(single, map.getLastNonNullSpan().getData());
60      }
61  
62      @Test
63      public void testForwardAdd() {
64          final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(Binary64Field.getInstance());
65          FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
66          checkCountConsistency(map);
67          for (int i = 1; i < 100; ++i) {
68              Integer entry = i;
69              Span<Integer, Binary64> span = map.addValidAfter(entry, ref.shiftedBy(i), false);
70              Assertions.assertSame(entry, span.getData());
71              checkCountConsistency(map);
72          }
73          Assertions.assertEquals(0, map.get(ref.shiftedBy(-1000.0)).intValue());
74          Assertions.assertEquals(0, map.get(ref.shiftedBy( -100.0)).intValue());
75          Span<Integer, Binary64> span = map.getSpan(ref.shiftedBy(-1000.0));
76          Assertions.assertEquals(0, span.getData().intValue());
77          Assertions.assertTrue(span.getStart().durationFrom(AbsoluteDate.J2000_EPOCH).getReal() < -Double.MAX_VALUE);
78          Assertions.assertEquals(1.0, span.getEnd().durationFrom(AbsoluteDate.J2000_EPOCH).getReal(), 1.0e-15);
79          for (int i = 0; i < 100; ++i) {
80              Assertions.assertEquals(i, map.get(ref.shiftedBy(i + 0.1)).intValue());
81              Assertions.assertEquals(i, map.get(ref.shiftedBy(i + 0.9)).intValue());
82              span = map.getSpan(ref.shiftedBy(i + 0.1));
83              Assertions.assertEquals(i, span.getData().intValue());
84              if (i == 0) {
85                  Assertions.assertTrue(span.getStart().durationFrom(AbsoluteDate.J2000_EPOCH).getReal() < -Double.MAX_VALUE);
86              } else {
87                  Assertions.assertEquals(i, span.getStart().durationFrom(AbsoluteDate.J2000_EPOCH).getReal(), 1.0e-15);
88              } if (i == 99) {
89                  Assertions.assertTrue(span.getEnd().durationFrom(AbsoluteDate.J2000_EPOCH).getReal() > Double.MAX_VALUE);
90              } else {
91                  Assertions.assertEquals(i + 1, span.getEnd().durationFrom(AbsoluteDate.J2000_EPOCH).getReal(), 1.0e-15);
92              }
93          }
94          Assertions.assertEquals(99, map.get(ref.shiftedBy(  100.0)).intValue());
95          Assertions.assertEquals(99, map.get(ref.shiftedBy( 1000.0)).intValue());
96          checkCountConsistency(map);
97      }
98  
99      @Test
100     public void testBackwardAdd() {
101         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(Binary64Field.getInstance());
102         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
103         checkCountConsistency(map);
104         for (int i = -1; i > -100; --i) {
105             Integer entry = i;
106             Span<Integer, Binary64> span = map.addValidBefore(entry, ref.shiftedBy(i), false);
107             Assertions.assertSame(entry, span.getData());
108             checkCountConsistency(map);
109         }
110         Assertions.assertEquals(0, map.get(ref.shiftedBy( 1000.0)).intValue());
111         Assertions.assertEquals(0, map.get(ref.shiftedBy(  100.0)).intValue());
112         for (int i = 0; i > -100; --i) {
113             Assertions.assertEquals(i, map.get(ref.shiftedBy(i - 0.1)).intValue());
114             Assertions.assertEquals(i, map.get(ref.shiftedBy(i - 0.9)).intValue());
115         }
116         Assertions.assertEquals(-99, map.get(ref.shiftedBy( -100.0)).intValue());
117         Assertions.assertEquals(-99, map.get(ref.shiftedBy(-1000.0)).intValue());
118         checkCountConsistency(map);
119     }
120 
121     @Test
122     public void testRandomAddNoErase() {
123         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(Binary64Field.getInstance());
124         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
125         map.addValidAfter(10, ref.shiftedBy(10.0), false);
126         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
127         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
128         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
129         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
130         Assertions.assertEquals( 0, map.get(ref.shiftedBy( -1.0)).intValue());
131         Assertions.assertEquals( 0, map.get(ref.shiftedBy(  1.9)).intValue());
132         Assertions.assertEquals( 2, map.get(ref.shiftedBy(  2.1)).intValue());
133         Assertions.assertEquals( 2, map.get(ref.shiftedBy(  2.9)).intValue());
134         Assertions.assertEquals( 3, map.get(ref.shiftedBy(  3.1)).intValue());
135         Assertions.assertEquals( 3, map.get(ref.shiftedBy(  4.9)).intValue());
136         Assertions.assertEquals( 5, map.get(ref.shiftedBy(  5.1)).intValue());
137         Assertions.assertEquals( 5, map.get(ref.shiftedBy(  8.9)).intValue());
138         Assertions.assertEquals( 9, map.get(ref.shiftedBy(  9.1)).intValue());
139         Assertions.assertEquals( 9, map.get(ref.shiftedBy(  9.9)).intValue());
140         Assertions.assertEquals(10, map.get(ref.shiftedBy( 10.1)).intValue());
141         Assertions.assertEquals(10, map.get(ref.shiftedBy(100.0)).intValue());
142         final StringBuilder builder = new StringBuilder();
143         map.forEach(i -> builder.append(' ').append(i));
144         Assertions.assertEquals(" 0 2 3 5 9 10", builder.toString());
145         Assertions.assertEquals(6, map.getSpansNumber());
146         checkCountConsistency(map);
147     }
148 
149     @Test
150     public void testRandomAddErase() {
151         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(Binary64Field.getInstance());
152         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
153         map.addValidAfter(10, ref.shiftedBy(10.0), false);
154         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
155         map.addValidBefore( 5, ref.shiftedBy( 7.0), false);
156         map.addValidAfter(null, ref.shiftedBy( 5.0), true);
157         map.addValidAfter( 1, ref.shiftedBy( 1.0), false);
158         map.addValidBefore(null, ref.shiftedBy( 3.0), true);
159         map.addValidBefore( 7, ref.shiftedBy( 9.0), false);
160         Assertions.assertNull(map.get(ref.shiftedBy( -1.0)));
161         Assertions.assertNull(map.get(ref.shiftedBy(  1.9)));
162         Assertions.assertNull(map.get(ref.shiftedBy(  2.1)));
163         Assertions.assertNull(map.get(ref.shiftedBy(  2.9)));
164         Assertions.assertEquals( 5, map.get(ref.shiftedBy(  3.1)).intValue());
165         Assertions.assertEquals( 5, map.get(ref.shiftedBy(  4.9)).intValue());
166         Assertions.assertEquals( 7, map.get(ref.shiftedBy(  5.1)).intValue());
167         Assertions.assertEquals( 7, map.get(ref.shiftedBy(  6.9)).intValue());
168         Assertions.assertEquals( 7, map.get(ref.shiftedBy(  7.1)).intValue());
169         Assertions.assertEquals( 7, map.get(ref.shiftedBy(  8.9)).intValue());
170         Assertions.assertNull(map.get(ref.shiftedBy(  9.1)));
171         Assertions.assertNull(map.get(ref.shiftedBy(  9.9)));
172         Assertions.assertNull(map.get(ref.shiftedBy( 10.1)));
173         Assertions.assertNull(map.get(ref.shiftedBy(100.0)));
174         final StringBuilder builder = new StringBuilder();
175         map.forEach(i -> builder.append(' ').append(i));
176         Assertions.assertEquals(" 5 7", builder.toString());
177         Assertions.assertEquals(4, map.getSpansNumber());
178         checkCountConsistency(map);
179     }
180 
181     @Test
182     public void testAddBetweenEmpty() {
183         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
184         map.addValidBetween(1, arbitraryEpoch.shiftedBy(-2), arbitraryEpoch.shiftedBy(+2));
185         Assertions.assertEquals(3, map.getSpansNumber());
186         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy(-3)).intValue());
187         Assertions.assertEquals(1, map.get(arbitraryEpoch).intValue());
188         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy(+3)).intValue());
189         checkCountConsistency(map);
190     }
191 
192     @Test
193     public void testAddBetweenBefore() {
194         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
195         map.addValidBefore(1, arbitraryEpoch, false);
196         map.addValidBetween(7, arbitraryEpoch.shiftedBy(-4), arbitraryEpoch.shiftedBy(-2));
197         Assertions.assertEquals(4, map.getSpansNumber());
198         Assertions.assertEquals(1, map.get(arbitraryEpoch.shiftedBy(-5)).intValue());
199         Assertions.assertEquals(7, map.get(arbitraryEpoch.shiftedBy(-3)).intValue());
200         Assertions.assertEquals(1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
201         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy(+3)).intValue());
202         checkCountConsistency(map);
203     }
204 
205     @Test
206     public void testAddBetweenAfter() {
207         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
208         map.addValidBefore(1, arbitraryEpoch, false);
209         map.addValidBetween(7, arbitraryEpoch.shiftedBy(2), arbitraryEpoch.shiftedBy(4));
210         Assertions.assertEquals(4, map.getSpansNumber());
211         Assertions.assertEquals(1, map.get(arbitraryEpoch.shiftedBy(-3)).intValue());
212         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy( 1)).intValue());
213         Assertions.assertEquals(7, map.get(arbitraryEpoch.shiftedBy( 3)).intValue());
214         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy( 5)).intValue());
215         checkCountConsistency(map);
216     }
217 
218     @Test
219     public void testAddBetweenCoveringAll() {
220         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
221         map.addValidAfter(1, arbitraryEpoch.shiftedBy(1), false);
222         map.addValidAfter(2, arbitraryEpoch.shiftedBy(2), false);
223         map.addValidAfter(3, arbitraryEpoch.shiftedBy(3), false);
224         map.addValidAfter(4, arbitraryEpoch.shiftedBy(4), false);
225         map.addValidAfter(5, arbitraryEpoch.shiftedBy(5), false);
226         Assertions.assertEquals(6, map.getSpansNumber());
227         map.addValidBetween(-1, arbitraryEpoch.shiftedBy(-1), arbitraryEpoch.shiftedBy(6));
228         Assertions.assertEquals( 3, map.getSpansNumber());
229         Assertions.assertEquals( 0, map.get(arbitraryEpoch.shiftedBy(-5)).intValue());
230         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy( 2)).intValue());
231         Assertions.assertEquals( 5, map.get(arbitraryEpoch.shiftedBy(+7)).intValue());
232         checkCountConsistency(map);
233     }
234 
235     @Test
236     public void testAddBetweenCoveringSome() {
237         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
238         map.addValidAfter(1, arbitraryEpoch.shiftedBy(1), false);
239         map.addValidAfter(2, arbitraryEpoch.shiftedBy(2), false);
240         map.addValidAfter(3, arbitraryEpoch.shiftedBy(3), false);
241         map.addValidAfter(4, arbitraryEpoch.shiftedBy(4), false);
242         map.addValidAfter(5, arbitraryEpoch.shiftedBy(5), false);
243         Assertions.assertEquals(6, map.getSpansNumber());
244         Integer entry = -1;
245         Span<Integer, Binary64> span = map.addValidBetween(entry, arbitraryEpoch.shiftedBy(1.5), arbitraryEpoch.shiftedBy(4.5));
246         Assertions.assertSame(entry, span.getData());
247         Assertions.assertEquals(5, map.getSpansNumber());
248         Assertions.assertEquals( 0, map.get(arbitraryEpoch.shiftedBy(0.75)).intValue());
249         Assertions.assertEquals( 1, map.get(arbitraryEpoch.shiftedBy(1.25)).intValue());
250         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(1.75)).intValue());
251         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(4.25)).intValue());
252         Assertions.assertEquals( 4, map.get(arbitraryEpoch.shiftedBy(4.75)).intValue());
253         Assertions.assertEquals( 5, map.get(arbitraryEpoch.shiftedBy(5.25)).intValue());
254         checkCountConsistency(map);
255     }
256 
257     @Test
258     public void testAddBetweenSplittingOneSpanOnly() {
259         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
260         map.addValidAfter(1, arbitraryEpoch.shiftedBy(1), false);
261         map.addValidAfter(2, arbitraryEpoch.shiftedBy(2), false);
262         map.addValidAfter(3, arbitraryEpoch.shiftedBy(3), false);
263         map.addValidAfter(4, arbitraryEpoch.shiftedBy(4), false);
264         map.addValidAfter(5, arbitraryEpoch.shiftedBy(5), false);
265         Assertions.assertEquals(6, map.getSpansNumber());
266         map.addValidBetween(-1, arbitraryEpoch.shiftedBy(2.25), arbitraryEpoch.shiftedBy(2.75));
267         Assertions.assertEquals(8, map.getSpansNumber());
268         Assertions.assertEquals( 0, map.get(arbitraryEpoch.shiftedBy(0.75)).intValue());
269         Assertions.assertEquals( 1, map.get(arbitraryEpoch.shiftedBy(1.99)).intValue());
270         Assertions.assertEquals( 2, map.get(arbitraryEpoch.shiftedBy(2.01)).intValue());
271         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(2.50)).intValue());
272         Assertions.assertEquals( 2, map.get(arbitraryEpoch.shiftedBy(2.99)).intValue());
273         Assertions.assertEquals( 3, map.get(arbitraryEpoch.shiftedBy(3.01)).intValue());
274         Assertions.assertEquals( 4, map.get(arbitraryEpoch.shiftedBy(4.25)).intValue());
275         Assertions.assertEquals( 5, map.get(arbitraryEpoch.shiftedBy(5.25)).intValue());
276         checkCountConsistency(map);
277     }
278 
279     @Test
280     public void testAddBetweenExistingDates() {
281         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
282         map.addValidAfter(1, arbitraryEpoch.shiftedBy(1), false);
283         map.addValidAfter(2, arbitraryEpoch.shiftedBy(2), false);
284         map.addValidAfter(3, arbitraryEpoch.shiftedBy(3), false);
285         map.addValidAfter(4, arbitraryEpoch.shiftedBy(4), false);
286         map.addValidAfter(5, arbitraryEpoch.shiftedBy(5), false);
287         Assertions.assertEquals(6, map.getSpansNumber());
288         map.addValidBetween(-1, arbitraryEpoch.shiftedBy(2), arbitraryEpoch.shiftedBy(4));
289         Assertions.assertEquals(5, map.getSpansNumber());
290         Assertions.assertEquals( 0, map.get(arbitraryEpoch.shiftedBy(0.99)).intValue());
291         Assertions.assertEquals( 1, map.get(arbitraryEpoch.shiftedBy(1.01)).intValue());
292         Assertions.assertEquals( 1, map.get(arbitraryEpoch.shiftedBy(1.99)).intValue());
293         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(2.01)).intValue());
294         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(3.99)).intValue());
295         Assertions.assertEquals( 4, map.get(arbitraryEpoch.shiftedBy(4.01)).intValue());
296         Assertions.assertEquals( 4, map.get(arbitraryEpoch.shiftedBy(4.99)).intValue());
297         Assertions.assertEquals( 5, map.get(arbitraryEpoch.shiftedBy(5.01)).intValue());
298         checkCountConsistency(map);
299     }
300 
301     @Test
302     public void testExtractRangeInfinity() {
303         final Binary64Field field = Binary64Field.getInstance();
304         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(field);
305         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, field);
306         map.addValidAfter(10, ref.shiftedBy(10.0), false);
307         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
308         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
309         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
310         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
311         FieldTimeSpanMap<Integer, Binary64> range = map.extractRange(FieldAbsoluteDate.getPastInfinity(field),
312                                                                      FieldAbsoluteDate.getFutureInfinity(field));
313         Assertions.assertEquals(map.getSpansNumber(), range.getSpansNumber());
314         checkCountConsistency(map);
315     }
316 
317     @Test
318     public void testExtractRangeSingleEntry() {
319         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(Binary64Field.getInstance());
320         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
321         map.addValidAfter(10, ref.shiftedBy(10.0), false);
322         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
323         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
324         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
325         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
326         FieldTimeSpanMap<Integer, Binary64> range = map.extractRange(ref.shiftedBy(6), ref.shiftedBy(8));
327         Assertions.assertEquals(1, range.getSpansNumber());
328         Assertions.assertEquals(5, range.get(ref.shiftedBy(-10000)).intValue());
329         Assertions.assertEquals(5, range.get(ref.shiftedBy(+10000)).intValue());
330         checkCountConsistency(map);
331     }
332 
333     @Test
334     public void testExtractFromPastInfinity() {
335         final Binary64Field field = Binary64Field.getInstance();
336         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(field);
337         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, field);
338         map.addValidAfter(10, ref.shiftedBy(10.0), false);
339         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
340         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
341         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
342         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
343         FieldTimeSpanMap<Integer, Binary64> range = map.extractRange(FieldAbsoluteDate.getPastInfinity(field), ref.shiftedBy(8));
344         Assertions.assertEquals(4, range.getSpansNumber());
345         Assertions.assertEquals( 0, range.get(ref.shiftedBy( -1.0)).intValue());
346         Assertions.assertEquals( 0, range.get(ref.shiftedBy(  1.9)).intValue());
347         Assertions.assertEquals( 2, range.get(ref.shiftedBy(  2.1)).intValue());
348         Assertions.assertEquals( 2, range.get(ref.shiftedBy(  2.9)).intValue());
349         Assertions.assertEquals( 3, range.get(ref.shiftedBy(  3.1)).intValue());
350         Assertions.assertEquals( 3, range.get(ref.shiftedBy(  4.9)).intValue());
351         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  5.1)).intValue());
352         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  8.9)).intValue());
353         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  9.1)).intValue());
354         Assertions.assertEquals( 5, range.get(ref.shiftedBy( 99.0)).intValue());
355         checkCountConsistency(map);
356     }
357 
358     @Test
359     public void testExtractToFutureInfinity() {
360         final Binary64Field field = Binary64Field.getInstance();
361         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(field);
362         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, field);
363         map.addValidAfter(10, ref.shiftedBy(10.0), false);
364         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
365         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
366         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
367         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
368         FieldTimeSpanMap<Integer, Binary64> range = map.extractRange(ref.shiftedBy(4),
369                                                                      FieldAbsoluteDate.getFutureInfinity(field));
370         Assertions.assertEquals(4, range.getSpansNumber());
371         Assertions.assertEquals( 3, range.get(ref.shiftedBy(-99.0)).intValue());
372         Assertions.assertEquals( 3, range.get(ref.shiftedBy(  4.9)).intValue());
373         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  5.1)).intValue());
374         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  8.9)).intValue());
375         Assertions.assertEquals( 9, range.get(ref.shiftedBy(  9.1)).intValue());
376         Assertions.assertEquals( 9, range.get(ref.shiftedBy(  9.9)).intValue());
377         Assertions.assertEquals(10, range.get(ref.shiftedBy( 10.1)).intValue());
378         Assertions.assertEquals(10, range.get(ref.shiftedBy(100.0)).intValue());
379         checkCountConsistency(map);
380     }
381 
382     @Test
383     public void testExtractIntermediate() {
384         final Binary64Field field = Binary64Field.getInstance();
385         final FieldAbsoluteDate<Binary64> ref = FieldAbsoluteDate.getJ2000Epoch(field);
386         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, field);
387         map.addValidAfter(10, ref.shiftedBy(10.0), false);
388         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
389         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
390         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
391         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
392         FieldTimeSpanMap<Integer, Binary64> range = map.extractRange(ref.shiftedBy(4), ref.shiftedBy(8));
393         Assertions.assertEquals(2, range.getSpansNumber());
394         Assertions.assertEquals( 3, range.get(ref.shiftedBy(-99.0)).intValue());
395         Assertions.assertEquals( 3, range.get(ref.shiftedBy(  4.9)).intValue());
396         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  5.1)).intValue());
397         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  8.9)).intValue());
398         Assertions.assertEquals( 5, range.get(ref.shiftedBy(  9.1)).intValue());
399         Assertions.assertEquals( 5, range.get(ref.shiftedBy(999.9)).intValue());
400         checkCountConsistency(map);
401     }
402 
403     @Test
404     public void testSpanToTransitionLinkEmpty() {
405         final Binary64Field field = Binary64Field.getInstance();
406         Span<Integer, Binary64> span = new FieldTimeSpanMap<>(1, field).getSpan(arbitraryEpoch);
407         Assertions.assertEquals(1, span.getData().intValue());
408         Assertions.assertEquals(FieldAbsoluteDate.getPastInfinity(field), span.getStart());
409         Assertions.assertNull(span.getStartTransition());
410         Assertions.assertEquals(FieldAbsoluteDate.getFutureInfinity(field), span.getEnd());
411         Assertions.assertNull(span.getEndTransition());
412     }
413 
414     @Test
415     public void testSpanToTransitionLink() {
416         final Binary64Field field = Binary64Field.getInstance();
417         final FieldAbsoluteDate<Binary64> ref = arbitraryEpoch;
418         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, field);
419         map.addValidAfter(10, ref.shiftedBy(10.0), false);
420         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
421         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
422         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
423         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
424 
425         Span<Integer, Binary64> first = map.getSpan(ref.shiftedBy(-99.0));
426         Assertions.assertEquals(0, first.getData().intValue());
427         Assertions.assertEquals(FieldAbsoluteDate.getPastInfinity(field), first.getStart());
428         Assertions.assertNull(first.getStartTransition());
429         Assertions.assertEquals(2.0, first.getEnd().durationFrom(ref).getReal(), 1.0e-15);
430         Assertions.assertNotNull(first.getEndTransition());
431 
432         Span<Integer, Binary64> middle = map.getSpan(ref.shiftedBy(6.0));
433         Assertions.assertEquals(5, middle.getData().intValue());
434         Assertions.assertEquals(5.0, middle.getStart().durationFrom(ref).getReal(), 1.0e-15);
435         Assertions.assertNotNull(middle.getStartTransition());
436         Assertions.assertEquals(9.0, middle.getEnd().durationFrom(ref).getReal(), 1.0e-15);
437         Assertions.assertNotNull(middle.getEndTransition());
438         Assertions.assertSame(middle.getStartTransition().getAfter(), middle.getEndTransition().getBefore());
439         Assertions.assertEquals(3, middle.getStartTransition().getBefore().intValue());
440         Assertions.assertEquals(5, middle.getStartTransition().getAfter().intValue());
441         Assertions.assertEquals(5, middle.getEndTransition().getBefore().intValue());
442         Assertions.assertEquals(9, middle.getEndTransition().getAfter().intValue());
443 
444         Span<Integer, Binary64> last = map.getSpan(ref.shiftedBy(+99.0));
445         Assertions.assertEquals(10, last.getData().intValue());
446         Assertions.assertEquals(10.0, last.getStart().durationFrom(ref).getReal(), 1.0e-15);
447         Assertions.assertNotNull(last.getStartTransition());
448         Assertions.assertEquals(FieldAbsoluteDate.getFutureInfinity(field), last.getEnd());
449         Assertions.assertNull(last.getEndTransition());
450 
451         checkCountConsistency(map);
452 
453     }
454 
455     @Test
456     public void testTransitionToSpanLink() {
457         final FieldAbsoluteDate<Binary64> ref = arbitraryEpoch;
458         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
459         map.addValidAfter(10, ref.shiftedBy(10.0), false);
460         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
461         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
462         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
463         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
464 
465         Transition<Integer, Binary64> first = map.getSpan(ref.shiftedBy(-99.0)).getEndTransition();
466         Assertions.assertEquals(2.0, first.getDate().durationFrom(ref).getReal(), 1.0e-15);
467         Assertions.assertEquals(0, first.getBefore().intValue());
468         Assertions.assertEquals(2, first.getAfter().intValue());
469 
470         Transition<Integer, Binary64> middle = map.getSpan(ref.shiftedBy(6.0)).getStartTransition();
471         Assertions.assertEquals( 5.0, middle.getDate().durationFrom(ref).getReal(), 1.0e-15);
472         Assertions.assertEquals( 3, middle.getBefore().intValue());
473         Assertions.assertEquals( 5, middle.getAfter().intValue());
474 
475         Transition<Integer, Binary64> last = map.getSpan(ref.shiftedBy(+99.0)).getStartTransition();
476         Assertions.assertEquals(10.0, last.getDate().durationFrom(ref).getReal(), 1.0e-15);
477         Assertions.assertEquals( 9, last.getBefore().intValue());
478         Assertions.assertEquals(10, last.getAfter().intValue());
479 
480         checkCountConsistency(map);
481 
482     }
483 
484     @Test
485     public void tesFirstLastEmpty() {
486         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
487         Assertions.assertNull(map.getFirstTransition());
488         Assertions.assertNull(map.getLastTransition());
489         Assertions.assertSame(map.getFirstSpan(), map.getLastSpan());
490         Assertions.assertNull(map.getFirstSpan().getStartTransition());
491         Assertions.assertNull(map.getFirstSpan().getEndTransition());
492         Assertions.assertNull(map.getFirstSpan().previous());
493         Assertions.assertNull(map.getLastSpan().next());
494         checkCountConsistency(map);
495     }
496 
497     @Test
498     public void testSpansNavigation() {
499         final FieldAbsoluteDate<Binary64> ref = arbitraryEpoch;
500         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
501         map.addValidAfter(10, ref.shiftedBy(10.0), false);
502         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
503         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
504         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
505         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
506         Assertions.assertNull(map.getFirstSpan().previous());
507         Assertions.assertNull(map.getLastSpan().next());
508 
509         Span<Integer, Binary64> span = map.getFirstSpan();
510         Assertions.assertEquals(0, span.getData().intValue());
511         span = span.next();
512         Assertions.assertEquals(2, span.getData().intValue());
513         span = span.next();
514         Assertions.assertEquals(3, span.getData().intValue());
515         span = span.next();
516         Assertions.assertEquals(5, span.getData().intValue());
517         span = span.next();
518         Assertions.assertEquals(9, span.getData().intValue());
519         span = span.next();
520         Assertions.assertEquals(10, span.getData().intValue());
521         Assertions.assertNull(span.next());
522         span = span.previous();
523         Assertions.assertEquals(9, span.getData().intValue());
524         span = span.previous();
525         Assertions.assertEquals(5, span.getData().intValue());
526         span = span.previous();
527         Assertions.assertEquals(3, span.getData().intValue());
528         span = span.previous();
529         Assertions.assertEquals(2, span.getData().intValue());
530         span = span.previous();
531         Assertions.assertEquals(0, span.getData().intValue());
532         Assertions.assertNull(span.previous());
533 
534         checkCountConsistency(map);
535 
536     }
537 
538     @Test
539     public void testTransitionsNavigation() {
540         final FieldAbsoluteDate<Binary64> ref = arbitraryEpoch;
541         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
542         map.addValidAfter(10, ref.shiftedBy(10.0), false);
543         map.addValidAfter( 3, ref.shiftedBy( 2.0), false);
544         map.addValidAfter( 9, ref.shiftedBy( 5.0), false);
545         map.addValidBefore( 2, ref.shiftedBy( 3.0), false);
546         map.addValidBefore( 5, ref.shiftedBy( 9.0), false);
547 
548         Assertions.assertEquals( 2.0, map.getFirstTransition().getDate().durationFrom(ref).getReal(), 1.0e-15);
549         Assertions.assertEquals(10.0, map.getLastTransition().getDate().durationFrom(ref).getReal(), 1.0e-15);
550 
551         Transition<Integer, Binary64> transition = map.getLastTransition();
552         Assertions.assertEquals(10.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
553         transition = transition.previous();
554         Assertions.assertEquals( 9.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
555         transition = transition.previous();
556         Assertions.assertEquals( 5.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
557         transition = transition.previous();
558         Assertions.assertEquals( 3.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
559         transition = transition.previous();
560         Assertions.assertEquals( 2.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
561         Assertions.assertNull(transition.previous());
562         transition = transition.next();
563         Assertions.assertEquals( 3.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
564         transition = transition.next();
565         Assertions.assertEquals( 5.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
566         transition = transition.next();
567         Assertions.assertEquals( 9.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
568         transition = transition.next();
569         Assertions.assertEquals(10.0, transition.getDate().durationFrom(ref).getReal(), 1.0e-15);
570         Assertions.assertNull(transition.next());
571 
572         checkCountConsistency(map);
573 
574     }
575 
576     @Test
577     public void testDuplicatedBeforeAfterAtEnd() {
578         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
579         map.addValidBefore(-1, arbitraryEpoch, false);
580         map.addValidAfter(+1, arbitraryEpoch, false);
581         Assertions.assertEquals(2, map.getSpansNumber());
582         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
583         Assertions.assertEquals(+1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
584         checkCountConsistency(map);
585     }
586 
587     @Test
588     public void testDuplicatedBeforeAfterMiddle() {
589         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
590         map.addValidBefore(-2, arbitraryEpoch.shiftedBy(-2), false);
591         map.addValidAfter(+2, arbitraryEpoch.shiftedBy(+2), false);
592         map.addValidBefore(-1, arbitraryEpoch, false);
593         map.addValidAfter(+1, arbitraryEpoch, false);
594         Assertions.assertEquals(4, map.getSpansNumber());
595         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
596         Assertions.assertEquals(+1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
597         checkCountConsistency(map);
598     }
599 
600     @Test
601     public void testDuplicatedBeforeBefore() {
602         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
603         map.addValidBefore(-2, arbitraryEpoch, false); // first call at ARBITRARY_EPOCH
604         map.addValidAfter(0, arbitraryEpoch.shiftedBy(-2), false);
605         map.addValidBefore(-1, arbitraryEpoch, false); // second call at ARBITRARY_EPOCH
606         Assertions.assertEquals(3, map.getSpansNumber());
607         Assertions.assertEquals(-2, map.get(arbitraryEpoch.shiftedBy(-10)).intValue());
608         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
609         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(+1)));
610         checkCountConsistency(map);
611     }
612 
613     @Test
614     public void testDuplicatedAfterBeforeAtEnd() {
615         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
616         map.addValidAfter(+1, arbitraryEpoch, false);
617         map.addValidBefore(-1, arbitraryEpoch, false);
618         Assertions.assertEquals(2, map.getSpansNumber());
619         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
620         Assertions.assertEquals(+1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
621         checkCountConsistency(map);
622     }
623 
624     @Test
625     public void testDuplicatedAfterBeforeMiddle() {
626         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
627         map.addValidBefore(-2, arbitraryEpoch.shiftedBy(-2), false);
628         map.addValidAfter(+2, arbitraryEpoch.shiftedBy(+2), false);
629         map.addValidAfter(+1, arbitraryEpoch, false);
630         map.addValidBefore(-1, arbitraryEpoch, false);
631         Assertions.assertEquals(4, map.getSpansNumber());
632         Assertions.assertEquals(-1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
633         Assertions.assertEquals(+1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
634         checkCountConsistency(map);
635     }
636 
637     @Test
638     public void testDuplicatedAfterAfter() {
639         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
640         map.addValidAfter(+2, arbitraryEpoch, false); // first call at ARBITRARY_EPOCH
641         map.addValidBefore(0, arbitraryEpoch.shiftedBy(+2), false);
642         map.addValidAfter(+1, arbitraryEpoch, false); // second call at ARBITRARY_EPOCH
643         Assertions.assertEquals(3, map.getSpansNumber());
644         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-1)));
645         Assertions.assertEquals(+1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
646         Assertions.assertEquals(+2, map.get(arbitraryEpoch.shiftedBy(+10)).intValue());
647         checkCountConsistency(map);
648     }
649 
650     @Test
651     public void testValidAllTime() {
652         FieldAbsoluteDate<Binary64> ref = arbitraryEpoch.shiftedBy(1);
653         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(0, Binary64Field.getInstance());
654 
655         // action
656         map.addValidAfter(1, ref, false);
657         map.addValidBefore(2, ref, false);
658 
659         // verify
660         Assertions.assertEquals(1, (int) map.get(ref.shiftedBy(1)));
661         Assertions.assertEquals(2, (int) map.get(ref.shiftedBy(-1)));
662         Assertions.assertEquals(1, (int) map.get(ref));
663     }
664 
665     @Test
666     public void testBetweenPastInfinity() {
667         final Binary64Field field = Binary64Field.getInstance();
668         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
669         Assertions.assertEquals(1, map.getSpansNumber());
670         map.addValidBetween(1, FieldAbsoluteDate.getPastInfinity(field), arbitraryEpoch);
671         Assertions.assertEquals(2, map.getSpansNumber());
672         Assertions.assertEquals(1, map.get(arbitraryEpoch.shiftedBy(-1)).intValue());
673         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(+1)));
674     }
675 
676     @Test
677     public void testBetweenFutureInfinity() {
678         final Binary64Field field = Binary64Field.getInstance();
679         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
680         Assertions.assertEquals(1, map.getSpansNumber());
681         map.addValidBetween(1, arbitraryEpoch, FieldAbsoluteDate.getFutureInfinity(field));
682         Assertions.assertEquals(2, map.getSpansNumber());
683         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-1)));
684         Assertions.assertEquals(1, map.get(arbitraryEpoch.shiftedBy(+1)).intValue());
685     }
686 
687     @Test
688     public void testBetweenBothInfinity() {
689         final Binary64Field field = Binary64Field.getInstance();
690         FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
691         Assertions.assertEquals(1, map.getSpansNumber());
692         map.addValidBetween(1, FieldAbsoluteDate.getPastInfinity(field), FieldAbsoluteDate.getFutureInfinity(field));
693         Assertions.assertEquals(1, map.getSpansNumber());
694         Assertions.assertEquals(1, map.get(arbitraryEpoch).intValue());
695     }
696 
697     @Test
698     public void testFirstNonNull() {
699         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
700         checkException(map, FieldTimeSpanMap::getFirstNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES);
701         for (double dt = 0; dt < 10; dt += 0.25) {
702             map.addValidAfter(null, arbitraryEpoch.shiftedBy(dt), false);
703         }
704         checkException(map, FieldTimeSpanMap::getFirstNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES);
705         map.addValidAfter(22, map.getLastTransition().getDate().shiftedBy( 60.0), false);
706         map.addValidAfter(17, map.getLastTransition().getDate().shiftedBy(-20.0), false);
707         Assertions.assertEquals(17, map.getFirstNonNullSpan().getData());
708     }
709 
710     @Test
711     public void testLastNonNull() {
712         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
713         checkException(map, FieldTimeSpanMap::getLastNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES);
714         for (double dt = 0; dt < 10; dt += 0.25) {
715             map.addValidBefore(null, arbitraryEpoch.shiftedBy(-dt), false);
716         }
717         checkException(map, FieldTimeSpanMap::getLastNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES);
718         map.addValidBefore(22, map.getLastTransition().getDate().shiftedBy(-60.0), false);
719         map.addValidBefore(17, map.getLastTransition().getDate().shiftedBy( 20.0), false);
720         Assertions.assertEquals(17, map.getLastNonNullSpan().getData());
721     }
722 
723     @Test
724     public void testMoveTowardsPastNoOverride() {
725         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
726         for (int i = 0; i < 100; i +=10) {
727             map.addValidAfter(i, arbitraryEpoch.shiftedBy(i), false);
728         }
729         Assertions.assertEquals(11, map.getSpansNumber());
730         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
731         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
732         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(35.5)));
733         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(34.5)));
734         transition.resetDate(arbitraryEpoch.shiftedBy(35), true);
735         Assertions.assertEquals(11, map.getSpansNumber());
736         Assertions.assertEquals(35.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
737         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(34.5)));
738         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(35.5)));
739     }
740 
741     @Test
742     public void testMoveTowardsPastOverride() {
743         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
744         for (int i = 0; i < 100; i +=10) {
745             map.addValidAfter(i, arbitraryEpoch.shiftedBy(i), false);
746         }
747         Assertions.assertEquals(11, map.getSpansNumber());
748         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
749         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
750         Assertions.assertEquals(10, map.get(arbitraryEpoch.shiftedBy(15.5)));
751         Assertions.assertEquals(10, map.get(arbitraryEpoch.shiftedBy(14.5)));
752         transition.resetDate(arbitraryEpoch.shiftedBy(15), true);
753         Assertions.assertEquals( 9, map.getSpansNumber());
754         Assertions.assertEquals(15.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
755         Assertions.assertEquals(10, map.get(arbitraryEpoch.shiftedBy(14.5)));
756         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(15.5)));
757     }
758 
759     @Test
760     public void testMoveTowardsPastOverrideAll() {
761         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
762         for (int i = 0; i < 100; i +=10) {
763             map.addValidBetween(i,
764                                 arbitraryEpoch.shiftedBy(i),
765                                 arbitraryEpoch.shiftedBy(i + 10));
766         }
767         Assertions.assertEquals(12, map.getSpansNumber());
768         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
769         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
770         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-10.5)));
771         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-9.5)));
772         transition.resetDate(arbitraryEpoch.shiftedBy(-10), true);
773         Assertions.assertEquals( 8, map.getSpansNumber());
774         Assertions.assertEquals(-10.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
775         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(-9.5)));
776         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-10.5)));
777     }
778 
779     @Test
780     public void testMoveTowardsPastFirst() {
781         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
782         for (int i = 0; i < 100; i +=10) {
783             map.addValidBetween(i,
784                                 arbitraryEpoch.shiftedBy(i),
785                                 arbitraryEpoch.shiftedBy(i + 10));
786         }
787         Assertions.assertEquals(12, map.getSpansNumber());
788         Transition<Integer, Binary64> transition = map.getFirstTransition();
789         Assertions.assertEquals(0.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
790         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-10.5)));
791         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-9.5)));
792         transition.resetDate(arbitraryEpoch.shiftedBy(-10), true);
793         Assertions.assertEquals(12, map.getSpansNumber());
794         Assertions.assertEquals(-10.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
795         Assertions.assertEquals(0, map.get(arbitraryEpoch.shiftedBy(-9.5)));
796         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(-10.5)));
797     }
798 
799     @Test
800     public void testMoveToPastInfinity() {
801         final Binary64Field field = Binary64Field.getInstance();
802         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
803         for (int i = 0; i < 100; i +=10) {
804             map.addValidBetween(i,
805                                 arbitraryEpoch.shiftedBy(i),
806                                 arbitraryEpoch.shiftedBy(i + 10));
807         }
808         Assertions.assertEquals(12, map.getSpansNumber());
809         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(55)).getStartTransition();
810         Assertions.assertEquals(50.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
811         transition.resetDate(FieldAbsoluteDate.getPastInfinity(field), true);
812         Assertions.assertEquals( 6, map.getSpansNumber());
813         Assertions.assertEquals(60.0, map.getFirstTransition().getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
814         Assertions.assertEquals(60, map.get(arbitraryEpoch.shiftedBy(60.5)));
815         Assertions.assertEquals(50, map.get(arbitraryEpoch.shiftedBy(-1000)));
816     }
817 
818     @Test
819     public void testMoveTransitionPastCollision() {
820         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
821         for (int i = 0; i < 100; i +=10) {
822             map.addValidBetween(i,
823                                 arbitraryEpoch.shiftedBy(i),
824                                 arbitraryEpoch.shiftedBy(i + 10));
825         }
826         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
827         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
828         try {
829             transition.resetDate(arbitraryEpoch.shiftedBy(-3600), false);
830             Assertions.fail("an exception should have been thrown");
831         } catch (OrekitException oe) {
832             Assertions.assertEquals(OrekitMessages.TRANSITION_DATES_COLLISION, oe.getSpecifier());
833             Assertions.assertEquals(   40.0, ((AbsoluteDate) oe.getParts()[0]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
834             Assertions.assertEquals(-3600.0, ((AbsoluteDate) oe.getParts()[1]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
835             Assertions.assertEquals(   30.0, ((AbsoluteDate) oe.getParts()[2]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
836         }
837     }
838 
839     @Test
840     public void testMoveTowardsFutureNoOverride() {
841         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
842         for (int i = 0; i < 100; i +=10) {
843             map.addValidBetween(i,
844                                 arbitraryEpoch.shiftedBy(i),
845                                 arbitraryEpoch.shiftedBy(i + 10));
846         }
847         Assertions.assertEquals(12, map.getSpansNumber());
848         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
849         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
850         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(45.5)));
851         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(44.5)));
852         transition.resetDate(arbitraryEpoch.shiftedBy(45), true);
853         Assertions.assertEquals(12, map.getSpansNumber());
854         Assertions.assertEquals(45.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
855         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(44.5)));
856         Assertions.assertEquals(40, map.get(arbitraryEpoch.shiftedBy(45.5)));
857     }
858 
859     @Test
860     public void testMoveTowardsFutureOverride() {
861         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
862         for (int i = 0; i < 100; i +=10) {
863             map.addValidBetween(i,
864                                 arbitraryEpoch.shiftedBy(i),
865                                 arbitraryEpoch.shiftedBy(i + 10));
866         }
867         Assertions.assertEquals(12, map.getSpansNumber());
868         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
869         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
870         Assertions.assertEquals(70, map.get(arbitraryEpoch.shiftedBy(75.5)));
871         Assertions.assertEquals(70, map.get(arbitraryEpoch.shiftedBy(74.5)));
872         transition.resetDate(arbitraryEpoch.shiftedBy(75), true);
873         Assertions.assertEquals( 9, map.getSpansNumber());
874         Assertions.assertEquals(75.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
875         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(74.5)));
876         Assertions.assertEquals(70, map.get(arbitraryEpoch.shiftedBy(75.5)));
877     }
878 
879     @Test
880     public void testMoveTowardsFutureOverrideAll() {
881         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
882         for (int i = 0; i < 100; i +=10) {
883             map.addValidBetween(i,
884                                 arbitraryEpoch.shiftedBy(i),
885                                 arbitraryEpoch.shiftedBy(i + 10));
886         }
887         Assertions.assertEquals(12, map.getSpansNumber());
888         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
889         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
890         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(110.5)));
891         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(109.5)));
892         transition.resetDate(arbitraryEpoch.shiftedBy(110), true);
893         Assertions.assertEquals( 6, map.getSpansNumber());
894         Assertions.assertEquals(110.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
895         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(109.5)));
896         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(110.5)));
897     }
898 
899     @Test
900     public void testMoveTowardsFutureLast() {
901         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
902         for (int i = 0; i < 100; i +=10) {
903             map.addValidBetween(i,
904                                 arbitraryEpoch.shiftedBy(i),
905                                 arbitraryEpoch.shiftedBy(i + 10));
906         }
907         Assertions.assertEquals(12, map.getSpansNumber());
908         Transition<Integer, Binary64> transition = map.getLastTransition();
909         Assertions.assertEquals(100.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
910         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(110.5)));
911         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(109.5)));
912         transition.resetDate(arbitraryEpoch.shiftedBy(110), true);
913         Assertions.assertEquals(12, map.getSpansNumber());
914         Assertions.assertEquals(110.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
915         Assertions.assertEquals(90,  map.get(arbitraryEpoch.shiftedBy(109.5)));
916         Assertions.assertNull(map.get(arbitraryEpoch.shiftedBy(110.5)));
917     }
918 
919     @Test
920     public void testMoveToFutureInfinity() {
921         final Binary64Field field = Binary64Field.getInstance();
922         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
923         for (int i = 0; i < 100; i +=10) {
924             map.addValidBetween(i,
925                                 arbitraryEpoch.shiftedBy(i),
926                                 arbitraryEpoch.shiftedBy(i + 10));
927         }
928         Assertions.assertEquals(12, map.getSpansNumber());
929         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
930         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
931         Assertions.assertEquals(70, map.get(arbitraryEpoch.shiftedBy(75.5)));
932         Assertions.assertEquals(70, map.get(arbitraryEpoch.shiftedBy(74.5)));
933         transition.resetDate(FieldAbsoluteDate.getFutureInfinity(field), true);
934         Assertions.assertEquals( 5, map.getSpansNumber());
935         Assertions.assertEquals(30.0, map.getLastTransition().getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
936         Assertions.assertEquals(30, map.get(arbitraryEpoch.shiftedBy(1000)));
937     }
938 
939     @Test
940     public void testExpungeNumberEarliestForward() {
941         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_EARLIEST, true,
942                       5, 50, 90, 25.0);
943     }
944 
945     @Test
946     public void testExpungeRangeEarliestForward() {
947         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_EARLIEST, true,
948                       7, 30, 90, 25.0);
949     }
950 
951     @Test
952     public void testExpungeNumberEarliestBackward() {
953         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_EARLIEST, false,
954                       5, 60, null, 25.0);
955     }
956 
957     @Test
958     public void testExpungeRangeEarliestBackward() {
959         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_EARLIEST, false,
960                       7, 40, null, 25.0);
961     }
962 
963     @Test
964     public void testExpungeNumberLatestForward() {
965         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_LATEST, true,
966                       5, null, 30, 75.0);
967     }
968 
969     @Test
970     public void testExpungeRangeLatestForward() {
971         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_LATEST, true,
972                       7, null, 50, 75.0);
973     }
974 
975     @Test
976     public void testExpungeNumberLatestBackward() {
977         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_LATEST, false,
978                       5, 0, 40, 75.0);
979     }
980 
981     @Test
982     public void testExpungeRangeLatestBackward() {
983         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_LATEST, false,
984                       7, 0, 60, 75.0);
985     }
986 
987     @Test
988     public void testExpungeNumberFarthestForward() {
989         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_FARTHEST, true,
990                       5, 50, 90, 25.0);
991     }
992 
993     @Test
994     public void testExpungeRangeFarthestForward() {
995         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_FARTHEST, true,
996                       7, 30, 90, 25.0);
997     }
998 
999     @Test
1000     public void testExpungeNumberFarthestBackward() {
1001         doTestExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_FARTHEST, false,
1002                       5, 0, 40, 75.0);
1003     }
1004 
1005     @Test
1006     public void testExpungeRangeFarthestBackward() {
1007         doTestExpunge(Integer.MAX_VALUE, 55.0, ExpungePolicy.EXPUNGE_FARTHEST, false,
1008                       7, 0, 60, 75.0);
1009     }
1010 
1011     private void doTestExpunge(final int maxNbSpans, final double maxRange, final ExpungePolicy expungePolicy,
1012                                final boolean fillUpForward, final int expectedNbSpans,
1013                                final Integer expectedFirst, final Integer expectedLast,
1014                                final double invalidOffset) {
1015         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
1016         map.configureExpunge(maxNbSpans, maxRange, expungePolicy);
1017         if (fillUpForward) {
1018             for (int i = 0; i < 100; i += 10) {
1019                 map.addValidAfter(i, arbitraryEpoch.shiftedBy(i), false);
1020             }
1021         } else {
1022             for (int i = 90; i >= 0; i -= 10) {
1023                 map.addValidBefore(i,  arbitraryEpoch.shiftedBy(i + 10), false);
1024             }
1025         }
1026         Assertions.assertEquals(expectedNbSpans, map.getSpansNumber());
1027         Assertions.assertEquals(expectedFirst,   map.getFirstSpan().getData());
1028         Assertions.assertEquals(expectedLast,    map.getLastSpan().getData());
1029         try {
1030             map.getSpan(arbitraryEpoch.shiftedBy(invalidOffset));
1031             Assertions.fail("an exception should have been thrown");
1032         } catch (OrekitException oe) {
1033             Assertions.assertEquals(OrekitMessages.EXPUNGED_SPAN, oe.getSpecifier());
1034         }
1035     }
1036 
1037     @Test
1038     public void testLateExpungeConfiguration() {
1039 
1040         // initial setup
1041         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, Binary64Field.getInstance());
1042         for (int i = 0; i < 100; i += 10) {
1043             map.addValidAfter(i, arbitraryEpoch.shiftedBy(i), false);
1044         }
1045         Assertions.assertEquals(  11, map.getSpansNumber());
1046         Assertions.assertNull(map.getFirstSpan().getData());
1047         Assertions.assertEquals(  90, map.getLastSpan().getData());
1048 
1049         // no changes just after configuration
1050         map.configureExpunge(5, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_EARLIEST);
1051         Assertions.assertEquals(  11, map.getSpansNumber());
1052         Assertions.assertNull(map.getFirstSpan().getData());
1053         Assertions.assertEquals(  90, map.getLastSpan().getData());
1054 
1055         // changes applied after addition
1056         map.addValidAfter(100, arbitraryEpoch.shiftedBy(100), false);
1057         Assertions.assertEquals(  5, map.getSpansNumber());
1058         Assertions.assertEquals( 60, map.getFirstSpan().getData());
1059         Assertions.assertEquals(100, map.getLastSpan().getData());
1060 
1061     }
1062 
1063     @Test
1064     public void testMoveTransitionFutureCollision() {
1065         final Binary64Field field = Binary64Field.getInstance();
1066         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
1067         for (int i = 0; i < 100; i +=10) {
1068             map.addValidBetween(i,
1069                                 arbitraryEpoch.shiftedBy(i),
1070                                 arbitraryEpoch.shiftedBy(i + 10));
1071         }
1072         Transition<Integer, Binary64> transition = map.getSpan(arbitraryEpoch.shiftedBy(45)).getStartTransition();
1073         Assertions.assertEquals(40.0, transition.getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
1074         try {
1075             transition.resetDate(arbitraryEpoch.shiftedBy(3600), false);
1076             Assertions.fail("an exception should have been thrown");
1077         } catch (OrekitException oe) {
1078             Assertions.assertEquals(OrekitMessages.TRANSITION_DATES_COLLISION, oe.getSpecifier());
1079             Assertions.assertEquals(   40.0, ((AbsoluteDate) oe.getParts()[0]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
1080             Assertions.assertEquals( 3600.0, ((AbsoluteDate) oe.getParts()[1]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
1081             Assertions.assertEquals(   50.0, ((AbsoluteDate) oe.getParts()[2]).durationFrom(arbitraryEpoch.toAbsoluteDate()), 1.0e-15);
1082         }
1083     }
1084 
1085     @Deprecated
1086     @Test
1087     public void testDeprecatedMethods() {
1088         final Binary64Field field = Binary64Field.getInstance();
1089         final FieldTimeSpanMap<Integer, Binary64> map = new FieldTimeSpanMap<>(null, field);
1090         for (int i = 0; i < 50; i +=10) {
1091             map.addValidAfter(i, arbitraryEpoch.shiftedBy(i));
1092         }
1093         for (int i = 50; i < 100; i +=10) {
1094             map.addValidBefore(i, arbitraryEpoch.shiftedBy(i + 10), false);
1095         }
1096         Assertions.assertEquals(11, map.getSpansNumber());
1097         SortedSet<FieldTimeSpanMap.Transition<Integer, Binary64>> transitions = map.getTransitions();
1098         Assertions.assertEquals(10, transitions.size());
1099         Assertions.assertEquals(  0.0, transitions.first().getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
1100         Assertions.assertEquals(100.0, transitions.last().getDate().durationFrom(arbitraryEpoch).getReal(), 1.0e-15);
1101     }
1102 
1103     private <T, F extends CalculusFieldElement<F>> void checkException(final FieldTimeSpanMap<T, F> map,
1104                                                                        final Consumer<FieldTimeSpanMap<T, F>> f,
1105                                                                        OrekitMessages expected) {
1106         try {
1107             f.accept(map);
1108             Assertions.fail("an exception should have been thrown");
1109         } catch (OrekitException oe) {
1110             Assertions.assertEquals(expected, oe.getSpecifier());
1111         }
1112     }
1113 
1114     private <T, F extends CalculusFieldElement<F>> void checkCountConsistency(final FieldTimeSpanMap<T, F> map) {
1115         final int count1 = map.getSpansNumber();
1116         int count2 = 0;
1117         for (Span<T, F> span = map.getFirstSpan(); span != null; span = span.next()) {
1118             ++count2;
1119         }
1120         Assertions.assertEquals(count1, count2);
1121     }
1122 
1123     @BeforeEach
1124     public void setUp() {
1125         Utils.setDataRoot("regular-data");
1126         this.arbitraryEpoch = FieldAbsoluteDate.getArbitraryEpoch(Binary64Field.getInstance());
1127     }
1128 
1129     @AfterEach
1130     public void tearDown() {
1131         this.arbitraryEpoch = null;
1132     }
1133 
1134 }