Răsfoiți Sursa

Cumulative changes

Billy Barrow 11 luni în urmă
părinte
comite
cf2f015020

+ 46 - 0
src/lib/Associative/Associative.vala

@@ -0,0 +1,46 @@
+namespace Invercargill {
+
+    public abstract class Associative<TKey, TValue> : Collection<KeyValuePair<TKey, TValue>> {
+
+        public abstract new void @set(TKey key, TValue value);
+        public abstract new TValue? @get(TKey key);
+        public abstract void clear(TKey key); 
+
+        public virtual void set_all(Enumerable<KeyValuePair<TKey, TValue>> key_values) {
+            foreach (var pair in key_values) {
+                @set(pair.key, pair.value);
+            }
+        }
+
+        public virtual void clear_all(Enumerable<TKey> keys) {
+            foreach (var key in keys) {
+                clear(key);
+            }
+        }
+
+        public virtual Enumerable<KeyValuePair<TKey, TValue>> get_all(Enumerable<TKey> keys) {
+            var vec = new Vector<KeyValuePair<TKey, TValue>>();
+            foreach (var key in keys) {
+                var item = @get(key);
+                if(item != null) {
+                    vec.add(new KeyValuePair<TKey, TValue>(key, item));
+                }
+            }
+            return vec.seal();
+        }
+
+        public override void add (KeyValuePair<TKey, TValue> item) {
+            @set(item.key, item.value);
+        }
+        public override void remove_first_where (Invercargill.PredicateDelegate<Invercargill.KeyValuePair<TKey,TValue>> predicate) {
+            var key = first_or_default(predicate);
+            if(key != null) {
+                clear(key);
+            }
+        }
+        public override void remove_where (Invercargill.PredicateDelegate<Invercargill.KeyValuePair<TKey,TValue>> predicate) {
+            clear_all(where(predicate).select<TKey>(i => i.key));
+        }
+    }
+
+}

+ 105 - 0
src/lib/Associative/Dictionary.vala

@@ -0,0 +1,105 @@
+using Invercargill.Convert;
+
+namespace Invercargill {
+
+    public class Dictionary<TKey, TValue> : Associative<TKey, TValue> {
+
+        private HashTable<TKey, TValue> hash_table;
+
+        public Dictionary(HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
+            var hash_func = key_hash_func;
+            var equal_func = key_equal_func;
+
+            var key_type = typeof(TKey);
+            if(hash_func == null) {
+                if(key_type == typeof(string)) {
+                    hash_func = GLib.str_hash;
+                }
+                else if(key_type == typeof(int64) || key_type == typeof(uint64)) {
+                    hash_func = GLib.int64_hash;
+                }
+                else if(key_type == typeof(int)) {
+                    hash_func = GLib.direct_hash;
+                }
+                else if(key_type == typeof(double)) {
+                    hash_func = GLib.double_hash;
+                }
+                else if(key_type.is_a(typeof(Hashable))) {
+                    hash_func = (k) => ((Hashable)k).hash_code();
+                }
+            }
+            if(equal_func == null) {
+                if(key_type == typeof(string)) {
+                    equal_func = GLib.str_equal;
+                }
+                else if(key_type == typeof(int64) || key_type == typeof(uint64)) {
+                    equal_func = GLib.int64_equal;
+                }
+                else if(key_type == typeof(int)) {
+                    equal_func = GLib.direct_equal;
+                }
+                else if(key_type == typeof(double)) {
+                    equal_func = GLib.double_equal;
+                }
+                else if(key_type.is_a(typeof(Equatable))) {
+                    equal_func = (a, b) => ((Equatable<TKey>)a).equals(b);
+                }
+            }
+
+
+            hash_table = new HashTable<TKey, TValue>(hash_func, equal_func);
+        }
+
+        public override void @set (TKey key, TValue value) {
+            lock(hash_table) {
+                hash_table.set (key, value);
+            }
+        }
+
+        public override void set_all (Enumerable<KeyValuePair<TKey, TValue>> key_values) {
+            lock(hash_table) {
+                key_values.iterate(kv => hash_table.set(kv.key, kv.value));
+            }
+        }
+
+        public override TValue @get (TKey key) {
+            lock(hash_table) {
+                return hash_table.get(key);
+            }
+        }
+        
+        public override void clear (TKey key) {
+            lock(hash_table) {
+                hash_table.remove(key);
+            }
+        }
+
+        public override Tracker<KeyValuePair<TKey, TValue>> get_tracker () {
+            return new DictionaryTracker<TKey, TValue> (this);
+        }
+
+        private class DictionaryTracker<TKey, TValue> : Tracker<KeyValuePair<TKey, TValue>> {
+
+            private Dictionary<TKey, TValue> dictionary;
+            private (unowned TKey)[] keys;
+            private int index;
+
+            public DictionaryTracker(Dictionary<TKey, TValue> dict) {
+                dictionary = dict;
+                keys = dict.hash_table.get_keys_as_array ();
+                index = 0;
+            }
+
+            public override bool has_next() {
+                return index < keys.length;
+            }
+            public override KeyValuePair<TKey, TValue> get_next() {
+                var key = keys[index];
+                var item = dictionary.get (key);
+                index++;
+                return new KeyValuePair<TKey, TValue> (key, item);
+            }
+
+        }
+    }
+}

