1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * Hash table.
7  *
8  * Copyright: Eugene Wissner 2018-2021.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/hashtable.d,
13  *                 tanya/container/hashtable.d)
14  */
15 module tanya.container.hashtable;
16 
17 import std.algorithm.iteration;
18 import tanya.algorithm.mutation;
19 import tanya.container.array;
20 import tanya.container.entry;
21 import tanya.hash.lookup;
22 import tanya.memory.allocator;
23 import tanya.memory.lifetime;
24 import tanya.meta.trait;
25 import tanya.meta.transform;
26 import tanya.range.primitive;
27 
28 /**
29  * Bidirectional range whose element type is a tuple of a key and the
30  * respective value.
31  *
32  * Params:
33  *  T = Type of the internal hash storage.
34  */
35 struct Range(T)
36 {
37     private alias KV = CopyConstness!(T, T.Bucket.KV);
38     static if (isMutable!T)
39     {
40         private alias DataRange = T.array.Range;
41     }
42     else
43     {
44         private alias DataRange = T.array.ConstRange;
45     }
46     private DataRange dataRange;
47 
48     @disable this();
49 
50     private this(DataRange dataRange)
51     {
52         while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
53         {
54             dataRange.popFront();
55         }
56         while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
57         {
58             dataRange.popBack();
59         }
60         this.dataRange = dataRange;
61     }
62 
63     @property Range save()
64     {
65         return this;
66     }
67 
68     @property bool empty() const
69     {
70         return this.dataRange.empty();
71     }
72 
73     void popFront()
74     in
75     {
76         assert(!empty);
77         assert(this.dataRange.front.status == BucketStatus.used);
78     }
79     out
80     {
81         assert(empty || this.dataRange.back.status == BucketStatus.used);
82     }
83     do
84     {
85         do
86         {
87             this.dataRange.popFront();
88         }
89         while (!empty && dataRange.front.status != BucketStatus.used);
90     }
91 
92     void popBack()
93     in
94     {
95         assert(!empty);
96         assert(this.dataRange.back.status == BucketStatus.used);
97     }
98     out
99     {
100         assert(empty || this.dataRange.back.status == BucketStatus.used);
101     }
102     do
103     {
104         do
105         {
106             this.dataRange.popBack();
107         }
108         while (!empty && dataRange.back.status != BucketStatus.used);
109     }
110 
111     @property ref inout(KV) front() inout
112     in
113     {
114         assert(!empty);
115         assert(this.dataRange.front.status == BucketStatus.used);
116     }
117     do
118     {
119         return this.dataRange.front.kv;
120     }
121 
122     @property ref inout(KV) back() inout
123     in
124     {
125         assert(!empty);
126         assert(this.dataRange.back.status == BucketStatus.used);
127     }
128     do
129     {
130         return this.dataRange.back.kv;
131     }
132 
133     Range opIndex()
134     {
135         return typeof(return)(this.dataRange[]);
136     }
137 
138     Range!(const T) opIndex() const
139     {
140         return typeof(return)(this.dataRange[]);
141     }
142 }
143 
144 /**
145  * Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
146  *
147  * Params:
148  *  T = Type of the internal hash storage.
149  */
150 struct ByKey(T)
151 {
152     private alias Key = CopyConstness!(T, T.Key);
153     static if (isMutable!T)
154     {
155         private alias DataRange = T.array.Range;
156     }
157     else
158     {
159         private alias DataRange = T.array.ConstRange;
160     }
161     private DataRange dataRange;
162 
163     @disable this();
164 
165     private this(DataRange dataRange)
166     {
167         while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
168         {
169             dataRange.popFront();
170         }
171         while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
172         {
173             dataRange.popBack();
174         }
175         this.dataRange = dataRange;
176     }
177 
178     @property ByKey save()
179     {
180         return this;
181     }
182 
183     @property bool empty() const
184     {
185         return this.dataRange.empty();
186     }
187 
188     @property void popFront()
189     in
190     {
191         assert(!empty);
192         assert(this.dataRange.front.status == BucketStatus.used);
193     }
194     out
195     {
196         assert(empty || this.dataRange.back.status == BucketStatus.used);
197     }
198     do
199     {
200         do
201         {
202             this.dataRange.popFront();
203         }
204         while (!empty && dataRange.front.status != BucketStatus.used);
205     }
206 
207     @property void popBack()
208     in
209     {
210         assert(!empty);
211         assert(this.dataRange.back.status == BucketStatus.used);
212     }
213     out 
214     {
215         assert(empty || this.dataRange.back.status == BucketStatus.used);
216     }
217     do
218     {
219         do
220         {
221             this.dataRange.popBack();
222         }
223         while (!empty && dataRange.back.status != BucketStatus.used);
224     }
225 
226     @property ref inout(Key) front() inout
227     in
228     {
229         assert(!empty);
230         assert(this.dataRange.front.status == BucketStatus.used);
231     }
232     do
233     {
234         return this.dataRange.front.key;
235     }
236 
237     @property ref inout(Key) back() inout
238     in
239     {
240         assert(!empty);
241         assert(this.dataRange.back.status == BucketStatus.used);
242     }
243     do
244     {
245         return this.dataRange.back.key;
246     }
247 
248     ByKey opIndex()
249     {
250         return typeof(return)(this.dataRange[]);
251     }
252 
253     ByKey!(const T) opIndex() const
254     {
255         return typeof(return)(this.dataRange[]);
256     }
257 }
258 
259 /**
260  * Bidirectional range iterating over the key of a $(D_PSYMBOL HashTable).
261  *
262  * Params:
263  *  T = Type of the internal hash storage.
264  */
265 struct ByValue(T)
266 {
267     private alias Value = CopyConstness!(T, T.Value);
268     static if (isMutable!T)
269     {
270         private alias DataRange = T.array.Range;
271     }
272     else
273     {
274         private alias DataRange = T.array.ConstRange;
275     }
276     private DataRange dataRange;
277 
278     @disable this();
279 
280     private this(DataRange dataRange)
281     {
282         while (!dataRange.empty && dataRange.front.status != BucketStatus.used)
283         {
284             dataRange.popFront();
285         }
286         while (!dataRange.empty && dataRange.back.status != BucketStatus.used)
287         {
288             dataRange.popBack();
289         }
290         this.dataRange = dataRange;
291     }
292 
293     @property ByValue save()
294     {
295         return this;
296     }
297 
298     @property bool empty() const
299     {
300         return this.dataRange.empty();
301     }
302 
303     @property void popFront()
304     in
305     {
306         assert(!empty);
307         assert(this.dataRange.front.status == BucketStatus.used);
308     }
309     out
310     {
311         assert(empty || this.dataRange.back.status == BucketStatus.used);
312     }
313     do
314     {
315         do
316         {
317             this.dataRange.popFront();
318         }
319         while (!empty && dataRange.front.status != BucketStatus.used);
320     }
321 
322     @property void popBack()
323     in
324     {
325         assert(!empty);
326         assert(this.dataRange.back.status == BucketStatus.used);
327     }
328     out
329     {
330         assert(empty || this.dataRange.back.status == BucketStatus.used);
331     }
332     do
333     {
334         do
335         {
336             this.dataRange.popBack();
337         }
338         while (!empty && dataRange.back.status != BucketStatus.used);
339     }
340 
341     @property ref inout(Value) front() inout
342     in
343     {
344         assert(!empty);
345         assert(this.dataRange.front.status == BucketStatus.used);
346     }
347     do
348     {
349         return this.dataRange.front.kv.value;
350     }
351 
352     @property ref inout(Value) back() inout
353     in
354     {
355         assert(!empty);
356         assert(this.dataRange.back.status == BucketStatus.used);
357     }
358     do
359     {
360         return this.dataRange.back.kv.value;
361     }
362 
363     ByValue opIndex()
364     {
365         return typeof(return)(this.dataRange[]);
366     }
367 
368     ByValue!(const T) opIndex() const
369     {
370         return typeof(return)(this.dataRange[]);
371     }
372 }
373 
374 /**
375  * Hash table is a data structure that stores pairs of keys and values without
376  * any particular order.
377  *
378  * This $(D_PSYMBOL HashTable) is implemented using closed hashing. Hash
379  * collisions are resolved with linear probing.
380  *
381  * $(D_PARAM Key) should be hashable with $(D_PARAM hasher). $(D_PARAM hasher)
382  * is a callable that accepts an argument of type $(D_PARAM Key) and returns a
383  * hash value for it ($(D_KEYWORD size_t)).
384  *
385  * Params:
386  *  Key    = Key type.
387  *  Value  = Value type.
388  *  hasher = Hash function for $(D_PARAM Key).
389  */
390 struct HashTable(Key, Value, alias hasher = hash)
391 if (isHashFunction!(hasher, Key))
392 {
393     private alias HashArray = .HashArray!(hasher, Key, Value);
394     private alias Buckets = HashArray.Buckets;
395 
396     private HashArray data;
397 
398     /// Type of the key-value pair stored in the hash table.
399     alias KeyValue = HashArray.Bucket.KV;
400 
401     /// The range types for $(D_PSYMBOL HashTable).
402     alias Range = .Range!HashArray;
403 
404     /// ditto
405     alias ConstRange = .Range!(const HashArray);
406 
407     /// ditto
408     alias ByKey = .ByKey!(const HashArray);
409 
410     /// ditto
411     alias ByValue = .ByValue!HashArray;
412 
413     /// ditto
414     alias ConstByValue = .ByValue!(const HashArray);
415 
416     invariant
417     {
418         assert(this.data.lengthIndex < primes.length);
419     }
420 
421     /**
422      * Constructor.
423      *
424      * Params:
425      *  n         = Minimum number of buckets.
426      *  allocator = Allocator.
427      *
428      * Precondition: $(D_INLINECODE allocator !is null).
429      */
430     this(size_t n, shared Allocator allocator = defaultAllocator)
431     in
432     {
433         assert(allocator !is null);
434     }
435     do
436     {
437         this(allocator);
438         this.data.rehash(n);
439     }
440 
441     ///
442     @nogc nothrow pure @safe unittest
443     {
444         auto hashTable = HashTable!(string, int)(5);
445         assert(hashTable.capacity == 7);
446     }
447 
448     /// ditto
449     this(shared Allocator allocator)
450     in
451     {
452         assert(allocator !is null);
453     }
454     do
455     {
456         this.data = HashArray(allocator);
457     }
458 
459     /**
460      * Initializes this $(D_PARAM HashTable) from another one.
461      *
462      * If $(D_PARAM init) is passed by reference, it will be copied.
463      * If $(D_PARAM init) is passed by value, it will be moved.
464      *
465      * Params:
466      *  S         = Source set type.
467      *  init      = Source set.
468      *  allocator = Allocator.
469      *
470      * Precondition: $(D_INLINECODE allocator !is null).
471      */
472     this(S)(ref S init, shared Allocator allocator = defaultAllocator)
473     if (is(Unqual!S == HashTable))
474     in
475     {
476         assert(allocator !is null);
477     }
478     do
479     {
480         this.data = HashArray(init.data, allocator);
481     }
482 
483     /// ditto
484     this(S)(S init, shared Allocator allocator = defaultAllocator)
485     if (is(S == HashTable))
486     in
487     {
488         assert(allocator !is null);
489     }
490     do
491     {
492         this.data.move(init.data, allocator);
493     }
494 
495     /**
496      * Constructs the hash table from a forward range.
497      *
498      * Params:
499      *  R         = Range type.
500      *  range     = Forward range.
501      *  allocator = Allocator.
502      *
503      * Precondition: $(D_INLINECODE allocator !is null).
504      */
505     this(R)(scope R range, shared Allocator allocator = defaultAllocator)
506     if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
507     in
508     {
509         assert(allocator !is null);
510     }
511     do
512     {
513         this(allocator);
514         insert(range);
515     }
516 
517     ///
518     @nogc nothrow pure @safe unittest
519     {
520         alias KeyValue = HashTable!(string, int).KeyValue;
521 
522         KeyValue[2] range = [KeyValue("one", 1), KeyValue("two", 2)];
523         auto hashTable = HashTable!(string, int)(range[]);
524 
525         assert(hashTable["one"] == 1);
526         assert(hashTable["two"] == 2);
527     }
528 
529     /**
530      * Initializes the hash table from a static array.
531      *
532      * Params:
533      *  n         = Array size.
534      *  array     = Static array.
535      *  allocator = Allocator.
536      *
537      * Precondition: $(D_INLINECODE allocator !is null).
538      */
539     this(size_t n)(KeyValue[n] array,
540          shared Allocator allocator = defaultAllocator)
541     in
542     {
543         assert(allocator !is null);
544     }
545     do
546     {
547         this(allocator);
548         insert(array[]);
549     }
550 
551     ///
552     @nogc nothrow pure @safe unittest
553     {
554         alias KeyValue = HashTable!(string, int).KeyValue;
555         auto hashTable = HashTable!(string, int)([KeyValue("one", 1), KeyValue("two", 2)]);
556 
557         assert(hashTable["one"] == 1);
558         assert(hashTable["two"] == 2);
559     }
560 
561     /**
562      * Assigns another hash table.
563      *
564      * If $(D_PARAM that) is passed by reference, it will be copied.
565      * If $(D_PARAM that) is passed by value, it will be moved.
566      *
567      * Params:
568      *  S    = Content type.
569      *  that = The value should be assigned.
570      *
571      * Returns: $(D_KEYWORD this).
572      */
573     ref typeof(this) opAssign(S)(ref S that)
574     if (is(Unqual!S == HashTable))
575     {
576         this.data = that.data;
577         return this;
578     }
579 
580     /// ditto
581     ref typeof(this) opAssign(S)(S that) @trusted
582     if (is(S == HashTable))
583     {
584         this.data.swap(that.data);
585         return this;
586     }
587 
588     /**
589      * Returns: Used allocator.
590      *
591      * Postcondition: $(D_INLINECODE allocator !is null)
592      */
593     @property shared(Allocator) allocator() const
594     out (allocator)
595     {
596         assert(allocator !is null);
597     }
598     do
599     {
600         return this.data.array.allocator;
601     }
602 
603     /**
604      * Maximum amount of elements this $(D_PSYMBOL HashTable) can hold without
605      * resizing and rehashing. Note that it doesn't mean that the
606      * $(D_PSYMBOL Set) will hold $(I exactly) $(D_PSYMBOL capacity) elements.
607      * $(D_PSYMBOL capacity) tells the size of the container under a best-case
608      * distribution of elements.
609      *
610      * Returns: $(D_PSYMBOL HashTable) capacity.
611      */
612     @property size_t capacity() const
613     {
614         return this.data.capacity;
615     }
616 
617     ///
618     @nogc nothrow pure @safe unittest
619     {
620         HashTable!(string, int) hashTable;
621         assert(hashTable.capacity == 0);
622 
623         hashTable["eight"] = 8;
624         assert(hashTable.capacity == 3);
625     }
626 
627     /**
628      * Returns the number of elements in the container.
629      *
630      * Returns: The number of elements in the container.
631      */
632     @property size_t length() const
633     {
634         return this.data.length;
635     }
636 
637     ///
638     @nogc nothrow pure @safe unittest
639     {
640         HashTable!(string, int) hashTable;
641         assert(hashTable.length == 0);
642 
643         hashTable["eight"] = 8;
644         assert(hashTable.length == 1);
645     }
646 
647     /**
648      * Tells whether the container contains any elements.
649      *
650      * Returns: Whether the container is empty.
651      */
652     @property bool empty() const
653     {
654         return length == 0;
655     }
656 
657     ///
658     @nogc nothrow pure @safe unittest
659     {
660         HashTable!(string, int) hashTable;
661         assert(hashTable.empty);
662         hashTable["five"] = 5;
663         assert(!hashTable.empty);
664     }
665 
666     /**
667      * Removes all elements.
668      */
669     void clear()
670     {
671         this.data.clear();
672     }
673 
674     ///
675     @nogc nothrow pure @safe unittest
676     {
677         HashTable!(string, int) hashTable;
678         hashTable["five"] = 5;
679         assert(!hashTable.empty);
680         hashTable.clear();
681         assert(hashTable.empty);
682     }
683 
684     /**
685      * Returns current bucket count in the container.
686      *
687      * Bucket count equals to the number of the elements can be saved in the
688      * container in the best case scenario for key distribution, i.d. every key
689      * has a unique hash value. In a worse case the bucket count can be less
690      * than the number of elements stored in the container.
691      *
692      * Returns: Current bucket count.
693      *
694      * See_Also: $(D_PSYMBOL rehash).
695      */
696     @property size_t bucketCount() const
697     {
698         return this.data.bucketCount;
699     }
700 
701     /// The maximum number of buckets the container can have.
702     enum size_t maxBucketCount = primes[$ - 1];
703 
704     /**
705      * Inserts a new value at $(D_PARAM key) or reassigns the element if
706      * $(D_PARAM key) already exists in the hash table.
707      *
708      * Params:
709      *  key   = The key to insert the value at.
710      *  value = The value to be inserted.
711      *
712      * Returns: Just inserted element.
713      */
714     ref Value opIndexAssign()(auto ref Value value, auto ref Key key)
715     {
716         auto e = ((ref v) @trusted => &this.data.insert(v))(key);
717         if (e.status != BucketStatus.used)
718         {
719             static if (__traits(isRef, key))
720             {
721                 e.key = key;
722             }
723             else
724             {
725                 e.moveKey(key);
726             }
727         }
728         static if (__traits(isRef, value))
729         {
730             return e.kv.value = value;
731         }
732         else
733         {
734             return e.kv.value = move(value);
735         }
736     }
737 
738     ///
739     @nogc nothrow pure @safe unittest
740     {
741         HashTable!(string, int) hashTable;
742         assert("Pachycephalosaurus" !in hashTable);
743 
744         hashTable["Pachycephalosaurus"] = 6;
745         assert(hashTable.length == 1);
746         assert("Pachycephalosaurus" in hashTable);
747 
748         hashTable["Pachycephalosaurus"] = 6;
749         assert(hashTable.length == 1);
750         assert("Pachycephalosaurus" in hashTable);
751     }
752 
753     /**
754      * Inserts a new element in the hash table.
755      *
756      * If the element with the same key was already in the table, it reassigns
757      * it with the new value, but $(D_PSYMBOL insert) returns `0`. Otherwise
758      * `1` is returned.
759      *
760      * Params:
761      *  keyValue = Key/value pair.
762      *
763      * Returns: The number of the inserted elements with a unique key.
764      */
765     size_t insert()(ref KeyValue keyValue)
766     {
767         auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
768         size_t inserted;
769         if (e.status != BucketStatus.used)
770         {
771             e.key = keyValue.key;
772             inserted = 1;
773         }
774         e.kv.value = keyValue.value;
775         return inserted;
776     }
777 
778     /// ditto
779     size_t insert()(KeyValue keyValue)
780     {
781         auto e = ((ref v) @trusted => &this.data.insert(v))(keyValue.key);
782         size_t inserted;
783         if (e.status != BucketStatus.used)
784         {
785             e.moveKey(keyValue.key);
786             inserted = 1;
787         }
788         move(keyValue.value, e.kv.value);
789         return inserted;
790     }
791 
792     ///
793     @nogc nothrow pure @safe unittest
794     {
795         HashTable!(string, int) hashTable;
796 
797         assert(hashTable.insert(hashTable.KeyValue("number", 1)) == 1);
798         assert(hashTable["number"] == 1);
799         assert(hashTable.insert(hashTable.KeyValue("number", 2)) == 0);
800         assert(hashTable["number"] == 2);
801     }
802 
803     /**
804      * Inserts a forward range of key/value pairs into the hash table.
805      *
806      * If some of the elements in the $(D_PARAM range) have the same key, they
807      * are reassigned but are not counted as inserted elements. So the value
808      * returned by this function will be less than the range length.
809      *
810      * Params:
811      *  R     = Range type.
812      *  range = Forward range.
813      *
814      * Returns: The number of the inserted elements with a unique key.
815      */
816     size_t insert(R)(scope R range)
817     if (isForwardRange!R && is(ElementType!R == KeyValue) && !isInfinite!R)
818     {
819         return fold!((acc, x) => acc + insert(x))(range, size_t.init);
820     }
821 
822     ///
823     @nogc nothrow pure @safe unittest
824     {
825         HashTable!(string, int) hashTable;
826 
827         hashTable.KeyValue[2] range = [
828             hashTable.KeyValue("one", 1),
829             hashTable.KeyValue("two", 2),
830         ];
831 
832         assert(hashTable.insert(range[]) == 2);
833         assert(hashTable["one"] == 1);
834         assert(hashTable["two"] == 2);
835     }
836 
837     /**
838      * Find the element with the key $(D_PARAM key).
839      *
840      * Params:
841      *  T   = Type comparable with the key type, used for the lookup.
842      *  key = The key to be find.
843      *
844      * Returns: The value associated with $(D_PARAM key).
845      *
846      * Precondition: Element with $(D_PARAM key) is in this hash table.
847      */
848     ref Value opIndex(T)(auto ref const T key)
849     if (ifTestable!(T, a => Key.init == a))
850     {
851         const code = this.data.locateBucket(key);
852 
853         for (auto range = this.data.array[code .. $]; !range.empty; range.popFront())
854         {
855             if (key == range.front.key)
856             {
857                 return range.front.kv.value;
858             }
859         }
860         assert(false, "Range violation");
861     }
862 
863     ///
864     @nogc nothrow pure @safe unittest
865     {
866         HashTable!(string, int) hashTable;
867         hashTable["Triceratops"] = 7;
868         assert(hashTable["Triceratops"] == 7);
869     }
870 
871     /**
872      * Removes the element with the key $(D_PARAM key).
873      *
874      * The method returns the number of elements removed. Since
875      * the hash table contains only unique keys, $(D_PARAM remove) always
876      * returns `1` if an element with the $(D_PARAM key) was found, `0`
877      * otherwise.
878      *
879      * Params:
880      *  key = The key to be removed.
881      *
882      * Returns: Number of the removed elements.
883      */
884     size_t remove(Key key)
885     {
886         return this.data.remove(key);
887     }
888 
889     ///
890     @nogc nothrow pure @safe unittest
891     {
892         HashTable!(string, int) hashTable;
893         hashTable["Euoplocephalus"] = 6;
894 
895         assert("Euoplocephalus" in hashTable);
896         assert(hashTable.remove("Euoplocephalus") == 1);
897         assert(hashTable.remove("Euoplocephalus") == 0);
898         assert("Euoplocephalus" !in hashTable);
899     }
900 
901     /**
902      * Looks for $(D_PARAM key) in this hash table.
903      *
904      * Params:
905      *  T   = Type comparable with the key type, used for the lookup.
906      *  key = The key to look for.
907      *
908      * Returns: $(D_KEYWORD true) if $(D_PARAM key) exists in the hash table,
909      *          $(D_KEYWORD false) otherwise.
910      */
911     bool opBinaryRight(string op : "in", T)(auto ref const T key) const
912     if (ifTestable!(T, a => Key.init == a))
913     {
914         return key in this.data;
915     }
916 
917     ///
918     @nogc nothrow pure @safe unittest
919     {
920         HashTable!(string, int) hashTable;
921 
922         assert("Shantungosaurus" !in hashTable);
923         hashTable["Shantungosaurus"] = 15;
924         assert("Shantungosaurus" in hashTable);
925 
926         assert("Ceratopsia" !in hashTable);
927     }
928 
929     /**
930      * Sets the number of buckets in the container to at least $(D_PARAM n)
931      * and rearranges all the elements according to their hash values.
932      *
933      * If $(D_PARAM n) is greater than the current $(D_PSYMBOL bucketCount)
934      * and lower than or equal to $(D_PSYMBOL maxBucketCount), a rehash is
935      * forced.
936      *
937      * If $(D_PARAM n) is greater than $(D_PSYMBOL maxBucketCount),
938      * $(D_PSYMBOL maxBucketCount) is used instead as a new number of buckets.
939      *
940      * If $(D_PARAM n) is less than or equal to the current
941      * $(D_PSYMBOL bucketCount), the function may have no effect.
942      *
943      * Rehashing is automatically performed whenever the container needs space
944      * to insert new elements.
945      *
946      * Params:
947      *  n = Minimum number of buckets.
948      */
949     void rehash(size_t n)
950     {
951         this.data.rehash(n);
952     }
953 
954     /**
955      * Returns a bidirectional range whose element type is a tuple of a key and
956      * the respective value.
957      *
958      * Returns: A bidirectional range that iterates over the container.
959      */
960     Range opIndex()
961     {
962         return typeof(return)(this.data.array[]);
963     }
964 
965     /// ditto
966     ConstRange opIndex() const
967     {
968         return typeof(return)(this.data.array[]);
969     }
970 
971     ///
972     @nogc nothrow pure @safe unittest
973     {
974         HashTable!(string, int) hashTable;
975         assert(hashTable[].empty);
976 
977         hashTable["Iguanodon"] = 9;
978         assert(!hashTable[].empty);
979         assert(hashTable[].front == hashTable.KeyValue("Iguanodon", 9));
980         assert(hashTable[].back == hashTable.KeyValue("Iguanodon", 9));
981     }
982 
983     /**
984      * Returns a bidirectional range that iterats over the keys of this
985      * $(D_PSYMBOL HashTable).
986      *
987      * This function always returns a $(D_KEYWORD const) range, since changing
988      * a key of a hash table would probably change its hash value and require
989      * rehashing.
990      *
991      * Returns: $(D_KEYWORD const) bidirectional range that iterates over the
992      *          keys of the container.
993      *
994      * See_Also: $(D_PSYMBOL byValue).
995      */
996     ByKey byKey() const
997     {
998         return typeof(return)(this.data.array[]);
999     }
1000 
1001     ///
1002     @nogc nothrow pure @safe unittest
1003     {
1004         HashTable!(string, int) hashTable;
1005         hashTable["one"] = 1;
1006         hashTable["two"] = 2;
1007 
1008         auto byKey = hashTable.byKey();
1009         assert(!byKey.empty);
1010 
1011         assert(byKey.front == "one" || byKey.front == "two");
1012         assert(byKey.back == "one" || byKey.back == "two");
1013         assert(byKey.front != byKey.back);
1014 
1015         byKey.popFront();
1016         assert(byKey.front == byKey.back);
1017 
1018         byKey.popBack();
1019         assert(byKey.empty);
1020     }
1021 
1022     /**
1023      * Returns a bidirectional range that iterats over the values of this
1024      * $(D_PSYMBOL HashTable).
1025      *
1026      * Returns: A bidirectional range that iterates over the values of the
1027      *          container.
1028      *
1029      * See_Also: $(D_PSYMBOL byKey).
1030      */
1031     ByValue byValue()
1032     {
1033         return typeof(return)(this.data.array[]);
1034     }
1035 
1036     /// ditto
1037     ConstByValue byValue() const
1038     {
1039         return typeof(return)(this.data.array[]);
1040     }
1041 
1042     ///
1043     @nogc nothrow pure @safe unittest
1044     {
1045         HashTable!(string, int) hashTable;
1046         hashTable["one"] = 1;
1047         hashTable["two"] = 2;
1048 
1049         auto byValue = hashTable.byValue();
1050         assert(!byValue.empty);
1051 
1052         assert(byValue.front == 1 || byValue.front == 2);
1053         assert(byValue.back == 1 || byValue.back == 2);
1054         assert(byValue.front != byValue.back);
1055 
1056         byValue.popFront();
1057         assert(byValue.front == byValue.back);
1058 
1059         byValue.popBack();
1060         assert(byValue.empty);
1061     }
1062 }
1063 
1064 @nogc nothrow pure @safe unittest
1065 {
1066     auto dinos = HashTable!(string, int)(17);
1067     assert(dinos.empty);
1068 
1069     dinos["Ornithominus"] = 4;
1070     dinos["Tyrannosaurus"] = 12;
1071     dinos["Deinonychus"] = 3;
1072     dinos["Stegosaurus"] = 6;
1073     dinos["Brachiosaurus"] = 25;
1074 
1075     assert(dinos.length == 5);
1076     assert(dinos["Ornithominus"] == 4);
1077     assert(dinos["Stegosaurus"] == 6);
1078     assert(dinos["Deinonychus"] == 3);
1079     assert(dinos["Tyrannosaurus"] == 12);
1080     assert(dinos["Brachiosaurus"] == 25);
1081 
1082     dinos.clear();
1083     assert(dinos.empty);
1084 }