Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* The MIT License
*
* Copyright (c) 2009 The Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package htsjdk.samtools.util;
import htsjdk.utils.ValidationUtils;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.BiFunction;
/**
* A Red-Black tree with intervals for keys.
* Not thread-safe, and cannot be made so.
*
* 7/24/2008: This was copied from the tedUtils package.
* IMPORTANT!!! It has been modified to use the Reseq way of
* handling coordinates (end-inclusive).
*
* @author tsharpe
*/
public class IntervalTree implements Iterable>
{
/**
* Return the number of intervals in the tree.
* @return The number of intervals.
*/
public int size()
{
return mRoot == null ? 0 : mRoot.getSize();
}
/**
* Remove all entries.
*/
public void clear()
{
mRoot = null;
}
/**
* Put a new interval into the tree (or update the value associated with an existing interval).
* If the interval is novel, the special sentinel value is returned.
* @param start The interval's start.
* @param end The interval's end.
* @param value The associated value.
* @return The old value associated with that interval, or the sentinel.
*/
public V put( final int start, final int end, final V value )
{
if ( start > end )
throw new IllegalArgumentException("Start cannot exceed end.");
V result = mSentinel;
if ( mRoot == null )
{
mRoot = new Node(start,end,value);
}
else
{
Node parent = null;
Node node = mRoot;
int cmpVal = 0;
while ( node != null )
{
parent = node; // last non-null node
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal == 0 )
{
result = parent.setValue(value);
}
else
{
if ( cmpVal < 0 )
{
mRoot = parent.insertLeft(start,end,value,mRoot);
}
else
{
mRoot = parent.insertRight(start,end,value,mRoot);
}
}
}
return result;
}
/**
* If the specified start and end positions are not already associated with a value or are
* associated with the sentinel ( see {@link #getSentinel()}, associates it with the given (non-sentinel) value.
* Otherwise, replaces the associated value with the results of the given
* remapping function, or removes if the result is equal to the sentinel value. This
* method may be of use when combining multiple values that have the same start and end position.
*
* @param start interval start position
* @param end interval end position
* @param value value to merge into the tree, must not be equal to the sentinel value
* @param remappingFunction a function that merges the new value with the existing value for the same start and end position,
* if the function returns the sentinel value then the mapping will be unset
* @return the updated value that is stored in the tree after the completion of this merge operation, this will
* be the sentinel value if nothing ended up being stored
*/
public V merge(int start, int end, V value, BiFunction super V, ? super V, ? extends V> remappingFunction) {
ValidationUtils.validateArg(!Objects.equals(value, mSentinel), "Values equal to the sentinel value may not be merged");
final V alreadyPresent = put(start, end, value);
if (!Objects.equals(alreadyPresent, mSentinel)) {
final V newComputedValue = remappingFunction.apply(value, alreadyPresent);
if (Objects.equals(newComputedValue, mSentinel)) {
remove(start, end);
} else {
put(start, end, newComputedValue);
}
return newComputedValue;
}
return value;
}
/**
* Remove an interval from the tree. If the interval does not exist in the tree the
* special sentinel value is returned.
* @param start The interval's start.
* @param end The interval's end.
* @return The value associated with that interval, or the sentinel.
*/
public V remove( final int start, final int end )
{
V result = mSentinel;
Node node = mRoot;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
result = node.getValue();
mRoot = node.remove(mRoot);
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
return result;
}
/**
* Find an interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The Node that represents that interval, or null.
*/
public Node find( final int start, final int end )
{
Node node = mRoot;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
return node;
}
/**
* Find the nth interval in the tree.
* @param idx The rank of the interval sought (from 0 to size()-1).
* @return The Node that represents the nth interval.
*/
public Node findByIndex( final int idx )
{
return Node.findByRank(mRoot,idx+1);
}
/**
* Find the rank of the specified interval. If the specified interval is not in the
* tree, then -1 is returned.
* @param start The interval's start.
* @param end The interval's end.
* @return The rank of that interval, or -1.
*/
public int getIndex( final int start, final int end )
{
return Node.getRank(mRoot,start,end) - 1;
}
/**
* Find the least interval in the tree.
* @return The earliest interval, or null if the tree is empty.
*/
public Node min()
{
Node result = null;
Node node = mRoot;
while ( node != null )
{
result = node;
node = node.getLeft();
}
return result;
}
/**
* Find the earliest interval in the tree greater than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The earliest >= interval, or null if there is none.
*/
@SuppressWarnings("null")
public Node min( final int start, final int end )
{
Node result = null;
Node node = mRoot;
int cmpVal = 0;
while ( node != null )
{
result = node;
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal > 0 )
{
result = result.getNext();
}
return result;
}
/**
* Find the earliest interval in the tree that overlaps the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The earliest overlapping interval, or null if there is none.
*/
public Node minOverlapper( final int start, final int end )
{
Node result = null;
Node node = mRoot;
if ( node != null && node.getMaxEnd() >= start )
{
while ( true )
{
if ( node.getStart() <= end && start <= node.getEnd() )
{ // this node overlaps. there might be a lesser overlapper down the left sub-tree.
// no need to consider the right sub-tree: even if there's an overlapper, if won't be minimal
result = node;
node = node.getLeft();
if ( node == null || node.getMaxEnd() < start )
break; // no left sub-tree or all nodes end too early
}
else
{ // no overlap. if there might be a left sub-tree overlapper, consider the left sub-tree.
final Node left = node.getLeft();
if ( left != null && left.getMaxEnd() >= start )
{
node = left;
}
else
{ // left sub-tree cannot contain an overlapper. consider the right sub-tree.
if ( node.getStart() > end )
break; // everything in the right sub-tree is past the end of the query interval
node = node.getRight();
if ( node == null || node.getMaxEnd() < start )
break; // no right sub-tree or all nodes end too early
}
}
}
}
return result;
}
/**
* Find the greatest interval in the tree.
* @return The latest interval, or null if the tree is empty.
*/
public Node max()
{
Node result = null;
Node node = mRoot;
while ( node != null )
{
result = node;
node = node.getRight();
}
return result;
}
/**
* Find the latest interval in the tree less than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return The latest >= interval, or null if there is none.
*/
@SuppressWarnings("null")
public Node max( final int start, final int end )
{
Node result = null;
Node node = mRoot;
int cmpVal = 0;
while ( node != null )
{
result = node;
cmpVal = node.compare(start,end);
if ( cmpVal == 0 )
{
break;
}
node = cmpVal < 0 ? node.getLeft() : node.getRight();
}
if ( cmpVal < 0 )
{
result = result.getPrev();
}
return result;
}
/**
* Return an iterator over the entire tree.
* @return An iterator.
*/
@Override
public Iterator> iterator()
{
return new FwdIterator(min());
}
/**
* Return an iterator over all intervals greater than or equal to the specified interval.
* @param start The interval's start.
* @param end The interval's end.
* @return An iterator.
*/
public Iterator> iterator( final int start, final int end )
{
return new FwdIterator(min(start,end));
}
/**
* Return an iterator over all intervals overlapping the specified range.
* @param start The range start.
* @param end The range end.
* @return An iterator.
*/
public Iterator> overlappers( final int start, final int end )
{
return new OverlapIterator(start,end);
}
/**
* Return an iterator over the entire tree that returns intervals in reverse order.
* @return An iterator.
*/
public Iterator> reverseIterator()
{
return new RevIterator(max());
}
/**
* Return an iterator over all intervals less than or equal to the specified interval, in reverse order.
* @param start The interval's start.
* @param end The interval's end.
* @return An iterator.
*/
public Iterator> reverseIterator( final int start, final int end )
{
return new RevIterator(max(start,end));
}
/**
* Get the special sentinel value that will be used to signal novelty when putting a new interval
* into the tree, or to signal "not found" when removing an interval. This is null by default.
* @return The sentinel value.
*/
public V getSentinel()
{
return mSentinel;
}
/**
* Set the special sentinel value that will be used to signal novelty when putting a new interval
* into the tree, or to signal "not found" when removing an interval.
* @param sentinel The new sentinel value.
* @return The old sentinel value.
*/
public V setSentinel( final V sentinel )
{
final V result = mSentinel;
mSentinel = sentinel;
return result;
}
/**
* This method is only for debugging.
* It verifies whether the tree is internally consistent with respect to the mMaxEnd cached on each node.
* @throws IllegalStateException If an inconsistency is detected.
*/
public void checkMaxEnds() {
if (mRoot != null) mRoot.checkMaxEnd();
}
/**
* This method draws a nested picture of the tree on System.out.
* Useful for debugging.
*/
public void printTree() {
if (mRoot != null) mRoot.printNode();
}
void removeNode( final Node node )
{
mRoot = node.remove(mRoot);
}
private Node mRoot;
private V mSentinel;
public static class Node
{
// bit-wise definitions from which the other constants are composed
public static final int HAS_LESSER_PART = 1;
public static final int HAS_OVERLAPPING_PART = 2;
public static final int HAS_GREATER_PART = 4;
public static final int IS_ADJACENT_AND_EMPTY = 0;
public static final int IS_STRICTLY_LESS = HAS_LESSER_PART; // 1
public static final int IS_SUBSET = HAS_OVERLAPPING_PART; // 2
public static final int IS_LEFT_OVERHANGING_OVERLAPPER = HAS_LESSER_PART | HAS_OVERLAPPING_PART; // 3
public static final int IS_STRICTLY_GREATER = HAS_GREATER_PART; // 4
// there is no value that equals 5, since that would imply overhanging on left and right without overlapping
public static final int IS_RIGHT_OVERHANGING_OVERLAPPER = HAS_GREATER_PART | HAS_OVERLAPPING_PART; // 6
public static final int IS_SUPERSET = HAS_LESSER_PART | HAS_OVERLAPPING_PART | HAS_GREATER_PART; // 7
Node( final int start, final int end, final V1 value )
{
mStart = start;
mEnd = end;
mValue = value;
mSize = 1;
mMaxEnd = mEnd;
mIsBlack = true;
}
Node( final Node parent, final int start, final int end, final V1 value )
{
mParent = parent;
mStart = start;
mEnd = end;
mValue = value;
mMaxEnd = mEnd;
mSize = 1;
}
public int getStart()
{
return mStart;
}
public int getEnd()
{
return mEnd;
}
public int getLength()
{
return mEnd - mStart + 1 ;
}
public int getRelationship( final Node interval )
{
int result = 0;
if ( mStart < interval.getStart() )
result = HAS_LESSER_PART;
if ( mEnd > interval.getEnd() )
result |= HAS_GREATER_PART;
if ( mStart <= interval.getEnd() && interval.getStart() <= mEnd )
result |= HAS_OVERLAPPING_PART;
return result;
}
public boolean isAdjacent( final Node interval )
{
return mStart == interval.getEnd() + 1 || mEnd + 1 == interval.getStart();
}
public V1 getValue()
{
return mValue;
}
public V1 setValue( final V1 value )
{
final V1 result = mValue;
mValue = value;
return result;
}
int getSize()
{
return mSize;
}
int getMaxEnd()
{
return mMaxEnd;
}
Node getLeft()
{
return mLeft;
}
Node insertLeft( final int start, final int end, final V1 value, final Node root )
{
mLeft = new Node(this,start,end,value);
return insertFixup(mLeft,root);
}
Node getRight()
{
return mRight;
}
Node insertRight( final int start, final int end, final V1 value, final Node root )
{
mRight = new Node(this,start,end,value);
return insertFixup(mRight,root);
}
Node getNext()
{
Node result;
if ( mRight != null )
{
result = mRight;
while ( result.mLeft != null )
{
result = result.mLeft;
}
}
else
{
Node node = this;
result = mParent;
while ( result != null && node == result.mRight )
{
node = result;
result = result.mParent;
}
}
return result;
}
Node getPrev()
{
Node result;
if ( mLeft != null )
{
result = mLeft;
while ( result.mRight != null )
{
result = result.mRight;
}
}
else
{
Node node = this;
result = mParent;
while ( result != null && node == result.mLeft )
{
node = result;
result = result.mParent;
}
}
return result;
}
boolean wasRemoved()
{
return mSize == 0;
}
Node remove( Node root )
{
if ( mSize == 0 )
{
throw new IllegalStateException("Entry was already removed.");
}
if ( mLeft == null )
{
if ( mRight == null )
{ // no children
if ( mParent == null )
{
root = null;
}
else if ( mParent.mLeft == this )
{
mParent.mLeft = null;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,null,root);
}
else
{
mParent.mRight = null;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,null,root);
}
}
else
{ // single child on right
root = spliceOut(mRight,root);
}
}
else if ( mRight == null )
{ // single child on left
root = spliceOut(mLeft,root);
}
else
{ // two children
final Node next = getNext();
root = next.remove(root);
// put next into tree in same position as this, effectively removing this
if ( (next.mParent = mParent) == null )
root = next;
else if ( mParent.mLeft == this )
mParent.mLeft = next;
else
mParent.mRight = next;
if ( (next.mLeft = mLeft) != null )
{
mLeft.mParent = next;
}
if ( (next.mRight = mRight) != null )
{
mRight.mParent = next;
}
next.mIsBlack = mIsBlack;
next.mSize = mSize;
// PIC-123 fix
fixup(next);
}
mSize = 0;
return root;
}
// backwards comparison! compares start+end to this.
int compare( final int start, final int end )
{
int result = 0;
if ( start > mStart )
result = 1;
else if ( start < mStart )
result = -1;
else if ( end > mEnd )
result = 1;
else if ( end < mEnd )
result = -1;
return result;
}
@SuppressWarnings("null")
static Node getNextOverlapper( Node node, final int start, final int end )
{
do
{
Node nextNode = node.mRight;
if ( nextNode != null && nextNode.mMaxEnd >= start )
{
node = nextNode;
while ( (nextNode = node.mLeft) != null && nextNode.mMaxEnd >= start )
node = nextNode;
}
else
{
nextNode = node;
while ( (node = nextNode.mParent) != null && node.mRight == nextNode )
nextNode = node;
}
if ( node != null && node.mStart > end )
node = null;
}
while ( node != null && !(node.mStart <= end && start <= node.mEnd) );
return node;
}
static Node findByRank( Node node, int rank )
{
while ( node != null )
{
final int nodeRank = node.getRank();
if ( rank == nodeRank )
break;
if ( rank < nodeRank )
{
node = node.mLeft;
}
else
{
node = node.mRight;
rank -= nodeRank;
}
}
return node;
}
static int getRank( Node node, final int start, final int end )
{
int rank = 0;
while ( node != null )
{
final int cmpVal = node.compare(start,end);
if ( cmpVal < 0 )
{
node = node.mLeft;
}
else
{
rank += node.getRank();
if ( cmpVal == 0 )
return rank; // EARLY RETURN!!!
node = node.mRight;
}
}
return 0;
}
private int getRank()
{
int result = 1;
if ( mLeft != null )
result = mLeft.mSize + 1;
return result;
}
private Node spliceOut( final Node child, Node root )
{
if ( (child.mParent = mParent) == null )
{
root = child;
child.mIsBlack = true;
}
else
{
if ( mParent.mLeft == this )
mParent.mLeft = child;
else
mParent.mRight = child;
fixup(mParent);
if ( mIsBlack )
root = removeFixup(mParent,child,root);
}
return root;
}
private Node rotateLeft( Node root )
{
final Node child = mRight;
final int childSize = child.mSize;
child.mSize = mSize;
mSize -= childSize;
if ( (mRight = child.mLeft) != null )
{
mRight.mParent = this;
mSize += mRight.mSize;
}
if ( (child.mParent = mParent) == null )
root = child;
else if ( this == mParent.mLeft )
mParent.mLeft = child;
else
mParent.mRight = child;
child.mLeft = this;
mParent = child;
setMaxEnd();
child.setMaxEnd();
return root;
}
private Node rotateRight( Node root )
{
final Node child = mLeft;
final int childSize = child.mSize;
child.mSize = mSize;
mSize -= childSize;
if ( (mLeft = child.mRight) != null )
{
mLeft.mParent = this;
mSize += mLeft.mSize;
}
if ( (child.mParent = mParent) == null )
root = child;
else if ( this == mParent.mLeft )
mParent.mLeft = child;
else
mParent.mRight = child;
child.mRight = this;
mParent = child;
setMaxEnd();
child.setMaxEnd();
return root;
}
private void setMaxEnd()
{
mMaxEnd = mEnd;
if ( mLeft != null )
mMaxEnd = Math.max(mMaxEnd,mLeft.mMaxEnd);
if ( mRight != null )
mMaxEnd = Math.max(mMaxEnd,mRight.mMaxEnd);
}
private static void fixup( Node node )
{
do
{
node.mSize = 1;
node.mMaxEnd = node.mEnd;
if ( node.mLeft != null )
{
node.mSize += node.mLeft.mSize;
node.mMaxEnd = Math.max(node.mMaxEnd,node.mLeft.mMaxEnd);
}
if ( node.mRight != null )
{
node.mSize += node.mRight.mSize;
node.mMaxEnd = Math.max(node.mMaxEnd,node.mRight.mMaxEnd);
}
}
while ( (node = node.mParent) != null );
}
private static Node insertFixup( Node daughter, Node root )
{
Node mom = daughter.mParent;
fixup(mom);
while( mom != null && !mom.mIsBlack )
{
final Node gramma = mom.mParent;
Node auntie = gramma.mLeft;
if ( auntie == mom )
{
auntie = gramma.mRight;
if ( auntie != null && !auntie.mIsBlack )
{
mom.mIsBlack = true;
auntie.mIsBlack = true;
gramma.mIsBlack = false;
daughter = gramma;
}
else
{
if ( daughter == mom.mRight )
{
root = mom.rotateLeft(root);
mom = daughter;
}
mom.mIsBlack = true;
gramma.mIsBlack = false;
root = gramma.rotateRight(root);
break;
}
}
else
{
if ( auntie != null && !auntie.mIsBlack )
{
mom.mIsBlack = true;
auntie.mIsBlack = true;
gramma.mIsBlack = false;
daughter = gramma;
}
else
{
if ( daughter == mom.mLeft )
{
root = mom.rotateRight(root);
mom = daughter;
}
mom.mIsBlack = true;
gramma.mIsBlack = false;
root = gramma.rotateLeft(root);
break;
}
}
mom = daughter.mParent;
}
root.mIsBlack = true;
return root;
}
private static Node removeFixup( Node parent, Node node, Node root )
{
do
{
if ( node == parent.mLeft )
{
Node sister = parent.mRight;
if ( !sister.mIsBlack )
{
sister.mIsBlack = true;
parent.mIsBlack = false;
root = parent.rotateLeft(root);
sister = parent.mRight;
}
if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
{
sister.mIsBlack = false;
node = parent;
}
else
{
if ( sister.mRight == null || sister.mRight.mIsBlack )
{
sister.mLeft.mIsBlack = true;
sister.mIsBlack = false;
root = sister.rotateRight(root);
sister = parent.mRight;
}
sister.mIsBlack = parent.mIsBlack;
parent.mIsBlack = true;
sister.mRight.mIsBlack = true;
root = parent.rotateLeft(root);
node = root;
}
}
else
{
Node sister = parent.mLeft;
if ( !sister.mIsBlack )
{
sister.mIsBlack = true;
parent.mIsBlack = false;
root = parent.rotateRight(root);
sister = parent.mLeft;
}
if ( (sister.mLeft == null || sister.mLeft.mIsBlack) && (sister.mRight == null || sister.mRight.mIsBlack) )
{
sister.mIsBlack = false;
node = parent;
}
else
{
if ( sister.mLeft == null || sister.mLeft.mIsBlack )
{
sister.mRight.mIsBlack = true;
sister.mIsBlack = false;
root = sister.rotateLeft(root);
sister = parent.mLeft;
}
sister.mIsBlack = parent.mIsBlack;
parent.mIsBlack = true;
sister.mLeft.mIsBlack = true;
root = parent.rotateRight(root);
node = root;
}
}
parent = node.mParent;
}
while ( parent != null && node.mIsBlack );
node.mIsBlack = true;
return root;
}
public void checkMaxEnd() {
if (mMaxEnd != calcMaxEnd()) {
throw new IllegalStateException("Max end mismatch " + mMaxEnd + " vs " + calcMaxEnd() + ": " + this);
}
if (mLeft != null) mLeft.checkMaxEnd();
if (mRight != null) mRight.checkMaxEnd();
}
private int calcMaxEnd() {
int end = mEnd;
if (mLeft != null) end = Math.max(end, mLeft.mMaxEnd);
if (mRight != null) end = Math.max(end, mRight.mMaxEnd);
return end;
}
public void printNode() {
this.printNodeInternal("", "root: ");
}
private void printNodeInternal(final String padding, final String tag) {
System.out.println(padding + tag + " " + this);
if (mLeft != null) mLeft.printNodeInternal(padding + " ", "left: ");
if (mRight != null) mRight.printNodeInternal(padding + " ", "right:");
}
public String toString() {
return "Node(" + mStart + "," + mEnd + "," + mValue + "," + mSize + "," + mMaxEnd + "," + mIsBlack + ")";
}
private Node mParent;
private Node mLeft;
private Node mRight;
private final int mStart;
private final int mEnd;
private V1 mValue;
private int mSize;
private int mMaxEnd;
private boolean mIsBlack;
}
public class FwdIterator
implements Iterator>
{
public FwdIterator( final Node node )
{
mNext = node;
}
@Override
public boolean hasNext()
{
return mNext != null;
}
@Override
public Node next()
{
if ( mNext == null )
{
throw new NoSuchElementException("No next element.");
}
if ( mNext.wasRemoved() )
{
mNext = min(mNext.getStart(),mNext.getEnd());
if ( mNext == null )
throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
}
mLast = mNext;
mNext = mNext.getNext();
return mLast;
}
@Override
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node mNext;
private Node mLast;
}
public class RevIterator
implements Iterator>
{
public RevIterator( final Node node )
{
mNext = node;
}
@Override
public boolean hasNext()
{
return mNext != null;
}
@Override
public Node next()
{
if ( mNext == null )
throw new NoSuchElementException("No next element.");
if ( mNext.wasRemoved() )
{
mNext = max(mNext.getStart(),mNext.getEnd());
if ( mNext == null )
throw new ConcurrentModificationException("Current element was removed, and there are no more elements.");
}
mLast = mNext;
mNext = mNext.getPrev();
return mLast;
}
@Override
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node mNext;
private Node mLast;
}
public class OverlapIterator
implements Iterator>
{
public OverlapIterator( final int start, final int end )
{
mNext = minOverlapper(start,end);
mStart = start;
mEnd = end;
}
@Override
public boolean hasNext()
{
return mNext != null;
}
@Override
public Node next()
{
if ( mNext == null )
{
throw new NoSuchElementException("No next element.");
}
if ( mNext.wasRemoved() )
{
throw new ConcurrentModificationException("Current element was removed.");
}
mLast = mNext;
mNext = Node.getNextOverlapper(mNext,mStart,mEnd);
return mLast;
}
@Override
public void remove()
{
if ( mLast == null )
{
throw new IllegalStateException("No entry to remove.");
}
removeNode(mLast);
mLast = null;
}
private Node mNext;
private Node mLast;
private final int mStart;
private final int mEnd;
}
public static class ValuesIterator
implements Iterator
{
public ValuesIterator( final Iterator> itr )
{
mItr = itr;
}
@Override
public boolean hasNext()
{
return mItr.hasNext();
}
@Override
public V1 next()
{
return mItr.next().getValue();
}
@Override
public void remove()
{
mItr.remove();
}
private final Iterator> mItr;
}
}