package trees;
// cordes
public abstract class Rope {
protected final int length;
protected Rope(int length) {
this.length = length;
}
public int length() {
return this.length;
}
public static Rope ofString(String s) {
return new Str(s);
}
@SuppressWarnings("unused")
private static final int SMALL_LENGTH = 256;
public Rope append(Rope r) {
if (this.length == 0) return r;
if (r.length == 0) return this;
Rope r1 = new App(this, r);
// if (r1.length < SMALL_LENGTH) return new Str(r1.toString()); // OPTIM
return r1;
}
protected void checkIndex(int i) {
if (i < 0 || i >= this.length) throw new IllegalArgumentException();
}
protected void checkRange(int begin, int end) {
if (begin < 0 || end < begin || end > this.length) throw new IllegalArgumentException();
}
public abstract char get(int i);
public abstract Rope sub(int begin, int end);
static final Rope empty = new Str("");
public String toString() {
StringBuffer b = new StringBuffer();
toStringHelper(b);
return b.toString();
}
protected abstract void toStringHelper(StringBuffer b);
static final int maxFib = 44;
static final int[] fib = new int[maxFib + 1];
static { fib[1] = 1; for(int i = 2; i <= maxFib; i++) fib[i] = fib[i-2] + fib[i-1]; }
public Rope balance() {
Rope[] queue = new Rope[maxFib];
for (int i = 0; i < maxFib; i++) queue[i] = empty;
this.insertLeaves(queue);
Rope r = empty;
for (int i = 2; i < maxFib; i++) r = queue[i].append(r);
return r;
}
protected void insertQueue(Rope[] queue, Rope r, int i) {
assert (i < maxFib);
Rope c = queue[i].append(r);
if (c.length < fib[i+1]) queue[i] = c;
else { queue[i] = empty; insertQueue(queue, c, i+1); }
}
protected abstract void insertLeaves(Rope[] queue);
public int cost() {
return cost(0);
}
protected abstract int cost(int depth);
}
class Str extends Rope {
private final String str;
Str(String str) {
super(str.length());
this.str = str;
}
@Override
public char get(int i) {
checkIndex(i);
return this.str.charAt(i);
}
@Override
public Rope sub(int begin, int end) {
if (begin == 0 && end == this.length) // OPTIM
return this;
checkRange(begin, end);
return new Str(this.str.substring(begin, end));
}
@Override
protected void insertLeaves(Rope[] queue) {
insertQueue(queue, this, 2);
}
@Override
protected int cost(int depth) {
return depth * length;
}
@Override
protected void toStringHelper(StringBuffer b) {
b.append(this.str);
}
@Override
public boolean equals(Object that) {
Rope r = (Rope)that;
return r.toString().equals(this.str);
}
}
class App extends Rope {
private final Rope left, right;
App(Rope left, Rope right) {
super(left.length + right.length);
this.left = left;
this.right = right;
}
@Override
public char get(int i) {
checkIndex(i);
return i < this.left.length ? this.left.get(i) : this.right.get(i - this.left.length);
}
@Override
public Rope sub(int begin, int end) {
if (begin == 0 && end == this.length) // OPTIM
return this;
checkRange(begin, end);
int endr = end - this.left.length;
if (endr <= 0) // all in left
return this.left.sub(begin, end);
int beginr = begin - this.left.length;
if (beginr >= 0) // all in right
return this.right.sub(beginr, endr);
return new App(this.left.sub(begin, this.left.length),
this.right.sub(0, endr));
}
@Override
protected void insertLeaves(Rope[] queue) {
this.left.insertLeaves(queue);
this.right.insertLeaves(queue);
}
@Override
protected int cost(int depth) {
return this.left.cost(depth + 1) + this.right.cost(depth + 1);
}
@Override
protected void toStringHelper(StringBuffer b) {
this.left.toStringHelper(b);
this.right.toStringHelper(b);
}
@Override
public boolean equals(Object that) {
Rope r = (Rope)that;
return this.length == r.length && this.left.equals(r.sub(0, this.left.length))
&& this.right.equals(r.sub(this.left.length, this.length));
}
}
class TestRope {
public static void main(String[] args) {
Rope r = new App(new Str("Ec"),
new App(new App(new Str("oleP"), new Str("o")),
new App(new App(new Str("lytech"), new Str("ni")), new Str("que"))));
System.out.println(r);
assert r.sub(0, 3).toString().equals("Eco");
assert r.sub(4, 8).toString().equals("ePol");
assert r.sub(7, 12).toString().equals("lytec");
assert r.sub(6, 16).toString().equals("olytechniq");
assert r.sub(0, r.length).toString().equals("EcolePolytechnique");
System.out.println(r.cost());
assert r.cost() == 58;
r = r.balance();
System.out.println(r);
System.out.println(r.cost());
assert r.cost() < 58;
r = Rope.empty;
for (char c = 'a'; c <= 'z'; c++)
r = r.append(new Str("" + c));
System.out.println(r);
assert r.length() == 26;
r = r.balance();
System.out.println(r);
assert r.length() == 26;
System.out.println("TestRope OK");
}
}