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 java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.hipparchus.CalculusFieldElement;
28 import org.hipparchus.Field;
29 import org.hipparchus.util.MathArrays;
30
31 /** String → CalculusFieldElement[] mapping, for small number of keys.
32 * <p>
33 * This class is a low overhead for a very small number of keys.
34 * It is based on simple array and string comparison. It plays
35 * the same role a {@code Map<String, T[]>} but with reduced
36 * features and not intended for large number of keys. For such
37 * needs the regular {@code Map<String, T[]>} should be preferred.
38 * </p>
39 * @param <T> the type of the field elements
40 * @since 11.1
41 */
42 public class FieldArrayDictionary<T extends CalculusFieldElement<T>> {
43
44 /** Default capacity. */
45 private static final int DEFAULT_INITIAL_CAPACITY = 4;
46
47 /** Field to which elements belong. */
48 private final Field<T> field;
49
50 /** Data container. */
51 private final List<Entry> data;
52
53 /** Constructor with {@link #DEFAULT_INITIAL_CAPACITY default initial capacity}.
54 * @param field field to which the elements belong
55 */
56 public FieldArrayDictionary(final Field<T> field) {
57 this(field, DEFAULT_INITIAL_CAPACITY);
58 }
59
60 /** Constructor with specified capacity.
61 * @param field field to which the elements belong
62 * @param initialCapacity initial capacity
63 */
64 public FieldArrayDictionary(final Field<T> field, final int initialCapacity) {
65 this.field = field;
66 this.data = new ArrayList<>(initialCapacity);
67 }
68
69 /** Constructor from another dictionary.
70 * @param dictionary dictionary to use for initializing entries
71 */
72 public FieldArrayDictionary(final FieldArrayDictionary<T> dictionary) {
73 // take care to call dictionary.getData() and not use dictionary.data,
74 // otherwise we get an empty dictionary when using a FieldArrayDictionary.view
75 this(dictionary.getField(), DEFAULT_INITIAL_CAPACITY + dictionary.getData().size());
76 for (final Entry entry : dictionary.getData()) {
77 // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
78 data.add(new Entry(entry.getKey(), entry.getValue()));
79 }
80 }
81
82 /** Constructor from a map.
83 * @param field field to which the elements belong
84 * @param map map to use for initializing entries
85 */
86 public FieldArrayDictionary(final Field<T> field, final Map<String, T[]> map) {
87 this(field, map.size());
88 for (final Map.Entry<String, T[]> entry : map.entrySet()) {
89 // we don't call put(key, value) to avoid the overhead of the unneeded call to remove(key)
90 data.add(new Entry(entry.getKey(), entry.getValue()));
91 }
92 }
93
94 /** Get the field to which elements belong.
95 * @return field to which elements belong
96 */
97 public Field<T> getField() {
98 return field;
99 }
100
101 /** Get an unmodifiable view of the dictionary entries.
102 * @return unmodifiable view of the dictionary entries
103 */
104 public List<Entry> getData() {
105 return Collections.unmodifiableList(data);
106 }
107
108 /** Get the number of dictionary entries.
109 * @return number of dictionary entries
110 */
111 public int size() {
112 return data.size();
113 }
114
115 /** Create a map from the instance.
116 * <p>
117 * The map contains a copy of the instance data
118 * </p>
119 * @return copy of the dictionary, as an independent map
120 */
121 public Map<String, T[]> toMap() {
122 final Map<String, T[]> map = new HashMap<>(data.size());
123 for (final Entry entry : data) {
124 map.put(entry.getKey(), entry.getValue());
125 }
126 return map;
127 }
128
129 /** Remove all entries.
130 */
131 public void clear() {
132 data.clear();
133 }
134
135 /** Add an entry.
136 * <p>
137 * If an entry with the same key already exists, it will be removed first.
138 * </p>
139 * <p>
140 * The new entry is always put at the end.
141 * </p>
142 * @param key entry key
143 * @param value entry value
144 */
145 public void put(final String key, final T[] value) {
146 remove(key);
147 data.add(new Entry(key, value));
148 }
149
150 /** Add an entry.
151 * <p>
152 * If an entry with the same key already exists, it will be removed first.
153 * </p>
154 * <p>
155 * The new entry is always put at the end.
156 * </p>
157 * @param key entry key
158 * @param value entry value
159 */
160 public void put(final String key, final double[] value) {
161 final T[] converted = MathArrays.buildArray(field, value.length);
162 for (int i = 0; i < value.length; ++i) {
163 converted[i] = field.getZero().newInstance(value[i]);
164 }
165 put(key, converted);
166 }
167
168 /** Put all the entries from the map in the dictionary.
169 * @param map map to copy into the instance
170 */
171 public void putAll(final Map<String, T[]> map) {
172 for (final Map.Entry<String, T[]> entry : map.entrySet()) {
173 put(entry.getKey(), entry.getValue());
174 }
175 }
176
177 /** Put all the entries from another dictionary.
178 * @param dictionary dictionary to copy into the instance
179 */
180 public void putAll(final FieldArrayDictionary<T> dictionary) {
181 for (final Entry entry : dictionary.data) {
182 put(entry.getKey(), entry.getValue());
183 }
184 }
185
186 /** Get the value corresponding to a key.
187 * @param key entry key
188 * @return copy of the value corresponding to the key or null if key not present
189 */
190 public T[] get(final String key) {
191 final Entry entry = getEntry(key);
192 return entry == null ? null : entry.getValue();
193 }
194
195 /** Get a complete entry.
196 * @param key entry key
197 * @return entry with key if it exists, null otherwise
198 */
199 public Entry getEntry(final String key) {
200 for (final Entry entry : data) {
201 if (entry.getKey().equals(key)) {
202 return entry;
203 }
204 }
205 return null;
206 }
207
208 /** remove an entry.
209 * @param key key of the entry to remove
210 * @return true if an entry has been removed, false if the key was not present
211 */
212 public boolean remove(final String key) {
213 final Iterator<Entry> iterator = data.iterator();
214 while (iterator.hasNext()) {
215 if (iterator.next().getKey().equals(key)) {
216 iterator.remove();
217 return true;
218 }
219 }
220 return false;
221 }
222
223 /** Get an unmodifiable view of the dictionary.
224 * <p>
225 * The return dictionary is backed by the original instance and offers {@code read-only}
226 * access to it, but all operations that modify it throw an {@link UnsupportedOperationException}.
227 * </p>
228 * @return unmodifiable view of the dictionary
229 */
230 public FieldArrayDictionary<T> unmodifiableView() {
231 return new View(field);
232 }
233
234 /** Get a string representation of the dictionary.
235 * <p>
236 * This string representation is intended for improving displays in debuggers only.
237 * </p>
238 * @return string representation of the dictionary
239 */
240 @Override
241 public String toString() {
242 final StringBuilder builder = new StringBuilder();
243 builder.append('{');
244 for (int i = 0; i < data.size(); ++i) {
245 if (i > 0) {
246 builder.append(", ");
247 }
248 builder.append(data.get(i).getKey());
249 builder.append('[');
250 builder.append(data.get(i).getValue().length);
251 builder.append(']');
252 }
253 builder.append('}');
254 return builder.toString();
255 }
256
257 /** Entry in a dictionary. */
258 public class Entry {
259
260 /** Key. */
261 private final String key;
262
263 /** Value. */
264 private final T[] value;
265
266 /** Simple constructor.
267 * @param key key
268 * @param value value
269 */
270 Entry(final String key, final T[] value) {
271 this.key = key;
272 this.value = value.clone();
273 }
274
275 /** Get the entry key.
276 * @return entry key
277 */
278 public String getKey() {
279 return key;
280 }
281
282 /** Get the value.
283 * @return a copy of the value (independent from internal array)
284 */
285 public T[] getValue() {
286 return value.clone();
287 }
288
289 /** Get the size of the value array.
290 * @return size of the value array
291 */
292 public int size() {
293 return value.length;
294 }
295
296 /** Increment the value.
297 * <p>
298 * For the sake of performance, no checks are done on argument.
299 * </p>
300 * @param increment increment to apply to the entry value
301 */
302 public void increment(final T[] increment) {
303 for (int i = 0; i < increment.length; ++i) {
304 value[i] = value[i].add(increment[i]);
305 }
306 }
307
308 /** Increment the value.
309 * <p>
310 * For the sake of performance, no checks are done on argument.
311 * </p>
312 * @param increment increment to apply to the entry value
313 */
314 public void increment(final double[] increment) {
315 for (int i = 0; i < increment.length; ++i) {
316 value[i] = value[i].add(increment[i]);
317 }
318 }
319
320 /** Increment the value with another scaled entry.
321 * <p>
322 * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
323 * </p>
324 * <p>
325 * For the sake of performance, no checks are done on arguments.
326 * </p>
327 * @param factor multiplicative factor for increment
328 * @param raw raw increment to be multiplied by {@code factor} and then added
329 * @since 11.1.1
330 */
331 public void scaledIncrement(final T factor, final Entry raw) {
332 for (int i = 0; i < raw.value.length; ++i) {
333 value[i] = value[i].add(raw.value[i].multiply(factor));
334 }
335 }
336
337 /** Increment the value with another scaled entry.
338 * <p>
339 * Each component {@code value[i]} will be replaced by {@code value[i] + factor * raw.value[i]}.
340 * </p>
341 * <p>
342 * For the sake of performance, no checks are done on arguments.
343 * </p>
344 * @param factor multiplicative factor for increment
345 * @param raw raw increment to be multiplied by {@code factor} and then added
346 * @since 11.1.1
347 */
348 public void scaledIncrement(final double factor, final Entry raw) {
349 for (int i = 0; i < raw.value.length; ++i) {
350 value[i] = value[i].add(raw.value[i].multiply(factor));
351 }
352 }
353
354 /** Reset the value to zero.
355 */
356 public void zero() {
357 Arrays.fill(value, field.getZero());
358 }
359
360 }
361 /** Unmodifiable view of the dictionary. */
362 private class View extends FieldArrayDictionary<T> {
363
364 /** Simple constructor.
365 * @param field field to which the elements belong
366 */
367 View(final Field<T> field) {
368 super(field);
369 }
370
371 /** {@inheritDoc} */
372 @Override
373 public List<Entry> getData() {
374 return FieldArrayDictionary.this.getData();
375 }
376
377 /** {@inheritDoc} */
378 @Override
379 public int size() {
380 return FieldArrayDictionary.this.size();
381 }
382
383 /** {@inheritDoc} */
384 @Override
385 public Map<String, T[]> toMap() {
386 return FieldArrayDictionary.this.toMap();
387 }
388
389 /** {@inheritDoc} */
390 @Override
391 public void clear() {
392 throw new UnsupportedOperationException();
393 }
394
395 /** {@inheritDoc} */
396 @Override
397 public void put(final String key, final T[] value) {
398 throw new UnsupportedOperationException();
399 }
400
401 /** {@inheritDoc} */
402 @Override
403 public void put(final String key, final double[] value) {
404 throw new UnsupportedOperationException();
405 }
406
407 /** {@inheritDoc} */
408 @Override
409 public void putAll(final Map<String, T[]> map) {
410 throw new UnsupportedOperationException();
411 }
412
413 /** {@inheritDoc} */
414 @Override
415 public void putAll(final FieldArrayDictionary<T> dictionary) {
416 throw new UnsupportedOperationException();
417 }
418
419 /** {@inheritDoc} */
420 @Override
421 public T[] get(final String key) {
422 return FieldArrayDictionary.this.get(key);
423 }
424
425 /** {@inheritDoc} */
426 @Override
427 public Entry getEntry(final String key) {
428 return FieldArrayDictionary.this.getEntry(key);
429 }
430
431 /** {@inheritDoc} */
432 @Override
433 public boolean remove(final String key) {
434 throw new UnsupportedOperationException();
435 }
436
437 }
438
439 }