SourceForge: suneido/jsuneido: src/suneido/util/MergeTree.java@ec033c1a515b
src/suneido/util/MergeTree.java
author amckinlay
Tue May 15 20:24:12 2012 -0600 (8 hours ago)
branchjsuneido
changeset 1357 ec033c1a515b
parent 1231 a802466e77de
permissions -rw-r--r--
immudb - simplified test - results more understandable
     1 /* Copyright 2010 (c) Suneido Software Corp. All rights reserved.
     2  * Licensed under GPLv2.
     3  */
     4 
     5 package suneido.util;
     6 
     7 import java.lang.ref.SoftReference;
     8 import java.util.Arrays;
     9 import java.util.Comparator;
    10 
    11 /**
    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)
    15  * Cache oblivious.
    16  * Cache friendly - mostly sequential access.
    17  * Optimal memory space (e.g. as opposed to array size doubling)
    18  */
    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;
    26 	private int size = 0;
    27 
    28 	public MergeTree() {
    29 		this(new Comparator<T>() {
    30 				@SuppressWarnings("unchecked")
    31 				@Override
    32 				public int compare(T x, T y) {
    33 					return ((Comparable<T>) x).compareTo(y);
    34 				}
    35 			});
    36 	}
    37 
    38 	public MergeTree(Comparator<T> cmp) {
    39 		this.cmp = cmp;
    40 	}
    41 
    42 	public void add(T x) {
    43 		assert x != null;
    44 		++size;
    45 		merge(x, firstUnused());
    46 	}
    47 
    48 	private int firstUnused() {
    49 		int firstUnused = 0;
    50 		while (nodes[firstUnused] != null)
    51 			firstUnused++;
    52 		return firstUnused;
    53 	}
    54 
    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
    59 			dst[0] = x;
    60 			return;
    61 		}
    62 		if (n == 1) { // another 25% of the cases
    63 			if (cmp(x, nodes[0][0]) < 0) {
    64 				dst[0] = x;
    65 				dst[1] = nodes[0][0];
    66 			} else {
    67 				dst[0] = nodes[0][0];
    68 				dst[1] = x;
    69 			}
    70 			nodes[0] = null;
    71 			return;
    72 		}
    73 		merge(x, n, dst);
    74 	}
    75 
    76 	private void merge(T x, int n, Object[] dst) {
    77 		int di = 0;
    78 		Arrays.fill(pos, 0, n, 0);
    79 		while (true) {
    80 			int iMin = -1;
    81 			Object min = x;
    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) {
    86 						min = val;
    87 						iMin = i;
    88 					}
    89 				}
    90 			if (min == null)
    91 				break;
    92 			if (iMin == -1)
    93 				x = null;
    94 			else
    95 				++pos[iMin];
    96 			dst[di++] = min;
    97 		}
    98 		assert di == dst.length;
    99 		Arrays.fill(nodes, 0, n, null);
   100 	}
   101 
   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]);
   106 		return nodes[i];
   107 	}
   108 
   109 	@SuppressWarnings("unchecked")
   110 	private int cmp(Object x, Object y) {
   111 		return cmp.compare((T) x, (T) y);
   112 	}
   113 
   114 	public void clear() {
   115 		size = 0;
   116 		Arrays.fill(nodes, 0, MAX_LEVELS, null);
   117 	}
   118 
   119 	public Iter iter() {
   120 		return new Iter();
   121 	}
   122 
   123 	private enum Dir { NEXT, PREV };
   124 
   125 	public class Iter {
   126 		private final int n;
   127 		private final NodeIter[] iters;
   128 		private Dir dir = null;
   129 
   130 		private Iter() {
   131 			n = Integer.bitCount(size);
   132 			iters = new NodeIter[n];
   133 			int di = 0;
   134 			for (int i = 0; i < MAX_LEVELS; ++i)
   135 				if (nodes[i] != null)
   136 					iters[di++] = new NodeIter(nodes[i]);
   137 		}
   138 
   139 		public T next() {
   140 			if (dir == null)
   141 				first();
   142 			else if (dir == Dir.PREV)
   143 				next2(); // have to skip when changing direction
   144 			return next2();
   145 		}
   146 
   147 		@SuppressWarnings("unchecked")
   148 		private T next2() {
   149 			int iMin = 0;
   150 			Object min = null;
   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();
   155 			if (min == null)
   156 				dir = Dir.PREV;
   157 			else {
   158 				dir = Dir.NEXT;
   159 				iters[iMin].next();
   160 			}
   161 			return (T) min;
   162 		}
   163 
   164 		public T prev() {
   165 			if (dir == null)
   166 				last();
   167 			else if (dir == Dir.NEXT)
   168 				prev2(); // have to skip when changing direction
   169 			return prev2();
   170 		}
   171 
   172 		@SuppressWarnings("unchecked")
   173 		private T prev2() {
   174 			int iMax = 0;
   175 			Object max = null;
   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();
   180 			if (max == null)
   181 				dir = Dir.NEXT;
   182 			else {
   183 				dir = Dir.PREV;
   184 				iters[iMax].prev();
   185 			}
   186 			return (T) max;
   187 		}
   188 
   189 		private void first() {
   190 			for (int i = 0; i < n; ++i)
   191 				iters[i].setPos(0);
   192 		}
   193 
   194 		private void last() {
   195 			for (int i = 0; i < n; ++i)
   196 				iters[i].setPos(iters[i].node.length);
   197 		}
   198 
   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));
   203 			dir = Dir.NEXT;
   204 		}
   205 
   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));
   210 			dir = Dir.PREV;
   211 		}
   212 
   213 		public void print() {
   214 			System.out.println("\nIter: dir " + dir);
   215 			if (dir != null)
   216 				for (int i = 0; i < n; ++i)
   217 					System.out.println(iters[i]);
   218 		}
   219 
   220 	}
   221 
   222 	/**
   223 	 * pos points to next, prev is pos - 1
   224 	 */
   225 	private static class NodeIter {
   226 		Object[] node;
   227 		int pos = 0;
   228 
   229 		NodeIter(Object[] node) {
   230 			this.node = node;
   231 			assert node.length > 0;
   232 		}
   233 
   234 		boolean hasNext() {
   235 			return pos < node.length;
   236 		}
   237 
   238 		boolean hasPrev() {
   239 			return (pos - 1) >= 0;
   240 		}
   241 
   242 		Object peekNext() {
   243 			return node[pos];
   244 		}
   245 
   246 		Object peekPrev() {
   247 			return node[pos - 1];
   248 		}
   249 
   250 		void next() {
   251 			++pos;
   252 		}
   253 
   254 		void prev() {
   255 			--pos;
   256 		}
   257 
   258 		void setPos(int pos) {
   259 			this.pos = pos;
   260 		}
   261 
   262 		@Override
   263 		public String toString() {
   264 			return pos + " " + Arrays.toString(node);
   265 		}
   266 
   267 	}
   268 
   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]));
   273 	}
   274 
   275 	public int size() {
   276 		return size;
   277 	}
   278 
   279 }