1 /* Copyright 2010 (c) Suneido Software Corp. All rights reserved.
2 * Licensed under GPLv2.
7 import java.lang.ref.SoftReference;
8 import java.util.Arrays;
9 import java.util.Comparator;
12 * Add items in a random order, iterate them in order. Stable sort.
13 * Fast (amortized) insertion.
14 * Not very good for lookups (so none implemented)
16 * Cache friendly - mostly sequential access.
17 * Optimal memory space (e.g. as opposed to array size doubling)
19 public class MergeTree<T> {
20 private static final int MAX_LEVELS = 32;
21 private final Object[][] nodes = new Object[MAX_LEVELS][];
22 @SuppressWarnings("unchecked")
23 private final SoftReference<Object[]>[] cache = new SoftReference[MAX_LEVELS];
24 private final int[] pos = new int[MAX_LEVELS]; // temp for merge
25 private final Comparator<T> cmp;
29 this(new Comparator<T>() {
30 @SuppressWarnings("unchecked")
32 public int compare(T x, T y) {
33 return ((Comparable<T>) x).compareTo(y);
38 public MergeTree(Comparator<T> cmp) {
42 public void add(T x) {
45 merge(x, firstUnused());
48 private int firstUnused() {
50 while (nodes[firstUnused] != null)
55 private void merge(T x, int n) {
56 assert nodes[n] == null;
57 Object[] dst = alloc(n);
58 if (n == 0) { // 50% of the cases
62 if (n == 1) { // another 25% of the cases
63 if (cmp(x, nodes[0][0]) < 0) {
76 private void merge(T x, int n, Object[] dst) {
78 Arrays.fill(pos, 0, n, 0);
82 for (int i = 0; i < n; ++i)
83 if (pos[i] < nodes[i].length) {
84 Object val = nodes[i][pos[i]];
85 if (min == null || cmp(val, min) <= 0) {
98 assert di == dst.length;
99 Arrays.fill(nodes, 0, n, null);
102 Object[] alloc(int i) {
103 nodes[i] = (cache[i] == null) ? null : cache[i].get();
104 if (nodes[i] == null)
105 cache[i] = new SoftReference<Object[]>(nodes[i] = new Object[1 << i]);
109 @SuppressWarnings("unchecked")
110 private int cmp(Object x, Object y) {
111 return cmp.compare((T) x, (T) y);
114 public void clear() {
116 Arrays.fill(nodes, 0, MAX_LEVELS, null);
123 private enum Dir { NEXT, PREV };
127 private final NodeIter[] iters;
128 private Dir dir = null;
131 n = Integer.bitCount(size);
132 iters = new NodeIter[n];
134 for (int i = 0; i < MAX_LEVELS; ++i)
135 if (nodes[i] != null)
136 iters[di++] = new NodeIter(nodes[i]);
142 else if (dir == Dir.PREV)
143 next2(); // have to skip when changing direction
147 @SuppressWarnings("unchecked")
151 for (int i = 0; i < n; ++i)
152 if (iters[i].hasNext())
153 if (min == null || cmp(iters[i].peekNext(), min) <= 0)
154 min = iters[iMin = i].peekNext();
167 else if (dir == Dir.NEXT)
168 prev2(); // have to skip when changing direction
172 @SuppressWarnings("unchecked")
176 for (int i = n - 1; i >= 0; --i)
177 if (iters[i].hasPrev())
178 if (max == null || cmp(iters[i].peekPrev(), max) >= 0)
179 max = iters[iMax = i].peekPrev();
189 private void first() {
190 for (int i = 0; i < n; ++i)
194 private void last() {
195 for (int i = 0; i < n; ++i)
196 iters[i].setPos(iters[i].node.length);
199 @SuppressWarnings("unchecked")
200 public void seekFirst(T x) {
201 for (int i = 0; i < n; ++i)
202 iters[i].setPos(Util.lowerBound((T[]) iters[i].node, x, cmp));
206 @SuppressWarnings("unchecked")
207 public void seekLast(T x) {
208 for (int i = 0; i < n; ++i)
209 iters[i].setPos(Util.upperBound((T[]) iters[i].node, x, cmp));
213 public void print() {
214 System.out.println("\nIter: dir " + dir);
216 for (int i = 0; i < n; ++i)
217 System.out.println(iters[i]);
223 * pos points to next, prev is pos - 1
225 private static class NodeIter {
229 NodeIter(Object[] node) {
231 assert node.length > 0;
235 return pos < node.length;
239 return (pos - 1) >= 0;
247 return node[pos - 1];
258 void setPos(int pos) {
263 public String toString() {
264 return pos + " " + Arrays.toString(node);
269 public void print() {
270 for (int i = 0; i < MAX_LEVELS; ++i)
271 if (nodes[i] != null)
272 System.out.println(Arrays.toString(nodes[i]));