+ 23 - 0
src/lib/Associative/Index.vala

@@ -0,0 +1,23 @@
+
+//  namespace Invercargill {
+
+//      public interface ReadOnlyIndex<TKey, TValue> : Enumerable<KeyValuePair<TKey, Enumerable<TValue>>> {
+
+//          public abstract ReadOnlyIndex<TKey, TValue> less_than(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> less_than_or_equal_to(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> greater_than(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> greater_than_or_equal_to(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> equal_to(TKey key);
+
+//      }
+
+//      public abstract class Index<TKey, TValue> : Associative<TKey, Enumerable<TValue>>, ReadOnlyIndex<TKey, TValue> {
+
+//          public abstract ReadOnlyIndex<TKey, TValue> less_than(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> less_than_or_equal_to(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> greater_than(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> greater_than_or_equal_to(TKey key);
+//          public abstract ReadOnlyIndex<TKey, TValue> equal_to(TKey key);
+//      }
+
+//  }

+ 15 - 0
src/lib/Associative/KeyValuePair.vala

@@ -0,0 +1,15 @@
+namespace Invercargill {
+
+    public class KeyValuePair<TKey, TValue> {
+
+        public TKey key { get; private set; }
+        public TValue value { get; private set; }
+
+        public KeyValuePair(TKey key, TValue value) {
+            this.key = key;
+            this.value = value;
+        }
+
+    }
+
+}

+ 9 - 1
src/lib/Collections/BinaryData.vala

