/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.util;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.TreeSet;
import org.eclipse.xtext.xbase.lib.Inline;
import org.eclipse.xtext.xbase.lib.Pure;
/** This class represents a list of numbers.
* The list is always sorted by number values.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:methodcount")
public class IntegerList implements SortedSet<Integer>, List<Integer> {
/** This is the list of values.
* The value segments are represented by 2 values: the first value
* and the last value of the segment.
*/
private int[] values;
/** This is the theorycal size of this list, ie. the count of integers.
*/
private int size;
/** Create a list with the specified values.
*/
public IntegerList() {
this.values = null;
this.size = 0;
}
/** Create a list with the specified value.
*
* @param value is the initial value.
*/
public IntegerList(int value) {
this.values = new int[] {value, value};
this.size = 1;
}
/** Create a list with the specified values.
*
* @param start is the first initial value
* @param end is the last initial value
*/
public IntegerList(int start, int end) {
int theStart = start;
int theEnd = end;
if (theStart > theEnd) {
final int tmp = theStart;
theStart = theEnd;
theEnd = tmp;
}
this.values = new int[] {theStart, theEnd};
this.size = theEnd - theStart + 1;
}
/** Create a list with the specified values.
*
* @param collection is the list of initial values
*/
public IntegerList(Collection<? extends Integer> collection) {
this.values = null;
this.size = 0;
addAll(collection);
}
@Pure
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append('[');
if (this.values != null) {
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
if (idxStart > 0) {
buffer.append(',');
}
if (this.values[idxStart] == this.values[idxStart + 1]) {
buffer.append(this.values[idxStart]);
} else if ((this.values[idxStart] + 1) == this.values[idxStart + 1]) {
buffer.append(this.values[idxStart]);
buffer.append(',');
buffer.append(this.values[idxStart + 1]);
} else {
buffer.append(this.values[idxStart]);
buffer.append('-');
buffer.append(this.values[idxStart + 1]);
}
}
}
buffer.append(']');
return buffer.toString();
}
@Pure
@Inline(value = "null", constantExpression = true)
@Override
public Comparator<? super Integer> comparator() {
return null;
}
@Pure
@Override
public Integer first() {
if (this.values == null) {
throw new NoSuchElementException();
}
return Integer.valueOf(this.values[0]);
}
@Pure
@Override
public SortedSet<Integer> headSet(Integer toElement) {
final SortedSet<Integer> theset = new TreeSet<>();
if (this.values != null) {
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
// The next segment is greater or equal to the given bound
if (this.values[idxStart] >= toElement) {
break;
}
// The given bound is inside the value segment
for (int intToAdd = this.values[idxStart]; (intToAdd < toElement)
&& (intToAdd <= this.values[idxStart + 1]); ++intToAdd) {
theset.add(intToAdd);
}
}
}
return theset;
}
@Pure
@Override
public Integer last() {
if (this.values == null) {
throw new NoSuchElementException();
}
return this.values[this.values.length - 1];
}
@Pure
@Override
public SortedSet<Integer> subSet(Integer fromElement, Integer toElement) {
final SortedSet<Integer> theset = new TreeSet<>();
if (this.values != null) {
// Search for the first matching segment
int min;
int max;
int firstSegment = -1;
for (int idxSegment = 0; firstSegment == -1 && idxSegment < this.values.length; idxSegment += 2) {
max = this.values[idxSegment + 1];
if (fromElement.compareTo(max) <= 0) {
firstSegment = idxSegment;
}
}
if (firstSegment != -1) {
// Go through the segments
for (int idxSegment = firstSegment; idxSegment < this.values.length; idxSegment += 2) {
min = this.values[idxSegment];
max = this.values[idxSegment + 1];
if (toElement.compareTo(min) <= 0) {
idxSegment = this.values.length;
} else {
for (int value = min; toElement.compareTo(value) > 0 && value <= max; ++value) {
assert toElement.compareTo(value) > 0;
if (fromElement.compareTo(value) <= 0) {
theset.add(value);
}
}
}
}
}
}
return theset;
}
@Pure
@Override
public SortedSet<Integer> tailSet(Integer fromElement) {
final SortedSet<Integer> theset = new TreeSet<>();
if (this.values != null) {
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
// The next segment is lower to the given bound
if (this.values[idxStart + 1] < fromElement) {
continue;
}
// The given bound is inside the value segment
if (fromElement >= this.values[idxStart]) {
for (int intToAdd = fromElement; intToAdd <= this.values[idxStart + 1]; ++intToAdd) {
theset.add(intToAdd);
}
}
// The given bound is inside the value segment
if (fromElement < this.values[idxStart]) {
for (int intToAdd = this.values[idxStart]; intToAdd <= this.values[idxStart + 1]; ++intToAdd) {
theset.add(intToAdd);
}
}
}
}
return theset;
}
@Override
public final void add(int index, Integer element) {
add(element);
}
@Override
public boolean add(Integer value) {
if (this.values == null) {
this.values = new int[] {value, value};
this.size = 1;
} else {
int first = 0;
int last = getSegmentCount() - 1;
while (first <= last) {
final int center = (first + last) / 2;
final int min = this.values[center * 2];
final int max = this.values[center * 2 + 1];
if (value.compareTo(min) >= 0 && value.compareTo(max) <= 0) {
return false;
}
if (value.compareTo(min) < 0) {
last = center - 1;
} else {
first = center + 1;
}
}
final int index = first * 2;
final boolean mergeWithPrevious = index > 0 && value.compareTo(this.values[index - 1] + 1) == 0;
final boolean mergeWithNext = index < this.values.length && value.compareTo(this.values[index] - 1) == 0;
++this.size;
if (mergeWithPrevious && mergeWithNext) {
this.values[index - 1] = this.values[index + 1];
final int[] nValues = new int[this.values.length - 2];
System.arraycopy(this.values, 0, nValues, 0, index);
System.arraycopy(this.values, index + 2, nValues, index, this.values.length - index - 2);
this.values = nValues;
} else if (mergeWithPrevious) {
this.values[index - 1] = value.intValue();
} else if (mergeWithNext) {
this.values[index] = value.intValue();
} else {
// Create a new segment
final int[] nValues = new int[this.values.length + 2];
System.arraycopy(this.values, 0, nValues, 0, index);
nValues[index] = value.intValue();
nValues[index + 1] = nValues[index];
System.arraycopy(this.values, index, nValues, index + 2, this.values.length - index);
this.values = nValues;
}
}
return true;
}
@Override
public final boolean addAll(int index, Collection<? extends Integer> collection) {
return addAll(collection);
}
@Override
public boolean addAll(Collection<? extends Integer> collection) {
boolean changed = false;
for (final Integer value : collection) {
changed |= add(value);
}
return changed;
}
@Override
public void clear() {
this.values = null;
this.size = 0;
}
@Pure
@Override
public boolean contains(Object obj) {
if ((this.values != null) && (obj instanceof Number)) {
final int e = ((Number) obj).intValue();
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
if (e < this.values[idxStart]) {
return false;
}
if ((e >= this.values[idxStart]) && (e <= this.values[idxStart + 1])) {
return true;
}
}
}
return false;
}
@Pure
@Override
public boolean containsAll(Collection<?> collection) {
if (this.values == null) {
return false;
}
final SortedSet<Integer> elements = new TreeSet<>();
for (final Object o : collection) {
if (o instanceof Number) {
elements.add(((Number) o).intValue());
}
}
int idxStart = 0;
for (final Integer e : elements) {
for (; idxStart < this.values.length - 1; idxStart += 2) {
if (e < this.values[idxStart]) {
return false;
}
if ((e >= this.values[idxStart]) && (e <= this.values[idxStart + 1])) {
break;
}
}
}
return true;
}
@Pure
@Override
public boolean isEmpty() {
return this.size == 0;
}
@Pure
@Override
public Iterator<Integer> iterator() {
return new IntegerListIterator();
}
/** Returns an iterator over the segments in this list in proper sequence.
*
* @return an iterator over the segments in this list in proper sequence
*/
@Pure
public Iterator<IntegerSegment> segmentIterator() {
return new SegmentIterator();
}
/** Returns an iterable object over the segments in this list in proper sequence.
*
* @return an iterable object over the segments in this list in proper sequence
*/
@Pure
public Iterable<IntegerSegment> toSegmentIterable() {
return new Iterable<IntegerSegment>() {
@Override
public Iterator<IntegerSegment> iterator() {
return new SegmentIterator();
}
};
}
@Override
public boolean remove(Object obj) {
if ((this.values != null) && (obj instanceof Number)) {
final int e = ((Number) obj).intValue();
final int segmentIndex = getSegmentIndexFor(e);
if (segmentIndex >= 0 && removeElementInSegment(segmentIndex, e)) {
return true;
}
}
return false;
}
@Override
public Integer remove(int index) {
if (this.values == null) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
int firstIndex = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
final int endIndex = this.values[idxStart + 1] - this.values[idxStart] + firstIndex;
if ((index >= firstIndex) && (index <= endIndex)) {
final int elementToRemove = this.values[idxStart] + index - firstIndex;
if (!removeElementInSegment(idxStart, elementToRemove)) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
return elementToRemove;
}
firstIndex = endIndex + 1;
}
throw new IndexOutOfBoundsException(Integer.toString(index));
}
/** Remove the {@code element} in the segment starting at index
* {@code segmentIndex}.
*
* @param segmentIndex is the index of the segment from which the
* element must be removed.
* @param element is the element to remove.
* @return <code>true</code> if the element was removed, otherwhise <code>false</code>
*/
protected boolean removeElementInSegment(int segmentIndex, int element) {
if ((element == this.values[segmentIndex]) && (element == this.values[segmentIndex + 1])) {
// Remove the segment
if (this.values.length == 2) {
this.values = null;
this.size = 0;
} else {
final int[] newTab = new int[this.values.length - 2];
System.arraycopy(this.values, 0, newTab, 0, segmentIndex);
System.arraycopy(this.values, segmentIndex + 2, newTab, segmentIndex, newTab.length - segmentIndex);
this.values = newTab;
--this.size;
}
return true;
}
if ((element >= this.values[segmentIndex]) && (element <= this.values[segmentIndex + 1])) {
if (element == this.values[segmentIndex]) {
// Move the lower bound
++this.values[segmentIndex];
--this.size;
} else if (element == this.values[segmentIndex + 1]) {
// Move the upper bound
--this.values[segmentIndex + 1];
--this.size;
} else {
// Split the segment
final int[] newTab = new int[this.values.length + 2];
System.arraycopy(this.values, 0, newTab, 0, segmentIndex + 1);
System.arraycopy(this.values, segmentIndex + 1, newTab, segmentIndex + 3, newTab.length - segmentIndex - 3);
newTab[segmentIndex + 1] = element - 1;
newTab[segmentIndex + 2] = element + 1;
this.values = newTab;
--this.size;
}
return true;
}
return false;
}
/** Remove the {@code element} in the segment starting at index
* {@code segmentIndex}.
*
* @param segmentIndex is the index of the segment to remove.
* @return <code>true</code> if the element was removed, otherwhise <code>false</code>
*/
protected boolean removeSegment(int segmentIndex) {
if ((this.values == null) || (segmentIndex < 0)
|| (segmentIndex >= this.values.length - 1)) {
return false;
}
if (this.values.length == 2) {
this.values = null;
this.size = 0;
} else {
final int count = this.values[segmentIndex + 1] - this.values[segmentIndex] + 1;
final int[] newTab = new int[this.values.length - 2];
System.arraycopy(this.values, 0, newTab, 0, segmentIndex);
System.arraycopy(this.values, segmentIndex + 2, newTab, segmentIndex, this.values.length - segmentIndex);
this.values = newTab;
this.size -= count;
}
return true;
}
@Override
public boolean removeAll(Collection<?> collection) {
boolean changed = false;
for (final Object o : collection) {
changed |= remove(o);
}
return changed;
}
@Override
public boolean retainAll(Collection<?> collection) {
final SortedSet<Integer> theset = toSortedSet();
final boolean changed = theset.retainAll(collection);
if (changed) {
set(theset);
}
return changed;
}
@Override
public Integer set(int index, Integer element) {
final Integer oldValue = remove(index);
add(element);
return oldValue;
}
/** Set the content of this list from the specified collection.
*
* @param collection is the values to pout inside this list.
*/
public void set(SortedSet<? extends Number> collection) {
this.values = null;
this.size = 0;
for (final Number number : collection) {
final int e = number.intValue();
if ((this.values != null) && (e == this.values[this.values.length - 1] + 1)) {
// Same group
++this.values[this.values.length - 1];
++this.size;
}
if ((this.values != null) && (e > this.values[this.values.length - 1] + 1)) {
// Create a new group
final int[] newTab = new int[this.values.length + 2];
System.arraycopy(this.values, 0, newTab, 0, this.values.length);
newTab[newTab.length - 2] = e;
newTab[newTab.length - 1] = newTab[newTab.length - 2];
this.values = newTab;
++this.size;
} else if (this.values == null) {
// Add the first group
this.values = new int[] {e, e};
this.size = 1;
}
}
}
@Pure
@Override
public int size() {
return this.size;
}
/**
* Replies the count of segments.
*
* @return the count of segments.
*/
protected int getSegmentCount() {
return (this.values == null) ? 0 : (this.values.length / 2);
}
/**
* Replies the last value of a segment.
*
* @param idxSegment is the index of the segment. It must be a multiple of 2.
* @return the last value in the segment.
*/
protected int getLastValueOnSegment(int idxSegment) {
assert idxSegment % 2 == 0;
if (this.values == null) {
throw new IndexOutOfBoundsException();
}
return this.values[idxSegment + 1];
}
/**
* Replies the first value of a segment.
*
* @param idxSegment is the index of the segment. It must be a multiple of 2.
* @return the first value in the segment
*/
protected int getFirstValueOnSegment(int idxSegment) {
assert idxSegment % 2 == 0;
if (this.values == null) {
throw new IndexOutOfBoundsException();
}
return this.values[idxSegment];
}
/**
* Replies the segment index for the specified value.
*
* @param value is the value to search for.
* @return the index or <code>-1</code>. It is a multiple of 2.
*/
protected int getSegmentIndexFor(int value) {
if (this.values != null) {
int first = 0;
int last = getSegmentCount() - 1;
while (first <= last) {
final int center = (first + last) / 2;
final int min = this.values[center * 2];
final int max = this.values[center * 2 + 1];
if (value >= min && value <= max) {
return center * 2;
}
if (value < min) {
last = center - 1;
} else {
first = center + 1;
}
}
}
return -1;
}
@Override
@Pure
public Integer get(int index) {
if (this.values == null) {
throw new IndexOutOfBoundsException(Integer.toString(index));
}
int firstIndex = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
final int endIndex = this.values[idxStart + 1] - this.values[idxStart] + firstIndex;
if ((index >= firstIndex) && (index <= endIndex)) {
return this.values[idxStart] + index - firstIndex;
}
firstIndex = endIndex + 1;
}
throw new IndexOutOfBoundsException(Integer.toString(index));
}
/**
* Replies the segment index for the specified value.
*
* <p>The given array must be pre-allocated with at least 2 cells.
* The first cell will the the index of the segment. The
* second cell will be the first integer value.
*
* @param offset is the number of integer values to skip from the
* begining of the value set.
* @param tofill is the 2-cell array to fill
* @return <code>true</code> on success, <code>false</code> otherwise.
*/
protected boolean get(int offset, int[] tofill) {
if (this.values != null) {
int idxTab = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
if (offset == idxTab) {
tofill[0] = idxStart;
tofill[1] = n;
return true;
}
++idxTab;
}
}
}
tofill[0] = -1;
tofill[1] = 0;
return false;
}
@Override
@Pure
public Object[] toArray() {
final Object[] tab = new Object[this.size];
if (this.values != null) {
int idxTab = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
tab[idxTab++] = n;
}
}
}
return tab;
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] array) {
final Class<T> clazz = (Class<T>) array.getClass().getComponentType();
if (!clazz.isAssignableFrom(Integer.class)) {
throw new ArrayStoreException();
}
T[] tab = array;
if (tab.length < this.size) {
tab = (T[]) Array.newInstance(clazz, this.size);
}
if (this.values != null) {
int idxTab = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
tab[idxTab++] = (T) Integer.valueOf(n);
}
}
}
return tab;
}
/**
* Returns an array containing all of the elements in this list in proper
* sequence (from first to last element).
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must
* allocate a new array even if this list is backed by an array).
* The caller is thus free to modify the returned array.
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* @return an array containing all of the elements in this list in proper
* sequence
* @see Arrays#asList(Object[])
*/
@Pure
public int[] toIntArray() {
final int[] tab = new int[this.size];
if (this.values != null) {
int idxTab = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
tab[idxTab++] = n;
}
}
}
return tab;
}
/** Replies the complete list of stored integers.
*
* @return the set of values.
*/
@Pure
public SortedSet<Integer> toSortedSet() {
final SortedSet<Integer> theset = new TreeSet<>();
if (this.values != null) {
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
theset.add(n);
}
}
}
return theset;
}
@Pure
@Override
public int indexOf(Object obj) {
if (obj instanceof Number) {
final int e = ((Number) obj).intValue();
if (this.values != null) {
int idx = 0;
for (int idxStart = 0; idxStart < this.values.length - 1; idxStart += 2) {
for (int n = this.values[idxStart]; n <= this.values[idxStart + 1]; ++n) {
if (n == e) {
return idx;
}
++idx;
}
}
}
return -1;
}
if (obj == null) {
throw new NullPointerException();
}
throw new ClassCastException();
}
@Pure
@Override
public int lastIndexOf(Object obj) {
if (obj instanceof Number) {
final int e = ((Number) obj).intValue();
if (this.values != null) {
int idx = this.size - 1;
for (int idxStart = this.values.length - 2; idxStart >= 0; idxStart -= 2) {
for (int n = this.values[idxStart + 1]; n >= this.values[idxStart]; --n) {
if (n == e) {
return idx;
}
--idx;
}
}
}
return -1;
}
if (obj == null) {
throw new NullPointerException();
}
throw new ClassCastException();
}
@Pure
@Override
public ListIterator<Integer> listIterator() {
return new IntegerListIterator();
}
@Pure
@Override
public ListIterator<Integer> listIterator(int index) {
return new IntegerListIterator(index);
}
@Pure
@Override
public List<Integer> subList(int fromIndex, int toIndex) {
final List<Integer> theList = new ArrayList<>();
if (this.values != null) {
// Search for the first matching segment
int firstSegment = -1;
int idxValue = 0;
for (int idxSegment = 0; firstSegment == -1 && idxSegment < this.values.length; idxSegment += 2) {
final int min = this.values[idxSegment];
final int max = this.values[idxSegment + 1];
final int nb = max - min + 1;
if (fromIndex < (idxValue + nb)) {
firstSegment = idxSegment;
} else {
idxValue += nb;
}
}
if (firstSegment != -1) {
// Go through the segments
for (int idxSegment = firstSegment; idxSegment < this.values.length; idxSegment += 2) {
final int min = this.values[idxSegment];
final int max = this.values[idxSegment + 1];
if (toIndex <= idxValue) {
idxSegment = this.values.length;
} else {
for (int value = min; idxValue < toIndex && value <= max; ++value) {
if (fromIndex <= idxValue) {
theList.add(value);
}
++idxValue;
}
}
}
}
}
return theList;
}
@Pure
@Override
public Spliterator<Integer> spliterator() {
return SortedSet.super.spliterator();
}
/** This class represents an iterator on lists of integers.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class IntegerListIterator implements ListIterator<Integer> {
private final int offset;
private int tabIndex;
private int segmentIndex;
private int number;
/** Construct an iterator.
*
* @param startingIndex the starting index.
*/
IntegerListIterator(int startingIndex) {
this.offset = startingIndex;
final int lsize = size();
if ((startingIndex >= 0) && (startingIndex < lsize)) {
this.tabIndex = startingIndex;
final int[] tab = new int[2];
get(startingIndex, tab);
this.segmentIndex = tab[0];
this.number = tab[1];
} else {
this.tabIndex = -1;
this.segmentIndex = -1;
}
}
/** Construct an iterator.
*/
IntegerListIterator() {
this.offset = 0;
if (isEmpty()) {
this.tabIndex = -1;
this.segmentIndex = -1;
} else {
this.tabIndex = 0;
this.segmentIndex = 0;
this.number = getFirstValueOnSegment(this.segmentIndex);
}
}
@Override
public boolean hasNext() {
return (this.tabIndex >= this.offset) && (this.tabIndex < size());
}
@Override
public int nextIndex() {
final int lsize = size();
if ((this.tabIndex >= this.offset) && (this.tabIndex < lsize)) {
return this.tabIndex;
}
return lsize;
}
@Override
public Integer next() {
if ((this.tabIndex < this.offset) || (this.tabIndex >= size())) {
throw new NoSuchElementException();
}
final int n = this.number;
final int lsize = size();
++this.tabIndex;
if ((this.tabIndex >= this.offset) && (this.tabIndex < lsize)) {
if (this.number == getLastValueOnSegment(this.segmentIndex)) {
this.segmentIndex += 2;
this.number = getFirstValueOnSegment(this.segmentIndex);
} else {
++this.number;
}
}
return n;
}
@Override
public boolean hasPrevious() {
final int idx = this.tabIndex - 1;
return (idx >= this.offset) && (idx < size());
}
@Override
public int previousIndex() {
final int idx = this.tabIndex - 1;
return ((idx >= this.offset) && (idx < size())) ? idx : -1;
}
@Override
public Integer previous() {
final int idx = this.tabIndex - 1;
if ((idx < this.offset) || (idx >= size())) {
throw new NoSuchElementException();
}
final int n = get(idx);
final int lsize = size();
--this.tabIndex;
if ((this.tabIndex >= this.offset) && (this.tabIndex < lsize)) {
if (this.number == getFirstValueOnSegment(this.segmentIndex)) {
this.segmentIndex -= 2;
this.number = getLastValueOnSegment(this.segmentIndex);
} else {
--this.number;
}
}
return n;
}
@Override
public void add(Integer value) {
if (value == this.number) {
return;
}
if (IntegerList.this.add(value) && value < this.number) {
++this.tabIndex;
this.segmentIndex = getSegmentIndexFor(this.number);
}
}
@Override
public void set(Integer vallue) {
add(vallue);
}
@Override
public void remove() {
final int lsize = size();
if ((this.tabIndex > 1) && (this.tabIndex < lsize)) {
IntegerList.this.remove(this.tabIndex - 1);
--this.tabIndex;
this.segmentIndex = getSegmentIndexFor(this.number);
}
throw new IllegalStateException();
}
}
/** This class describes a integers' segment.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class IntegerSegment {
/** First value in the segment.
*/
private final int first;
/** Last value in the segment.
*/
private final int last;
/** Construct a segment.
*
* @param first first value.
* @param last last value.
*/
IntegerSegment(int first, int last) {
this.first = first;
this.last = last;
}
/** Replies the first value in the segment.
*
* @return the first value.
*/
public int getFirst() {
return this.first;
}
/** Replies the last value in the segment.
*
* @return the last value.
*/
public int getLast() {
return this.last;
}
}
/** Iterator on segments.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
private class SegmentIterator implements Iterator<IntegerSegment> {
private int index;
private boolean removable;
/** Construct iterator.
*/
SegmentIterator() {
this.index = 0;
this.removable = false;
}
@Override
@SuppressWarnings("synthetic-access")
public boolean hasNext() {
return this.index < IntegerList.this.values.length;
}
@Override
@SuppressWarnings("synthetic-access")
public IntegerSegment next() {
if (this.index >= IntegerList.this.values.length) {
throw new ConcurrentModificationException();
}
try {
final int first = IntegerList.this.values[this.index];
final int last = IntegerList.this.values[this.index + 1];
this.index += 2;
this.removable = true;
return new IntegerSegment(first, last);
} catch (IndexOutOfBoundsException exception) {
throw new NoSuchElementException();
}
}
@Override
@SuppressWarnings("synthetic-access")
public void remove() {
if (this.removable) {
final int idx = this.index - 2;
if (idx < 0 || idx >= IntegerList.this.values.length) {
throw new ConcurrentModificationException();
}
removeSegment(idx);
this.removable = false;
} else {
throw new NoSuchElementException();
}
}
}
}