Browse Source

Add SortedVector

Billy Barrow 3 tuần trước cách đây
mục cha
commit
6a440a07dd

+ 140 - 0
src/lib/DataStructures/SortedVector.vala

@@ -0,0 +1,140 @@
+
+namespace Invercargill.DataStructures {
+
+    public class SortedVector<T> : Enumerable<T>, Lot<T>, ReadOnlyCollection<T>, ReadOnlyAddressable<T>, Collection<T> {
+
+        private Vector<T> vector;
+        private CompareDelegate<T> comparitor;
+
+        public SortedVector(owned CompareDelegate<T>? comparitor = null) {
+            this.comparitor = (owned)comparitor ?? Operators.comparison<T>();
+            vector = new Vector<T>();
+        }
+
+        public override int? peek_count() {
+            return vector.count();
+        }
+
+        public override EnumerableInfo get_info() {
+            return new EnumerableInfo.infer_ultimate (this, EnumerableCategory.IN_MEMORY);
+        }
+
+        public override Tracker<T> get_tracker() {
+            return vector.get_tracker();
+        }
+
+        public void add(T item) {
+            lock(vector) {
+                uint index;
+                optimised_search(item, out index);
+                try {
+                    vector.insert_at(index, item);
+                }
+                catch(Error e) {
+                    assert_no_error(e);
+                }
+            }
+        }
+
+        private bool optimised_search(T item, out uint index) {
+            if(vector.count() == 0) {
+                index = 0;
+                return false;
+            }
+
+            // Could it be at the end?
+            index = vector.count()-1;
+            var end_cmp = compare_index(item, index);
+            if(end_cmp == 0) {
+                return true;
+            }
+            if(end_cmp > 0) {
+                index++;
+                var cmp = compare_index(item, index);
+                if(cmp == null || cmp < 0) {
+                    return false;
+                }
+            }
+            else {
+                var cmp = compare_index(item, index-1);
+                if(cmp == null || cmp > 0) {
+                    return false;
+                }
+            }
+
+            // Could it be at the start?
+            if(index != 0) {
+                index = 0;
+                var first_cmp = compare_index(item, index);
+                if(first_cmp == 0) {
+                    return true;
+                }
+                if(first_cmp > 0) {
+                    index++;
+                    var cmp = compare_index(item, index);
+                    if(cmp == null || cmp < 0) {
+                        return false;
+                    }
+                }
+                else {
+                    var cmp = compare_index(item, index-1);
+                    if(cmp == null || cmp > 0) {
+                        return false;
+                    }
+                }
+            }
+
+            return binary_search(s => comparitor(item, s), out index);
+        }
+
+        private int? compare_index(T item, uint index) {
+            if(vector.count() <= index) {
+                return null;
+            }
+            return comparitor(item, vector.get_or_default(index));
+        }
+
+
+        public void add_all(Enumerable<T> items) {
+            items.iterate(i => add(i));
+        }
+
+        public void remove_first_where(Invercargill.PredicateDelegate<T> predicate) {
+            remove_items(predicate, true);
+        }
+        public void remove_all_where(Invercargill.PredicateDelegate<T> predicate) {
+            remove_items(predicate, false);
+        }
+
+        private void remove_items(Invercargill.PredicateDelegate<T> predicate, bool first_only) {
+            lock(vector) {
+                if(first_only)
+                    vector.remove_first_where(predicate);
+                else
+                    vector.remove_all_where(predicate);
+            }
+        }
+
+        public void clear() {
+            lock(vector) {
+                vector.clear();
+            }
+        }
+
+        public new T get(uint index) throws IndexError {
+            return vector[index]; 
+        }
+
+        public bool try_get(uint index, out T value) {
+            var result = vector.try_get(index, out value);
+            return result;
+        }
+
+        public uint? first_index_of(Invercargill.PredicateDelegate<T> predicate) {
+            var result = vector.first_index_of(predicate);
+            return result;
+        }
+
+    }
+
+}

