diff options
author | Rico Tzschichholz <ricotz@ubuntu.com> | 2017-06-01 11:22:27 +0200 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2017-06-27 13:01:43 +0200 |
commit | 497f370a8c39f16eece6f97d379f24f66b56e1d4 (patch) | |
tree | b9f7ea53a40aac5d1b65988cf33e20f2627c47a5 /gee | |
parent | a828342c4195e7f3cea23f56cb037dff69b5d506 (diff) | |
download | vala-497f370a8c39f16eece6f97d379f24f66b56e1d4.tar.gz |
gee: Add some useful symbols from gee-0.8
Diffstat (limited to 'gee')
-rw-r--r-- | gee/Makefile.am | 1 | ||||
-rw-r--r-- | gee/arraylist.vala | 33 | ||||
-rw-r--r-- | gee/collection.vala | 165 | ||||
-rw-r--r-- | gee/hashmap.vala | 74 | ||||
-rw-r--r-- | gee/hashset.vala | 60 | ||||
-rw-r--r-- | gee/iterator.vala | 20 | ||||
-rw-r--r-- | gee/list.vala | 46 | ||||
-rw-r--r-- | gee/timsort.vala | 713 |
8 files changed, 1090 insertions, 22 deletions
diff --git a/gee/Makefile.am b/gee/Makefile.am index 04dcf3702..fd8779c6f 100644 --- a/gee/Makefile.am +++ b/gee/Makefile.am @@ -24,6 +24,7 @@ libgee_la_VALASOURCES = \ list.vala \ map.vala \ set.vala \ + timsort.vala \ $(NULL) libgee_la_SOURCES = \ diff --git a/gee/arraylist.vala b/gee/arraylist.vala index fe398e432..80b20e9c2 100644 --- a/gee/arraylist.vala +++ b/gee/arraylist.vala @@ -36,8 +36,8 @@ public class Vala.ArrayList<G> : List<G> { set { _equal_func = value; } } - private G[] _items = new G[4]; - private int _size; + internal G[] _items = new G[4]; + internal int _size; private EqualFunc<G> _equal_func; // concurrent modification protection @@ -110,14 +110,16 @@ public class Vala.ArrayList<G> : List<G> { return false; } - public override void remove_at (int index) { + public override G remove_at (int index) { assert (index >= 0 && index < _size); + G item = _items[index]; _items[index] = null; shift (index + 1, -1); _stamp++; + return item; } public override void clear () { @@ -162,6 +164,7 @@ public class Vala.ArrayList<G> : List<G> { private ArrayList<G> _list; private int _index = -1; + protected bool _removed = false; // concurrent modification protection public int _stamp = 0; @@ -174,12 +177,19 @@ public class Vala.ArrayList<G> : List<G> { assert (_stamp == _list._stamp); if (_index < _list._size) { _index++; + _removed = false; } return (_index < _list._size); } + public override bool has_next () { + assert (_stamp == _list._stamp); + return (_index + 1 < _list._size); + } + public override G? get () { assert (_stamp == _list._stamp); + assert (! _removed); if (_index < 0 || _index >= _list._size) { return null; @@ -187,6 +197,23 @@ public class Vala.ArrayList<G> : List<G> { return _list.get (_index); } + + public override void remove () { + assert (_stamp == _list._stamp); + assert (! _removed && _index >= 0); + assert (_index < _list._size); + + _list.remove_at (_index); + _index--; + _removed = true; + _stamp = _list._stamp; + } + + public override bool valid { + get { + return _index >= 0 && _index < _list._size && ! _removed; + } + } } } diff --git a/gee/collection.vala b/gee/collection.vala index 76f568a1f..84162232a 100644 --- a/gee/collection.vala +++ b/gee/collection.vala @@ -31,6 +31,11 @@ public abstract class Vala.Collection<G> : Iterable<G> { public abstract int size { get; } /** + * Specifies whether this collection is empty. + */ + public virtual bool is_empty { get { return size == 0; } } + + /** * Determines whether this collection contains the specified item. * * @param item the item to locate in the collection @@ -64,5 +69,165 @@ public abstract class Vala.Collection<G> : Iterable<G> { * read-only collections. */ public abstract void clear (); + + /** + * Adds all items in the input collection to this collection. + * + * @param collection the collection which items will be added to this + * collection. + * + * @return ``true`` if the collection has been changed, ``false`` otherwise + */ + public virtual bool add_all (Collection<G> collection) { + bool changed = false; + for (Iterator<G> iter = collection.iterator (); iter.next ();) { + G item = iter.get (); + if (!contains (item)) { + add (item); + changed = true; + } + } + return changed; + } + + /** + * Returns an array containing all of items from this collection. + * + * @return an array containing all of items from this collection + */ + public virtual G[] to_array () { + var t = typeof (G); + if (t == typeof (bool)) { + return (G[]) to_bool_array ((Collection<bool>) this); + } else if (t == typeof (char)) { + return (G[]) to_char_array ((Collection<char>) this); + } else if (t == typeof (uchar)) { + return (G[]) to_uchar_array ((Collection<uchar>) this); + } else if (t == typeof (int)) { + return (G[]) to_int_array ((Collection<int>) this); + } else if (t == typeof (uint)) { + return (G[]) to_uint_array ((Collection<uint>) this); + } else if (t == typeof (int64)) { + return (G[]) to_int64_array ((Collection<int64>) this); + } else if (t == typeof (uint64)) { + return (G[]) to_uint64_array ((Collection<uint64>) this); + } else if (t == typeof (long)) { + return (G[]) to_long_array ((Collection<long>) this); + } else if (t == typeof (ulong)) { + return (G[]) to_ulong_array ((Collection<ulong>) this); + } else if (t == typeof (float)) { + return (G[]) to_float_array ((Collection<float>) this); + } else if (t == typeof (double)) { + return (G[]) to_double_array ((Collection<double>) this); + } else if (t.is_enum () || t.is_flags ()) { + return (G[]) to_int_array ((Collection<int>) this); + } else { + G[] array = new G[size]; + int index = 0; + foreach (G element in this) { + array[index++] = (owned)element; + } + return array; + } + } + + private static bool[] to_bool_array (Collection<bool> coll) { + bool[] array = new bool[coll.size]; + int index = 0; + foreach (bool element in coll) { + array[index++] = element; + } + return array; + } + + private static char[] to_char_array (Collection<char> coll) { + char[] array = new char[coll.size]; + int index = 0; + foreach (char element in coll) { + array[index++] = element; + } + return array; + } + + private static uchar[] to_uchar_array (Collection<uchar> coll) { + uchar[] array = new uchar[coll.size]; + int index = 0; + foreach (uchar element in coll) { + array[index++] = element; + } + return array; + } + + private static int[] to_int_array (Collection<int> coll) { + int[] array = new int[coll.size]; + int index = 0; + foreach (int element in coll) { + array[index++] = element; + } + return array; + } + + private static uint[] to_uint_array (Collection<uint> coll) { + uint[] array = new uint[coll.size]; + int index = 0; + foreach (uint element in coll) { + array[index++] = element; + } + return array; + } + + private static int64?[] to_int64_array (Collection<int64?> coll) { + int64?[] array = new int64?[coll.size]; + int index = 0; + foreach (int64? element in coll) { + array[index++] = (owned)element; + } + return array; + } + + private static uint64?[] to_uint64_array (Collection<uint64?> coll) { + uint64?[] array = new uint64?[coll.size]; + int index = 0; + foreach (uint64? element in coll) { + array[index++] = (owned)element; + } + return array; + } + + private static long[] to_long_array (Collection<long> coll) { + long[] array = new long[coll.size]; + int index = 0; + foreach (long element in coll) { + array[index++] = element; + } + return array; + } + + private static ulong[] to_ulong_array (Collection<ulong> coll) { + ulong[] array = new ulong[coll.size]; + int index = 0; + foreach (ulong element in coll) { + array[index++] = element; + } + return array; + } + + private static float?[] to_float_array (Collection<float?> coll) { + float?[] array = new float?[coll.size]; + int index = 0; + foreach (float? element in coll) { + array[index++] = (owned)element; + } + return array; + } + + private static double?[] to_double_array (Collection<double?> coll) { + double?[] array = new double?[coll.size]; + int index = 0; + foreach (double? element in coll) { + array[index++] = (owned)element; + } + return array; + } } diff --git a/gee/hashmap.vala b/gee/hashmap.vala index 9085f10e4..8590c7dc1 100644 --- a/gee/hashmap.vala +++ b/gee/hashmap.vala @@ -282,6 +282,7 @@ public class Vala.HashMap<K,V> : Map<K,V> { private HashMap<K,V> _map; private int _index = -1; private weak Node<K,V> _node; + private weak Node<K,V> _next; // concurrent modification protection private int _stamp; @@ -291,21 +292,45 @@ public class Vala.HashMap<K,V> : Map<K,V> { } public override bool next () { - if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _map._array_size) { - _index++; - _node = _map._nodes[_index]; + assert (_stamp == _map._stamp); + if (!has_next ()) { + return false; } + _node = _next; + _next = null; return (_node != null); } + public override bool has_next () { + assert (_stamp == _map._stamp); + if (_next == null) { + _next = _node; + if (_next != null) { + _next = _next.next; + } + while (_next == null && _index + 1 < _map._array_size) { + _index++; + _next = _map._nodes[_index]; + } + } + return (_next != null); + } + public override K? get () { assert (_stamp == _map._stamp); assert (_node != null); return _node.key; } + + public override void remove () { + assert_not_reached (); + } + + public override bool valid { + get { + return _node != null; + } + } } private class ValueCollection<K,V> : Collection<V> { @@ -365,6 +390,7 @@ public class Vala.HashMap<K,V> : Map<K,V> { private HashMap<V,K> _map; private int _index = -1; private weak Node<K,V> _node; + private weak Node<K,V> _next; // concurrent modification protection private int _stamp; @@ -374,21 +400,45 @@ public class Vala.HashMap<K,V> : Map<K,V> { } public override bool next () { - if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _map._array_size) { - _index++; - _node = _map._nodes[_index]; + assert (_stamp == _map._stamp); + if (!has_next ()) { + return false; } + _node = _next; + _next = null; return (_node != null); } + public override bool has_next () { + assert (_stamp == _map._stamp); + if (_next == null) { + _next = _node; + if (_next != null) { + _next = _next.next; + } + while (_next == null && _index + 1 < _map._array_size) { + _index++; + _next = _map._nodes[_index]; + } + } + return (_next != null); + } + public override V? get () { assert (_stamp == _map._stamp); assert (_node != null); return _node.value; } + + public override void remove () { + assert_not_reached (); + } + + public override bool valid { + get { + return _node != null; + } + } } } diff --git a/gee/hashset.vala b/gee/hashset.vala index 6ca0dfa22..ff20e8127 100644 --- a/gee/hashset.vala +++ b/gee/hashset.vala @@ -127,6 +127,24 @@ public class Vala.HashSet<G> : Set<G> { resize (); } + private inline bool remove_helper (G key) { + Node<G>** node = lookup_node (key); + if (*node != null) { + assert (*node != null); + Node<G> next = (owned) (*node)->next; + + (*node)->key = null; + delete *node; + + *node = (owned) next; + + _nnodes--; + _stamp++; + return true; + } + return false; + } + private void resize () { if ((_array_size >= 3 * _nnodes && _array_size >= MIN_SIZE) || (3 * _array_size <= _nnodes && _array_size < MAX_SIZE)) { @@ -177,6 +195,7 @@ public class Vala.HashSet<G> : Set<G> { private HashSet<G> _set; private int _index = -1; private weak Node<G> _node; + private weak Node<G> _next; // concurrent modification protection private int _stamp = 0; @@ -186,21 +205,50 @@ public class Vala.HashSet<G> : Set<G> { } public override bool next () { - if (_node != null) { - _node = _node.next; - } - while (_node == null && _index + 1 < _set._array_size) { - _index++; - _node = _set._nodes[_index]; + assert (_stamp == _set._stamp); + if (!has_next ()) { + return false; } + _node = _next; + _next = null; return (_node != null); } + public override bool has_next () { + assert (_stamp == _set._stamp); + if (_next == null) { + _next = _node; + if (_next != null) { + _next = _next.next; + } + while (_next == null && _index + 1 < _set._array_size) { + _index++; + _next = _set._nodes[_index]; + } + } + return (_next != null); + } + public override G? get () { assert (_stamp == _set._stamp); assert (_node != null); return _node.key; } + + public override void remove () { + assert (_stamp == _set._stamp); + assert (_node != null); + has_next (); + _set.remove_helper (_node.key); + _node = null; + _stamp = _set._stamp; + } + + public override bool valid { + get { + return _node != null; + } + } } } diff --git a/gee/iterator.vala b/gee/iterator.vala index 5190fb652..224fa6281 100644 --- a/gee/iterator.vala +++ b/gee/iterator.vala @@ -33,10 +33,30 @@ public abstract class Vala.Iterator<G> { public abstract bool next (); /** + * Checks whether there is a next element in the iteration. + * + * @return ``true`` if the iterator has a next element + */ + public abstract bool has_next (); + + /** * Returns the current element in the iteration. * * @return the current element in the iteration */ public abstract G? get (); + + /** + * Removes the current element in the iteration. The cursor is set in an + * in-between state. Both {@link get} and {@link remove} will fail until + * the next move of the cursor (calling {@link next}). + */ + public abstract void remove (); + + /** + * Determines wheather the call to {@link get} is legal. It is false at the + * beginning and after {@link remove} call and true otherwise. + */ + public abstract bool valid { get; } } diff --git a/gee/list.vala b/gee/list.vala index d73028337..57ba35da6 100644 --- a/gee/list.vala +++ b/gee/list.vala @@ -61,7 +61,51 @@ public abstract class Vala.List<G> : Collection<G> { * Removes the item at the specified index of this list. * * @param index zero-based index of the item to be removed + * + * @return the removed element + */ + public abstract G remove_at (int index); + + /** + * Returns the first item of the list. Fails if the list is empty. + * + * @return first item in the list + */ + public virtual G first () { + return @get (0); + } + + /** + * Returns the last item of the list. Fails if the list is empty. + * + * @return last item in the list + */ + public virtual G last () { + return @get (size - 1); + } + + /** + * Inserts items into this list for the input collection at the + * specified position. + * + * @param index zero-based index of the items to be inserted + * @param collection collection of items to be inserted + */ + public virtual void insert_all (int index, Collection<G> collection) { + for (Iterator<G> iter = collection.iterator (); iter.next ();) { + G item = iter.get (); + insert (index, item); + index++; + } + } + + /** + * Sorts items by comparing with the specified compare function. + * + * @param compare_func compare function to use to compare items */ - public abstract void remove_at (int index); + public virtual void sort (owned CompareDataFunc<G> compare_func) { + TimSort.sort<G> (this, compare_func); + } } diff --git a/gee/timsort.vala b/gee/timsort.vala new file mode 100644 index 000000000..58c4d08e8 --- /dev/null +++ b/gee/timsort.vala @@ -0,0 +1,713 @@ +/* timsort.vala + * + * Copyright (C) 2009 Didier Villevalois + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Didier 'Ptitjes Villevalois <ptitjes@free.fr> + */ + +/** + * A stable, adaptive, iterative mergesort that requires far fewer than n*lg(n) + * comparisons when running on partially sorted arrays, while offering + * performance comparable to a traditional mergesort when run on random arrays. + * Like all proper mergesorts, this sort is stable and runs O(n*log(n)) time + * (worst case). In the worst case, this sort requires temporary storage space + * for n/2 object references; in the best case, it requires only a small + * constant amount of space. + * + * This implementation was adapted from Tim Peters's list sort for Python, + * which is described in detail here: + * [[http://svn.python.org/projects/python/trunk/Objects/listsort.txt]] + * + * Tim's C code may be found here: + * [[http://svn.python.org/projects/python/trunk/Objects/listobject.c]] + * + * The underlying techniques are described in this paper (and may have even + * earlier origins): + * + * "Optimistic Sorting and Information Theoretic Complexity" + * Peter McIlroy + * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), pp + * 467-474, Austin, Texas, 25-27 January 1993. + */ +internal class Vala.TimSort<G> { + + public static void sort<G> (List<G> list, CompareDataFunc<G> compare) { + if (list is ArrayList) { + TimSort.sort_arraylist<G> ((ArrayList<G>) list, compare); + } else { + TimSort.sort_list<G> (list, compare); + } + } + + private static void sort_list<G> (List<G> list, CompareDataFunc<G> compare) { + TimSort<G> helper = new TimSort<G> (); + + helper.list_collection = list; + helper.array = list.to_array (); + helper.list = helper.array; + helper.index = 0; + helper.size = list.size; + helper.compare = compare; + + helper.do_sort (); + + // TODO Use a list iterator and use iter.set (item) + list.clear (); + foreach (G item in helper.array) { + list.add (item); + } + } + + private static void sort_arraylist<G> (ArrayList<G> list, CompareDataFunc<G> compare) { + TimSort<G> helper = new TimSort<G> (); + + helper.list_collection = list; + helper.list = list._items; + helper.index = 0; + helper.size = list._size; + helper.compare = compare; + + helper.do_sort (); + } + + private const int MINIMUM_GALLOP = 7; + + private List<G> list_collection; + private G[] array; + private void** list; + private int index; + private int size; + private Slice<G>[] pending; + private int minimum_gallop; + private unowned CompareDataFunc<G> compare; + + private void do_sort () { + if (size < 2) { + return; + } + + pending = new Slice<G>[0]; + minimum_gallop = MINIMUM_GALLOP; + + Slice<G> remaining = new Slice<G> (list, index, size); + int minimum_length = compute_minimum_run_length (remaining.length); + + while (remaining.length > 0) { + // Get the next run + bool descending; + Slice<G> run = compute_longest_run (remaining, out descending); + #if DEBUG + message ("New run (%d, %d) %s", run.index, run.length, + descending ? "descending" : "ascending"); + #endif + if (descending) { + run.reverse (); + } + + // Extend it to minimum_length, if needed + if (run.length < minimum_length) { + int sorted_count = run.length; + run.length = int.min (minimum_length, remaining.length); + insertion_sort (run, sorted_count); + #if DEBUG + message ("Extended to (%d, %d) and sorted from index %d", + run.index, run.length, sorted_count); + #endif + } + + // Move remaining after run + remaining.shorten_start (run.length); + + // Add run to pending runs and try to merge + pending += (owned) run; + merge_collapse (); + } + + assert (remaining.index == size); + + merge_force_collapse (); + + assert (pending.length == 1); + assert (pending[0].index == 0); + assert (pending[0].length == size); + } + + private delegate bool LowerFunc (G left, G right); + + private inline bool lower_than (G left, G right) { + return compare (left, right) < 0; + } + + private inline bool lower_than_or_equal_to (G left, G right) { + return compare (left, right) <= 0; + } + + private int compute_minimum_run_length (int length) { + int run_length = 0; + while (length >= 64) { + run_length |= length & 1; + length >>= 1; + } + return length + run_length; + } + + private Slice<G> compute_longest_run (Slice<G> a, out bool descending) { + int run_length; + if (a.length <= 1) { + run_length = a.length; + descending = false; + } else { + run_length = 2; + if (lower_than (a.list[a.index + 1], a.list[a.index])) { + descending = true; + for (int i = a.index + 2; i < a.index + a.length; i++) { + if (lower_than (a.list[i], a.list[i-1])) { + run_length++; + } else { + break; + } + } + } else { + descending = false; + for (int i = a.index + 2; i < a.index + a.length; i++) { + if (lower_than (a.list[i], a.list[i-1])) { + break; + } else { + run_length++; + } + } + } + } + return new Slice<G> (a.list, a.index, run_length); + } + + private void insertion_sort (Slice<G> a, int offset) { + #if DEBUG + message ("Sorting (%d, %d) at %d", a.index, a.length, offset); + #endif + for (int start = a.index + offset; start < a.index + a.length; start++) { + int left = a.index; + int right = start; + void* pivot = a.list[right]; + + while (left < right) { + int p = left + ((right - left) >> 1); + if (lower_than (pivot, a.list[p])) { + right = p; + } else { + left = p + 1; + } + } + assert (left == right); + + Memory.move (&a.list[left + 1], &a.list[left], sizeof (G) * (start - left)); + a.list[left] = pivot; + } + } + + private void merge_collapse () { + #if DEBUG + message ("Merge Collapse"); + #endif + int count = pending.length; + while (count > 1) { + #if DEBUG + message ("Pending count: %d", count); + if (count >= 3) { + message ("pending[count-3]=%p; pending[count-2]=%p; pending[count-1]=%p", + pending[count-3], pending[count-2], pending[count-1]); + } + #endif + if (count >= 3 && pending[count-3].length <= pending[count-2].length + pending[count-1].length) { + if (pending[count-3].length < pending[count-1].length) { + merge_at (count-3); + } else { + merge_at (count-2); + } + } else if (pending[count-2].length <= pending[count-1].length) { + merge_at (count-2); + } else { + break; + } + count = pending.length; + #if DEBUG + message ("New pending count: %d", count); + #endif + } + } + + private void merge_force_collapse () { + #if DEBUG + message ("Merge Force Collapse"); + #endif + int count = pending.length; + #if DEBUG + message ("Pending count: %d", count); + #endif + while (count > 1) { + if (count >= 3 && pending[count-3].length < pending[count-1].length) { + merge_at (count-3); + } else { + merge_at (count-2); + } + count = pending.length; + #if DEBUG + message ("New pending count: %d", count); + #endif + } + } + + private void merge_at (int index) { + #if DEBUG + message ("Merge at %d", index); + #endif + Slice<G> a = (owned) pending[index]; + Slice<G> b = (owned) pending[index + 1]; + + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + pending[index] = new Slice<G> (list, a.index, a.length + b.length); + pending.move (index + 2, index + 1, pending.length - index - 2); + pending.length -= 1; + + int sorted_count = gallop_rightmost (b.peek_first (), a, 0); + a.shorten_start (sorted_count); + if (a.length == 0) { + return; + } + + b.length = gallop_leftmost (a.peek_last (), b, b.length - 1); + if (b.length == 0) { + return; + } + + if (a.length <= b.length) { + merge_low ((owned) a, (owned) b); + } else { + merge_high ((owned) a, (owned) b); + } + } + + private int gallop_leftmost (G key, Slice<G> a, int hint) { + #if DEBUG + message ("Galop leftmost in (%d, %d), hint=%d", a.index, a.length, hint); + #endif + assert (0 <= hint); + assert (hint < a.length); + + int p = a.index + hint; + int last_offset = 0; + int offset = 1; + if (lower_than (a.list[p], key)) { + int max_offset = a.length - hint; + while (offset < max_offset) { + if (lower_than (a.list[p + offset], key)) { + last_offset = offset; + offset <<= 1; + offset++; + } else { + break; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + last_offset = hint + last_offset; + offset = hint + offset; + } else { + int max_offset = hint + 1; + while (offset < max_offset) { + if (lower_than (a.list[p - offset], key)) { + break; + } else { + last_offset = offset; + offset <<= 1; + offset++; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + int temp_last_offset = last_offset; + int temp_offset = offset; + last_offset = hint - temp_offset; + offset = hint - temp_last_offset; + } + + assert (-1 <= last_offset); + assert (last_offset < offset); + assert (offset <= a.length); + + last_offset += 1; + while (last_offset < offset) { + int m = last_offset + ((offset - last_offset) >> 1); + if (lower_than (a.list[a.index + m], key)) { + last_offset = m + 1; + } else { + offset = m; + } + } + + assert (last_offset == offset); + return offset; + } + + private int gallop_rightmost (G key, Slice<G> a, int hint) { + #if DEBUG + message ("Galop rightmost in (%d, %d), hint=%d", a.index, a.length, hint); + #endif + assert (0 <= hint); + assert (hint < a.length); + + int p = a.index + hint; + int last_offset = 0; + int offset = 1; + if (lower_than_or_equal_to (a.list[p], key)) { + int max_offset = a.length - hint; + while (offset < max_offset) { + if (lower_than_or_equal_to (a.list[p + offset], key)) { + last_offset = offset; + offset <<= 1; + offset++; + } else { + break; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + last_offset = hint + last_offset; + offset = hint + offset; + } else { + int max_offset = hint + 1; + while (offset < max_offset) { + if (lower_than_or_equal_to (a.list[p - offset], key)) { + break; + } else { + last_offset = offset; + offset <<= 1; + offset++; + } + } + + if (offset > max_offset) { + offset = max_offset; + } + + int temp_last_offset = last_offset; + int temp_offset = offset; + last_offset = hint - temp_offset; + offset = hint - temp_last_offset; + } + + assert (-1 <= last_offset); + assert (last_offset < offset); + assert (offset <= a.length); + + last_offset += 1; + while (last_offset < offset) { + int m = last_offset + ((offset - last_offset) >> 1); + if (lower_than_or_equal_to (a.list[a.index + m], key)) { + last_offset = m + 1; + } else { + offset = m; + } + } + + assert (last_offset == offset); + return offset; + } + + private void merge_low (owned Slice<G> a, owned Slice<G> b) { + #if DEBUG + message ("Merge low (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length); + #endif + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + int minimum_gallop = this.minimum_gallop; + int dest = a.index; + a.copy (); + + try { + list[dest++] = b.pop_first (); + if (a.length == 1 || b.length == 0) { + return; + } + + while (true) { + int a_count = 0; + int b_count = 0; + + while (true) { + if (lower_than (b.peek_first (), a.peek_first ())) { + list[dest++] = b.pop_first (); + if (b.length == 0) { + return; + } + + b_count++; + a_count = 0; + if (b_count >= minimum_gallop) { + break; + } + } else { + list[dest++] = a.pop_first (); + if (a.length == 1) { + return; + } + + a_count++; + b_count = 0; + if (a_count >= minimum_gallop) { + break; + } + } + } + + minimum_gallop++; + + while (true) { + minimum_gallop -= (minimum_gallop > 1 ? 1 : 0); + this.minimum_gallop = minimum_gallop; + + a_count = gallop_rightmost (b.peek_first (), a, 0); + a.merge_in (list, a.index, dest, a_count); + dest += a_count; + a.shorten_start (a_count); + if (a.length <= 1) { + return; + } + + list[dest++] = b.pop_first (); + if (b.length == 0) { + return; + } + + b_count = gallop_leftmost (a.peek_first (), b, 0); + b.merge_in (list, b.index, dest, b_count); + dest += b_count; + b.shorten_start (b_count); + if (b.length == 0) { + return; + } + + list[dest++] = a.pop_first (); + if (a.length == 1) { + return; + } + + if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) { + break; + } + } + + minimum_gallop++; + this.minimum_gallop = minimum_gallop; + } + } finally { + assert (a.length >= 0); + assert (b.length >= 0); + b.merge_in (list, b.index, dest, b.length); + a.merge_in (list, a.index, dest + b.length, a.length); + } + } + + private void merge_high (owned Slice<G> a, owned Slice<G> b) { + #if DEBUG + message ("Merge high (%d, %d) (%d, %d)", a.index, a.length, b.index, b.length); + #endif + assert (a.length > 0); + assert (b.length > 0); + assert (a.index + a.length == b.index); + + int minimum_gallop = this.minimum_gallop; + int dest = b.index + b.length; + b.copy (); + + try { + list[--dest] = a.pop_last (); + if (a.length == 0 || b.length == 1) { + return; + } + + while (true) { + int a_count = 0; + int b_count = 0; + + while (true) { + if (lower_than (b.peek_last (), a.peek_last ())) { + list[--dest] = a.pop_last (); + if (a.length == 0) { + return; + } + + a_count++; + b_count = 0; + if (a_count >= minimum_gallop) { + break; + } + } else { + list[--dest] = b.pop_last (); + if (b.length == 1) { + return; + } + + b_count++; + a_count = 0; + if (b_count >= minimum_gallop) { + break; + } + } + } + + minimum_gallop++; + + while (true) { + minimum_gallop -= (minimum_gallop > 1 ? 1 : 0); + this.minimum_gallop = minimum_gallop; + + int k = gallop_rightmost (b.peek_last (), a, a.length - 1); + a_count = a.length - k; + a.merge_in_reversed (list, a.index + k, dest - a_count, a_count); + dest -= a_count; + a.shorten_end (a_count); + if (a.length == 0) { + return; + } + + list[--dest] = b.pop_last (); + if (b.length == 1) { + return; + } + + k = gallop_leftmost (a.peek_last (), b, b.length - 1); + b_count = b.length - k; + b.merge_in_reversed (list, b.index + k, dest - b_count, b_count); + dest -= b_count; + b.shorten_end (b_count); + if (b.length <= 1) { + return; + } + + list[--dest] = a.pop_last (); + if (a.length == 0) { + return; + } + + if (a_count < MINIMUM_GALLOP && b_count < MINIMUM_GALLOP) { + break; + } + } + + minimum_gallop++; + this.minimum_gallop = minimum_gallop; + } + } finally { + assert (a.length >= 0); + assert (b.length >= 0); + a.merge_in_reversed (list, a.index, dest - a.length, a.length); + b.merge_in_reversed (list, b.index, dest - a.length - b.length, b.length); + } + } + + [Compact] + private class Slice<G> { + + public void** list; + public void** new_list; + public int index; + public int length; + + public Slice (void** list, int index, int length) { + this.list = list; + this.index = index; + this.length = length; + } + + ~Slice () { + if (new_list != null) + free (new_list); + } + + public void copy () { + new_list = Memory.dup (&list[index], (uint) sizeof (G) * length); + list = new_list; + index = 0; + } + + public inline void merge_in (void** dest_array, int index, int dest_index, int count) { + Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count); + } + + public inline void merge_in_reversed (void** dest_array, int index, int dest_index, int count) { + Memory.move (&dest_array[dest_index], &list[index], sizeof (G) * count); + } + + public inline void shorten_start (int n) { + index += n; + length -= n; + } + + public inline void shorten_end (int n) { + length -= n; + } + + public inline void* pop_first () { + length--; + return list[index++]; + } + + public inline void* pop_last () { + length--; + return list[index + length]; + } + + public inline unowned void* peek_first () { + return list[index]; + } + + public inline unowned void* peek_last () { + return list[index + length - 1]; + } + + public void reverse () { + int low = index; + int high = index + length - 1; + while (low < high) { + swap (low++, high--); + } + } + + private inline void swap (int i, int j) { + void* temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + } +} + |