RangedList.java
/*******************************************************************************
* Copyright (c) 2021 Handy Tools for Distributed Computing (HanDist) project.
*
* This program and the accompanying materials are made available to you under
* the terms of the Eclipse Public License 1.0 which accompanies this
* distribution,
* and is available at https://www.eclipse.org/legal/epl-v10.html
*
* SPDX-License-Identifier: EPL-1.0
******************************************************************************/
package handist.collections;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import handist.collections.function.LongTBiConsumer;
/**
* Abstract class describing a list defined on long indices. Entries can be
* defined on any index contained within the {@link LongRange} used to
* initialize the collection.
*
* @param <T> type handled by the collection
*/
public abstract class RangedList<T> implements Iterable<T> {
static class Box<U> {
U val;
Box(U val) {
this.val = val;
}
}
public static boolean equals(RangedList<?> rlist1, Object o) {
if (o == null) {
return (rlist1 == null);
}
if (!(o instanceof RangedList)) {
return false;
}
final RangedList<?> rlist2 = (RangedList<?>) o;
// TODO this version is too slow,
// setupFrom will be the good candidate for fast simul scanner.
if (!rlist1.getRange().equals(rlist2.getRange())) {
return false;
}
for (final long index : rlist1.getRange()) {
if (!rlist1.get(index).equals(rlist2.get(index))) {
return false;
}
}
return true;
}
public static int hashCode(RangedList<?> rlist) {
int hashCode = 1;
// code from JavaAPI doc of List
for (final Object o : rlist) {
hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode());
}
return hashCode;
}
/**
* Returns a copy of this instance, restricted to the contents that are included
* in the specified range.
*
* @param range portion of the {@link RangedList} to copy
* @return a new RangedList which contains the entries of this instance on
* provided range
*/
public abstract RangedList<T> cloneRange(LongRange range);
/**
* Indicates if this list contains the provided object. More formally if the
* list contains at least one object {@code a} such that
* <code>(a == null) ? o == null : a.equals(o);</code> is true.
*
* @param o the object whose presence is to be checked
* @return {@code true} if the collection contains {@code o}, {@code false}
* otherwise
*/
public abstract boolean contains(Object o);
/**
* Checks if all the elements provided in the collection are present in this
* instance.
*
* @param c collection of all the elements whose presence in the RangedList is
* to be checked
* @return {@code true} if all the elements in the provided collection can be
* found in this instance, {@code false} otherwise
*/
public boolean containsAll(Collection<? extends T> c) {
for (final T t : c) {
if (!contains(t)) {
return false;
}
}
return true;
}
/**
* Performs the provided action on each element contained by this instance, and
* potentially collect/extract some information into the provided receiver.
* <p>
* The BiConsumer is applied on each element contained in the collection (first
* parameter of the BiConsumer) with the receiver provided as second parameter
* of this method as the second parameter of the BiConsumer. This allows you to
* make modifications to individual elements and potentially extract some
* information (of type U) and store it in the receiver provided as second
* parameter.
* <p>
* If you do not need to extract any information from the elements contained in
* this instance, you should use {@link #forEach(Consumer)} instead.
*
* @param <U> type of the collected instances
* @param action action to perform on each element, potentially
* @param receiver collector of information extracted
*/
public final <U> void forEach(BiConsumer<? super T, Consumer<? super U>> action, Consumer<? super U> receiver) {
forEach(getRange(), action, receiver);
}
/**
* Performs the provided action on every element in the collection
*/
@Override
public void forEach(Consumer<? super T> action) {
forEach(getRange(), action);
}
/**
* Performs the provided action on elements contained by this instance, and
* potentially collect/extract some information into the provided receiver. This
* method has the same effect as {@link #forEach(BiConsumer, Consumer)} but its
* application is restricted to the range specified as first parameter.
* <p>
* The BiConsumer is applied on each element contained in the collection (first
* parameter of the BiConsumer) with the receiver provided as second parameter
* of this method as the second parameter of the BiConsumer. This allows you to
* make modifications to individual elements and potentially extract some
* information (of type U) and store it in the receiver provided as second
* parameter.
* <p>
* If you do not need to extract any information from the elements contained in
* this instance, you should use {@link #forEach(LongRange, Consumer)} instead.
*
* @param <U> type of the collected instances
* @param range range on which the action is to be applied
* @param action action to perform on each element, potentially
* @param receiver collector of information extracted
* @see #forEach(LongRange, BiConsumer, Consumer)
*/
public final <U> void forEach(LongRange range, BiConsumer<? super T, Consumer<? super U>> action,
Consumer<? super U> receiver) {
forEachImpl(range, action, receiver);
}
protected <U> void forEachImpl(LongRange range, BiConsumer<? super T, Consumer<? super U>> action,
Consumer<? super U> receiver) {
rangeCheck(range);
Iterator<T> iter0 = subIterator(range);
while (iter0.hasNext()) {
action.accept(iter0.next(), receiver);
}
}
/**
* Applies the specified action on the elements of this collection that are
* present in the specified range. This method is similar to
* {@link #forEach(Consumer)} but the application of the specified action is
* restricted to the range specified as first parameter
*
* @param range range of application of the action
* @param action action to perform on individual elements
*/
public final void forEach(LongRange range, Consumer<? super T> action) {
forEachImpl(range, action);
}
protected void forEachImpl(LongRange range, Consumer<? super T> action) {
rangeCheck(range);
if(range.size()==0) return;
Iterator<T> iter0 = subIterator(range);
while (iter0.hasNext()) {
action.accept(iter0.next());
}
}
/**
* Applies the given action on the index/value pairs present in the specified
* range.
* <p>
* This method is almost identical to {@link #forEach(LongTBiConsumer)} but its
* application is restricted to the range of indices specified as parameter.
*
* @param range range of indices on which to apply the action
* @param action action to perform taking a long and a T as parameter
*/
public final void forEach(LongRange range, LongTBiConsumer<? super T> action) {
forEachImpl(range, action);
}
protected void forEachImpl(LongRange range, LongTBiConsumer<? super T> action) {
rangeCheck(range);
long index = range.from;
Iterator<T> iter0 = subIterator(range);
while (iter0.hasNext()) {
action.accept(index++, iter0.next());
}
}
/**
* Performs the specified action on every index/value pair contained in this
* collection
*
* @param action action to perform taking a long and a T as parameter
*/
public final void forEach(LongTBiConsumer<? super T> action) {
forEach(getRange(), action);
}
/**
* Returns the value associated with the provided {@code long} index.
*
* @param index index of the value to return.
* @return the value associated with this index
*/
public abstract T get(long index);
/**
* Obtain the {@link LongRange} on which this instance is defined.
*
* @return the {@link LongRange} object representing the
*/
public abstract LongRange getRange();
/**
* Indicates if this RangedList is empty, i.e. if it cannot contain any entry
* because it is defined on an empty {@link LongRange}.
*
* @return {@code true} is the instance is defined on an empty
* {@link LongRange}, {@code false} otherwise.
*/
public boolean isEmpty() {
return getRange().size() == 0;
}
public abstract Iterator<T> iterator();
public abstract RangedListIterator<T> listIterator();
public abstract RangedListIterator<T> listIterator(long from);
protected abstract Iterator<T> subIterator(LongRange range);
protected abstract RangedListIterator<T> subListIterator(LongRange range);
protected abstract RangedListIterator<T> subListIterator(LongRange range, long from);
/**
* Creates a new collection from the elements contained in this instance by
* transforming them into a new type
*
* @param <U> type of the collection to create
* @param func function that returns a type U from the provided T
* @return a newly created collection which contains the mapping of the elements
* contained by this instance to type U
*/
public <U> RangedList<U> map(Function<? super T, ? extends U> func) {
final Chunk<U> result = new Chunk<>(this.getRange());
result.setupFrom(this, func);
return result;
}
/**
* Creates a new collection from the elements contained in this instance on the
* specified range by transforming them into a different type
*
* @param <U> type of the collection to create
* @param range the range on which to apply the method
* @param func function that returns a type U from the provided T
* @return a newly created collection which contains the mapping of the elements
* contained by this instance (restricted to the specified range) to
* type U
*/
public <U> RangedList<U> map(LongRange range, Function<? super T, ? extends U> func) {
return this.subList(range.from, range.to).map(func);
}
/**
* Iterates on the elements of this instance and the {@code target}
* {@link RangedList} and applies the given function to each pair of this and
* {@code target} element of matching indices on the specified range
*
* @param range the range on which to apply the method
* @param target other {@link RangedList} supplying the second parameter of
* fuction {@code func}
* @param func function that receives two object (type T and U) extracted from
* two ranged list and does not return result.
* @param <U> the type handled by the {@link RangedList} given as parameter,
* second input for the function
*/
public final <U> void map(LongRange range, RangedList<U> target, BiConsumer<T,U> func) {
mapImpl(range, target, func);
}
protected <U> void mapImpl(LongRange range, RangedList<U> target, BiConsumer<T,U> func) {
rangeCheck(range);
target.rangeCheck(range);
Iterator<T> iter0 = subIterator(range);
Iterator<U> iter = target.subList(range).iterator();
while(iter0.hasNext()) {
func.accept(iter0.next(), iter.next());
}
}
public final <S, U> RangedList<U> map(LongRange range, RangedList<S> target, BiFunction<T, S, U> func) {
final Chunk<U> result = new Chunk<>(range);
rangeCheck(range);
target.rangeCheck(range);
result.setupFrom(range, this, target, func);
return result;
}
/**
* Checks if the provided {@code long index} is included in the range this
* instance is defined on, i.e. if method {@link #get(long)}, or
* {@link #set(long,Object)} can be safely called with the provided parameter.
*
* @param target the index to check
* @throws IndexOutOfBoundsException if the provided index is outside the range
* this instance is defined on
*/
public void rangeCheck(long target) {
if (!this.getRange().contains(target)) {
throw new IndexOutOfBoundsException(
"[RangedList] range mismatch: " + this.getRange() + " does not include " + target);
}
}
/**
* Checks if the provided {@link LongRange} is included in the range of this
* instance.
*
* @param target LongRange whose inclusion in this instance is to be checked
* @throws ArrayIndexOutOfBoundsException if the provided {@link LongRange} is
* not included in this instance
*/
public void rangeCheck(LongRange target) {
if (!this.getRange().contains(target)) {
throw new ArrayIndexOutOfBoundsException(
"[RangedList] range mismatch:" + this.getRange() + " must include " + target);
}
}
public T reduce(BiFunction<T, T, T> reduce) {
final Box<T> box = new Box<>(null);
forEach((T t) -> {
if (box.val == null) {
box.val = t;
} else {
box.val = reduce.apply(box.val, t);
}
});
return box.val;
}
public <U> U reduce(BiFunction<U, T, U> reduce, U zero) {
final Box<U> box = new Box<>(zero);
box.val = zero;
forEach((T t) -> {
box.val = reduce.apply(box.val, t);
});
return box.val;
}
public <U, S> U reduce(RangedList<S> source2, BiFunction<T, S, U> map, U zero, BiFunction<U, U, U> reduce) {
final Box<U> box = new Box<>(zero);
box.val = zero;
map(getRange(), source2, (T t, S s) -> {
box.val = reduce.apply(box.val, map.apply(t, s));
});
return box.val;
}
/**
* Sets the provided value at the specified index
*
* @param index index at which the value should be stored
* @param value value to store at the specified index
* @return previous value that was stored at this index, {@code null} if there
* was no previous value or the previous value stored was {@code null}
*/
public abstract T set(long index, T value);
/**
* Initializes the values in this instance by applying the provided function on
* the elements contained in {@code source}
*
* @param <S> the type handled by the {@link RangedList} given as parameter,
* input for the function
* @param source {@link RangedList} instance from which entried for this
* instance will be extracted
* @param func function that takes an object of type S as parameter and
* returns a type T
*/
public <S> void setupFrom(RangedList<S> source, Function<? super S, ? extends T> func) {
setupFrom(source.getRange(), source, func);
}
/**
* Initializes the values in the {code range} of this instance by applying the provided function on
* the elements contained in {@code source}
*
* @param <S> the type handled by the {@link RangedList} given as parameter,
* input for the function
* @param range the range where initialization are applied
* @param source {@link RangedList} instance from which entries for this
* instance will be extracted
* @param func function that takes an object of type S as parameter and
* returns a type T
*/
public final <S> void setupFrom(LongRange range, RangedList<S> source, Function<? super S, ? extends T> func) {
setupFromImpl(range, source, func);
}
protected <S> void setupFromImpl(LongRange range, RangedList<S> source, Function<? super S, ? extends T> func) {
rangeCheck(range);
source.rangeCheck(range);
RangedListIterator<T> iter0 = subListIterator(range);
Iterator<S> iter = source.subIterator(range);
while(iter0.hasNext()) {
iter0.next();
iter0.set(func.apply(iter.next()));
};
}
/**
* Initializes the values in the {code range} of this instance by applying the
* provided function on the elements contained in {@code source1} and
* {@code source2}
*
* @param <S> the first type handled by the {@link RangedList} given as
* parameter, input for the function
* @param <U> the second type handled by the {@link RangedList} given as
* parameter, input for the function
* @param range the range where initialization are applied
* @param source1 the first {@link RangedList} instance from which entries for
* this instance will be extracted
* @param source2 the second {@link RangedList} instance from which entries for
* this instance will be extracted
* @param func function that takes two objects of type S and U as parameter
* and returns a type T
*/
public final <S,U> void setupFrom(LongRange range, RangedList<S> source1, RangedList<U> source2, BiFunction<S,U,T> func) {
setupFromImpl(range, source1, source2, func);
}
protected <S,U> void setupFromImpl(LongRange range, RangedList<S> source1, RangedList<U> source2, BiFunction<S,U,T> func) {
rangeCheck(range);
source1.rangeCheck(range);
source2.rangeCheck(range);
RangedListIterator<T> iter0 = subListIterator(range);
final Iterator<S> iter1 = source1.subIterator(range);
final Iterator<U> iter2 = source2.subIterator(range);
while(iter0.hasNext()) {
iter0.next();
iter0.set(func.apply(iter1.next(), iter2.next()));
}
}
/**
* Returns the number of entries in this collection as a {@code long}
*
* @return size of the collection
*/
public long size() {
final LongRange r = getRange();
return r.to - r.from;
}
/**
* Separates this instance into multiple {@link RangedList}s using the points
* given as parameter.
* <p>
* For instance, if this instance is defined on a range [a,b) and points l, m,
* and n are given as parameter, this method will return 4 {@link RangedList}
* defined on [a,l), [l,m), [m,n), and [n,b).
* <p>
* The user will be careful to sort the points given as parameter in ascending
* order. Exceptions during the creation of {@link RangedList} will be thrown
* otherwise.
*
* @param splitPoints the points at which this instance needs to be cut
* @return this instance entries split into several {@link RangedList}
*/
public LinkedList<RangedList<T>> splitRange(long... splitPoints) {
final LinkedList<RangedList<T>> toReturn = new LinkedList<>();
final LongRange range = getRange();
long start = range.from;
for (final long split : splitPoints) {
toReturn.add(new RangedListView<>(this, new LongRange(start, split)));
start = split;
}
toReturn.add(new RangedListView<>(this, new LongRange(start, range.to)));
return toReturn;
}
/**
* Provides a RangedList of the elements contained in this instance from index
* <em>begin</em> to index <em>end</em>.
* <p>
* If the provided range exceeds the indices contained in this instance (i.e. if
* <em>begin</em> is lower than the lowest index contained in this instance, or
* if <em>end</em> is higher than the highest index contained in this instance)
* the method will return the elements it contains that fit within the provided
* range.
*
* @param begin starting index of the desired sub-list
* @param end last index of the desired sub-list (exlusive)
* @return a ranged list of the elements contained in this {@link RangedList}
* that fit in the provided range.
* @throws IllegalArgumentException if <em>begin</em> is superior to
* <em>end</em>.
* @throws IndexOutOfBoundsException if the provided range has no intersection
* with the range of this instance.
*/
public RangedList<T> subList(long begin, long end) {
if (begin > end) {
throw new IllegalArgumentException("Cannot obtain a sublist from " + begin + " to " + end);
}
final long from = Math.max(begin, getRange().from);
final long to = Math.min(end, getRange().to);
if (from > to) {
throw new IndexOutOfBoundsException("[RangedList] no intersection with [" + begin + "," + end + ")");
}
final LongRange newRange = new LongRange(from, to);
if (newRange.equals(getRange())) {
return this;
}
return new RangedListView<>(this, newRange);
}
/**
* Provides a RangedList of the elements contained in this instance on the
* specified {@link LongRange}.
* <p>
* If the provided range exceeds the indices contained in this instance (i.e. if
* lower bound of the {@link LongRange} is lower than the lowest index contained
* in this instance, or if the upper bound of the provided {@link LongRange} is
* higher than the highest index contained in this instance) the method will
* return the elements it contains that fit within the provided range.
*
* @param range range of indices of which a copy is desired
* @return a ranged list of the elements contained in this {@link RangedList}
* that fit in the provided range.
* @throws IllegalArgumentException if <em>begin</em> is superior to
* <em>end</em>.
*/
public RangedList<T> subList(LongRange range) {
return subList(range.from, range.to);
}
/**
* Returns the elements contained in this instance in an array
*
* @return array containing the objects contained in this instance
*/
public abstract Object[] toArray();
/**
* Returns the elements contained in this instance in an array
*
* @param r the range of elements to take
* @return an object array containing the elements of this instance within the
* specified range
*/
public abstract Object[] toArray(LongRange r);
/**
* Creates a Chunk containing all the elements of this instance
*
* @return a new {@link Chunk} with the same range as this instance containing
* all the elements of this instance
*/
public Chunk<T> toChunk() {
return toChunk(getRange());
}
/**
* Creates a Chunk containing the elements of this instance included in the
* specified range
*
* @param r the range of elements to create a {@link Chunk} with.
* @return a new {@link Chunk} with the specified range containing the elements
* of this instance
*/
public abstract Chunk<T> toChunk(LongRange r);
/**
* Returns the elements contained in this instance in a {@link List}. Note that
* the indices of the returned list do not reflect the long indices used in this
* implementation.
*
* @return a list containing the elements of this instance within the specified
* range
*/
public List<T> toList() {
return toList(getRange());
}
/**
* Returns the elements contained in this instance within the specified range in
* a {@link List}. Note that the indices of the returned list do not reflect the
* long indices used in this implementation.
*
* @param r the range of indices of this instance to include in the returned
* list
* @return a list containing the elements of this instance within the specified
* range
*/
public abstract List<T> toList(LongRange r);
}