package xapi.util.impl;
import xapi.fu.In1;
import xapi.fu.Lazy;
import xapi.fu.Rethrowable;
import static xapi.fu.Lazy.deferred1;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.ObjIntConsumer;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/**
* Created by James X. Nelson (james @wetheinter.net) on 8/12/16.
*/
public class LeonardoHeap<T> {
private interface TreeJoiner <T> {
LeonardoTree<T> join(T value, int score, LeonardoTree<T> left, LeonardoTree<T> right);
}
static abstract class LeonardoTree <T> implements Cloneable, Rethrowable {
LeonardoTree<T> next;
T value;
int score;
private final TreeJoiner<T> joiner;
private LeonardoTree(T value, int score, TreeJoiner<T> joiner) {
this.value = value;
this.score = score;
this.joiner = joiner;
}
public LeonardoTree<T> getNext() {
return next;
}
public T getValue() {
return value;
}
abstract int size();
abstract int ordinal();
int score() {
return score;
}
LeonardoTree<T> join(T value, int score) {
final LeonardoTree<T> tree = joiner.join(value, score, next, this);
return tree;
}
@Override
protected LeonardoTree clone() {
try {
return (LeonardoTree) super.clone();
} catch (CloneNotSupportedException e) {
throw rethrow(e); // won't happen
}
}
public void buildString(StringBuilder b) {
b
.append("{")
.append(value)
.append(", ")
.append(score)
.append("} ");
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
buildString(b);
return b.toString();
}
}
private static final class LeoHead <T> extends LeonardoTree <T> {
private LeoHead() {
super(null, Integer.MAX_VALUE, (ig, no, re, d)->{
throw new UnsupportedOperationException();
});
}
@Override
int size() {
return 0;
}
@Override
int ordinal() {
return -1;
}
}
static abstract class LeonardoForest <T> extends LeonardoTree <T> {
private final LeonardoTree<T> left;
private final LeonardoTree<T> right;
LeonardoForest(T value, int score, LeonardoTree<T> left, LeonardoTree<T> right, TreeJoiner<T> joiner) {
super(value, score, joiner);
this.left = left;
this.right = right;
// A forest within a forest should no longer be linked.
left.next = null;
right.next = null;
}
LeonardoTree<T> getLeft() {
return left;
}
LeonardoTree<T> getMaxChild() {
return left.score > right.score ? left : right;
}
LeonardoTree<T> getRight() {
return right;
}
@Override
public void buildString(StringBuilder b) {
super.buildString(b);
b.append("\nL(");
getLeft().buildString(b);
b.append(")\nR(");
getRight().buildString(b);
b.append(")");
}
}
private static class LeoTree0 <T> extends LeonardoTree <T> {
LeoTree0(T value, int score) {
super(value, score, LeoTree2::new);
}
@Override
int size() {
return 1;
}
@Override
int ordinal() {
return 0;
}
}
private static class LeoTree1 <T> extends LeonardoTree <T> {
LeoTree1(T value, int score) {
super(value, score, LeoTree3::new);
}
@Override
int size() {
return 1;
}
@Override
int ordinal() {
return 1;
}
}
private static class LeoTree2 <T> extends LeonardoForest<T> {
LeoTree2(T value, int score, LeoTree1 <T> left, LeoTree0 <T> right) {
super(value, score, left, right, LeoTree4::new);
}
private LeoTree2(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree1<T>)left, (LeoTree0<T>)right);
}
@Override
int size() {
return 3;
}
@Override
int ordinal() {
return 2;
}
}
private static class LeoTree3 <T> extends LeonardoForest <T> {
LeoTree3(T value, int score, LeoTree2<T> left, LeoTree1<T> right) {
super(value, score, left, right, LeoTree5::new);
}
private LeoTree3(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree2<T>)left, (LeoTree1<T>)right);
}
@Override
int size() {
return 5;
}
@Override
int ordinal() {
return 3;
}
}
private static class LeoTree4 <T> extends LeonardoForest <T> {
LeoTree4(T value, int score, LeoTree3<T> left, LeoTree2<T> right) {
super(value, score, left, right, LeoTree6::new);
}
private LeoTree4(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree3<T>)left, (LeoTree2<T>)right);
}
@Override
int size() {
return 9;
}
@Override
int ordinal() {
return 4;
}
}
private static class LeoTree5 <T> extends LeonardoForest <T> {
LeoTree5(T value, int score, LeoTree4 <T> left, LeoTree3 <T> right) {
super(value, score, left, right, LeoTree7::new);
}
private LeoTree5(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree4<T>)left, (LeoTree3<T>)right);
}
@Override
int size() {
return 15;
}
@Override
int ordinal() {
return 5;
}
}
private static class LeoTree6 <T> extends LeonardoForest <T> {
LeoTree6(T value, int score, LeoTree5 <T> left, LeoTree4 <T> right) {
super(value, score, left, right, LeoTree8::new);
}
private LeoTree6(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree5<T>)left, (LeoTree4<T>)right);
}
@Override
int size() {
return 25;
}
@Override
int ordinal() {
return 6;
}
}
private static class LeoTree7 <T> extends LeonardoForest <T> {
LeoTree7(T value, int score, LeoTree6<T> left, LeoTree5 <T> right) {
super(value, score, left, right, LeoTreeN::merge);
}
private LeoTree7(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree6<T>)left, (LeoTree5<T>)right);
}
@Override
int size() {
return 41;
}
@Override
int ordinal() {
return 7;
}
}
private static class LeoTree8 <T> extends LeonardoForest <T> {
LeoTree8(T value, int score, LeoTree7 <T> left, LeoTree6 <T> right) {
super(value, score, left, right, LeoTreeN::merge);
}
private LeoTree8(T value, int score, LeonardoTree <T> left, LeonardoTree <T> right) {
this(value, score, (LeoTree7<T>)left, (LeoTree6<T>)right);
}
@Override
int size() {
return 67;
}
@Override
int ordinal() {
return 8;
}
}
private static class LeoTreeN <T> extends LeonardoForest <T> {
private final int size;
private final int ordinal;
public static <T> LeoTreeN<T> merge(T value, int score, LeonardoTree<T> left, LeonardoTree<T> right) {
return new LeoTreeN<>(value, score, left.size() + right.size() + 1, right.ordinal()+1, left, right);
}
LeoTreeN(T value, int score, int size, int ordinal, LeonardoTree <T> left, LeonardoTree <T> right) {
super(value, score, left, right, LeoTreeN::merge);
this.size = size;
this.ordinal = ordinal;
assert size == right.size() + left.size() + 1;
}
@Override
int size() {
return size;
}
@Override
int ordinal() {
return ordinal;
}
}
private ObjIntConsumer<T> inserter;
private final LeonardoTree<T> head;
private Lazy<Iterable<T>> itr;
public LeonardoHeap() {
head = new LeoHead<>();
itr = deferred1(this::buildIterator);
inserter = (item1, score1) -> {
final LeoTree0<T> first = new LeoTree0<>(item1, score1);
head.next = first;
inserter = (item2, score2) -> {
LeoTree1<T> second;
if (score1 > score2) {
// The item in slot 0 was higher; just add the new item in slot 1
second = new LeoTree1<>(item2, score2);
first.next = second;
} else {
// The existing item was lower; move it over to slot 0
second = new LeoTree1<>(item1, score1);
head.next = new LeoTree0<>(item2, score2);
head.next.next = second;
}
inserter = new ObjIntConsumer<T>() {
@Override
public void accept(T t, int value) {
addAndBalance(t, value);
}
};
};
};
}
private class Itr implements Iterator<T> {
private LeonardoTree<T> max;
public Itr() {
this.max = head.next == null ? null : head.next.clone();
}
@Override
public boolean hasNext() {
return max != null;
}
@Override
public T next() {
try {
return max.getValue();
} finally {
max = dequeAndBalance(max);
}
}
}
private LeonardoTree<T> dequeAndBalance(LeonardoTree<T> max) {
if (max instanceof LeonardoForest) {
// If this is a forest, then we need to remove the root
// and rebalance the children.
LeonardoForest<T> forest = (LeonardoForest<T>) max;
final LeonardoTree<T> left = forest.getLeft();
final LeonardoTree<T> right = forest.getRight();
right.next = left;
left.next = max.next;
rebalance(left);
rebalance(right);
return right;
} else {
// If this was a 0 or 1 singleton, just return the next node
return max.next;
}
}
private Iterable<T> buildIterator() {
return Itr::new;
}
/**
* Called when there is already at least two heaps in the forest.
*/
private void addAndBalance(T item, int score) {
final LeonardoTree<T> first = head.next;
final LeonardoTree<T> second = first.next;
final LeonardoTree<T> newNode;
if (second == null) {
// just one heap; toss on a new 0 node and rebalance
assert !(first instanceof LeoTree1);
head.next = newNode = new LeoTree1<>(item, score);
newNode.next = first;
}
else if (second.ordinal() == first.ordinal() + 1) {
final LeonardoTree<T> newNext = second.next;
newNode = first.join(item, score);
assert newNode instanceof LeonardoForest;
head.next = newNode;
newNode.next = newNext;
} else if (!(first instanceof LeonardoForest)){
// The current head is a size 1 tree, but the next node is a larger ordinal;
if (first.score() > score) {
// The new item is less than the head, so we will need to balance from the new node
head.next = new LeoTree0<>(first.value, first.score);
head.next.next = newNode = new LeoTree1<>(item, score);
newNode.next = second;
} else {
// The new item is the largest item, it will be the new head.
head.next = newNode = new LeoTree0<>(item, score);
newNode.next = first;
// No balancing needed; the tree is already balanced
return;
}
} else {
newNode = new LeoTree1<>(item, score);
newNode.next = head.next;
head.next = newNode;
}
rebalance(newNode);
}
private void rebalance(LeonardoTree<T> newNode) {
if (newNode.next == null) {
shuffle(newNode);
return;
}
final LeonardoTree<T> nextNode = newNode.next;
int nextScore = nextNode.score;
if (newNode.score < nextScore) {
if (newNode instanceof LeonardoForest) {
// When the newNode is a forest, we can only swap heads if the root of the
// next tree is greater than both the left and right child of the current node.
LeonardoForest<T> forest = (LeonardoForest<T>) newNode;
if (forest.getLeft().score < nextScore && forest.getRight().score < nextScore) {
valueSwap(newNode, nextNode);
rebalance(nextNode);
} else {
shuffle(newNode);
}
} else {
// The new node is either a 0 or a 1 node
// And, the new node is lower than its next node, so swap heads.
valueSwap(newNode, nextNode);
rebalance(nextNode);
}
} else {
shuffle(newNode);
}
}
private void shuffle(LeonardoTree<T> node) {
if (node instanceof LeonardoForest) {
LeonardoForest<T> forest = (LeonardoForest<T>) node;
LeonardoTree<T> maxChild = forest.getMaxChild();
if (node.score < maxChild.score) {
valueSwap(node, maxChild);
shuffle(maxChild);
}
}
}
private void valueSwap(LeonardoTree<T> newNode, LeonardoTree<T> nextNode) {
T val = newNode.value;
int score = newNode.score;
newNode.value = nextNode.value;
newNode.score = nextNode.score;
nextNode.value = val;
nextNode.score = score;
}
public void addItem(T item, int score) {
// reset the iterable builder, to reflect our changes
if (itr.isResolved()) {
itr = deferred1(this::buildIterator);
}
inserter.accept(item, score);
}
public Iterable<T> forEach() {
return itr.out1();
}
public static void main(String ... a) {
// for (int i :new int[]{ 10, 0, 2, 6, 17, 15, 13, 5, 4, 8, 7, 11, 9, 3, 1}) {
// l1.add(i);
// l2.add(i);
// }
long s1=0, s2=0, s3=0;
List<Integer> sorted = null;
List<Integer> l1 = null, l2 = null;
for (int j = 5000; j-->0;) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
l1 = new ArrayList<>();
l2 = new ArrayList<>();
Set<Integer> unique = new HashSet<>();
LeonardoHeap<Integer> heap = new LeonardoHeap<>();
In1<Integer> add = i->heap.addItem(i, i);
int max = Integer.MAX_VALUE / 500000;
for (int i = max/2; i-->0;) {
int n = (int)(Math.random() * max);
while (!unique.add(n)) {
n = (int)(Math.random() * max);
}
l1.add(n);
l2.add(n);
}
// s1 = System.nanoTime();
// Collections.sort(l1);
s2 = System.nanoTime();
for (Integer i : l2) {
heap.addItem(i, i);
}
sorted = StreamSupport.stream(heap.forEach().spliterator(), false)
.collect(Collectors.toList());
s3 = System.nanoTime();
}
System.out.println((s3 - s2) + "\n" + (s2 - s1));
Collections.reverse(l1);
System.out.println(sorted.equals(l1));
System.out.println(sorted.size());
System.out.println(l1.size());
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
for (LeonardoTree<T> tree : ReverseIterable.reverse(
new LinkedIterable<>(head, LeonardoTree::getNext, true))) {
b.append("[");
tree.buildString(b);
b.append("]\n");
}
return b.toString();
}
}