+ 16 - 3
src/lib/DataStructures/Vector.vala

@@ -1,6 +1,6 @@
 namespace Invercargill.DataStructures {
 
-    public class Vector<T> : Enumerable<T>, Lot<T>, ReadOnlyCollection<T>, ReadOnlyAddressable<T>, Collection<T>, Addressable<T> {
+    public class Vector<T> : Enumerable<T>, Lot<T>, ReadOnlyCollection<T>, ReadOnlyAddressable<T>, Collection<T>, Addressable<T>, AddressableCollection<T> {
 
         private T[] array;
         private int n_items = 0;
@@ -101,7 +101,7 @@ namespace Invercargill.DataStructures {
             return false;
         }
 
-        public new void @set(int index, T value) throws IndexError {
+        public new void @set(uint index, T value) throws IndexError {
             IndexError? e = null;
             rw_lock.writer_lock();
             if(index < 0) {
@@ -125,7 +125,20 @@ namespace Invercargill.DataStructures {
             }
         }
 
-        public void remove_at(int index) throws IndexError {
+        public void insert_at(uint index, T item) throws IndexError {
+            rw_lock.writer_lock();
+            if(index > n_items) {
+                rw_lock.writer_unlock();
+                throw new IndexError.INDEX_EXCEEDS_UPPER_BOUNDS(@"Insertion is only allowed at indexes that are less than or equal to the count of the vector. Tried to insert at index $(index), vector has count of $(n_items)");
+            }
+            ensure_room(1);
+            safely_move_items_in_array<T>(array, index, index+1, n_items-index);
+            array_write(index, item);
+            n_items++;
+            rw_lock.writer_unlock();
+        }
+
+        public void remove_at(uint index) throws IndexError {
             var e = remove_internal(index);
             if(e != null) {
                 throw e;

+ 26 - 15
src/lib/Debug.vala

@@ -101,33 +101,44 @@ namespace Invercargill {
 
         private static Enumerable<string> notable_interfaces(Type type) {
             var names = new DataStructures.Vector<string>();
+
             if(type.is_a(typeof(Lot))) {
                 names.add("Lot");
             }
-            if(type.is_a(typeof(ReadOnlyAddressable))) {
-                names.add("ReadOnlyAddressable");
-            }
-            if(type.is_a(typeof(ReadOnlyAssociative))) {
-                names.add("ReadOnlyAssociative");
-            }
-            if(type.is_a(typeof(ReadOnlyCollection))) {
-                names.add("ReadOnlyCollection");
-            }
-            if(type.is_a(typeof(ReadOnlySet))) {
-                names.add("ReadOnlySet");
+
+            if(type.is_a(typeof(AddressableCollection))) {
+                names.add("AddressableCollection");
             }
-            if(type.is_a(typeof(Addressable))) {
-                names.add("Addressable");
+            else {
+                if(type.is_a(typeof(Collection))) {
+                    names.add("Collection");
+                }
+                else if(type.is_a(typeof(ReadOnlyCollection))) {
+                    names.add("ReadOnlyCollection");
+                }
+
+                if(type.is_a(typeof(Addressable))) {
+                    names.add("Addressable");
+                }
+                else if(type.is_a(typeof(ReadOnlyAddressable))){
+                    names.add("ReadOnlyAddressable");
+                }
             }
+
             if(type.is_a(typeof(Associative))) {
                 names.add("Associative");
             }
-            if(type.is_a(typeof(Collection))) {
-                names.add("Collection");
+            else if(type.is_a(typeof(ReadOnlyAssociative))) {
+                names.add("ReadOnlyAssociative");
             }
+
             if(type.is_a(typeof(Set))) {
                 names.add("Set");
             }
+            else if(type.is_a(typeof(ReadOnlySet))) {
+                names.add("ReadOnlySet");
+            }
+
             if(type.is_a(typeof(Elements))) {
                 names.add("Elements");
             }

+ 2 - 0
src/lib/Delegates.vala

@@ -14,6 +14,8 @@ namespace Invercargill {
 
     public delegate int CompareDelegate<T>(T a, T b);
 
+    public delegate int BinarySearchDelegate<T>(T item);
+
     public delegate bool EqualityDelegate<T>(T a, T b);
 
     public delegate uint HashDelegate<T>(T item);

+ 1 - 1
src/lib/Generators/Range.vala

@@ -25,7 +25,7 @@ namespace Invercargill.Generators {
             var i = start;
 
             return new LambdaTracker<int>(
-                () => i < stop,
+                () => (stride > 0) ? (i < stop) : (i > stop),
                 () => {
                     var val = i;
                     i += stride;

+ 1 - 2
src/lib/Interfaces/Addressable.vala

@@ -3,8 +3,7 @@ namespace Invercargill {
     [GenericAccessors]
     public interface Addressable<T> : ReadOnlyAddressable<T> {
 
-        public abstract void @set(int index, T item) throws IndexError;
-        public abstract void remove_at(int index) throws IndexError;
+        public abstract void @set(uint index, T item) throws IndexError;
 
     }
 

+ 11 - 0
src/lib/Interfaces/AddressableCollection.vala

@@ -0,0 +1,11 @@
+namespace Invercargill {
+
+    [GenericAccessors]
+    public interface AddressableCollection<T> : Addressable<T>, Collection<T> {
+
+        public abstract void remove_at(uint index) throws IndexError;
+        public abstract void insert_at(uint index, T item) throws IndexError;
+
+    }
+
+}

+ 38 - 0
src/lib/Interfaces/ReadOnlyAddressable.vala

@@ -20,6 +20,44 @@ namespace Invercargill {
             return null;
         }
 
+        public virtual bool binary_search(BinarySearchDelegate<T> search_func, out uint index) {
+            uint n_items = count();
+            uint low = 0;
+            uint high = n_items;
+            index = 0;
+            while(true) {
+                index = ((high - low) / 2) + low;
+                var cmp = search_func(this.get_or_default(index));
+                if(cmp == 0) {
+                    return true;
+                }
+                else if(cmp > 0 && index+1 < n_items) {
+                    index++;
+                    cmp = search_func(this.get_or_default(index));
+                    if(cmp < 0) {
+                        return false;
+                    }
+                    low = index;
+                }
+                else if(cmp < 0 && index > 0) {
+                    cmp = search_func(this.get_or_default(index));
+                    if(cmp > 0) {
+                        return false;
+                    }
+                    high = index;
+                }
+                else if(cmp > 0){
+                    // Not found, and we are at the end of the array
+                    index++;
+                    return false;
+                }
+                else {
+                    // Not found, and we are at the start of the array
+                    return false;
+                }
+            }
+        }
+
     }
 
 }

+ 4 - 1
src/lib/Operators/Comparison.vala

@@ -1,10 +1,13 @@
-namespace Invercargill {
+namespace Invercargill.Operators {
     
     public static CompareDelegate<T> comparison<T>() {
         var type = typeof(T);
         if(type == typeof(string)) {
             return (a, b) => GLib.strcmp((string)a, (string)b);
         }
+        else if(type == typeof(int)) {
+            return (a, b) => (int)(((int)a) - ((int)b));
+        }
         else if(type == typeof(int64)) {
             return (a, b) => (int)(((int64)a) - ((int64)b));
         }

+ 40 - 0
src/lib/Safety.vala

@@ -84,6 +84,46 @@ namespace Invercargill {
         }
     }
 
+    internal void safely_move_items_in_array<T>(T[] array, uint source, uint destination, uint count) {
+        var t = typeof (T);
+        if (t == typeof (bool)) {
+            ((bool[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (char)) {
+            ((char[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (uchar)) {
+            ((uchar[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (int) || t.is_enum () || t.is_flags ()) {
+            ((int[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (uint)) {
+            ((uint[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (int64)) {
+            ((int64[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (uint64)) {
+            ((uint64[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (long)) {
+            ((long[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (ulong)) {
+            ((ulong[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (float)) {
+            ((float?[])array).move((int)source, (int)destination, (int)count);
+        }
+        else if (t == typeof (double)) {
+            ((double?[])array).move((int)source, (int)destination, (int)count);
+        }
+        else {
+            array.move((int)source, (int)destination, (int)count);
+        }
+    }
+
 
     internal bool safely_read_bool_from_array(bool[] array, uint index) {
         return ((bool[])array)[index];

+ 2 - 0
src/lib/meson.build

@@ -72,6 +72,7 @@ sources += files('Interfaces/Addressable.vala')
 sources += files('Interfaces/Set.vala')
 sources += files('Interfaces/Properties.vala')
 sources += files('Interfaces/Elements.vala')
+sources += files('Interfaces/AddressableCollection.vala')
 
 sources += files('DataStructures/Series.vala')
 sources += files('DataStructures/Fifo.vala')
@@ -80,6 +81,7 @@ sources += files('DataStructures/Vector.vala')
 sources += files('DataStructures/HashSet.vala')
 sources += files('DataStructures/Dictionary.vala')
 sources += files('DataStructures/PropertyDictionary.vala')
+sources += files('DataStructures/SortedVector.vala')
 
 sources += files('Mapping/Mapper.vala')
 sources += files('Mapping/PropertyMapper.vala')

+ 51 - 0
src/tests/Integration/SortedVector.vala

@@ -0,0 +1,51 @@
+using Invercargill;
+using Invercargill.Convert;
+using Invercargill.DataStructures;
+
+
+void sorted_vector_tests() {
+
+    Test.add_func("/invercargill/structure/sorted_series/add_item", () => {
+        var series = new SortedVector<int>();
+        series.add(8);
+
+        assert(series.first_or_default() == 8);
+        assert(series.count() == 1);
+    });
+
+    Test.add_func("/invercargill/structure/sorted_series/add_many", () => {
+
+        var series = new SortedVector<int>();
+        var items = range(0, 10000000);
+        series.add_all(items);
+        assert(series.count() == 10000000);
+
+        series.matches(items, (a, b) => a == b);
+    });
+
+    Test.add_func("/invercargill/structure/sorted_series/add_twice", () => {
+
+        var series = new SortedVector<int>();
+        var items = range(0, 100);
+        series.add_all(items);
+        series.add_all(items);
+        assert(series.count() == 200);
+
+        series.matches(items.interleave(items), (a, b) => a == b);
+    });
+
+    Test.add_func("/invercargill/structure/sorted_series/to_array", () => {
+
+        var items = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var expected = new int[] { 1, 2, 3, 3, 4, 5, 6, 7, 8, 10 };
+        var series = new SortedVector<int>();
+        series.add_all(Convert.ate(items).debug_trace(i => i.to_string()));
+        var array = series.to_array();
+        assert_cmpint(array.length, CompareOperator.EQ, 10);
+        series.debug_dump(n => n.to_string());
+
+        for(int i = 0; i < expected.length; i++) {
+            assert_cmpint(array[i], CompareOperator.EQ, expected[i]);
+        }
+    });
+}

+ 2 - 0
src/tests/TestRunner.vala

@@ -20,11 +20,13 @@ public static int main(string[] args) {
     dictionary_tests();
     property_mapper_tests();
     cache_tests();
+    sorted_vector_tests();
     
     Test.run();
     
     series_speed_test();
     vector_speed_test();
+    //  sorted_vector_speed_test();
     set_speed_test();
     dictionary_speed_test();
     fifo_speed_test();

+ 1 - 0
src/tests/meson.build

@@ -19,6 +19,7 @@ sources += files('Integration/Numbers.vala')
 sources += files('Integration/Dictionary.vala')
 sources += files('Integration/PropertyMapper.vala')
 sources += files('Integration/Cache.vala')
+sources += files('Integration/SortedVector.vala')
 
 sources += files('Speed/SpeedTest.vala')
 sources += files('Speed/Series.vala')