package ch.usi.da.dmap.local;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
/*
* Client
*/
public class Btree<K extends Comparable<K>,V> {
private TreeNode<K,V> root = null;
private SortedMap<Range<K>,TreeNode<K,V>> cache = new TreeMap<Range<K>,TreeNode<K,V>>();
private final boolean usecache = true;
public int cachehit = 0;
public int treewalk = 0;
public Btree(String ID){
root = findRoot(ID);
}
private TreeNode<K,V> findRoot(String ID){
/* TODO:
* how to find root? (ask any node?; how to find nodes? multicast?)
* maybe make a tree visible in zookeeper: /btree/<ID> (order)/nodes (RPC address)
*/
if(root == null){
root = new TreeNode<K,V>(null,3);
}
return root;
}
public TreeNode<K,V> lookupNode(K k){
TreeNode<K,V> node = null;
// cache
//if(usecache && !cache.isEmpty() && k.compareTo(cache.firstKey().getMinKey()) <= 0){
// node = cache.get(cache.firstKey()); //TODO: most left cache only possible with double linked leafs
//}else{
if(usecache){
for(Entry<Range<K>,TreeNode<K,V>> e : cache.entrySet()){
if(k.compareTo(e.getKey().getMinKey()) >= 0 && (e.getKey().getMaxKey() == null || k.compareTo(e.getKey().getMaxKey()) < 0)){
node = e.getValue();
break;
}
}
if(node != null){
cachehit++;
return node;
}
}
// tree walk
treewalk++;
node = root;
while(true){
if(node.getChildren().isEmpty()){
// fill cache
if(usecache && node != root){
Range<K> range;
if(node.getNextNode() != null){
range = new Range<K>(node.getMinKey(),node.getNextNode().getMinKey());
}else{
range = new Range<K>(node.getMinKey(),null);
}
cache.put(range,node);
}
break; // is leaf
}else{
Iterator<TreeNode<K,V>> i = node.getChildren().iterator();
TreeNode<K,V> n0 = i.next();
if(k.compareTo(n0.getMinKey()) <= 0){
node = n0; // go left
continue;
}
while(i.hasNext()){
TreeNode<K,V> n1 = i.next(); //FIXME: can throw concurrent mod exception (even with fully synchronized TreeNode)
if(k.compareTo(n0.getMinKey()) >= 0 && k.compareTo(n1.getMinKey()) < 0){
node = n0; // inner node
break;
}
n0 = n1;
}
node = n0; // go right
}
}
return node;
}
public void put(K k,V v){
boolean done = false;
while(!done){
TreeNode<K,V> node = lookupNode(k);
done = node.put(k,v);
// clear cache
if(!done){
cache.clear(); //TODO: do more efficient
}
}
}
public V get(K k){
while(true){
Result<V> r = lookupNode(k).get(k);
if(r.isValid()){
return r.getValue();
}else{
cache.clear(); //TODO: do more efficient
}
}
}
@Override
public String toString() {
return TreePrinter.getString(this);
}
private static class TreePrinter {
public static <K extends Comparable<K>,V> String getString(Btree<K,V> tree) {
return print(tree.root,"",true);
}
private static <K extends Comparable<K>,V> String print(TreeNode<K,V> node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
builder.append(prefix).append((isTail ? "└──" : "├──"));
if(node.getData().size() > 0){
builder.append(node.getData());
}else{
builder.append("─┐ ");
/*builder.append("( ");
for(TreeNode<K,V> n : node.getChildren()){
builder.append(n.getMinKey() + " ");
}
builder.append(")");*/
}
builder.append("\n");
if (node.getChildren().size() > 0) {
for(TreeNode<K,V> n : node.getChildren()){
if(n.equals(node.getChildren().last())){
builder.append(print(n, prefix + (isTail ? " " : "│ "), true));
break;
}
builder.append(print(n, prefix + (isTail ? " " : "│ "), false));
}
}
return builder.toString();
}
}
public static void main(String[] args) {
String ID = "d4e3eb95-5bd5-474f-a756-3f5d37dec1c8";
Btree<Integer,String> tree = new Btree<Integer, String>(ID);
/*Thread t1 = new Thread(new TreeRunner<Integer,String>(tree));
Thread t2 = new Thread(new TreeRunner<Integer,String>(tree));
Thread t3 = new Thread(new TreeRunner<Integer,String>(tree));
Thread t4 = new Thread(new TreeRunner<Integer,String>(tree));
t1.start();
t2.start();
t3.start();
t4.start();*/
/*tree.put(10,"10");
tree.put(2,"2");
tree.put(9,"9");
tree.put(4,"4");
tree.put(8,"8");
tree.put(6,"6");
tree.put(7,"7");
tree.put(3,"3");
tree.put(1,"1");
tree.put(5,"5");
System.out.println(tree);*/
// insert random values
Random rnd = new Random(System.currentTimeMillis());
Set<Integer> in = new TreeSet<Integer>();
for(int i=1;i<100;i++){
//System.err.println(i);
//System.err.println(tree);
int k = rnd.nextInt(1000);
//System.out.println(k);
in.add(k);
tree.put(k,Integer.toString(k));
}
System.out.println(tree);
// test if all values can be found
for(int i : in){
String s = tree.get(i);
if(s == null){
System.err.println("not found " + i);
}
}
// test navigation links
TreeNode<Integer,String> start = tree.lookupNode(0);
int n = -1;
for(int i : start.getData().keySet()){
in.remove(i);
if(i <= n){
System.err.println("violate order " + i);
}
n = i;
}
while(start.getNextNode() != null){
start = start.getNextNode();
for(int i : start.getData().keySet()){
in.remove(i);
if(i < n){
System.err.println("violate order " + i);
}
n = i;
}
}
if(in.size() > 0){
System.err.println(in);
}
// test small bigger non existence key
if(tree.get(-1) != null){ System.err.println("non exists get error");}
if(tree.get(100000000) != null){ System.err.println("non exists get error");}
System.out.println("Cache hit: " + tree.cachehit + " / tree walk: " + tree.treewalk + " (" + (int)((float)tree.cachehit/((float)(tree.treewalk+tree.cachehit)/100)) + "%)");
}
}