@@ -2,7 +2,7 @@ using Invercargill.Convert;
 
 namespace Invercargill {
 
-    public class BinaryData : Enumerable<uint8>, Promotion<uint8> {
+    public class BinaryData : Enumerable<uint8>, Promotion<uint8>, Equatable<Enumerable<uint8>>, Hashable {
 
         public enum Endianness {
             Native,
@@ -188,6 +188,10 @@ namespace Invercargill {
             return this == other || matches(other, (a, b) => a == b);
         }
 
+        public uint hash_code() {
+            return aggrigate<uint>(5381, (h, b) => h * 33 + b);
+        }
+
         public void push_int32(int32 value) {
             var chunk = new uint8[sizeof(int32)];
             int32 val;
@@ -426,6 +430,10 @@ namespace Invercargill {
             return Base64.encode(to_array());
         }
 
+        public Bytes to_bytes() {
+            return new Bytes(to_array());
+        }
+
         public BinaryData slice(int start, int end) {
             return new BinaryData.from_enumerable(skip(start).take(end-start));
         }

+ 24 - 0
src/lib/Collections/Collection.vala

@@ -0,0 +1,24 @@
+namespace Invercargill {
+
+    public abstract class Collection<T> : Enumerable<T> {
+
+        public abstract void add(T item);
+        public abstract void remove_first_where(PredicateDelegate<T> predicate);
+        public abstract void remove_where(PredicateDelegate<T> predicate);
+
+        public virtual void add_all(Enumerable<T> items) {
+            items.iterate(i => add(i));
+        }
+
+    }
+
+    public abstract class IndexedCollection<T> : Collection<T> {
+
+        public new abstract T @get(int index) throws IndexError;
+        public new abstract void @set(int index, T item) throws IndexError;
+        public abstract void remove(int index) throws IndexError;
+        public abstract int index_of(PredicateDelegate<T> predicate);
+
+    }
+    
+}

+ 32 - 3
src/lib/Collections/Series.vala

@@ -1,6 +1,6 @@
 namespace Invercargill {
 
-    public class Series<T> : Enumerable<T> {
+    public class Series<T> : Collection<T> {
      
         internal class SeriesItem<T> {
             public SeriesItem next = null;
@@ -42,7 +42,7 @@ namespace Invercargill {
             return arr;
         }
 
-        public void add(T item) {
+        public override void add(T item) {
             lock(root) {
                 n_items++;
                 var si = new SeriesItem<T>(item);
@@ -56,7 +56,7 @@ namespace Invercargill {
             }
         }
 
-        public void add_all(Enumerable<T> items) {
+        public override void add_all(Enumerable<T> items) {
             items.iterate(i => add(i));
         }
 
@@ -64,6 +64,35 @@ namespace Invercargill {
             return n_items;
         }
 
+        public override void remove_first_where(Invercargill.PredicateDelegate<T> predicate) {
+            remove_items(predicate, true);
+        }
+        public override void remove_where(Invercargill.PredicateDelegate<T> predicate) {
+            remove_items(predicate, false);
+        }
+
+        private void remove_items(Invercargill.PredicateDelegate<T> predicate, bool first_only) {
+            lock(root) {
+                SeriesItem<T> previous = null;
+                var node = root;
+                while(node != null) {
+                    if(predicate(node.value)) {
+                        if(previous == null) {
+                            root = node.next;
+                        }
+                        else {
+                            previous.next = node.next;
+                        }
+                        if(first_only) {
+                            break;
+                        }
+                    }
+                    previous = node;
+                    node = node.next;
+                }
+            }
+        }
+
         private class SeriesTracker<T> : Tracker<T> {
 
             private SeriesItem<T>? current;

+ 157 - 0
src/lib/Collections/Set.vala

@@ -0,0 +1,157 @@
+using Invercargill.Convert;
+
+namespace Invercargill {
+
+    public class Set<T> : Collection<T> {
+
+        private HashTable<T, T> hash_table;
+        private HashFunc<T> hash_func;
+        private EqualFunc<T> equal_func;
+
+        public Set(HashFunc<T>? value_hash_func = null, EqualFunc<T>? value_equal_func = null) {
+            hash_func = value_hash_func;
+            equal_func = value_equal_func;
+
+            var key_type = typeof(T);
+            if(hash_func == null) {
+                if(key_type == typeof(string)) {
+                    hash_func = GLib.str_hash;
+                }
+                else if(key_type == typeof(int64) || key_type == typeof(uint64)) {
+                    hash_func = GLib.int64_hash;
+                }
+                else if(key_type == typeof(int)) {
+                    hash_func = GLib.direct_hash;
+                }
+                else if(key_type == typeof(double)) {
+                    hash_func = GLib.double_hash;
+                }
+                else if(key_type.is_a(typeof(Hashable))) {
+                    hash_func = (k) => ((Hashable)k).hash_code();
+                }
+            }
+            if(equal_func == null) {
+                if(key_type == typeof(string)) {
+                    equal_func = GLib.str_equal;
+                }
+                else if(key_type == typeof(int64) || key_type == typeof(uint64)) {
+                    equal_func = GLib.int64_equal;
+                }
+                else if(key_type == typeof(int)) {
+                    equal_func = GLib.direct_equal;
+                }
+                else if(key_type == typeof(double)) {
+                    equal_func = GLib.double_equal;
+                }
+                else if(key_type.is_a(typeof(Equatable))) {
+                    equal_func = (a, b) => ((Equatable<T>)a).equals(b);
+                }
+            }
+
+
+            hash_table = new HashTable<T, T>(hash_func, equal_func);
+        }
+
+
+        public override Tracker<T> get_tracker () {
+            return Convert.ate(hash_table.get_keys_as_array()).get_tracker();
+        }
+
+        public override void add (T item) {
+            lock(hash_table) {
+                hash_table.set(item, item);
+            }
+        }
+
+        private void with_lock(Func<Set<T>> action) {
+            lock(hash_table) {
+                action(this);
+            }
+        }
+
+        public override void add_all (Enumerable<T> items) {
+            lock(hash_table) {
+                if(items.get_type().is_a (typeof(Set))) {
+                    if(items == this) {
+                        return;
+                    }
+                    ((Set<T>)items).with_lock((s) => s.hash_table.foreach (i => hash_table.set(i, i)));
+                }
+                else {
+                    items.iterate(i => hash_table.set(i, i));
+                }
+            }
+        }
+
+        public Set<T> union(Enumerable<T> items) {
+            var new_set = to_set (hash_func, equal_func);
+            new_set.add_all (items);
+            return new_set;
+        }
+
+        public Set<T> difference(Enumerable<T> items) {
+            var new_set = to_set (hash_func, equal_func);
+            new_set.remove_all(items);
+            return new_set;
+        }
+
+        public Set<T> intersection(Enumerable<T> items) {
+            var new_set = to_set (hash_func, equal_func);
+            new_set.remove_except(items);
+            return new_set;
+        }
+
+        public new T find(T item) {
+            lock(hash_table) {
+                return hash_table.get(item);
+            }
+        }
+
+        public override void remove_first_where (Invercargill.PredicateDelegate<T> predicate) {
+            remove(first_or_default(predicate));
+        }
+
+        public override void remove_where (Invercargill.PredicateDelegate<T> predicate) {
+            lock(hash_table) {
+                hash_table.foreach_remove(k => predicate(k));
+            }
+        }
+
+        public void remove(T item) {
+            lock(hash_table) {
+                hash_table.remove (item);
+            }
+        }
+
+        public void remove_all(Enumerable<T> items) {
+            lock(hash_table) {
+                if(items.get_type().is_a (typeof(Set))) {
+                    if(items == this) {
+                        hash_table.remove_all();
+                    }
+                    else {
+                        ((Set<T>)items).with_lock((s) => s.hash_table.foreach (i => hash_table.remove(i)));
+                    }
+                }
+                else {
+                    items.iterate(i => hash_table.remove(i));
+                }
+            }
+        }
+
+        public void remove_except(Enumerable<T> items) {
+            lock(hash_table) {
+                Set<T> other = items.to_set(hash_func, equal_func);
+                hash_table.foreach_remove(i => !other.has(i));
+            }
+        }
+
+        public bool has(T item) {
+            lock(hash_table) {
+                return hash_table.contains(item);
+            }
+        }
+
+
+    }
+}

+ 27 - 10
src/lib/Collections/Vector.vala

@@ -1,6 +1,6 @@
 namespace Invercargill {
 
-    public class Vector<T> : Enumerable<T> {
+    public class Vector<T> : IndexedCollection<T> {
 
         private T[] array;
         private int n_items = 0;
@@ -36,15 +36,25 @@ namespace Invercargill {
             }
         }
 
-        public void add(T item) {
+        public override void add(T item) {
             lock(array) {
                 ensure_room(1);
                 array_write(n_items, item);
                 n_items++;
             }
         }
+    
+        public override void add_all(Enumerable<T> items) {
+            lock(array) {
+                foreach (var item in items) {
+                    ensure_room(1);
+                    array_write(n_items, item);
+                    n_items++;
+                }
+            }
+        }
 
-        public new T get(int index) throws IndexError {
+        public override T @get(int index) throws IndexError {
             IndexError? e = null;
             lock(array) {
                 if(index < 0) {
@@ -69,7 +79,7 @@ namespace Invercargill {
             }
         }
 
-        public new void set(int index, T value) throws IndexError {
+        public override void set(int index, T value) throws IndexError {
             IndexError? e = null;
             lock(array) {
                 if(index < 0) {
@@ -86,10 +96,6 @@ namespace Invercargill {
             throw e;
         }
 
-        public void add_all(Enumerable<T> items) {
-            items.iterate(i => add(i));
-        }
-
         private void ensure_room(int items) {
             if(array.length <= n_items + items) {
                 array.resize(array.length * 2);
@@ -100,7 +106,7 @@ namespace Invercargill {
             return n_items;
         }
 
-        public void remove(int index) throws IndexError {
+        public override void remove(int index) throws IndexError {
             var e = remove_internal(index);
             if(e != null) {
                 throw e;
@@ -182,7 +188,7 @@ namespace Invercargill {
             return array[index];
         }
 
-        public int index_of(PredicateDelegate<T> predicate) {
+        public override int index_of(PredicateDelegate<T> predicate) {
             var i = -1;
             foreach (var item in this) {
                 i++;
@@ -193,6 +199,17 @@ namespace Invercargill {
             return -1;
         }
 
+        public override void remove_first_where(Invercargill.PredicateDelegate<T> predicate) {
+            remove_internal(index_of(predicate));
+        }
+
+        public override void remove_where(Invercargill.PredicateDelegate<T> predicate) {
+            with_positions()
+                .where(i => predicate(i.item))
+                .select<int>(i => i.position)
+                .iterate(i => remove_internal(i));
+        }
+
         private class VectorTracker<T> : Tracker<T> {
 
             private T? next_item = null;

+ 20 - 0
src/lib/Concrete/EquatableEnumerable.vala

@@ -0,0 +1,20 @@
+namespace Invercargill {
+
+    public class EquatableEnumerable<T> : ProxyEnumerable<T>, Promotion<T>, Equatable<Enumerable<T>> {
+
+        public bool can_wrap(GLib.Type element_type) {
+            return element_type.is_a(typeof(Equatable));
+        }
+
+        public Enumerable<T> wrap (Enumerable<T> enumerable) {
+            inner = enumerable;
+            return this;
+        }
+
+        public bool equals(Enumerable<T> other) {
+            return this == other || this.matches(other, (a, b) => ((Equatable<T>)a).equals (b));
+        }
+
+    }
+
+}

+ 5 - 1
src/lib/Concrete/Numbers/NumberEnumerable.vala

@@ -1,6 +1,6 @@
 namespace Invercargill {
 
-    public abstract class NumberEnumerable<T> : ProxyEnumerable<T>, Promotion<T> {
+    public abstract class NumberEnumerable<T> : ProxyEnumerable<T>, Promotion<T>, Equatable<Enumerable<T>> {
         public abstract bool can_wrap (GLib.Type element_type);
 
         protected abstract bool greater_than(T a, T b);
@@ -71,6 +71,10 @@ namespace Invercargill {
             return this;
         }
 
+        public bool equals(Enumerable<T> other) {
+            return this == other || this.matches(other, (a, b) => equal_to(a, b));
+        }
+
     }
 
 }

+ 25 - 0
src/lib/Concrete/ProxyEnumerable.vala

@@ -183,6 +183,31 @@ namespace Invercargill {
             return inner.promote_to<TPromotion>();
         }
 
+        public override Enumerable<T> seal() {
+            return inner.seal();
+        }
+
+        public override Dictionary<TKey, T> to_dictionary<TKey>(TransformDelegate<T, TKey> key_selecter, HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
+            return inner.to_dictionary<TKey>(key_selecter, key_hash_func, key_equal_func);
+        }
+
+        public override Dictionary<TKey, TValue> select_to_dictionary<TKey, TValue>(TransformDelegate<T, TKey> key_selecter, TransformDelegate<T, TValue> value_selecter, HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
+            return inner.select_to_dictionary<TKey, TValue>(key_selecter, value_selecter, key_hash_func, key_equal_func);
+        }
+
+        public override Enumerable<Pair<TFirst, TSecond>> select_pairs<TFirst, TSecond>(owned TransformDelegate<T, TFirst> transform1, owned TransformDelegate<T, TSecond> transform2) {
+            return inner.select_pairs<TFirst, TSecond>((owned) transform1, (owned) transform2);
+        }
+
+        public override Enumerable<PositionItemPair<T>> with_positions() {
+            return inner.with_positions();
+        }
+
+        
+        public override Set<T> to_set(HashFunc<T>? hash_func = null, EqualFunc<T>? equal_func = null) {
+            return inner.to_set(hash_func, equal_func);
+        }
+
     }
 
 }

+ 11 - 0
src/lib/Concrete/SealedEnumerable.vala

@@ -0,0 +1,11 @@
+namespace Invercargill {
+
+    public class SealedEnumerable<T> : ProxyEnumerable<T> {
+
+        public SealedEnumerable (Enumerable<T> source) {
+            inner = source;
+        }
+
+    }
+
+}

+ 33 - 0
src/lib/Enumerable.vala

@@ -91,6 +91,10 @@ namespace Invercargill {
             return new TransformQuery<T, Tout>(this, (owned)transform);
         }
 
+        public virtual Enumerable<Pair<TFirst, TSecond>> select_pairs<TFirst, TSecond>(owned TransformDelegate<T, TFirst> transform1, owned TransformDelegate<T, TSecond> transform2) {
+            return select<Pair<TFirst, TSecond>>(i => new Pair<TFirst, TSecond>(transform1(i), true, transform2(i), true));
+        }
+
         public virtual Enumerable<Tout> select_many<Tout>(owned TransformDelegate<T, Enumerable<Tout>> transform) {
             return new MergeQuery<Tout>(select((owned)transform));
         }
@@ -365,6 +369,35 @@ namespace Invercargill {
             }
         }
 
+        public virtual Enumerable<T> seal() {
+            if(this.get_type().is_a(typeof(SealedEnumerable))) {
+                return this;
+            }
+            return new SealedEnumerable<T>(this);
+        }
+
+        public virtual Dictionary<TKey, T> to_dictionary<TKey>(TransformDelegate<T, TKey> key_selecter, HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
+            var dict = new Dictionary<TKey, T>(key_hash_func, key_equal_func);       
+            dict.set_all(select<KeyValuePair<TKey, T>>(i => new KeyValuePair<TKey, T>(key_selecter(i), i)));
+            return dict;
+        }
+
+        public virtual Dictionary<TKey, TValue> select_to_dictionary<TKey, TValue>(TransformDelegate<T, TKey> key_selecter, TransformDelegate<T, TValue> value_selecter, HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
+            var dict = new Dictionary<TKey, T>(key_hash_func, key_equal_func);       
+            dict.set_all(select<KeyValuePair<TKey, T>>(i => new KeyValuePair<TKey, T>(key_selecter(i), value_selecter(i))));
+            return dict;
+        }
+
+        public virtual Set<T> to_set(HashFunc<T>? hash_func = null, EqualFunc<T>? equal_func = null) {
+            var @set = new Set<T>(hash_func, equal_func);       
+            @set.add_all(this);
+            return @set;
+        }
+
+        public virtual Enumerable<PositionItemPair<T>> with_positions() {
+            return new PositionQuery<T>(this);
+        }
+
         private PredicateDelegate<T> resolve_nullable_predicate(PredicateDelegate<T>? predicate) {
             if(predicate == null) {
                 return (p) => true;

+ 16 - 0
src/lib/Interfaces.vala

@@ -0,0 +1,16 @@
+namespace Invercargill {
+
+
+    public interface Equatable<T> {
+        public abstract bool equals(T other);
+    }
+
+    public interface Hashable {
+        public abstract uint hash_code();
+    }
+
+    public interface Comparable<T> : Equatable<T> {
+        public abstract int compare(T other);
+    }
+
+}

+ 4 - 4
src/lib/Pair.vala

@@ -1,15 +1,15 @@
 
 namespace Invercargill {
 
-    public class Pair<T1, T2> {
+    public class Pair<TFirst, TSecond> {
 
-        public T1 value1 {get; set;}
-        public T2 value2 {get; set;}
+        public TFirst value1 {get; set;}
+        public TSecond value2 {get; set;}
 
         public bool value1_is_set {get; set;}
         public bool value2_is_set {get; set;}
 
-        public Pair(T1 v1, bool v1set, T2 v2, bool v2set) {
+        public Pair(TFirst v1, bool v1set, TSecond v2, bool v2set) {
             value1 = v1;
             value1_is_set = v1set;
             value2 = v2;

+ 14 - 0
src/lib/PositionItemPair.vala

@@ -0,0 +1,14 @@
+
+namespace Invercargill {
+
+    public class PositionItemPair<T> {
+        public int position { get; private set; }
+        public T item { get; private set; }
+
+        public PositionItemPair(int position, T item) {
+            this.position = position;
+            this.item = item;
+        }
+    }
+
+}

+ 32 - 0
src/lib/Queries/Position.vala

@@ -0,0 +1,32 @@
+namespace Invercargill {
+
+    private class PositionQuery<T> : BaseQuery<T, PositionItemPair<T>> {
+
+        public PositionQuery(Enumerable<T> input) {
+            this.input = input;
+        }
+
+        public override Tracker<PositionItemPair<T>> get_tracker() {
+            return new PositionTracker<T>(input);
+        }
+
+        private class PositionTracker<T> : Tracker<PositionItemPair<T>> {
+
+            private Tracker<T> base_tracker;
+            private int position;
+            public PositionTracker(Enumerable<T> input) {
+                base_tracker = input.get_tracker();
+                position = -1;
+            }
+
+            public override bool has_next() {
+                return base_tracker.has_next();
+            }
+            public override PositionItemPair<T> get_next() {
+                return new PositionItemPair<T>(position++, base_tracker.get_next());
+            }
+            
+        }
+    }
+
+}

+ 12 - 0
src/lib/meson.build

@@ -10,6 +10,7 @@ sources = files('Invercargill.vala')
 sources += files('Enumerable.vala')
 sources += files('Delegates.vala')
 sources += files('Pair.vala')
+sources += files('PositionItemPair.vala')
 sources += files('Tracker.vala')
 sources += files('Errors.vala')
 sources += files('SelectionContext.vala')
@@ -17,6 +18,7 @@ sources += files('Convert.vala')
 sources += files('Safety.vala')
 sources += files('Promotion.vala')
 sources += files('Grouping.vala')
+sources += files('Interfaces.vala')
 
 sources += files('Queries/Query.vala')
 sources += files('Queries/Transform.vala')
@@ -27,6 +29,7 @@ sources += files('Queries/Skip.vala')
 sources += files('Queries/Take.vala')
 sources += files('Queries/Parallel.vala')
 sources += files('Queries/Unique.vala')
+sources += files('Queries/Position.vala')
 
 sources += files('Concrete/ArrayEnumerable.vala')
 sources += files('Concrete/GeeEnumerable.vala')
@@ -42,11 +45,20 @@ sources += files('Concrete/Generator.vala')
 sources += files('Concrete/Numbers/NumberEnumerable.vala')
 sources += files('Concrete/Numbers/Implementations.vala')
 sources += files('Concrete/ProxyEnumerable.vala')
+sources += files('Concrete/SealedEnumerable.vala')
+sources += files('Concrete/EquatableEnumerable.vala')
 
+sources += files('Collections/Collection.vala')
 sources += files('Collections/Series.vala')
 sources += files('Collections/Fifo.vala')
 sources += files('Collections/BinaryData.vala')
 sources += files('Collections/Vector.vala')
+sources += files('Collections/Set.vala')
+
+sources += files('Associative/Associative.vala')
+sources += files('Associative/KeyValuePair.vala')
+sources += files('Associative/Dictionary.vala')
+sources += files('Associative/Index.vala')
 
 invercargill = shared_library('invercargill', sources,
     dependencies: dependencies,

+ 40 - 0
src/tests/Integration/Dictionary.vala

@@ -0,0 +1,40 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void dictionary_tests() {
+
+    Test.add_func("/invercargill/dictionary/string", () => {
+        var names = new string[] {"Billy Barrow", "William Robert", "Johnny Gala", "John Doe"};
+        var dict = ate(names).to_dictionary<string>(n => n.split(" ")[0]);
+
+        dict["James"] = "James Smith";
+
+        var billy = dict["Billy"];
+        var john = dict["John"];
+        var james = dict["James"];
+        var bob = dict["Bob"];
+
+        assert_cmpstr("Billy Barrow", CompareOperator.EQ, billy);
+        assert_cmpstr("John Doe", CompareOperator.EQ, john);
+        assert_cmpstr("James Smith", CompareOperator.EQ, james);
+        assert_null(bob);        
+    });
+
+    Test.add_func("/invercargill/dictionary/int", () => {
+        var dict = new Dictionary<int, string>();
+        dict[1999] = "Billy";
+        dict[1962] = "John";
+        dict[2002] = "James";
+
+        var billy = dict[1999];
+        var john = dict[1962];
+        var james = dict[2002];
+        var bob = dict[1840];
+
+        assert_cmpstr("Billy", CompareOperator.EQ, billy);
+        assert_cmpstr("John", CompareOperator.EQ, john);
+        assert_cmpstr("James", CompareOperator.EQ, james);
+        assert_null(bob);        
+    });
+
+}

+ 1 - 0
src/tests/Integration/Vector.vala

@@ -10,6 +10,7 @@ void vector_tests() {
 
         assert(vector.first_or_default() == 8);
         assert(vector.get_or_default(0) == 8);
+        assert(vector[0] == 8);
         assert(vector.count() == 1);
     });
 

+ 7 - 0
src/tests/Speed/Dictionary.vala

@@ -0,0 +1,7 @@
+using Invercargill;
+
+void dictionary_speed_test() {
+    var dict = new Dictionary<int, int>();
+    var view = dict.select<int>(i => i.value);
+    speed_test_runner_int("Dictionary", view, i => dict[i] = i, i => dict.set_all(i.select<KeyValuePair<int, int>>(v => new KeyValuePair<int, int>(v, v))));
+}

+ 1 - 1
src/tests/Speed/Fifo.vala

@@ -2,5 +2,5 @@ using Invercargill;
 
 void fifo_speed_test() {
     var fifo = new Fifo<int>();
-    speed_test_runner_int(fifo, i => fifo.push(i), i => { i.iterate(j => fifo.push(j)); fifo.complete(); });
+    speed_test_runner_int("Fifo", fifo, i => fifo.push(i), i => { i.iterate(j => fifo.push(j)); fifo.complete(); });
 }

+ 1 - 1
src/tests/Speed/Series.vala

@@ -2,5 +2,5 @@ using Invercargill;
 
 void series_speed_test() {
     var series = new Series<int>();
-    speed_test_runner_int(series, i => series.add(i), i => series.add_all(i));
+    speed_test_runner_int("Series", series, i => series.add(i), i => series.add_all(i));
 }

+ 6 - 0
src/tests/Speed/Set.vala

@@ -0,0 +1,6 @@
+using Invercargill;
+
+void set_speed_test() {
+    var @set = new Set<int>();
+    speed_test_runner_int("Set", @set, i => @set.add(i), i => @set.add_all(i));
+}

+ 4 - 8
src/tests/Speed/SpeedTest.vala

@@ -4,23 +4,19 @@ using Invercargill;
 delegate void AddDelegate<T>(T item);
 delegate void BulkAddDelegate<T>(Invercargill.Enumerable<T> items);
 
-void speed_test_runner_int(Enumerable<int> e, AddDelegate<int> add, BulkAddDelegate<int> bulk) {
-    print(@"\nSpeed test for $(e.get_type().name()):\n");
+void speed_test_runner_int(string type_name, Enumerable<int> e, AddDelegate<int> add, BulkAddDelegate<int> bulk) {
+    print(@"\nSpeed test for $type_name:\n");
 
     int individual_writes = 10000000;
     run_speed_test(@"$individual_writes individual writes", () => {
-        for(int i = 0; i < individual_writes; i++) {
-            add(i);
-        }
+        range(0, individual_writes).iterate(i => add(i));
     });
 
 
     int bulk_writes = 10000;
     int batch_size = 1000;
     run_speed_test(@"$bulk_writes bulk writes of $batch_size items", () => {
-        for(int i = 0; i < bulk_writes; i++) {
-            bulk(range(0, batch_size));
-        }
+        range(0, bulk_writes).iterate(i => bulk(range(0, batch_size)));
     });
 
 

+ 1 - 1
src/tests/Speed/Vector.vala

@@ -2,5 +2,5 @@ using Invercargill;
 
 void vector_speed_test() {
     var vec = new Vector<int>();
-    speed_test_runner_int(vec, i => vec.add(i), i => vec.add_all(i));
+    speed_test_runner_int("Vector", vec, i => vec.add(i), i => vec.add_all(i));
 }

+ 5 - 2
src/tests/TestRunner.vala

@@ -17,11 +17,14 @@ public static int main(string[] args) {
     array_tests();
     promotion_tests();
     numbers_test();
-
+    dictionary_tests();
+    
     Test.run();
-
+    
     series_speed_test();
     vector_speed_test();
+    set_speed_test();
+    dictionary_speed_test();
     fifo_speed_test();
 
     return 0;

+ 3 - 0
src/tests/meson.build

@@ -16,10 +16,13 @@ sources += files('Integration/Series.vala')
 sources += files('Integration/Arrays.vala')
 sources += files('Integration/Promotion.vala')
 sources += files('Integration/Numbers.vala')
+sources += files('Integration/Dictionary.vala')
 
 sources += files('Speed/SpeedTest.vala')
 sources += files('Speed/Series.vala')
 sources += files('Speed/Vector.vala')
 sources += files('Speed/Fifo.vala')
+sources += files('Speed/Dictionary.vala')
+sources += files('Speed/Set.vala')
 
 executable('invercargill-test-suite', sources, dependencies: dependencies, install: true)