001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
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+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.collections;
018
019import java.io.Serializable;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.EnumSet;
025import java.util.HashMap;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Map;
029import java.util.Map.Entry;
030import java.util.Objects;
031import java.util.Optional;
032import java.util.Set;
033import java.util.Spliterator;
034import java.util.Spliterators;
035import java.util.function.BiConsumer;
036import java.util.function.BiFunction;
037import java.util.function.BinaryOperator;
038import java.util.function.Consumer;
039import java.util.function.Function;
040import java.util.function.Supplier;
041import java.util.stream.Collector;
042import java.util.stream.Collectors;
043import java.util.stream.Stream;
044import java.util.stream.StreamSupport;
045
046import org.conqat.lib.commons.assertion.CCSMAssert;
047import org.conqat.lib.commons.collections.CollectionUtils.FunctionWithException;
048import org.conqat.lib.commons.equals.HashCodeUtils;
049
050import com.fasterxml.jackson.annotation.JsonProperty;
051
052/**
053 * A list for storing pairs in a specific order.
054 */
055public class PairList<S, T> implements Serializable, Iterable<Pair<S, T>> {
056
057        /** Version used for serialization. */
058        private static final long serialVersionUID = 1;
059
060        /** The current size. */
061        @JsonProperty("size")
062        private int size = 0;
063
064        /** The array used for storing the S. */
065        @JsonProperty("firstElements")
066        private Object[] firstElements;
067
068        /** The array used for storing the T. */
069        @JsonProperty("secondElements")
070        private Object[] secondElements;
071
072        /** Constructor. */
073        public PairList() {
074                this(16);
075        }
076
077        /** Constructor. */
078        public PairList(int initialCapacity) {
079                if (initialCapacity < 1) {
080                        initialCapacity = 1;
081                }
082                firstElements = new Object[initialCapacity];
083                secondElements = new Object[initialCapacity];
084        }
085
086        /** Copy constructor. */
087        public PairList(PairList<S, T> other) {
088                this(other.size);
089                addAll(other);
090        }
091
092        /**
093         * Constructor to convert a map into a pair list.
094         */
095        public PairList(Map<S, T> map) {
096                this(map.size());
097                for (Entry<S, T> entry : map.entrySet()) {
098                        add(entry.getKey(), entry.getValue());
099                }
100        }
101
102        /**
103         * Creates a new pair list initialized with a single key/value pair. This is
104         * especially helpful for construction of small pair lists, as type inference
105         * reduces writing overhead.
106         */
107        public static <S, T> PairList<S, T> from(S key, T value) {
108                PairList<S, T> result = new PairList<>();
109                result.add(key, value);
110                return result;
111        }
112
113        /**
114         * Creates a new {@link PairList} from given {@link Pair}s.
115         */
116        @SafeVarargs
117        public static <S, T> PairList<S, T> fromPairs(Pair<S, T> pair, Pair<S, T>... additionalPairs) {
118                PairList<S, T> result = new PairList<>();
119
120                result.add(pair.getFirst(), pair.getSecond());
121                for (Pair<S, T> additionalPair : additionalPairs) {
122                        result.add(additionalPair.getFirst(), additionalPair.getSecond());
123                }
124
125                return result;
126        }
127
128        /** Returns whether the list is empty. */
129        public boolean isEmpty() {
130                return size == 0;
131        }
132
133        /** Returns the size of the list. */
134        public int size() {
135                return size;
136        }
137
138        /** Add the given pair to the list. */
139        public void add(S first, T second) {
140                ensureSpace(size + 1);
141                firstElements[size] = first;
142                secondElements[size] = second;
143                ++size;
144        }
145
146        /**
147         * Inserts the given element at the given index, shifting all other elements.
148         * Note that this operation is liner in the length of this list.
149         */
150        public void insert(int index, S first, T second) {
151                ensureSpace(size + 1);
152                for (int i = size; i > index; --i) {
153                        firstElements[i] = firstElements[i - 1];
154                        secondElements[i] = secondElements[i - 1];
155                }
156                ++size;
157                firstElements[index] = first;
158                secondElements[index] = second;
159        }
160
161        /** Adds all pairs from another list. */
162        public void addAll(PairList<S, T> other) {
163                // we have to store this in a local var, as other.size may change if
164                // other == this
165                int otherSize = other.size;
166
167                ensureSpace(size + otherSize);
168                for (int i = 0; i < otherSize; ++i) {
169                        firstElements[size] = other.firstElements[i];
170                        secondElements[size] = other.secondElements[i];
171                        ++size;
172                }
173        }
174
175        /** Make sure there is space for at least the given amount of elements. */
176        protected void ensureSpace(int space) {
177                if (space <= firstElements.length) {
178                        return;
179                }
180
181                Object[] oldFirst = firstElements;
182                Object[] oldSecond = secondElements;
183                int newSize = firstElements.length * 2;
184                while (newSize < space) {
185                        newSize *= 2;
186                }
187
188                firstElements = new Object[newSize];
189                secondElements = new Object[newSize];
190                System.arraycopy(oldFirst, 0, firstElements, 0, size);
191                System.arraycopy(oldSecond, 0, secondElements, 0, size);
192        }
193
194        /** Returns the first element at given index. */
195        @SuppressWarnings("unchecked")
196        public S getFirst(int i) {
197                checkWithinBounds(i);
198                return (S) firstElements[i];
199        }
200
201        /**
202         * Checks whether the given <code>i</code> is within the bounds. Throws an
203         * exception otherwise.
204         */
205        private void checkWithinBounds(int i) {
206                if (i < 0 || i >= size) {
207                        throw new IndexOutOfBoundsException("Out of bounds: " + i);
208                }
209        }
210
211        /** Sets the first element at given index. */
212        public void setFirst(int i, S value) {
213                checkWithinBounds(i);
214                firstElements[i] = value;
215        }
216
217        /** Returns the second element at given index. */
218        @SuppressWarnings("unchecked")
219        public T getSecond(int i) {
220                checkWithinBounds(i);
221                return (T) secondElements[i];
222        }
223
224        /** Sets the first element at given index. */
225        public void setSecond(int i, T value) {
226                checkWithinBounds(i);
227                secondElements[i] = value;
228        }
229
230        /** Creates a new list containing all first elements. */
231        @SuppressWarnings("unchecked")
232        public List<S> extractFirstList() {
233                List<S> result = new ArrayList<>(size + 1);
234                for (int i = 0; i < size; ++i) {
235                        result.add((S) firstElements[i]);
236                }
237                return result;
238        }
239
240        /** Returns a list view of the first elements backed directly by the array. */
241        @SuppressWarnings("unchecked")
242        public UnmodifiableList<S> getFirstList() {
243                return (UnmodifiableList<S>) CollectionUtils.asUnmodifiable(Arrays.asList(firstElements).subList(0, size));
244        }
245
246        /** Creates a new list containing all second elements. */
247        @SuppressWarnings("unchecked")
248        public List<T> extractSecondList() {
249                List<T> result = new ArrayList<>(size + 1);
250                for (int i = 0; i < size; ++i) {
251                        result.add((T) secondElements[i]);
252                }
253                return result;
254        }
255
256        /** Returns a list view of the second elements backed directly by the array. */
257        @SuppressWarnings("unchecked")
258        public UnmodifiableList<T> getSecondList() {
259                return (UnmodifiableList<T>) CollectionUtils.asUnmodifiable(Arrays.asList(secondElements).subList(0, size));
260        }
261
262        /**
263         * Swaps the pairs of this list. Is S and T are different types, this will be
264         * extremely dangerous.
265         */
266        public void swapPairs() {
267                Object[] temp = firstElements;
268                firstElements = secondElements;
269                secondElements = temp;
270        }
271
272        /** Swaps the entries located at indexes i and j. */
273        public void swapEntries(int i, int j) {
274                S tmp1 = getFirst(i);
275                T tmp2 = getSecond(i);
276                setFirst(i, getFirst(j));
277                setSecond(i, getSecond(j));
278                setFirst(j, tmp1);
279                setSecond(j, tmp2);
280        }
281
282        /** Clears this list. */
283        public void clear() {
284                size = 0;
285        }
286
287        /** Removes the last element of the list. */
288        public void removeLast() {
289                CCSMAssert.isTrue(size > 0, "Size must be positive!");
290                size -= 1;
291
292                // clear elements to allow GC
293                firstElements[size] = null;
294                secondElements[size] = null;
295        }
296
297        /**
298         * Removes the element at given index. Note that this operation is liner in the
299         * length of this list.
300         */
301        public void remove(int index) {
302                for (int i = index + 1; i < size; ++i) {
303                        firstElements[i - 1] = firstElements[i];
304                        secondElements[i - 1] = secondElements[i];
305                }
306                removeLast();
307        }
308
309        @Override
310        public String toString() {
311                StringBuffer result = new StringBuffer();
312                result.append('[');
313                for (int i = 0; i < size; i++) {
314                        if (i != 0) {
315                                result.append(',');
316                        }
317                        result.append('(');
318                        result.append(String.valueOf(firstElements[i]));
319                        result.append(',');
320                        result.append(String.valueOf(secondElements[i]));
321                        result.append(')');
322                }
323                result.append(']');
324                return result.toString();
325        }
326
327        /** {@inheritDoc} */
328        @Override
329        public int hashCode() {
330                int prime = 31;
331                int hash = size;
332                hash = prime * hash + HashCodeUtils.hashArrayPart(firstElements, 0, size);
333                return prime * hash + HashCodeUtils.hashArrayPart(secondElements, 0, size);
334        }
335
336        /** {@inheritDoc} */
337        @Override
338        public boolean equals(Object obj) {
339                if (this == obj) {
340                        return true;
341                }
342                if (!(obj instanceof PairList)) {
343                        return false;
344                }
345
346                PairList<?, ?> other = (PairList<?, ?>) obj;
347                if (size != other.size) {
348                        return false;
349                }
350                for (int i = 0; i < size; i++) {
351                        if (!Objects.equals(firstElements[i], other.firstElements[i])
352                                        || !Objects.equals(secondElements[i], secondElements[i])) {
353                                return false;
354                        }
355                }
356                return true;
357        }
358
359        /** {@inheritDoc} */
360        @Override
361        public Iterator<Pair<S, T>> iterator() {
362                return new Iterator<Pair<S, T>>() {
363                        int index = 0;
364
365                        /** {@inheritDoc} */
366                        @Override
367                        public boolean hasNext() {
368                                return index < size;
369                        }
370
371                        /** {@inheritDoc} */
372                        @Override
373                        public Pair<S, T> next() {
374                                checkWithinBounds(index);
375                                int oldIndex = index;
376                                index++;
377                                return createPairForIndex(oldIndex);
378                        }
379                };
380        }
381
382        /**
383         * Returns a non-parallel stream of {@link Pair}s from this list.
384         * <p>
385         * NOTE: the underlying {@link Spliterator} implementation does currently not
386         * support splitting. Thus, trying to use the returned stream in parallel
387         * execution will fail.
388         *
389         * @see PairListSpliterator#trySplit()
390         */
391        public Stream<Pair<S, T>> stream() {
392                return StreamSupport.stream(new PairListSpliterator(), false);
393        }
394
395        /**
396         * Converts this {@link PairList} into a {@link Map}.
397         *
398         * If the first elements contain duplicates (according to
399         * {@link Object#equals(Object)}, an {@link IllegalStateException} is thrown. If
400         * the first elements may have duplicates, use {@link #toMap(BinaryOperator)}
401         * instead.
402         */
403        public Map<S, T> toMap() {
404                Map<S, T> result = new HashMap<>();
405                // must use explicit put, as stream collector does not support null
406                // values
407                forEach(result::put);
408                return result;
409        }
410
411        /**
412         * Converts this {@link PairList} into a {@link Map}.
413         *
414         * If the first elements contain duplicates (according to
415         * {@link Object#equals(Object)}, the respective second elements are merged
416         * using the provided merging function..
417         */
418        public Map<S, T> toMap(BinaryOperator<T> mergeFunction) {
419                return stream().collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, mergeFunction));
420        }
421
422        /**
423         * Creates a pair from the values at the given index in this list.
424         *
425         * We suppress unchecked cast warnings since the PairList stores all elements as
426         * plain Objects.
427         */
428        @SuppressWarnings("unchecked")
429        private Pair<S, T> createPairForIndex(int index) {
430                return new Pair<>((S) firstElements[index], (T) secondElements[index]);
431        }
432
433        /**
434         * Spliterator for the {@link PairList}.
435         */
436        private final class PairListSpliterator implements Spliterator<Pair<S, T>> {
437
438                /**
439                 * The next element to process when {@link #tryAdvance(Consumer)} is called.
440                 */
441                @JsonProperty("nextElementToProcess")
442                private int nextElementToProcess;
443
444                /** {@inheritDoc} */
445                @Override
446                public int characteristics() {
447                        // the Spliterator is not subsized since we don't support splitting
448                        // at the moment
449                        return Spliterator.IMMUTABLE | Spliterator.ORDERED | Spliterator.SIZED;
450                }
451
452                /** {@inheritDoc} */
453                @Override
454                public long estimateSize() {
455                        return size - nextElementToProcess;
456                }
457
458                /** {@inheritDoc} */
459                @Override
460                public boolean tryAdvance(Consumer<? super Pair<S, T>> action) {
461                        if (nextElementToProcess == size) {
462                                return false;
463                        }
464
465                        action.accept(createPairForIndex(nextElementToProcess));
466                        nextElementToProcess++;
467                        return true;
468                }
469
470                /**
471                 * {@inheritDoc}
472                 *
473                 * We return <code>null</code> to indicate that this {@link Spliterator} cannot
474                 * be split. If parallel execution is required, this method must be implemented
475                 * and the subsized characteristic should be adhered to.
476                 */
477                @Override
478                public Spliterator<Pair<S, T>> trySplit() {
479                        return null;
480                }
481        }
482
483        /**
484         * Collects a stream of pairs into a {@link PairList}.
485         */
486        private static class PairListCollector<S, T> implements Collector<Pair<S, T>, PairList<S, T>, PairList<S, T>> {
487
488                /** {@inheritDoc} */
489                @Override
490                public Supplier<PairList<S, T>> supplier() {
491                        return PairList::new;
492                }
493
494                /** {@inheritDoc} */
495                @Override
496                public BiConsumer<PairList<S, T>, Pair<S, T>> accumulator() {
497                        return (list, pair) -> list.add(pair.getFirst(), pair.getSecond());
498                }
499
500                /** {@inheritDoc} */
501                @Override
502                public BinaryOperator<PairList<S, T>> combiner() {
503                        return (list1, list2) -> {
504                                list1.addAll(list2);
505                                return list1;
506                        };
507                }
508
509                /** {@inheritDoc} */
510                @Override
511                public Function<PairList<S, T>, PairList<S, T>> finisher() {
512                        return list -> list;
513                }
514
515                /** {@inheritDoc} */
516                @Override
517                public Set<java.util.stream.Collector.Characteristics> characteristics() {
518                        return EnumSet.of(Characteristics.IDENTITY_FINISH);
519                }
520
521        }
522
523        /**
524         * Returns a collector for collecting a stream of pairs into a {@link PairList}.
525         */
526        public static <S, T> PairListCollector<S, T> toPairList() {
527                return new PairListCollector<>();
528        }
529
530        /**
531         * Creates a {@link PairList} that consists of pairs of values taken from the
532         * two {@link List}s. I.e. entry i in the resulting {@link PairList} will
533         * consists of the pair (a, b) where a is the entry at index i in the first
534         * collection and b is the entry at index i of the second collection.
535         *
536         * Both collections must be of the same size. The order of insertion into the
537         * new {@link PairList} is determined by the order imposed by the collections'
538         * iterators.
539         */
540        public static <S, T> PairList<S, T> zip(List<S> firstValues, List<T> secondValues) {
541                CCSMAssert.isTrue(firstValues.size() == secondValues.size(),
542                                "Can only zip together collections of the same size.");
543                PairList<S, T> result = new PairList<>(firstValues.size());
544                Iterator<S> firstIterator = firstValues.iterator();
545                Iterator<T> secondIterator = secondValues.iterator();
546                while (firstIterator.hasNext()) {
547                        result.add(firstIterator.next(), secondIterator.next());
548                }
549                return result;
550        }
551
552        /**
553         * For each element pair in this list, calls the given consumer with the first
554         * and second value from the pair as the only arguments.
555         */
556        public void forEach(BiConsumer<S, T> consumer) {
557                for (int i = 0; i < size; i++) {
558                        consumer.accept(getFirst(i), getSecond(i));
559                }
560        }
561
562        /**
563         * Returns a new {@link PairList}, where both mappers are applied to the
564         * elements of the current {@link PairList} (input for each mapper is the pair
565         * of elements at each position).
566         */
567        public <S2, T2> PairList<S2, T2> map(BiFunction<S, T, S2> firstMapper, BiFunction<S, T, T2> secondMapper) {
568                PairList<S2, T2> result = new PairList<>(size);
569                forEach((key, value) -> result.add(firstMapper.apply(key, value), secondMapper.apply(key, value)));
570                return result;
571        }
572
573        /**
574         * Returns a new {@link PairList}, where the first elements are obtained by
575         * applying the given mapper function to the first elements of this list.
576         */
577        public <U, E extends Exception> PairList<U, T> mapFirst(FunctionWithException<S, U, ? extends E> mapper) throws E {
578                PairList<U, T> mappedList = new PairList<>(size);
579                for (int i = 0; i < size; i++) {
580                        mappedList.add(mapper.apply(getFirst(i)), getSecond(i));
581                }
582                return mappedList;
583        }
584
585        /**
586         * Returns a new {@link PairList}, where the second elements are obtained by
587         * applying the given mapper function to the second elements of this list.
588         */
589        public <U, E extends Exception> PairList<S, U> mapSecond(FunctionWithException<T, U, ? extends E> mapper) throws E {
590                PairList<S, U> mappedList = new PairList<>(size);
591                for (int i = 0; i < size; i++) {
592                        mappedList.add(getFirst(i), mapper.apply(getSecond(i)));
593                }
594                return mappedList;
595        }
596
597        /**
598         * Filters the pairlist by testing all items against the predicate and returning
599         * a pairlist of those for which it returns true. This method does not modify
600         * the original pairlist.
601         */
602        public PairList<S, T> filter(BiFunction<S, T, Boolean> filterPredicate) {
603                PairList<S, T> filteredList = new PairList<>(size);
604                for (int i = 0; i < size; i++) {
605                        if (filterPredicate.apply(getFirst(i), getSecond(i))) {
606                                filteredList.add(getFirst(i), getSecond(i));
607                        }
608                }
609                return filteredList;
610        }
611
612        /**
613         * Tests all the items against the predicate and returns true, if any of the
614         * items matched the predicate. Otherwise, returns false.
615         */
616        public boolean anyMatch(BiFunction<S, T, Boolean> predicate) {
617                for (int i = 0; i < size; i++) {
618                        if (predicate.apply(getFirst(i), getSecond(i))) {
619                                return true;
620                        }
621                }
622                return false;
623        }
624
625        /** Returns a reversed copy of this list. */
626        public PairList<S, T> reverse() {
627                PairList<S, T> reversed = new PairList<>();
628                for (int i = size() - 1; i >= 0; i--) {
629                        reversed.add(getFirst(i), getSecond(i));
630                }
631                return reversed;
632        }
633
634        /**
635         * Returns a newly allocated array containing all of the elements in this
636         * PairList as an array of Pair<S,T>s.
637         */
638        @SuppressWarnings("unchecked")
639        public Pair<S, T>[] toArray() {
640                Pair<S, T>[] result = new Pair[size];
641                for (int i = 0; i < size; i++) {
642                        result[i] = new Pair<>((S) firstElements[i], (T) secondElements[i]);
643                }
644                return result;
645        }
646
647        /**
648         * Returns a newly allocated list containing all of the elements in this
649         * PairList as a list of Pair<S,T>s.
650         */
651        public List<Pair<S, T>> toList() {
652                List<Pair<S, T>> result = new ArrayList<>(size);
653                for (int i = 0; i < size; i++) {
654                        result.add(new Pair<>((S) firstElements[i], (T) secondElements[i]));
655                }
656                return result;
657        }
658
659        /**
660         * Sorts the PairList according to the given comparator.
661         */
662        public void sort(Comparator<Pair<S, T>> comparator) {
663                Pair<S, T>[] sorted = this.toArray();
664                Arrays.sort(sorted, comparator);
665                for (int i = 0; i < sorted.length; i++) {
666                        setFirst(i, sorted[i].getFirst());
667                        setSecond(i, sorted[i].getSecond());
668                }
669        }
670
671        /**
672         * Returns a new {@link PairList} with all elements from both lists in order.
673         */
674        public static <S, T> PairList<S, T> concatenate(PairList<S, T> list1, PairList<S, T> list2) {
675                PairList<S, T> result = new PairList<>(list1.size() + list2.size());
676                result.addAll(list1);
677                result.addAll(list2);
678                return result;
679        }
680
681        /**
682         * The empty list (immutable). This list is serializable.
683         */
684        @SuppressWarnings("rawtypes")
685        private static final PairList EMPTY_PAIR_LIST = new PairList<Object, Object>(Collections.emptyMap()) {
686
687                /** default */
688                private static final long serialVersionUID = 1;
689
690                /** {@inheritDoc} */
691                @Override
692                public Iterator<Pair<Object, Object>> iterator() {
693                        return Collections.emptyIterator();
694                }
695
696                /** {@inheritDoc} */
697                @Override
698                public Spliterator<Pair<Object, Object>> spliterator() {
699                        return Spliterators.emptySpliterator();
700                }
701
702                /** {@inheritDoc} */
703                @Override
704                public void add(Object first, Object second) {
705                        throw new UnsupportedOperationException();
706                }
707
708                /** {@inheritDoc} */
709                @Override
710                public void addAll(PairList<Object, Object> other) {
711                        throw new UnsupportedOperationException();
712                }
713
714                /** {@inheritDoc} */
715                @Override
716                public List<Object> extractFirstList() {
717                        return Collections.emptyList();
718                }
719
720                /** {@inheritDoc} */
721                @Override
722                public List<Object> extractSecondList() {
723                        return Collections.emptyList();
724                }
725        };
726
727        /**
728         * Returns an empty list (immutable). This list is serializable. This is
729         * inspired by the Java Collections.emptyList() method.
730         *
731         * <p>
732         * This example illustrates the type-safe way to obtain an empty Pairlist:
733         *
734         * <pre>
735         * PairList&lt;String, String&gt; s = PairList.emptyPairList();
736         * </pre>
737         *
738         * @param <S>
739         *            key type of elements, if there were any, in the list
740         * @param <T>
741         *            value type of elements, if there were any, in the list
742         * @return an empty immutable list
743         *
744         */
745        @SuppressWarnings("unchecked")
746        public static final <S, T> PairList<S, T> emptyPairList() {
747                return EMPTY_PAIR_LIST;
748        }
749
750        /** Add the given pair to the list, if it is present. */
751        public void addIfPresent(Optional<Pair<S, T>> pair) {
752                if (pair.isPresent()) {
753                        add(pair.get().getFirst(), pair.get().getSecond());
754                }
755        }
756
757        /**
758         * Maps a pair list to another one using separate mappers for keys and values.
759         */
760        public <S2, T2> PairList<S2, T2> map(Function<S, S2> keyMapper, Function<T, T2> valueMapper) {
761                PairList<S2, T2> result = new PairList<>();
762                forEach((key, value) -> result.add(keyMapper.apply(key), valueMapper.apply(value)));
763                return result;
764        }
765
766        /**
767         * Returns the index of the given object in the "first" list of this PairList or
768         * Optional.empty if none of the first objects is equal to the given object.
769         */
770        public Optional<Integer> indexOfFirst(S firstSearchObject) {
771                for (int i = 0; i < size; i++) {
772                        if (Objects.equals(firstElements[i], firstSearchObject)) {
773                                return Optional.of(i);
774                        }
775                }
776                return Optional.empty();
777        }
778
779}