package org.torrent.internal.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Helper class representing a set of ranges containing values. This class
* provides the functionality to store and find ranges containing the same data.
*
* @author dante
*
*/
public class Partitions<T> implements SparseArray<T> {
private Partitions<T> parent, a, b;
private Range range;
private T content;
private class MyEntry implements Entry<T> {
private T value;
private Range range;
public MyEntry(Range range, T value) {
super();
this.range = range;
this.value = value;
}
@Override
public Range getRange() {
return range;
}
@Override
public T getValue() {
return value;
}
@Override
public String toString() {
return "{Range: " + range + ", value: " + value + "}";
}
}
/**
* Creates a new Partition with the given range containing the given value.
*
* @param r
* @param base
*/
public Partitions(Range r, T base) {
Validator.notNull(r, "Range is null!");
range = r;
content = base;
}
private Partitions(Partitions<T> p, Range range, T val) {
parent = p;
this.range = range;
content = val;
}
private boolean isLeaf() {
return a == null;
}
private boolean isRoot() {
return parent == null;
}
/**
* Returns the range in which partitions can lie.
*
* @return
*/
public Range getRange() {
return range;
}
@Override
public void put(Range r, T value) {
Validator.notNull(r, "Range is null!");
if (!r.intersects(range)) {
return;
}
if (r.contains(range)) {
content = value;
a = b = null;
if (!isRoot()) {
parent.checkMergeChildren();
}
return;
}
// Don't create children unless necessary
if (isLeaf() && sameValue(value, content)) {
return;
}
// If this is a leaf, create children first and then propagate insert
// (if necessary)
if (isLeaf()) {
long m = (range.getStart() + range.getEnd()) / 2;
a = new Partitions<T>(this, Range.getRangeByNumbers(range
.getStart(), m), content);
b = new Partitions<T>(this, Range.getRangeByNumbers(m + 1, range
.getEnd()), content);
}
a.put(r, value);
// If b is null then a and b were successfully merged
if (b != null) {
b.put(r, value);
}
}
@Override
public Range findFirst(Range r, T val) {
Validator.notNull(r, "Range is null!");
if (!range.intersects(r)) {
return null;
}
if (isLeaf()) {
if (sameValue(content, val)) {
return Range.getRangeByNumbers(Math.max(r.getStart(), range
.getStart()), Math.min(r.getEnd(), range.getEnd()));
} else {
return null;
}
}
Range ra = a.findFirst(r, val);
Range rb = b.findFirst(r, val);
if (ra == null)
return rb;
if (rb == null)
return ra;
if (ra.getEnd() + 1 == rb.getStart()) {
return Range.getRangeByNumbers(ra.getStart(), rb.getEnd());
}
return ra;
}
private void checkMergeChildren() {
if (a.isLeaf() && b.isLeaf() && sameValue(a.content, b.content)) {
content = a.content;
a = null;
b = null;
if (!isRoot()) {
parent.checkMergeChildren();
}
}
}
private boolean sameValue(T a, T b) {
return (a == null && b == null) || (a != null && a.equals(b));
}
/**
* Counts the number of occurrences of the given value in the given range.
*
* @param r
* @param val
* @return
*/
public long count(Range r, T val) {
Validator.notNull(r, "Range is null!");
if (isLeaf()) {
if (!sameValue(content, val)) {
return 0;
}
return range.intersectionLength(r);
}
return a.count(r, val) + b.count(r, val);
}
@Override
public Range findFirst(T value) {
return findFirst(getRange(), value);
}
@Override
public T get(long pos) {
Validator.isTrue(range.contains(pos), "Invalid position: " + pos);
return getLeafAt(pos).content;
}
private Partitions<T> getLeafAt(long pos) {
if (isLeaf()) {
return this;
}
if (a.getRange().contains(pos)) {
return a.getLeafAt(pos);
} else {
return b.getLeafAt(pos);
}
}
@Override
public void set(long pos, T value) {
Validator.isTrue(range.contains(pos), "Invalid position: " + pos);
put(Range.getRangeByLength(pos, 1), value);
}
@Override
public List<Entry<T>> getValues(Range in) {
Validator.notNull(in, "Range is null");
Validator.isTrue(range.contains(in), "Range out of bounds: " + in);
List<Entry<T>> list = new ArrayList<Entry<T>>();
for (Iterator<Entry<T>> i = iterator(in); i.hasNext();) {
list.add(i.next());
}
return list;
}
@Override
public List<Entry<T>> getValues() {
return getValues(getRange());
}
@Override
public Iterator<org.torrent.internal.util.SparseArray.Entry<T>> iterator(
final Range r) {
Validator.notNull(r, "Range is null");
Validator.isTrue(range.contains(r), "Range out of bounds: " + r);
return new Iterator<Entry<T>>() {
Range remaining = r;
@Override
public boolean hasNext() {
return remaining.getLength() > 0;
}
@Override
public org.torrent.internal.util.SparseArray.Entry<T> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Partitions<T> cur = getLeafAt(remaining.getStart());
T val = cur.content;
Range res = cur.getRange();
while (remaining.getLength() > 0) {
if (cur.getRange().getEnd() >= remaining.getEnd()) {
remaining = Range.getRangeByLength(0, 0);
continue;
}
remaining = Range.getRangeByNumbers(
cur.getRange().getEnd() + 1, remaining.getEnd());
cur = getLeafAt(remaining.getStart());
if (!sameValue(cur.content, val)) {
break;
}
res = Range.getRangeByNumbers(res.getStart(), cur
.getRange().getEnd());
}
return new MyEntry(res, val);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Iterator<org.torrent.internal.util.SparseArray.Entry<T>> iterator() {
return iterator(getRange());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Partitions [");
boolean first = true;
for (Entry<T> e : this) {
if (!first) {
builder.append(", ");
} else {
first = false;
}
builder.append(e);
}
builder.append(']');
return builder.toString();
}
}