001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.ext.internal.util;
021
022import static java.lang.Math.max;
023import static java.lang.String.format;
024import static java.lang.System.arraycopy;
025import static java.util.Arrays.copyOf;
026import static java.util.Objects.requireNonNull;
027
028import java.util.Arrays;
029import java.util.function.IntConsumer;
030import java.util.stream.IntStream;
031
032/**
033 * Resizable-int array implementation
034 *
035 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
036 * @version 4.3
037 * @since 4.1
038 */
039public final class IntList {
040
041        private static final int MAX_SIZE = Integer.MAX_VALUE - 8;
042        private static final int DEFAULT_CAPACITY = 10;
043        private static final int[] EMPTY_ARRAY = {};
044        private static final int[] DEFAULT_EMPTY_ARRAY = {};
045
046        private int[] _data;
047        private int _size;
048
049        /**
050         * Constructs an empty list with the specified initial capacity.
051         *
052         * @param  capacity  the initial capacity of the list
053         * @throws IllegalArgumentException if the specified initial capacity
054         *         is negative
055         */
056        public IntList(final int capacity) {
057                if (capacity > 0) {
058                        _data = new int[capacity];
059                } else if (capacity == 0) {
060                        _data = EMPTY_ARRAY;
061                } else {
062                        throw new IllegalArgumentException("Illegal Capacity: "+ capacity);
063                }
064        }
065
066        /**
067         * Constructs an empty list with an initial capacity of ten.
068         */
069        public IntList() {
070                _data = DEFAULT_EMPTY_ARRAY;
071        }
072
073        /**
074         * Returns the element at the specified position in this list.
075         *
076         * @param  index index of the element to return
077         * @return the element at the specified position in this list
078         * @throws IndexOutOfBoundsException if the index is out of range
079         *         {@code (index < 0 || index > size())}
080         */
081        public int get(final int index) {
082                rangeCheck(index);
083
084                return _data[index];
085        }
086
087        /**
088         * Performs the given action for each element of the list.
089         *
090         * @param action the action to be performed for each element
091         * @throws NullPointerException if the specified action is {@code null}
092         */
093        public void forEach(final IntConsumer action) {
094                requireNonNull(action);
095
096                final int size = _size;
097                for (int i = 0; i < size; ++i) {
098                        action.accept(_data[i]);
099                }
100        }
101
102        /**
103         * Returns a sequential {@link IntStream} with the specified list as its
104         * source.
105         *
106         * @return a sequential {@link IntStream}
107         */
108        public IntStream stream() {
109                return Arrays.stream(_data, 0, _size);
110        }
111
112        /**
113         * Appends the specified element to the end of this list.
114         *
115         * @param element element to be appended to this list
116         */
117        public void add(final int element) {
118                ensureSize(_size + 1);
119                _data[_size++] = element;
120        }
121
122        /**
123         * Inserts the specified element at the specified position in this list.
124         * Shifts the element currently at that position (if any) and any subsequent
125         * elements to the right (adds one to their indices).
126         *
127         * @param index index at which the specified element is to be inserted
128         * @param element element to be inserted
129         * @throws IndexOutOfBoundsException if the index is out of range
130         *         {@code (index < 0 || index > size())}
131         */
132        public void add(final int index, final int element) {
133                addRangeCheck(index);
134
135                ensureSize(_size + 1);
136                arraycopy(
137                        _data, index,
138                        _data, index + 1,
139                        _size - index
140                );
141                _data[index] = element;
142                _size++;
143        }
144
145        /**
146         * Appends all of the elements in the specified array to the end of this
147         * list.
148         *
149         * @param elements array containing elements to be added to this list
150         * @return {@code true} if this list changed as a result of the call
151         * @throws NullPointerException if the specified array is null
152         */
153        public boolean addAll(final int[] elements) {
154                final int count = elements.length;
155                ensureSize(_size + count);
156                arraycopy(elements, 0, _data, _size, count);
157                _size += count;
158
159                return count != 0;
160        }
161
162        /**
163         * Inserts all the elements in the specified array into this list, starting
164         * at the specified position.
165         *
166         * @param index index at which to insert the first element from the
167         *              specified collection
168         * @param elements collection containing elements to be added to this list
169         * @return {@code true} if this list changed as a result of the call
170         * @throws IndexOutOfBoundsException if the index is out of range
171         *         {@code (index < 0 || index > size())}
172         * @throws NullPointerException if the specified array is null
173         */
174        public boolean addAll(final int index, final int[] elements) {
175                addRangeCheck(index);
176
177                final int count = elements.length;
178                ensureSize(_size + count);
179
180                final int moved = _size - index;
181                if (moved > 0) {
182                        arraycopy(_data, index, _data, index + count, moved);
183                }
184
185                arraycopy(elements, 0, _data, index, count);
186                _size += count;
187                return count != 0;
188        }
189
190        /**
191         * Removes all of the elements from this list. The list will be empty after
192         * this call returns.
193         */
194        public void clear() {
195                _size = 0;
196        }
197
198        /**
199         * Trims the capacity of this {@code ArrayList} instance to be the list's
200         * current size.  An application can use this operation to minimize the
201         * storage of an {@code ArrayList} instance.
202         */
203        public void trimToSize() {
204                if (_size < _data.length) {
205                        _data = _size == 0
206                                ? EMPTY_ARRAY
207                                : copyOf(_data, _size);
208                }
209        }
210
211        /**
212         * Returns the number of elements in this list.
213         *
214         * @return the number of elements in this list
215         */
216        public int size() {
217                return _size;
218        }
219
220        /**
221         * Return {@code true} if the list is empty.
222         *
223         * @return {@code true} if the list is empty, {@code false} otherwise
224         */
225        public boolean isEmpty() {
226                return _size == 0;
227        }
228
229        /**
230         * Return the current elements as an int array.
231         *
232         * @return the current elements as an int array
233         */
234        public int[] toArray() {
235                return copyOf(_data, _size);
236        }
237
238        private void ensureSize(int size) {
239                ensureExplicitSize(capacity(_data, size));
240        }
241
242        private void ensureExplicitSize(int size) {
243                if (size - _data.length > 0) {
244                        grow(size);
245                }
246        }
247
248        private void rangeCheck(final int index) {
249                if (index >= _size)
250                        throw new IndexOutOfBoundsException(format(
251                                "Index: %d, Size: %d", index, _size
252                        ));
253        }
254
255        private void addRangeCheck(int index) {
256                if (index > _size || index < 0)
257                        throw new IndexOutOfBoundsException(format(
258                                "Index: %d, Size: %d", index, _size
259                        ));
260        }
261
262        private static int capacity(final int[] data, final int capacity) {
263                if (data == DEFAULT_EMPTY_ARRAY) {
264                        return max(DEFAULT_CAPACITY, capacity);
265                }
266                return capacity;
267        }
268
269        private void grow(final int size) {
270                final int oldSize = _data.length;
271
272                int newSize = oldSize + (oldSize >> 1);
273                if (newSize - size < 0) {
274                        newSize = size;
275                }
276                if (newSize - MAX_SIZE > 0) {
277                        newSize = hugeCapacity(size);
278                }
279
280                _data = copyOf(_data, newSize);
281        }
282
283        private static int hugeCapacity(final int minCapacity) {
284                if (minCapacity < 0) {
285                        throw new OutOfMemoryError();
286                }
287
288                return minCapacity > MAX_SIZE
289                        ? Integer.MAX_VALUE
290                        : MAX_SIZE;
291        }
292
293}