From bfe0748a631a3f4606f97cb932af40b3851242c8 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 30 May 2018 18:39:27 +0200 Subject: [PATCH] Insert a range into the hash table and set --- source/tanya/container/hashtable.d | 146 ++++++++++++++++++++++++++++- source/tanya/container/set.d | 95 +++++++++++++++++++ 2 files changed, 239 insertions(+), 2 deletions(-) diff --git a/source/tanya/container/hashtable.d b/source/tanya/container/hashtable.d index b1a347d..d25c75b 100644 --- a/source/tanya/container/hashtable.d +++ b/source/tanya/container/hashtable.d @@ -20,6 +20,7 @@ import tanya.hash.lookup; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; +import tanya.range.primitive; /** * Bidirectional range whose element type is a tuple of a key and the @@ -225,6 +226,8 @@ if (is(typeof(hasher(Key.init)) == size_t)) * S = Source set type. * init = Source set. * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). */ this(S)(ref S init, shared Allocator allocator = defaultAllocator) if (is(Unqual!S == HashTable)) @@ -249,6 +252,71 @@ if (is(typeof(hasher(Key.init)) == size_t)) this.data.move(init.data, allocator); } + /** + * Constructs the hash table from a forward range. + * + * Params: + * R = Range type. + * range = Forward range. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). + */ + this(R)(R range, shared Allocator allocator = defaultAllocator) + if (isForwardRange!R && is(ElementType!R == KeyValue)) + in + { + assert(allocator !is null); + } + do + { + this(allocator); + insert(range); + } + + /// + @nogc nothrow pure @safe unittest + { + alias KeyValue = HashTable!(string, int).KeyValue; + + KeyValue[2] range = [KeyValue("one", 1), KeyValue("two", 2)]; + auto hashTable = HashTable!(string, int)(range[]); + + assert(hashTable["one"] == 1); + assert(hashTable["two"] == 2); + } + + /** + * Initializes the hash table from a static array. + * + * Params: + * n = Array size. + * array = Static array. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). + */ + this(size_t n)(KeyValue[n] array, + shared Allocator allocator = defaultAllocator) + in + { + assert(allocator !is null); + } + do + { + insert(array[]); + } + + /// + @nogc nothrow pure @safe unittest + { + alias KeyValue = HashTable!(string, int).KeyValue; + auto hashTable = HashTable!(string, int)([KeyValue("one", 1), KeyValue("two", 2)]); + + assert(hashTable["one"] == 1); + assert(hashTable["two"] == 2); + } + /** * Assigns another hash table. * @@ -392,8 +460,7 @@ if (is(typeof(hasher(Key.init)) == size_t)) { e.key = key; } - e.kv.value = value; - return e.kv.value; + return e.kv.value = value; } /// @@ -411,6 +478,81 @@ if (is(typeof(hasher(Key.init)) == size_t)) assert("Pachycephalosaurus" in hashTable); } + /** + * Inserts a new element in the hash table. + * + * If the element with the same key was already in the table, it reassigns + * it with the new value, but $(D_PSYMBOL insert) returns `0`. Otherwise + * `1` is returned. + * + * Params: + * keyValue = Key/value pair. + * + * Returns: The number of the inserted elements with a unique key. + */ + size_t insert(KeyValue keyValue) + { + auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key); + size_t inserted; + if (e.status != BucketStatus.used) + { + e.key = keyValue.key; + inserted = 1; + } + e.kv.value = keyValue.value; + return inserted; + } + + /// + @nogc nothrow pure @safe unittest + { + HashTable!(string, int) hashTable; + + assert(hashTable.insert(hashTable.KeyValue("number", 1)) == 1); + assert(hashTable["number"] == 1); + assert(hashTable.insert(hashTable.KeyValue("number", 2)) == 0); + assert(hashTable["number"] == 2); + } + + /** + * Inserts a forward range of key/value pairs into the hash table. + * + * If some of the elements in the $(D_PARAM range) have the same key, they + * are reassigned but are not counted as inserted elements. So the value + * returned by this function will be less than the range length. + * + * Params: + * R = Range type. + * range = Forward range. + * + * Returns: The number of the inserted elements with a unique key. + */ + size_t insert(R)(R range) + if (isForwardRange!R && is(ElementType!R == KeyValue)) + { + size_t count; + foreach (e; range) + { + count += insert(e); + } + return count; + } + + /// + @nogc nothrow pure @safe unittest + { + HashTable!(string, int) hashTable; + + hashTable.KeyValue[2] range = [ + hashTable.KeyValue("one", 1), + hashTable.KeyValue("two", 2), + ]; + + assert(hashTable.insert(range[]) == 2); + assert(hashTable["one"] == 1); + assert(hashTable["two"] == 2); + } + /** * Find the element with the key $(D_PARAM key). * diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index b8fd0e6..9ee1472 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -22,6 +22,7 @@ import tanya.hash.lookup; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; +import tanya.range.primitive; /** * Bidirectional range that iterates over the $(D_PSYMBOL Set)'s values. @@ -222,6 +223,8 @@ if (is(typeof(hasher(T.init)) == size_t)) * S = Source set type. * init = Source set. * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). */ this(S)(ref S init, shared Allocator allocator = defaultAllocator) if (is(Unqual!S == Set)) @@ -246,6 +249,66 @@ if (is(typeof(hasher(T.init)) == size_t)) this.data.move(init.data, allocator); } + /** + * Initializes the set from a forward range. + * + * Params: + * R = Range type. + * range = Forward range. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). + */ + this(R)(R range, shared Allocator allocator = defaultAllocator) + if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T)) + in + { + assert(allocator !is null); + } + do + { + insert(range); + } + + /// + @nogc nothrow pure @safe unittest + { + int[2] range = [1, 2]; + Set!int set = Set!int(range[]); + + assert(1 in set); + assert(2 in set); + } + + /** + * Initializes the set from a static array. + * + * Params: + * n = Array size. + * array = Static array. + * allocator = Allocator. + * + * Precondition: $(D_INLINECODE allocator !is null). + */ + this(size_t n)(T[n] array, shared Allocator allocator = defaultAllocator) + in + { + assert(allocator !is null); + } + do + { + insert(array[]); + } + + /// + @nogc nothrow pure @safe unittest + { + Set!int set = Set!int([1, 2]); + + assert(1 in set); + assert(2 in set); + } + /** * Assigns another set. * @@ -409,6 +472,38 @@ if (is(typeof(hasher(T.init)) == size_t)) assert(set.insert(8) == 1); } + /** + * Inserts the value from a forward range into the set. + * + * Params: + * R = Range type. + * range = Forward range. + * + * Returns: The number of new elements inserted. + */ + size_t insert(R)(R range) + if (isForwardRange!R && isImplicitlyConvertible!(ElementType!R, T)) + { + size_t count; + foreach (e; range) + { + count += insert(e); + } + return count; + } + + /// + @nogc nothrow pure @safe unittest + { + Set!int set; + + int[3] range = [2, 1, 2]; + + assert(set.insert(range[]) == 2); + assert(1 in set); + assert(2 in set); + } + /** * Removes an element. *