package org.basex.index;
import org.basex.util.Num;
import org.basex.util.Token;
import org.basex.util.hash.TokenIntMap;
import org.basex.util.list.BoolList;
import org.basex.util.list.IntList;
import org.basex.util.list.TokenList;
/**
* This class indexes keys in a balanced binary tree, including their id
* values. Iterator methods are available to traverse through the tree.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
* @author Sebastian Gath
*/
public class IndexTree {
/** Factor for resize. */
protected static final double FACTOR = 1.2;
/** Keys saved in the tree. */
public final TokenList keys = new TokenList(FACTOR);
/** Compressed id values. */
public TokenList values = new TokenList(FACTOR);
/** Mapping for using existing tree. */
protected TokenIntMap maps = new TokenIntMap();
/** Current iterator node. */
protected int cn;
/** Tree structure [left, right, parent]. */
private final IntList tree = new IntList(FACTOR);
/** Flag if a node has been modified. */
private final BoolList mod = new BoolList();
/** Tree root node. */
private int root = -1;
/**
* Indexes the specified key and value.
*
* @param key key to be indexed
* @param value value to be indexes
*/
public final void index(final byte[] key, final int value) {
index(key, value, true);
}
/**
* Indexes the specified key and value. If the key has already been
* indexed, its value is added to the existing value array.
* Otherwise, a new index entry is created.
* @param key key to be indexed
* @param value value to be indexed
* @param exist flag for using existing index
* @return int node
*/
protected final int index(final byte[] key, final int value,
final boolean exist) {
// index is empty.. create root node
if(root == -1) {
root = n(key, value, -1, exist);
return root;
}
int n = root;
while(true) {
final int c = Token.diff(key, keys.get(n));
if(c == 0) {
if(exist) {
values.set(n, Num.add(values.get(n), value));
} else {
final int i = maps.value(Num.num(n));
if(i < 0) {
maps.add(Num.num(n), values.size());
values.add(Num.newNum(value));
} else {
values.set(i, Num.add(values.get(i), value));
}
}
return n;
}
int ch = c < 0 ? l(n) : r(n);
if(ch != -1) {
n = ch;
} else {
ch = n(key, value, n, exist);
if(c < 0) {
l(n, ch);
a(l(n));
} else {
r(n, ch);
a(r(n));
}
return ch;
}
}
}
/**
* Returns the number of entries.
* @return number of entries
*/
public final int size() {
return values.size();
}
/**
* Initializes the index iterator.
* will be removed to save memory.
*/
public final void init() {
cn = root;
if(cn != -1) while(l(cn) != -1) cn = l(cn);
}
/**
* Checks if the iterator returns more keys.
* @return true if more keys exist
*/
public final boolean more() {
return cn != -1;
}
/**
* Returns the next pointer.
* @return next pointer
*/
public final int next() {
/* Last iterator node. */
final int ln = cn;
if(r(cn) != -1) {
cn = r(cn);
while(l(cn) != -1) cn = l(cn);
} else {
int t = cn;
cn = p(cn);
while(cn != -1 && t == r(cn)) {
t = cn;
cn = p(cn);
}
}
return ln;
}
// PRIVATE METHODS ==========================================================
/**
* Creates a new node.
* @param key node key
* @param value node value
* @param par pointer on parent node
* @param exist flag for reusing existing tree
* @return pointer of the new node
*/
private int n(final byte[] key, final int value, final int par,
final boolean exist) {
tree.add(-1); // left node
tree.add(-1); // right node
tree.add(par); // parent node
mod.add(false);
keys.add(key);
values.add(Num.newNum(value));
if(!exist) maps.add(Num.num(keys.size() - 1), values.size() - 1);
return mod.size() - 1;
}
/**
* Gets the left child.
* @param nd current node
* @return left node
*/
private int l(final int nd) {
return tree.get((nd << 1) + nd);
}
/**
* Gets the right child.
* @param nd current node
* @return right node
*/
private int r(final int nd) {
return tree.get((nd << 1) + nd + 1);
}
/**
* Gets the parent node.
* @param nd current node
* @return parent node
*/
private int p(final int nd) {
return tree.get((nd << 1) + nd + 2);
}
/**
* Sets the left child.
* @param nd current node
* @param val left node
*/
private void l(final int nd, final int val) {
tree.set((nd << 1) + nd, val);
}
/**
* Sets the right child.
* @param nd current node
* @param val right node
*/
private void r(final int nd, final int val) {
tree.set((nd << 1) + nd + 1, val);
}
/**
* Sets the parent node.
* @param nd current node
* @param val parent node
*/
private void p(final int nd, final int val) {
tree.set((nd << 1) + nd + 2, val);
}
/**
* Adjusts the tree balance.
* @param nd node to be adjusted
*/
private void a(final int nd) {
int n = nd;
mod.set(n, true);
while(n != -1 && n != root && mod.get(p(n))) {
if(p(n) == l(p(p(n)))) {
final int y = r(p(p(n)));
if(y != -1 && mod.get(y)) {
mod.set(p(n), false);
mod.set(y, false);
mod.set(p(p(n)), true);
n = p(p(n));
} else {
if(n == r(p(n))) {
n = p(n);
rl(n);
}
mod.set(p(n), false);
mod.set(p(p(n)), true);
if(p(p(n)) != -1) rr(p(p(n)));
}
} else {
final int y = l(p(p(n)));
if(y != -1 && mod.get(y)) {
mod.set(p(n), false);
mod.set(y, false);
mod.set(p(p(n)), true);
n = p(p(n));
} else {
if(n == l(p(n))) {
n = p(n);
rr(n);
}
mod.set(p(n), false);
mod.set(p(p(n)), true);
if(p(p(n)) != -1) rl(p(p(n)));
}
}
}
mod.set(root, false);
}
/**
* Left rotation.
* @param n node to be rotated
*/
private void rl(final int n) {
final int r = r(n);
r(n, l(r));
if(l(r) != -1) p(l(r), n);
p(r, p(n));
if(p(n) == -1) root = r;
else if(l(p(n)) == n) l(p(n), r);
else r(p(n), r);
l(r, n);
p(n, r);
}
/**
* Right rotation.
* @param n node to be rotated
*/
private void rr(final int n) {
final int l = l(n);
l(n, r(l));
if(r(l) != -1) p(r(l), n);
p(l, p(n));
if(p(n) == -1) root = l;
else if(r(p(n)) == n) r(p(n), l);
else l(p(n), l);
r(l, n);
p(n, l);
}
}