FieldTimeSpanMap.java

  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. import java.util.Collections;
  19. import java.util.Comparator;
  20. import java.util.SortedSet;
  21. import java.util.TreeSet;
  22. import java.util.function.Consumer;

  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.time.FieldAbsoluteDate;
  26. import org.orekit.time.FieldTimeStamped;
  27. import org.hipparchus.Field;
  28. import org.hipparchus.CalculusFieldElement;
  29. import org.orekit.time.AbsoluteDate;

  30. /** Container for objects that apply to spans of time.

  31.  * <p>
  32.  * Time span maps can be seen either as an ordered collection of
  33.  * {@link Span time spans} or as an ordered collection
  34.  * of {@link Transition transitions}. Both views are dual one to
  35.  * each other. A time span extends from one transition to the
  36.  * next one, and a transition separates one time span from the
  37.  * next one. Each time span contains one entry that is valid during
  38.  * the time span; this entry may be null if nothing is valid during
  39.  * this time span.
  40.  * </p>
  41.  * <p>
  42.  * Typical uses of {@link FieldTimeSpanMap} are to hold piecewise data, like for
  43.  * example an orbit count that changes at ascending nodes (in which case the
  44.  * entry would be an {@link Integer}), or a visibility status between several
  45.  * objects (in which case the entry would be a {@link Boolean}), or a drag
  46.  * coefficient that is expected to be estimated daily or three-hourly.
  47.  * </p>
  48.  * <p>
  49.  * Time span maps are built progressively. At first, they contain one
  50.  * {@link Span time span} only whose validity extends from past infinity to
  51.  * future infinity. Then new entries are added one at a time, associated with
  52.  * transition dates, in order to build up the complete map. The transition dates
  53.  * can be either the start of validity (when calling {@link #addValidAfter(Object,
  54.  * FieldAbsoluteDate, boolean)}), or the end of the validity (when calling {@link
  55.  * #addValidBefore(Object, FieldAbsoluteDate, boolean)}). Entries are often added at one
  56.  * end only (and mainly in chronological order), but this is not required. It is
  57.  * possible for example to first set up a map that covers a large range (say one day),
  58.  * and then to insert intermediate dates using for example propagation and event
  59.  * detectors to carve out some parts. This is akin to the way Binary Space Partitioning
  60.  * Trees work.
  61.  * </p>
  62.  * <p>
  63.  * Since 13.1, this class is thread-safe
  64.  * </p>
  65.  * @param <T> Type of the data.
  66.  * @param <F> type of the date field elements

  67.  * @author Luc Maisonobe
  68.  * @since 7.1
  69.  */
  70. public class FieldTimeSpanMap<T, F extends CalculusFieldElement<F>> {

  71.     /** Field.*/
  72.     private final Field<F> field;

  73.     /** Reference to last accessed data.
  74.      * @since 13.1
  75.      */
  76.     private Span<T, F> current;

  77.     /** First span.
  78.      * @since 13.1
  79.      */
  80.     private Span<T, F> firstSpan;

  81.     /** Last span.
  82.      * @since 13.1
  83.      */
  84.     private Span<T, F> lastSpan;

  85.     /** End of early expunged range.
  86.      * @since 13.1
  87.      */
  88.     private FieldAbsoluteDate<F> expungedEarly;

  89.     /** Start of late expunged range.
  90.      * @since 13.1
  91.      */
  92.     private FieldAbsoluteDate<F> expungedLate;

  93.     /** Number of time spans.
  94.      * @since 13.1
  95.      */
  96.     private int nbSpans;

  97.     /** Maximum number of time spans.
  98.      * @since 13.1
  99.      */
  100.     private int maxNbSpans;

  101.     /** Maximum time range between the earliest and the latest transitions.
  102.      * @since 13.1
  103.      */
  104.     private double maxRange;

  105.     /** Expunge policy.
  106.      * @since 13.1
  107.      */
  108.     private ExpungePolicy expungePolicy;

  109.     /** Create a map containing a single object, initially valid throughout the timeline.
  110.      * <p>
  111.      * The real validity of this first entry will be truncated as other
  112.      * entries are either {@link #addValidBefore(Object, FieldAbsoluteDate, boolean)
  113.      * added before} it or {@link #addValidAfter(Object, FieldAbsoluteDate, boolean)
  114.      * added after} it.
  115.      * </p>
  116.      * <p>
  117.      * The initial {@link #configureExpunge(int, double, ExpungePolicy) expunge policy}
  118.      * is to never expunge any entries, it can be changed afterward by calling
  119.      * {@link #configureExpunge(int, double, ExpungePolicy)}
  120.      * </p>
  121.      * @param entry entry (initially valid throughout the timeline)
  122.      * @param field field used by default.
  123.      */
  124.     public FieldTimeSpanMap(final T entry, final Field<F> field) {
  125.         this.field     = field;
  126.         this.current   = new Span<>(field, entry);
  127.         this.firstSpan = current;
  128.         this.lastSpan  = current;
  129.         this.nbSpans   = 1;
  130.         configureExpunge(Integer.MAX_VALUE, Double.POSITIVE_INFINITY, ExpungePolicy.EXPUNGE_FARTHEST);
  131.     }

  132.     /** Configure (or reconfigure) expunge policy for later additions.
  133.      * <p>
  134.      * When an entry is added to the map (using either {@link #addValidBefore(Object, FieldAbsoluteDate, boolean)},
  135.      * {@link #addValidBetween(Object, FieldAbsoluteDate, FieldAbsoluteDate)}, or
  136.      * {@link #addValidAfter(Object, FieldAbsoluteDate, boolean)} that exceeds the allowed capacity in terms
  137.      * of number of time spans or maximum time range between the earliest and the latest transitions,
  138.      * then exceeding data is expunged according to the {@code expungePolicy}.
  139.      * </p>
  140.      * <p>
  141.      * Note that as the policy depends on the date at which new entries are added, the policy will be enforced
  142.      * only for the <em>next</em> calls to {@link #addValidBefore(Object, FieldAbsoluteDate, boolean)},
  143.      * {@link #addValidBetween(Object, FieldAbsoluteDate, FieldAbsoluteDate)}, and {@link #addValidAfter(Object,
  144.      * FieldAbsoluteDate, boolean)}, it is <em>not</em> enforce immediately.
  145.      * </p>
  146.      * @param newMaxNbSpans maximum number of time spans
  147.      * @param newMaxRange maximum time range between the earliest and the latest transitions
  148.      * @param newExpungePolicy expunge policy to apply when capacity is exceeded
  149.      * @since 13.1
  150.      */
  151.     public synchronized void configureExpunge(final int newMaxNbSpans, final double newMaxRange, final ExpungePolicy newExpungePolicy) {
  152.         this.maxNbSpans    = newMaxNbSpans;
  153.         this.maxRange      = newMaxRange;
  154.         this.expungePolicy = newExpungePolicy;
  155.         this.expungedEarly = FieldAbsoluteDate.getPastInfinity(field);
  156.         this.expungedLate  = FieldAbsoluteDate.getFutureInfinity(field);
  157.     }

  158.     /** Get the number of spans.
  159.      * <p>
  160.      * The number of spans is always at least 1. The number of transitions
  161.      * is always 1 lower than the number of spans.
  162.      * </p>
  163.      * @return number of spans
  164.      * @since 13.1
  165.      */
  166.     public synchronized int getSpansNumber() {
  167.         return nbSpans;
  168.     }

  169.     /** Add an entry valid before a limit date.
  170.      * <p>
  171.      * This method just calls {@link #addValidBefore(Object, FieldAbsoluteDate, boolean)
  172.      * addValidBefore(entry, latestValidityDate, false)}.
  173.      * </p>
  174.      * @param entry entry to add
  175.      * @param latestValidityDate date before which the entry is valid
  176.      * @deprecated as of 13.1, replaced by {@link #addValidBefore(Object, FieldAbsoluteDate, boolean)}
  177.      */
  178.     @Deprecated
  179.     public void addValidBefore(final T entry, final FieldAbsoluteDate<F> latestValidityDate) {
  180.         addValidBefore(entry, latestValidityDate, false);
  181.     }

  182.     /** Add an entry valid before a limit date.
  183.      * <p>
  184.      * As an entry is valid, it truncates the validity of the neighboring
  185.      * entries already present in the map.
  186.      * </p>
  187.      * <p>
  188.      * If the map already contains transitions that occur earlier than {@code latestValidityDate},
  189.      * the {@code erasesEarlier} parameter controls what to do with them. Let's consider
  190.      * the time span [tₖ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  191.      * {@code latestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  192.      * {@code latestValidityDate} &lt; tₖ₊₁).
  193.      * </p>
  194.      * <ul>
  195.      *  <li>if {@code erasesEarlier} is {@code true}, then all earlier transitions
  196.      *      up to and including tₖ are erased, and the {@code entry} will be valid from past infinity
  197.      *      to {@code latestValidityDate}</li>
  198.      *  <li>if {@code erasesEarlier} is {@code false}, then all earlier transitions
  199.      *      are preserved, and the {@code entry} will be valid from tₖ
  200.      *      to {@code latestValidityDate}</li>
  201.      *  </ul>
  202.      * <p>
  203.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  204.      * only from {@code latestValidityDate} to tₖ₊₁.
  205.      * </p>
  206.      * @param entry entry to add
  207.      * @param latestValidityDate date before which the entry is valid
  208.      * @param erasesEarlier if true, the entry erases all existing transitions
  209.      * that are earlier than {@code latestValidityDate}
  210.      * @return span with added entry
  211.      * @since 13.1
  212.      */
  213.     public synchronized Span<T, F> addValidBefore(final T entry, final FieldAbsoluteDate<F> latestValidityDate, final boolean erasesEarlier) {

  214.         // update current reference to transition date
  215.         locate(latestValidityDate);

  216.         if (erasesEarlier) {

  217.             // drop everything before date
  218.             current.start = null;

  219.             // update count
  220.             nbSpans = 0;
  221.             for (Span<T, F> span = current; span != null; span = span.next()) {
  222.                 ++nbSpans;
  223.             }

  224.         }

  225.         final Span<T, F> span = new Span<>(field, entry);

  226.         final Transition<T, F> start = current.getStartTransition();
  227.         if (start != null && start.getDate().equals(latestValidityDate)) {
  228.             // the transition at the start of the current span is at the exact same date
  229.             // we update it, without adding a new transition
  230.             if (start.previous() != null) {
  231.                 start.previous().setAfter(span);
  232.             }
  233.             start.setBefore(span);
  234.             updateFirstIfNeeded(span);
  235.         } else {

  236.             if (current.getStartTransition() != null) {
  237.                 current.getStartTransition().setAfter(span);
  238.             }

  239.             // we need to add a new transition somewhere inside the current span
  240.             insertTransition(latestValidityDate, span, current);

  241.         }

  242.         // we consider the last added transition as the new current one
  243.         current = span;

  244.         expungeOldData(latestValidityDate);

  245.         return span;

  246.     }

  247.     /** Add an entry valid after a limit date.
  248.      * <p>
  249.      * This method just calls {@link #addValidAfter(Object, FieldAbsoluteDate, boolean)
  250.      * addValidAfter(entry, earliestValidityDate, false)}.
  251.      * </p>
  252.      * @param entry entry to add
  253.      * @param earliestValidityDate date after which the entry is valid
  254.      * @deprecated as of 13.1, replaced by {@link #addValidAfter(Object, FieldAbsoluteDate, boolean)}
  255.      */
  256.     @Deprecated
  257.     public void addValidAfter(final T entry, final FieldAbsoluteDate<F> earliestValidityDate) {
  258.         addValidAfter(entry, earliestValidityDate, false);
  259.     }

  260.     /** Add an entry valid after a limit date.
  261.      * <p>
  262.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  263.      * entries already present in the map.
  264.      * </p>
  265.      * <p>
  266.      * If the map already contains transitions that occur later than {@code earliestValidityDate},
  267.      * the {@code erasesLater} parameter controls what to do with them. Let's consider
  268.      * the time span [tₖ; tₖ₊₁[ associated with entry eₖ that would have been valid at time
  269.      * {@code earliestValidityDate} prior to the call to the method (i.e. tₖ &lt;
  270.      * {@code earliestValidityDate} &lt; tₖ₊₁).
  271.      * </p>
  272.      * <ul>
  273.      *  <li>if {@code erasesLater} is {@code true}, then all later transitions
  274.      *      from and including tₖ₊₁ are erased, and the {@code entry} will be valid from
  275.      *      {@code earliestValidityDate} to future infinity</li>
  276.      *  <li>if {@code erasesLater} is {@code false}, then all later transitions
  277.      *      are preserved, and the {@code entry} will be valid from {@code earliestValidityDate}
  278.      *      to tₖ₊₁</li>
  279.      *  </ul>
  280.      * <p>
  281.      * In both cases, the existing entry eₖ time span will be truncated and will be valid
  282.      * only from tₖ to {@code earliestValidityDate}.
  283.      * </p>
  284.      * @param entry entry to add
  285.      * @param earliestValidityDate date after which the entry is valid
  286.      * @param erasesLater if true, the entry erases all existing transitions
  287.      * that are later than {@code earliestValidityDate}
  288.      * @return span with added entry
  289.      * @since 13.1
  290.      */
  291.     public synchronized Span<T, F> addValidAfter(final T entry, final FieldAbsoluteDate<F> earliestValidityDate, final boolean erasesLater) {

  292.         // update current reference to transition date
  293.         locate(earliestValidityDate);

  294.         if (erasesLater) {

  295.             // drop everything after date
  296.             current.end = null;

  297.             // update count
  298.             nbSpans = 0;
  299.             for (Span<T, F> span = current; span != null; span = span.previous()) {
  300.                 ++nbSpans;
  301.             }

  302.         }

  303.         final Span<T, F> span = new Span<>(field, entry);
  304.         if (current.getEndTransition() != null) {
  305.             current.getEndTransition().setBefore(span);
  306.         }

  307.         final Transition<T, F> start = current.getStartTransition();
  308.         if (start != null && start.getDate().equals(earliestValidityDate)) {
  309.             // the transition at the start of the current span is at the exact same date
  310.             // we update it, without adding a new transition
  311.             start.setAfter(span);
  312.             updateLastIfNeeded(span);
  313.         } else {
  314.             // we need to add a new transition somewhere inside the current span
  315.             insertTransition(earliestValidityDate, current, span);
  316.         }

  317.         // we consider the last added transition as the new current one
  318.         current = span;

  319.         // update metadata
  320.         expungeOldData(earliestValidityDate);

  321.         return span;

  322.     }

  323.     /** Add an entry valid between two limit dates.
  324.      * <p>
  325.      * As an entry is valid, it truncates or overrides the validity of the neighboring
  326.      * entries already present in the map.
  327.      * </p>
  328.      * @param entry entry to add
  329.      * @param earliestValidityDate date after which the entry is valid
  330.      * @param latestValidityDate date before which the entry is valid
  331.      * @return span with added entry
  332.      * @since 13.1
  333.      */
  334.     public synchronized Span<T, F> addValidBetween(final T entry,
  335.                                                    final FieldAbsoluteDate<F> earliestValidityDate,
  336.                                                    final FieldAbsoluteDate<F> latestValidityDate) {

  337.         // handle special cases
  338.         if (AbsoluteDate.PAST_INFINITY.equals(earliestValidityDate.toAbsoluteDate())) {
  339.             if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate.toAbsoluteDate())) {
  340.                 // we wipe everything in the map
  341.                 current   = new Span<>(field, entry);
  342.                 firstSpan = current;
  343.                 lastSpan  = current;
  344.                 return current;
  345.             } else {
  346.                 // we wipe from past infinity
  347.                 return addValidBefore(entry, latestValidityDate, true);
  348.             }
  349.         } else if (AbsoluteDate.FUTURE_INFINITY.equals(latestValidityDate.toAbsoluteDate())) {
  350.             // we wipe up to future infinity
  351.             return addValidAfter(entry, earliestValidityDate, true);
  352.         } else {

  353.             // locate spans at earliest and latest dates
  354.             locate(earliestValidityDate);
  355.             Span<T, F> latest = current;
  356.             while (latest.getEndTransition() != null && latest.getEnd().isBeforeOrEqualTo(latestValidityDate)) {
  357.                 latest = latest.next();
  358.                 --nbSpans;
  359.             }
  360.             if (latest == current) {
  361.                 // the interval splits one transition in the middle, we need to duplicate the instance
  362.                 latest = new Span<>(field, current.data);
  363.                 if (current.getEndTransition() != null) {
  364.                     current.getEndTransition().setBefore(latest);
  365.                 }
  366.             }

  367.             final Span<T, F> span = new Span<>(field, entry);

  368.             // manage earliest transition
  369.             final Transition<T, F> start = current.getStartTransition();
  370.             if (start != null && start.getDate().equals(earliestValidityDate)) {
  371.                 // the transition at the start of the current span is at the exact same date
  372.                 // we update it, without adding a new transition
  373.                 start.setAfter(span);
  374.                 updateLastIfNeeded(span);
  375.             } else {
  376.                 // we need to add a new transition somewhere inside the current span
  377.                 insertTransition(earliestValidityDate, current, span);
  378.             }

  379.             // manage latest transition
  380.             insertTransition(latestValidityDate, span, latest);

  381.             // we consider the last added transition as the new current one
  382.             current = span;

  383.             // update metadata
  384.             final FieldAbsoluteDate<F> midDate = earliestValidityDate.
  385.                                                  shiftedBy(latestValidityDate.durationFrom(earliestValidityDate).multiply(0.5));
  386.             expungeOldData(midDate);

  387.             return span;

  388.         }

  389.     }

  390.     /** Get the entry valid at a specified date.
  391.      * <p>
  392.      * The expected complexity is O(1) for successive calls with
  393.      * neighboring dates, which is the more frequent use in propagation
  394.      * or orbit determination applications, and O(n) for random calls.
  395.      * </p>
  396.      * @param date date at which the entry must be valid
  397.      * @return valid entry at specified date
  398.      * @see #getSpan(FieldAbsoluteDate)
  399.      */
  400.     public synchronized T get(final FieldAbsoluteDate<F> date) {
  401.         return getSpan(date).getData();
  402.     }

  403.     /** Get the time span containing a specified date.
  404.      * <p>
  405.      * The expected complexity is O(1) for successive calls with
  406.      * neighboring dates, which is the more frequent use in propagation
  407.      * or orbit determination applications, and O(n) for random calls.
  408.      * </p>
  409.      * @param date date belonging to the desired time span
  410.      * @return time span containing the specified date
  411.      * @since 13.1
  412.      */
  413.     public synchronized Span<T, F> getSpan(final FieldAbsoluteDate<F> date) {

  414.         // safety check
  415.         if (date.isBefore(expungedEarly) || date.isAfter(expungedLate)) {
  416.             throw new OrekitException(OrekitMessages.EXPUNGED_SPAN, date);
  417.         }

  418.         locate(date);
  419.         return current;
  420.     }

  421.     /** Locate the time span containing a specified date.
  422.      * <p>
  423.      * The {@code current} field is updated to the located span.
  424.      * After the method returns, {@code current.getStartTransition()} is either
  425.      * null or its date is before or equal to date, and {@code
  426.      * current.getEndTransition()} is either null or its date is after date.
  427.      * </p>
  428.      * @param date date belonging to the desired time span
  429.      * @since 13.1
  430.      */
  431.     private synchronized void locate(final FieldAbsoluteDate<F> date) {

  432.         while (current.getStart().isAfter(date)) {
  433.             // the current span is too late
  434.             current = current.previous();
  435.         }

  436.         while (current.getEnd().isBeforeOrEqualTo(date)) {

  437.             final Span<T, F> next = current.next();
  438.             if (next == null) {
  439.                 // this happens when date is FUTURE_INFINITY
  440.                 return;
  441.             }

  442.             // the current span is too early
  443.             current = next;

  444.         }

  445.     }

  446.     /** Insert a transition.
  447.      * @param date transition date
  448.      * @param before span before transition
  449.      * @param after span after transition
  450.      * @since 13.1
  451.      */
  452.     private void insertTransition(final FieldAbsoluteDate<F> date, final Span<T, F> before, final Span<T, F> after) {
  453.         final Transition<T, F> transition = new Transition<>(this, date);
  454.         transition.setBefore(before);
  455.         transition.setAfter(after);
  456.         updateFirstIfNeeded(before);
  457.         updateLastIfNeeded(after);
  458.         ++nbSpans;
  459.     }

  460.     /** Get the first (earliest) transition.
  461.      * @return first (earliest) transition, or null if there are no transitions
  462.      * @since 13.1
  463.      */
  464.     public synchronized Transition<T, F> getFirstTransition() {
  465.         return getFirstSpan().getEndTransition();
  466.     }

  467.     /** Get the last (latest) transition.
  468.      * @return last (latest) transition, or null if there are no transitions
  469.      * @since 13.1
  470.      */
  471.     public synchronized Transition<T, F> getLastTransition() {
  472.         return getLastSpan().getStartTransition();
  473.     }

  474.     /** Get the first (earliest) span.
  475.      * @return first (earliest) span
  476.      * @since 13.1
  477.      */
  478.     public synchronized Span<T, F> getFirstSpan() {
  479.         return firstSpan;
  480.     }

  481.     /** Get the first (earliest) span with non-null data.
  482.      * @return first (earliest) span with non-null data
  483.      * @since 13.1
  484.      */
  485.     public synchronized Span<T, F> getFirstNonNullSpan() {
  486.         Span<T, F> span = getFirstSpan();
  487.         while (span.getData() == null) {
  488.             if (span.getEndTransition() == null) {
  489.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  490.             }
  491.             span = span.next();
  492.         }
  493.         return span;
  494.     }

  495.     /** Get the last (latest) span.
  496.      * @return last (latest) span
  497.      * @since 13.1
  498.      */
  499.     public synchronized Span<T, F> getLastSpan() {
  500.         return lastSpan;
  501.     }

  502.     /** Get the last (latest) span with non-null data.
  503.      * @return last (latest) span with non-null data
  504.      * @since 13.1
  505.      */
  506.     public synchronized Span<T, F> getLastNonNullSpan() {
  507.         Span<T, F> span = getLastSpan();
  508.         while (span.getData() == null) {
  509.             if (span.getStartTransition() == null) {
  510.                 throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES);
  511.             }
  512.             span = span.previous();
  513.         }
  514.         return span;
  515.     }

  516.     /** Extract a range of the map.
  517.      * <p>
  518.      * The object returned will be a new independent instance that will contain
  519.      * only the transitions that lie in the specified range.
  520.      * </p>
  521.      * <p>
  522.      * Consider, for example, a map containing objects O₀ valid before t₁, O₁ valid
  523.      * between t₁ and t₂, O₂ valid between t₂ and t₃, O₃ valid between t₃ and t₄,
  524.      * and O₄ valid after t₄. then calling this method with a {@code start}
  525.      * date between t₁ and t₂ and a {@code end} date between t₃ and t₄
  526.      * will result in a new map containing objects O₁ valid before t₂, O₂
  527.      * valid between t₂ and t₃, and O₃ valid after t₃. The validity of O₁
  528.      * is therefore extended in the past, and the validity of O₃ is extended
  529.      * in the future.
  530.      * </p>
  531.      * @param start earliest date at which a transition is included in the range
  532.      * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
  533.      * @param end latest date at which a transition is included in the r
  534.      * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
  535.      * @return a new instance with all transitions restricted to the specified range
  536.      * @since 13.1
  537.      */
  538.     public synchronized FieldTimeSpanMap<T, F> extractRange(final FieldAbsoluteDate<F> start,
  539.                                                             final FieldAbsoluteDate<F> end) {

  540.         Span<T, F>  span  = getSpan(start);
  541.         final FieldTimeSpanMap<T, F> range = new FieldTimeSpanMap<>(span.getData(), field);
  542.         while (span.getEndTransition() != null && span.getEndTransition().getDate().isBeforeOrEqualTo(end)) {
  543.             span = span.next();
  544.             range.addValidAfter(span.getData(), span.getStartTransition().getDate(), false);
  545.         }

  546.         return range;

  547.     }

  548.     /**
  549.      * Performs an action for each non-null element of the map.
  550.      * <p>
  551.      * The action is performed chronologically.
  552.      * </p>
  553.      * @param action action to perform on the non-null elements
  554.      * @since 13.1
  555.      */
  556.     public synchronized void forEach(final Consumer<T> action) {
  557.         for (Span<T, F> span = getFirstSpan(); span != null; span = span.next()) {
  558.             if (span.getData() != null) {
  559.                 action.accept(span.getData());
  560.             }
  561.         }
  562.     }

  563.     /**
  564.      * Expunge old data.
  565.      * @param date date of the latest added data
  566.      */
  567.     private synchronized void expungeOldData(final FieldAbsoluteDate<F> date) {

  568.         while (nbSpans > maxNbSpans || lastSpan.getStart().durationFrom(firstSpan.getEnd()).getReal() > maxRange) {
  569.             // capacity exceeded, we need to purge old data
  570.             if (expungePolicy.expungeEarliest(date.toAbsoluteDate(),
  571.                                               firstSpan.getEnd().toAbsoluteDate(),
  572.                                               lastSpan.getStart().toAbsoluteDate())) {
  573.                 // we need to purge the earliest data
  574.                 if (firstSpan.getEnd().isAfter(expungedEarly)) {
  575.                     expungedEarly  = firstSpan.getEnd();
  576.                 }
  577.                 firstSpan       = firstSpan.next();
  578.                 firstSpan.start = null;
  579.                 if (current.start == null) {
  580.                     // the current span was the one we just expunged
  581.                     // we need to update it
  582.                     current = firstSpan;
  583.                 }
  584.             } else {
  585.                 // we need to purge the latest data
  586.                 if (lastSpan.getStart().isBefore(expungedLate)) {
  587.                     expungedLate = lastSpan.getStart();
  588.                 }
  589.                 lastSpan     = lastSpan.previous();
  590.                 lastSpan.end = null;
  591.                 if (current.end == null) {
  592.                     // the current span was the one we just expunged
  593.                     // we need to update it
  594.                     current = lastSpan;
  595.                 }
  596.             }
  597.             --nbSpans;
  598.         }

  599.     }

  600.     /** Update first span if needed.
  601.      * @param candidate candidate first span
  602.      * @since 13.1
  603.      */
  604.     private void updateFirstIfNeeded(final Span<T, F> candidate) {
  605.         if (candidate.getStartTransition() == null) {
  606.             firstSpan = candidate;
  607.         }
  608.     }

  609.     /** Update last span if needed.
  610.      * @param candidate candidate last span
  611.      * @since 13.1
  612.      */
  613.     private void updateLastIfNeeded(final Span<T, F> candidate) {
  614.         if (candidate.getEndTransition() == null) {
  615.             lastSpan = candidate;
  616.         }
  617.     }

  618.     /** Get an unmodifiable view of the sorted transitions.
  619.      * <p>
  620.      * Note that since 13.1, this method creates a copy of the current data,
  621.      * it therefore does not update when new spans are added
  622.      * </p>
  623.      * @return unmodifiable view of the sorted transitions
  624.      * @deprecated as of 13.1, this method is replaced by {@link #getFirstTransition()}
  625.      * and then following intertwined links between {@link Span Span} and {@link Transition Transition}
  626.      */
  627.     @Deprecated
  628.     public SortedSet<Transition<T, F>> getTransitions() {
  629.         final SortedSet<Transition<T, F>> copy =
  630.                 new TreeSet<>(Comparator.comparing(Transition::getDate));
  631.         for (Transition<T, F> transition = getFirstTransition(); transition != null; transition = transition.next()) {
  632.             copy.add(transition);
  633.         }
  634.         return Collections.unmodifiableSortedSet(copy);
  635.     }

  636.     /** Class holding transition times.
  637.      * <p>
  638.      * This data type is dual to {@link Span}, it is
  639.      * focused on one transition date, and gives access to
  640.      * surrounding valid data whereas {@link Span} is focused
  641.      * on one valid data, and gives access to surrounding
  642.      * transition dates.
  643.      * </p>
  644.      * @param <S> Type of the data.
  645.      * @param <F> Type of the field elements
  646.      * @since 13.1
  647.      */
  648.     public static class Transition<S, F extends CalculusFieldElement<F>> implements FieldTimeStamped<F> {

  649.         /** Map this transition belongs to. */
  650.         private final FieldTimeSpanMap<S, F> map;

  651.         /** Transition date. */
  652.         private FieldAbsoluteDate<F> date;

  653.         /** Entry valid before the transition. */
  654.         private Span<S, F> before;

  655.         /** Entry valid after the transition. */
  656.         private Span<S, F> after;

  657.         /** Simple constructor.
  658.          * @param map map this transition belongs to
  659.          * @param date transition date
  660.          */
  661.         private Transition(final FieldTimeSpanMap<S, F> map, final FieldAbsoluteDate<F> date) {
  662.             this.map  = map;
  663.             this.date = date;
  664.         }

  665.         /** Set the span valid before transition.
  666.          * @param before span valid before transition (must be non-null)
  667.          */
  668.         void setBefore(final Span<S, F> before) {
  669.             this.before = before;
  670.             before.end  = this;
  671.         }

  672.         /** Set the span valid after transition.
  673.          * @param after span valid after transition (must be non-null)
  674.          */
  675.         void setAfter(final Span<S, F> after) {
  676.             this.after  = after;
  677.             after.start = this;
  678.         }

  679.         /** Get the transition date.
  680.          * @return transition date
  681.          */
  682.         @Override
  683.         public FieldAbsoluteDate<F> getDate() {
  684.             return date;
  685.         }

  686.         /** Move transition.
  687.          * <p>
  688.          * When moving a transition to past or future infinity, it will be disconnected
  689.          * from the time span it initially belonged to as the next or previous time
  690.          * span validity will be extends to infinity.
  691.          * </p>
  692.          * @param newDate new transition date
  693.          * @param eraseOverridden if true, spans that are entirely between current
  694.          * and new transition dates will be silently removed, if false and such
  695.          * spans exist, an exception will be triggered
  696.          */
  697.         public void resetDate(final FieldAbsoluteDate<F> newDate, final boolean eraseOverridden) {
  698.             if (newDate.isAfter(date)) {
  699.                 // we are moving the transition towards future

  700.                 // find span after new date
  701.                 Span<S, F> newAfter = after;
  702.                 while (newAfter.getEndTransition() != null &&
  703.                        newAfter.getEndTransition().getDate().isBeforeOrEqualTo(newDate)) {
  704.                     if (eraseOverridden) {
  705.                         map.nbSpans--;
  706.                     } else {
  707.                         // forbidden collision detected
  708.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  709.                                                   date.toAbsoluteDate(),
  710.                                                   newDate.toAbsoluteDate(),
  711.                                                   newAfter.getEndTransition().getDate().toAbsoluteDate());
  712.                     }
  713.                     newAfter = newAfter.next();
  714.                 }

  715.                 synchronized (map) {
  716.                     // perform update
  717.                     date = newDate;
  718.                     after = newAfter;
  719.                     after.start = this;
  720.                     map.current = before;

  721.                     if (newDate.toAbsoluteDate().isInfinite()) {
  722.                         // we have just moved the transition to future infinity, it should really disappear
  723.                         map.nbSpans--;
  724.                         map.lastSpan = before;
  725.                         before.end   = null;
  726.                     }
  727.                 }

  728.             } else {
  729.                 // we are moving transition towards the past

  730.                 // find span before new date
  731.                 Span<S, F> newBefore = before;
  732.                 while (newBefore.getStartTransition() != null &&
  733.                        newBefore.getStartTransition().getDate().isAfterOrEqualTo(newDate)) {
  734.                     if (eraseOverridden) {
  735.                         map.nbSpans--;
  736.                     } else {
  737.                         // forbidden collision detected
  738.                         throw new OrekitException(OrekitMessages.TRANSITION_DATES_COLLISION,
  739.                                                   date.toAbsoluteDate(),
  740.                                                   newDate.toAbsoluteDate(),
  741.                                                   newBefore.getStartTransition().getDate().toAbsoluteDate());
  742.                     }
  743.                     newBefore = newBefore.previous();
  744.                 }

  745.                 synchronized (map) {
  746.                     // perform update
  747.                     date = newDate;
  748.                     before = newBefore;
  749.                     before.end = this;
  750.                     map.current = after;

  751.                     if (newDate.toAbsoluteDate().isInfinite()) {
  752.                         // we have just moved the transition to past infinity, it should really disappear
  753.                         map.nbSpans--;
  754.                         map.firstSpan = after;
  755.                         after.start   = null;
  756.                     }
  757.                 }

  758.             }
  759.         }

  760.         /** Get the previous transition.
  761.          * @return previous transition, or null if this transition was the first one
  762.          */
  763.         public Transition<S, F> previous() {
  764.             return before.getStartTransition();
  765.         }

  766.         /** Get the next transition.
  767.          * @return next transition, or null if this transition was the last one
  768.          */
  769.         public Transition<S, F> next() {
  770.             return after.getEndTransition();
  771.         }

  772.         /** Get the entry valid before transition.
  773.          * @return entry valid before transition
  774.          * @see #getSpanBefore()
  775.          */
  776.         public S getBefore() {
  777.             return before.getData();
  778.         }

  779.         /** Get the {@link Span} valid before transition.
  780.          * @return {@link Span} valid before transition
  781.          */
  782.         public Span<S, F> getSpanBefore() {
  783.             return before;
  784.         }

  785.         /** Get the entry valid after transition.
  786.          * @return entry valid after transition
  787.          * @see #getSpanAfter()
  788.          */
  789.         public S getAfter() {
  790.             return after.getData();
  791.         }

  792.         /** Get the {@link Span} valid after transition.
  793.          * @return {@link Span} valid after transition
  794.          */
  795.         public Span<S, F> getSpanAfter() {
  796.             return after;
  797.         }

  798.     }

  799.     /** Holder for one time span.
  800.      * <p>
  801.      * This data type is dual to {@link Transition}, it
  802.      * is focused on one valid data, and gives access to
  803.      * surrounding transition dates whereas {@link Transition}
  804.      * is focused on one transition date, and gives access to
  805.      * surrounding valid data.
  806.      * </p>
  807.      * @param <S> Type of the data.
  808.      * @param <F> Type of the field elements
  809.      * @since 13.1
  810.      */
  811.     public static class Span<S, F extends CalculusFieldElement<F>> {

  812.         /** Field.*/
  813.         private final Field<F> field;

  814.         /** Valid data. */
  815.         private final S data;

  816.         /** Start of validity for the data (null if span extends to past infinity). */
  817.         private Transition<S, F> start;

  818.         /** End of validity for the data (null if span extends to future infinity). */
  819.         private Transition<S, F> end;

  820.         /** Simple constructor.
  821.          * @param field field to which dates belong
  822.          * @param data valid data
  823.          */
  824.         private Span(final Field<F> field, final S data) {
  825.             this.field = field;
  826.             this.data  = data;
  827.         }

  828.         /** Get the data valid during this time span.
  829.          * @return data valid during this time span
  830.          */
  831.         public S getData() {
  832.             return data;
  833.         }

  834.         /** Get the previous time span.
  835.          * @return previous time span, or null if this time span was the first one
  836.          */
  837.         public Span<S, F> previous() {
  838.             return start == null ? null : start.getSpanBefore();
  839.         }

  840.         /** Get the next time span.
  841.          * @return next time span, or null if this time span was the last one
  842.          */
  843.         public Span<S, F> next() {
  844.             return end == null ? null : end.getSpanAfter();
  845.         }

  846.         /** Get the start of this time span.
  847.          * @return start of this time span (will be {@link FieldAbsoluteDate#getPastInfinity(Field)}
  848.          * if {@link #getStartTransition()} returns null)
  849.          * @see #getStartTransition()
  850.          */
  851.         public FieldAbsoluteDate<F> getStart() {
  852.             return start == null ? FieldAbsoluteDate.getPastInfinity(field) : start.getDate();
  853.         }

  854.         /** Get the transition at the start of this time span.
  855.          * @return transition at the start of this time span (null if span extends to past infinity)
  856.          * @see #getStart()
  857.          */
  858.         public Transition<S, F> getStartTransition() {
  859.             return start;
  860.         }

  861.         /** Get the end of this time span.
  862.          * @return end of this time span (will be {@link FieldAbsoluteDate#getFutureInfinity(Field)}
  863.          * if {@link #getEndTransition()} returns null)
  864.          * @see #getEndTransition()
  865.          */
  866.         public FieldAbsoluteDate<F> getEnd() {
  867.             return end == null ? FieldAbsoluteDate.getFutureInfinity(field) : end.getDate();
  868.         }

  869.         /** Get the transition at the end of this time span.
  870.          * @return transition at the end of this time span (null if span extends to future infinity)
  871.          * @see #getEnd()
  872.          */
  873.         public Transition<S, F> getEndTransition() {
  874.             return end;
  875.         }

  876.     }

  877. }