codegen: Fix floating reference regression with Variants
[vala-gnome.git] / gee / timsort.vala
blob2db14fd3edeaeb73e6bdd2e7dafb8252c258b29f
1 /* timsort.vala
3 * Copyright (C) 2009 Didier Villevalois
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Didier 'Ptitjes Villevalois <ptitjes@free.fr>
23 /**
24 * A stable, adaptive, iterative mergesort that requires far fewer than n*lg(n)
25 * comparisons when running on partially sorted arrays, while offering
26 * performance comparable to a traditional mergesort when run on random arrays.
27 * Like all proper mergesorts, this sort is stable and runs O(n*log(n)) time
28 * (worst case). In the worst case, this sort requires temporary storage space
29 * for n/2 object references; in the best case, it requires only a small
30 * constant amount of space.
32 * This implementation was adapted from Tim Peters's list sort for Python,
33 * which is described in detail here:
34 * [[http://svn.python.org/projects/python/trunk/Objects/listsort.txt]]
36 * Tim's C code may be found here:
37 * [[http://svn.python.org/projects/python/trunk/Objects/listobject.c]]
39 * The underlying techniques are described in this paper (and may have even
40 * earlier origins):
42 * "Optimistic Sorting and Information Theoretic Complexity"
43 * Peter McIlroy
44 * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), pp
45 * 467-474, Austin, Texas, 25-27 January 1993.
47 internal class Vala.TimSort<G> {
49 public static void sort<G> (List<G> list, CompareDataFunc<G> compare) {
50 if (list is ArrayList) {
51 TimSort.sort_arraylist<G> ((ArrayList<G>) list, compare);
52 } else {
53 TimSort.sort_list<G> (list, compare);
57 private static void sort_list<G> (List<G> list, CompareDataFunc<G> compare) {
58 TimSort<G> helper = new TimSort<G> ();
60 helper.list_collection = list;
61 helper.array = list.to_array ();
62 helper.list = helper.array;
63 helper.index = 0;
64 helper.size = list.size;
65 helper.compare = compare;
67 helper.do_sort ();
69 // TODO Use a list iterator and use iter.set (item)
70 list.clear ();
71 foreach (G item in helper.array) {
72 list.add (item);
76 private static void sort_arraylist<G> (ArrayList<G> list, CompareDataFunc<G> compare) {
77 TimSort<G> helper = new TimSort<G> ();
79 helper.list_collection = list;
80 helper.list = list._items;
81 helper.index = 0;
82 helper.size = list._size;
83 helper.compare = compare;
85 helper.do_sort ();
88 private const int MINIMUM_GALLOP = 7;
90 private List<G> list_collection;
91 private G[] array;
92 private void** list;
93 private int index;
94 private int size;
95 private Slice<G>[] pending;
96 private int minimum_gallop;
97 private unowned CompareDataFunc<G> compare;
99 private void do_sort () {
100 if (size < 2) {
101 return;
104 pending = new Slice<G>[0];
105 minimum_gallop = MINIMUM_GALLOP;
107 Slice<G> remaining = new Slice<G> (list, index, size);
108 int minimum_length = compute_minimum_run_length (remaining.length);
110 while (remaining.length > 0) {
111 // Get the next run
112 bool descending;
113 Slice<G> run = compute_longest_run (remaining, out descending);
114 #if DEBUG
115 message ("New run (%d, %d) %s", run.index, run.length,
116 descending ? "descending" : "ascending");
117 #endif
118 if (descending) {
119 run.reverse ();
122 // Extend it to minimum_length, if needed
123 if (run.length < minimum_length) {
124 int sorted_count = run.length;
125 run.length = int.min (minimum_length, remaining.length);
126 insertion_sort (run, sorted_count);
127 #if DEBUG
128 message ("Extended to (%d, %d) and sorted from index %d",
129 run.index, run.length, sorted_count);
130 #endif
133 // Move remaining after run
134 remaining.shorten_start (run.length);
136 // Add run to pending runs and try to merge
137 pending += (owned) run;
138 merge_collapse ();
141 assert (remaining.index == size);
143 merge_force_collapse ();
145 assert (pending.length == 1);
146 assert (pending[0].index == 0);
147 assert (pending[0].length == size);
150 private delegate bool LowerFunc (G left, G right);
152 private inline bool lower_than (G left, G right) {
153 return compare (left, right) < 0;
156 private inline bool lower_than_or_equal_to (G left, G right) {
157 return compare (left, right) <= 0;
160 private int compute_minimum_run_length (int length) {
161 int run_length = 0;
162 while (length >= 64) {
163 run_length |= length & 1;
164 length >>= 1;
166 return length + run_length;
169 private Slice<G> compute_longest_run (Slice<G> a, out bool descending) {
170 int run_length;
171 if (a.length <= 1) {
172 run_length = a.length;
173 descending = false;
174 } else {
175 run_length = 2;
176 if (lower_than (a.list[a.index + 1], a.list[a.index])) {
177 descending = true;
178 for (int i = a.index + 2; i < a.index + a.length; i++) {
179 if (lower_than (a.list[i], a.list[i-1])) {
180 run_length++;
181 } else {
182 break;
185 } else {
186 descending = false;
187 for (int i = a.index + 2; i < a.index + a.length; i++) {
188 if (lower_than (a.list[i], a.list[i-1])) {
189 break;
190 } else {
191 run_length++;
196 return new Slice<G> (a.list, a.index, run_length);
199 private void insertion_sort (Slice<G> a, int offset) {
200 #if DEBUG
201 message ("Sorting (%d, %d) at %d", a.index, a.length, offset);
202 #endif
203 for (int start = a.index + offset; start < a.index + a.length; start++) {
204 int left = a.index;
205 int right = start;
206 void* pivot = a.list[right];
208 while (left < right) {
209 int p = left + ((right - left) >> 1);
210 if (lower_than (pivot, a.list[p])) {
211 right = p;
212 } else {
213 left = p + 1;
216 assert (left == right);
218 Memory.move (&a.list[left + 1], &a.list[left], sizeof (G) * (start - left));
219 a.list[left] = pivot;
223 private void merge_collapse () {
224 #if DEBUG
225 message ("Merge Collapse");
226 #endif
227 int count = pending.length;
228 while (count > 1) {
229 #if DEBUG
230 message ("Pending count: %d", count);
231 if (count >= 3) {
232 message ("pending[count-3]=%p; pending[count-2]=%p; pending[count-1]=%p",
233 pending[count-3], pending[count-2], pending[count-1]);
235 #endif
236 if (count >= 3 && pending[count-3].length <= pending[count-2].length + pending[count-1].length) {
237 if (pending[count-3].length < pending[count-1].length) {
238 merge_at (count-3);
239 } else {
240 merge_at (count-2);
242 } else if (pending[count-2].length <= pending[count-1].length) {
243 merge_at (count-2);
244 } else {
245 break;
247 count = pending.length;
248 #if DEBUG
249 message ("New pending count: %d", count);
250 #endif
254 private void merge_force_collapse () {
255 #if DEBUG
256 message ("Merge Force Collapse");
257 #endif
258 int count = pending.length;
259 #if DEBUG
260 message ("Pending count: %d", count);
261 #endif
262 while (count > 1) {
263 if (count >= 3 && pending[count-3].length < pending[count-1].length) {
264 merge_at (count-3);
265 } else {
266 merge_at (count-2);
268 count = pending.length;
269 #if DEBUG
270 message ("New pending count: %d", count);
271 #endif
275 private void merge_at (int index) {
276 #if DEBUG
277 message ("Merge at %d", index);
278 #endif
279 Slice<G> a = (owned) pending[index];
280 Slice<G> b = (owned) pending[index + 1];
282 assert (a.length > 0);
283 assert (b.length > 0);
284 assert (a.index + a.length == b.index);
286 pending[index] = new Slice<G> (list, a.index, a.length + b.length);
287 pending.move (index + 2, index + 1, pending.length - index - 2);
288 pending.length -= 1;
290 int sorted_count = gallop_rightmost (b.peek_first (), a, 0);
291 a.shorten_start (sorted_count);
292 if (a.length == 0) {
293 return;
296 b.length = gallop_leftmost (a.peek_last (), b, b.length - 1);
297 if (b.length == 0) {
298 return;
301 if (a.length <= b.length) {
302 merge_low ((owned) a, (owned) b);
303 } else {
304 merge_high ((owned) a, (owned) b);
308 private int gallop_leftmost (G key, Slice<G> a, int hint) {
309 #if DEBUG
310 message ("Galop leftmost in (%d, %d), hint=%d", a.index, a.length, hint);
311 #endif
312 assert (0 <= hint);
313 assert (hint < a.length);
315 int p = a.index + hint;
316 int last_offset = 0;
317 int offset = 1;
318 if (lower_than (a.list[p], key)) {
319 int max_offset = a.length - hint;
320 while (offset < max_offset) {
321 if (lower_than (a.list[p + offset], key)) {
322 last_offset = offset;
323 offset <<= 1;
324 offset++;
325 } else {
326 break;
330 if (offset > max_offset) {
331 offset = max_offset;
334 last_offset = hint + last_offset;
335 offset = hint + offset;
336 } else {
337 int max_offset = hint + 1;
338 while (offset < max_offset) {
339 if (lower_than (a.list[p - offset], key)) {
340 break;
341 } else {
342 last_offset = offset;
343 offset <<= 1;
344 offset++;
348 if (offset > max_offset) {
349 offset = max_offset;
352 int temp_last_offset = last_offset;
353 int temp_offset = offset;
354 last_offset = hint - temp_offset;
355 offset = hint - temp_last_offset;
358 assert (-1 <= last_offset);
359 assert (last_offset < offset);
360 assert (offset <= a.length);
362 last_offset += 1;
363 while (last_offset < offset) {
364 int m = last_offset + ((offset - last_offset) >> 1);
365 if (lower_than (a.list[a.index + m], key)) {
366 last_offset = m + 1;
367 } else {
368 offset = m;
372 assert (last_offset == offset);
373 return offset;
376 private int gallop_rightmost (G key, Slice<G> a, int hint) {
377 #if DEBUG
378 message ("Galop rightmost in (%d, %d), hint=%d", a.index, a.length, hint);
379 #endif
380 assert (0 <= hint);
381 assert (hint < a.length);
383 int p = a.index + hint;
384 int last_offset = 0;
385 int offset = 1;
386 if (lower_than_or_equal_to (a.list[p], key)) {
387 int max_offset = a.length - hint;
388 while (offset < max_offset) {
389 if (lower_than_or_equal_to (a.list[p + offset], key)) {
390 last_offset = offset;
391 offset <<= 1;
392 offset++;
393 } else {
394 break;
398 if (offset > max_offset) {
399 offset = max_offset;
402 last_offset = hint + last_offset;
403 offset = hint + offset;
404 } else {
405 int max_offset = hint + 1;
406 while (offset < max_offset) {
407 if (lower_than_or_equal_to (a.list[p - offset], key)) {
408 break;
409 } else {
410 last_offset = offset;
411 offset <<= 1;
412 offset++;
416 if (offset > max_offset) {
417 offset = max_offset;
420 int temp_last_offset = last_offset;
421 int temp_offset = offset;
422 last_offset = hint - temp_offset;
423 offset = hint - temp_last_offset;
426 assert (-1 <= last_offset);
427 assert (last_offset < offset);
428 assert (offset <= a.length);
430 last_offset += 1;
431 while (last_offset < offset) {
432 int m = last_offset + ((offset - last_offset) >> 1);
433 if (lower_than_or_equal_to (a.list[a.index + m], key)) {
434 last_offset = m + 1;
435 } else {
436 offset = m;
440 assert (last_offset == offset);
441 return offset;
444 private void merge_low (owned Slice<G> a, owned Slice<G> b) {
445 #if DEBUG
446 message ("Merge low (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length);
447 #endif
448 assert (a.length > 0);
449 assert (b.length > 0);
450 assert (a.index + a.length == b.index);
452 int minimum_gallop = this.minimum_gallop;
453 int dest = a.index;
454 a.copy ();
456 try {
457 list[dest++] = b.pop_first ();
458 if (a.length == 1 || b.length == 0) {
459 return;
462 while (true) {
463 int a_count = 0;
464 int b_count = 0;
466 while (true) {
467 if (lower_than (b.peek_first (), a.peek_first ())) {
468 list[dest++] = b.pop_first ();
469 if (b.length == 0) {
470 return;
473 b_count++;
474 a_count = 0;
475 if (b_count >= minimum_gallop) {
476 break;
478 } else {
479 list[dest++] = a.pop_first ();
480 if (a.length == 1) {
481 return;
484 a_count++;
485 b_count = 0;
486 if (a_count >= minimum_gallop) {
487 break;
492 minimum_gallop++;
494 while (true) {
495 minimum_gallop -= (minimum_gallop > 1 ? 1 : 0);
496 this.minimum_gallop = minimum_gallop;
498 a_count = gallop_rightmost (b.peek_first (), a, 0);
499 a.merge_in (list, a.index, dest, a_count);
500 dest += a_count;
501 a.shorten_start (a_count);
502 if (a.length <= 1) {
503 return;
506 list[dest++] = b.pop_first ();
507 if (b.length == 0) {
508 return;
511 b_count = gallop_leftmost (a.peek_first (), b, 0);
512 b.merge_in (list, b.index, dest, b_count);
513 dest += b_count;
514 b.shorten_start (b_count);
515 if (b.length == 0) {
516 return;
519 list[dest++] = a.pop_first ();
520 if (a.length == 1) {
521 return;
524 if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) {
525 break;
529 minimum_gallop++;
530 this.minimum_gallop = minimum_gallop;
532 } finally {
533 assert (a.length >= 0);
534 assert (b.length >= 0);
535 b.merge_in (list, b.index, dest, b.length);
536 a.merge_in (list, a.index, dest + b.length, a.length);
540 private void merge_high (owned Slice<G> a, owned Slice<G> b) {
541 #if DEBUG
542 message ("Merge high (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length);
543 #endif
544 assert (a.length > 0);
545 assert (b.length > 0);
546 assert (a.index + a.length == b.index);
548 int minimum_gallop = this.minimum_gallop;
549 int dest = b.index + b.length;
550 b.copy ();
552 try {
553 list[--dest] = a.pop_last ();
554 if (a.length == 0 || b.length == 1) {
555 return;
558 while (true) {
559 int a_count = 0;
560 int b_count = 0;
562 while (true) {
563 if (lower_than (b.peek_last (), a.peek_last ())) {
564 list[--dest] = a.pop_last ();
565 if (a.length == 0) {
566 return;
569 a_count++;
570 b_count = 0;
571 if (a_count >= minimum_gallop) {
572 break;
574 } else {
575 list[--dest] = b.pop_last ();
576 if (b.length == 1) {
577 return;
580 b_count++;
581 a_count = 0;
582 if (b_count >= minimum_gallop) {
583 break;
588 minimum_gallop++;
590 while (true) {
591 minimum_gallop -= (minimum_gallop > 1 ? 1 : 0);
592 this.minimum_gallop = minimum_gallop;
594 int k = gallop_rightmost (b.peek_last (), a, a.length - 1);
595 a_count = a.length - k;
596 a.merge_in_reversed (list, a.index + k, dest - a_count, a_count);
597 dest -= a_count;
598 a.shorten_end (a_count);
599 if (a.length == 0) {
600 return;
603 list[--dest] = b.pop_last ();
604 if (b.length == 1) {
605 return;
608 k = gallop_leftmost (a.peek_last (), b, b.length - 1);
609 b_count = b.length - k;
610 b.merge_in_reversed (list, b.index + k, dest - b_count, b_count);
611 dest -= b_count;
612 b.shorten_end (b_count);
613 if (b.length <= 1) {
614 return;
617 list[--dest] = a.pop_last ();
618 if (a.length == 0) {
619 return;
622 if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) {
623 break;
627 minimum_gallop++;
628 this.minimum_gallop = minimum_gallop;
630 } finally {
631 assert (a.length >= 0);
632 assert (b.length >= 0);
633 a.merge_in_reversed (list, a.index, dest - a.length, a.length);
634 b.merge_in_reversed (list, b.index, dest - a.length - b.length, b.length);
638 [Compact]
639 private class Slice<G> {
641 public void** list;
642 public void** new_list;
643 public int index;
644 public int length;
646 public Slice (void** list, int index, int length) {
647 this.list = list;
648 this.index = index;
649 this.length = length;
652 ~Slice () {
653 if (new_list != null)
654 free (new_list);
657 public void copy () {
658 new_list = Memory.dup (&list[index], (uint) sizeof (G) * length);
659 list = new_list;
660 index = 0;
663 public inline void merge_in (void** dest_array, int index, int dest_index, int count) {
664 Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count);
667 public inline void merge_in_reversed (void** dest_array, int index, int dest_index, int count) {
668 Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count);
671 public inline void shorten_start (int n) {
672 index += n;
673 length -= n;
676 public inline void shorten_end (int n) {
677 length -= n;
680 public inline void* pop_first () {
681 length--;
682 return list[index++];
685 public inline void* pop_last () {
686 length--;
687 return list[index + length];
690 public inline unowned void* peek_first () {
691 return list[index];
694 public inline unowned void* peek_last () {
695 return list[index + length - 1];
698 public void reverse () {
699 int low = index;
700 int high = index + length - 1;
701 while (low < high) {
702 swap (low++, high--);
706 private inline void swap (int i, int j) {
707 void* temp = list[i];
708 list[i] = list[j];
709 list[j] = temp;