1   /* Copyright 2002-2026 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 java.io.Serial;
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  /** String → double[] mapping, for small number of keys.
30   * <p>
31   * This class is a low overhead for a very small number of keys.
32   * It is based on simple array and string comparison. It plays
33   * the same role a {@code Map<String, double[]>} but with reduced
34   * features and not intended for large number of keys. For such
35   * needs the regular {@code Map<String, double[]>} should be preferred.
36   * </p>
37   *
38   * @see DataDictionary
39   * @since 11.1
40   */
41  public class DoubleArrayDictionary implements Serializable {
42  
43      /** Serializable UID. */
44      @Serial
45      private static final long serialVersionUID = 20211121L;
46  
47      /** Default capacity. */
48      private static final int DEFAULT_INITIAL_CAPACITY = 4;
49  
50      /** Data container. */
51      private final List<Entry> data;
52  
53      /** Constructor with {@link #DEFAULT_INITIAL_CAPACITY default initial capacity}.
54       */
55      public DoubleArrayDictionary() {
56          this(DEFAULT_INITIAL_CAPACITY);
57      }
58  
59      /** Constructor with specified capacity.
60       * @param initialCapacity initial capacity
61       */
62      public DoubleArrayDictionary(final int initialCapacity) {
63          data = new ArrayList<>(initialCapacity);
64      }
65  
66      /** Constructor from another dictionary.
67       * @param dictionary dictionary to use for initializing entries
68       */
69      public DoubleArrayDictionary(final DoubleArrayDictionary dictionary) {
70          // take care to call dictionary.getData() and not use dictionary.data,
71          // otherwise we get an empty dictionary when using a DoubleArrayDictionary.view
72          this(DEFAULT_INITIAL_CAPACITY + dictionary.getData().size());
73          for (final Entry entry : dictionary.getData()) {
74              // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
75              data.add(new Entry(entry.getKey(), entry.getValue()));
76          }
77      }
78  
79      /** Constructor from a map.
80       * @param map map to use for initializing entries
81       */
82      public DoubleArrayDictionary(final Map<String, double[]> map) {
83          this(map.size());
84          for (final Map.Entry<String, double[]> entry : map.entrySet()) {
85              // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
86              data.add(new Entry(entry.getKey(), entry.getValue()));
87          }
88      }
89  
90      /** Get an unmodifiable view of the dictionary entries.
91       * @return unmodifiable view of the dictionary entries
92       */
93      public List<Entry> getData() {
94          return Collections.unmodifiableList(data);
95      }
96  
97      /** Get the number of dictionary entries.
98       * @return number of dictionary entries
99       */
100     public int size() {
101         return data.size();
102     }
103 
104     /** Create a map from the instance.
105      * <p>
106      * The map contains a copy of the instance data
107      * </p>
108      * @return copy of the dictionary, as an independent map
109      */
110     public Map<String, double[]> toMap() {
111         final Map<String, double[]> map = new HashMap<>(data.size());
112         for (final Entry entry : data) {
113             map.put(entry.getKey(), entry.getValue());
114         }
115         return map;
116     }
117 
118     /** Remove all entries.
119      */
120     public void clear() {
121         data.clear();
122     }
123 
124     /** Add an entry.
125      * <p>
126      * If an entry with the same key already exists, it will be removed first.
127      * </p>
128      * <p>
129      * The new entry is always put at the end.
130      * </p>
131      * @param key entry key
132      * @param value entry value
133      */
134     public void put(final String key, final double[] value) {
135         remove(key);
136         data.add(new Entry(key, value));
137     }
138 
139     /** Put all the entries from the map in the dictionary.
140      * @param map map to copy into the instance
141      */
142     public void putAll(final Map<String, double[]> map) {
143         for (final Map.Entry<String, double[]> entry : map.entrySet()) {
144             put(entry.getKey(), entry.getValue());
145         }
146     }
147 
148     /** Put all the entries from another dictionary.
149      * @param dictionary dictionary to copy into the instance
150      */
151     public void putAll(final DoubleArrayDictionary dictionary) {
152         for (final Entry entry : dictionary.data) {
153             put(entry.getKey(), entry.getValue());
154         }
155     }
156 
157     /** Get the value corresponding to a key.
158      * @param key entry key
159      * @return copy of the value corresponding to the key or null if key not present
160      */
161     public double[] get(final String key) {
162         final Entry entry = getEntry(key);
163         return entry == null ? null : entry.getValue();
164     }
165 
166     /** Get a complete entry.
167      * @param key entry key
168      * @return entry with key if it exists, null otherwise
169      */
170     public Entry getEntry(final String key) {
171         for (final Entry entry : data) {
172             if (entry.getKey().equals(key)) {
173                 return entry;
174             }
175         }
176         return null;
177     }
178 
179     /** Remove an entry.
180      * @param key key of the entry to remove
181      * @return true if an entry has been removed, false if the key was not present
182      */
183     public boolean remove(final String key) {
184         final Iterator<Entry> iterator = data.iterator();
185         while (iterator.hasNext()) {
186             if (iterator.next().getKey().equals(key)) {
187                 iterator.remove();
188                 return true;
189             }
190         }
191         return false;
192     }
193 
194 
195     /** Get a string representation of the dictionary.
196      * <p>
197      * This string representation is intended for improving displays in debuggers only.
198      * </p>
199      * @return string representation of the dictionary
200      */
201     @Override
202     public String toString() {
203         final StringBuilder builder = new StringBuilder();
204         builder.append('{');
205         for (int i = 0; i < data.size(); ++i) {
206             if (i > 0) {
207                 builder.append(", ");
208             }
209             builder.append(data.get(i).getKey());
210             builder.append('[');
211             builder.append(data.get(i).getValue().length);
212             builder.append(']');
213         }
214         builder.append('}');
215         return builder.toString();
216     }
217 
218     /** Entry in a dictionary. */
219     public static class Entry implements Serializable {
220 
221         /** Serializable UID. */
222         @Serial
223         private static final long serialVersionUID = 20211121L;
224 
225         /** Key. */
226         private final String key;
227 
228         /** Value. */
229         private final double[] value;
230 
231         /** Simple constructor.
232          * @param key key
233          * @param value value
234          */
235         Entry(final String key, final double[] value) {
236             this.key   = key;
237             this.value = value.clone();
238         }
239 
240         /** Get the entry key.
241          * @return entry key
242          */
243         public String getKey() {
244             return key;
245         }
246 
247         /** Get the value.
248          * @return a copy of the value (independent from internal array)
249          */
250         public double[] getValue() {
251             return value.clone();
252         }
253 
254         /** Get the size of the value array.
255          * @return size of the value array
256          */
257         public int size() {
258             return value.length;
259         }
260 
261         /** Increment the value.
262          * <p>
263          * For the sake of performance, no checks are done on argument.
264          * </p>
265          * @param increment increment to apply to the entry value
266          */
267         public void increment(final double[] increment) {
268             for (int i = 0; i < increment.length; ++i) {
269                 value[i] += increment[i];
270             }
271         }
272 
273         /** Increment the value with another scaled entry.
274          * <p>
275          * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
276          * </p>
277          * <p>
278          * For the sake of performance, no checks are done on arguments.
279          * </p>
280          * @param factor multiplicative factor for increment
281          * @param raw raw increment to be multiplied by {@code factor} and then added
282          * @since 11.1.1
283          */
284         public void scaledIncrement(final double factor, final Entry raw) {
285             for (int i = 0; i < raw.value.length; ++i) {
286                 value[i] += factor * raw.value[i];
287             }
288         }
289 
290         /** Reset the value to zero.
291          */
292         public void zero() {
293             Arrays.fill(value, 0.0);
294         }
295 
296     }
297 }