blob: fb11c1ee30631e5a3e1f517d05f21fcc671c9f21 [file] [log] [blame]
package accord.api;
import accord.topology.KeyRanges;
import accord.txn.Keys;
import com.google.common.base.Preconditions;
import java.util.Objects;
/**
* A range of keys
* @param <K>
*/
public abstract class KeyRange<K extends Key<K>>
{
public static abstract class EndInclusive<K extends Key<K>> extends KeyRange<K>
{
public EndInclusive(K start, K end)
{
super(start, end);
}
@Override
public int compareKey(K key)
{
if (key.compareTo(start()) <= 0)
return -1;
if (key.compareTo(end()) > 0)
return 1;
return 0;
}
@Override
public boolean startInclusive()
{
return false;
}
@Override
public boolean endInclusive()
{
return true;
}
@Override
public KeyRange<K> tryMerge(KeyRange<K> that)
{
return KeyRange.tryMergeExclusiveInclusive(this, that);
}
}
public static abstract class StartInclusive<K extends Key<K>> extends KeyRange<K>
{
public StartInclusive(K start, K end)
{
super(start, end);
}
@Override
public int compareKey(K key)
{
if (key.compareTo(start()) < 0)
return -1;
if (key.compareTo(end()) >= 0)
return 1;
return 0;
}
@Override
public boolean startInclusive()
{
return true;
}
@Override
public boolean endInclusive()
{
return false;
}
@Override
public KeyRange<K> tryMerge(KeyRange<K> that)
{
return KeyRange.tryMergeExclusiveInclusive(this, that);
}
}
private static <K extends Key<K>> KeyRange<K> tryMergeExclusiveInclusive(KeyRange<K> left, KeyRange<K> right)
{
if (left.getClass() != right.getClass())
return null;
Preconditions.checkArgument(left instanceof EndInclusive || left instanceof StartInclusive);
int cmp = left.compareIntersecting(right);
if (cmp == 0)
return left.subRange(left.start.compareTo(right.start) < 0 ? left.start : right.start,
left.end.compareTo(right.end) > 0 ? left.end : right.end);
if (cmp > 0 && right.end.equals(left.start))
return left.subRange(right.start, left.end);
if (cmp < 0 && left.end.equals(right.start))
return left.subRange(left.start, right.end);
return null;
}
private final K start;
private final K end;
private KeyRange(K start, K end)
{
Preconditions.checkArgument(start.compareTo(end) < 0);
this.start = start;
this.end = end;
}
public final K start()
{
return start;
}
public final K end()
{
return end;
}
public abstract boolean startInclusive();
public abstract boolean endInclusive();
/**
* Return a new range covering this and the given range if the ranges are intersecting or touching. That is,
* no keys can exist between the touching ends of the range.
*/
public abstract KeyRange<K> tryMerge(KeyRange<K> that);
public abstract KeyRange<K> subRange(K start, K end);
/**
* Split this range into roughly equally sized subranges
* @param count the number of subranges to create
*/
public abstract KeyRanges split(int count);
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyRange<?> that = (KeyRange<?>) o;
return Objects.equals(start, that.start) && Objects.equals(end, that.end);
}
@Override
public int hashCode()
{
return Objects.hash(start, end);
}
@Override
public String toString()
{
return "Range[" + start + ", " + end + ']';
}
/**
* Returns a negative integer, zero, or a positive integer as the provided key is less than, contained by,
* or greater than this range.
*/
public abstract int compareKey(K key);
public boolean containsKey(K key)
{
return compareKey(key) == 0;
}
/**
* Returns a negative integer, zero, or a positive integer if both points of the provided range are less than, the
* range intersects this range, or both points are greater than this range
*/
public int compareIntersecting(KeyRange<K> that)
{
if (this.start.compareTo(that.end) >= 0)
return 1;
if (this.end.compareTo(that.start) <= 0)
return -1;
return 0;
}
public boolean fullyContains(KeyRange<K> that)
{
return that.start.compareTo(this.start) >= 0 && that.end.compareTo(this.end) <= 0;
}
public boolean intersects(Keys keys)
{
return lowKeyIndex(keys) >= 0;
}
/**
* Returns a range covering the overlapping parts of this and the provided range, returns
* null if the ranges do not overlap
*/
public KeyRange<K> intersection(KeyRange<K> that)
{
if (this.compareIntersecting(that) != 0)
return null;
K start = this.start.compareTo(that.start) > 0 ? this.start : that.start;
K end = this.end.compareTo(that.end) < 0 ? this.end : that.end;
return subRange(start, end);
}
/**
* returns the index of the first key larger than what's covered by this range
*/
public int higherKeyIndex(Keys keys, int lowerBound, int upperBound)
{
int i = keys.search(lowerBound, upperBound, this,
(k, r) -> ((KeyRange) r).compareKey((Key) k) <= 0 ? -1 : 1);
if (i < 0) i = -1 - i;
return i;
}
public int higherKeyIndex(Keys keys)
{
return higherKeyIndex(keys, 0, keys.size());
}
/**
* returns the index of the lowest key contained in this range. If the keys object contains no intersecting
* keys, <code>(-(<i>insertion point</i>) - 1)</code> is returned. Where <i>insertion point</i> is where an
* intersecting key would be inserted into the keys array
* @param keys
*/
public int lowKeyIndex(Keys keys, int lowerBound, int upperBound)
{
if (keys.isEmpty()) return -1;
int i = keys.search(lowerBound, upperBound, this,
(k, r) -> ((KeyRange) r).compareKey((Key) k) < 0 ? -1 : 1);
int minIdx = -1 - i;
return (minIdx < keys.size() && containsKey((K) keys.get(minIdx))) ? minIdx : i;
}
public int lowKeyIndex(Keys keys)
{
return lowKeyIndex(keys, 0, keys.size());
}
}