1 /*******************************************************************************
2 
3     Smart enum template class, encapsulates an enum with a map of its codes
4     and descriptions.
5 
6     Contains two main mixin templates:
7 
8         1. SmartEnum
9         2. AutoSmartEnum
10 
11     1. SmartEnum
12     ----------------------------------------------------------------------------
13 
14     Mixin template declaring a class containing an enum with code<->description
15     lookup. All methods are static, so that the class can be used without an
16     instance.
17 
18     This mixin creates classes which contain a real (anonymous) enum, and two
19     associative arrays mapping from the enum values to their string descriptions
20     and vice versa, allowing two-way lookup between codes <-> descriptions, and
21     an implementation of the 'in' operator to tell if a code or description is
22     valid.
23 
24     This is especially useful for "Const" classes which define a list of command
25     or status codes where a kind of reverse lookup is needed, going from a code
26     to its name / description (or vice versa).
27 
28     The classes also implement a foreach iterator over the values of the enum,
29     and several other methods (see SmartEunmCore, below).
30 
31     The mixin template takes as parameters the name of the class to be generated
32     and a variadic list of SmartEnumValue structs, specifying the names and
33     values of the enum's members.
34 
35     The SmartEnumValue struct is also a template, which takes as parameter the
36     base type of the enum, which must be an integral type (see
37     http://www.digitalmars.com/d/1.0/enum.html).
38 
39     Note that as the class generated by the mixin template contains only static
40     members, it is not necessary to actually instantiate it, only to declare the
41     class (as demonstrated in the example below).
42 
43     Usage example:
44 
45     ---
46 
47         import ocean.core.SmartEnum;
48         import ocean.io.Stdout;
49 
50         alias SmartEnumValue!(int) Code;
51 
52         mixin(SmartEnum!("Commands",
53             Code("first", 1),
54             Code("second", 2)
55         ));
56 
57         // Getting descriptions of codes
58         // (Note that the getter methods work like opIn, returning pointers.)
59         Stdout.formatln("Description for code first = {}", *Commands.description(Commands.first));
60         Stdout.formatln("Description for code 1 = {}", *Commands.description(1));
61 
62         // Getting codes by description
63         // (Note that the getter methods work like opIn, returning pointers.)
64         Stdout.formatln("Code for description 'first' = {}", *Commands.code("first"));
65 
66         // Testing whether codes exist
67         Stdout.formatln("1 in enum? {}", !!(1 in Commands));
68         Stdout.formatln("first in enum? {}", !!(Commands.first in Commands));
69 
70         // Testing whether codes exist by description
71         Stdout.formatln("'first' in enum? {}", !!("first" in Commands));
72         Stdout.formatln("'third' in enum? {}", !!("third" in Commands));
73 
74         // Iteration over enum
75         foreach ( code, descr; Commands )
76         {
77             Stdout.formatln("{} -> {}", code, descr);
78         }
79 
80     ---
81 
82     2. AutoSmartEnum
83     ----------------------------------------------------------------------------
84 
85     Template to automatically create a SmartEnum from a list of strings. The
86     enum's base type is specified, and the enum values are automatically
87     numbered, starting at 0.
88 
89     Usage example:
90 
91     ---
92 
93         import ocean.core.SmartEnum;
94 
95         mixin(AutoSmartEnum!("Commands", int,
96             "first",
97             "second"));
98 
99     ---
100 
101     Copyright:
102         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
103         All rights reserved.
104 
105     License:
106         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
107         Alternatively, this file may be distributed under the terms of the Tango
108         3-Clause BSD License (see LICENSE_BSD.txt for details).
109 
110 *******************************************************************************/
111 
112 module ocean.core.SmartEnum;
113 
114 
115 
116 import ocean.meta.types.Qualifiers;
117 
118 public import ocean.core.Enforce;
119 
120 import CTFE = ocean.meta.codegen.CTFE;
121 
122 import ocean.core.Tuple;
123 import ocean.core.Verify;
124 
125 version (unittest)
126 {
127     import ocean.core.Test;
128 }
129 
130 
131 /*******************************************************************************
132 
133     Abstract base class for SmartEnums. Contains no members, just provided as a
134     convenient way of checking that a class is in fact a SmartEnum, using
135     is(T : ISmartEnum).
136 
137 *******************************************************************************/
138 
139 public abstract class ISmartEnum
140 {
141 }
142 
143 
144 
145 /*******************************************************************************
146 
147     Struct template representing a single member of an enum -- containing a
148     string for the enum identifier and a code for the corresponding value.
149 
150     Params:
151         T = base type of enum
152 
153 *******************************************************************************/
154 
155 public struct SmartEnumValue ( T )
156 {
157     alias T BaseType;
158 
159     istring name;
160     T value;
161 }
162 
163 unittest
164 {
165     alias SmartEnumValue!(int) _;
166 }
167 
168 
169 
170 /*******************************************************************************
171 
172     Members forming the core of each class generated by the SmartEnum mixin.
173     This template is mixed into each class created by the SmartEnum template.
174 
175     Params:
176         BaseType = base type of enum
177 
178 *******************************************************************************/
179 
180 public template SmartEnumCore ( BaseType )
181 {
182     import ocean.meta.types.Qualifiers;
183 
184     /***************************************************************************
185 
186         Two way mapping between codes <-> descriptions.
187 
188     ***************************************************************************/
189 
190     static public TwoWayMap!(BaseType) map;
191 
192 
193     /***************************************************************************
194 
195         Looks up the description of a code.
196 
197         Aliased to opIn.
198 
199         Params:
200             code = code to look up
201 
202         Returns:
203             pointer to code's description, or null if code not in enum
204 
205     ***************************************************************************/
206 
207     static public istring* description ( BaseType code )
208     {
209         return code in map;
210     }
211 
212     public alias opBinaryRight ( istring op : "in" ) = description;
213 
214 
215     /***************************************************************************
216 
217         Looks up the code of a description.
218 
219         Aliased to opIn.
220 
221         Params:
222             description = description to look up
223 
224         Returns:
225             pointer to description's code, or null if description not in enum
226 
227     ***************************************************************************/
228 
229     static public BaseType* code ( cstring description )
230     {
231         return description in map;
232     }
233 
234     public alias opBinaryRight ( istring op : "in" ) = code;
235 
236 
237 
238     /***************************************************************************
239 
240         Gets the description of a code.
241 
242         Params:
243             code = code to look up
244 
245         Returns:
246             code's description
247 
248         Throws:
249             if code is not in the map
250 
251     ***************************************************************************/
252 
253     static public istring opIndex ( BaseType code )
254     {
255         auto description = code in map;
256         enforce(description, "code not found in SmartEnum");
257         return *description;
258     }
259 
260 
261     /***************************************************************************
262 
263         Gets the code of a description.
264 
265         Params:
266             description = description to look up
267 
268         Returns:
269             description's code
270 
271         Throws:
272             if description is not in the map
273 
274     ***************************************************************************/
275 
276     static public BaseType opIndex ( cstring description )
277     {
278         auto code = description in map;
279         enforce(code, cast(istring)(description ~ " not found in SmartEnum"));
280         return *code;
281     }
282 
283 
284     /***************************************************************************
285 
286         Looks up the index of a code in the enum (ie code is the nth code in the
287         enum). This can be useful if the actual enum codes are not consecutive.
288 
289         Params:
290             code = code to get index for
291 
292         Returns:
293             pointer to code's index, or null if code not in enum
294 
295     ***************************************************************************/
296 
297     static public size_t* indexOf ( BaseType code )
298     {
299         return map.indexOf(code);
300     }
301 
302 
303     /***************************************************************************
304 
305         Looks up the index of a description in the enum (ie description is for
306         the nth code in the enum).
307 
308         Params:
309             description = description to get index for
310 
311         Returns:
312             pointer to description's index, or null if description not in enum
313 
314     ***************************************************************************/
315 
316     static public size_t* indexOf ( cstring description )
317     {
318         return map.indexOf(description);
319     }
320 
321 
322     /***************************************************************************
323 
324         Looks up a code in the enum by its index. (ie gets the nth code in the
325         enum).
326 
327         Params:
328             index = index of code to get
329 
330         Returns:
331             nth code in enum
332 
333         Throws:
334             array out of bounds if index is > number of codes in the enum
335 
336     ***************************************************************************/
337 
338     static public BaseType codeFromIndex ( size_t index )
339     {
340         return map.keys[index];
341     }
342 
343 
344     /***************************************************************************
345 
346         Looks up a description in the enum by the index of its code. (ie gets
347         the description of the nth code in the enum).
348 
349         Params:
350             index = index of code to get description for
351 
352         Returns:
353             description of nth code in enum
354 
355         Throws:
356             array out of bounds if index is > number of codes in the enum
357 
358     ***************************************************************************/
359 
360     static public istring descriptionFromIndex ( size_t index )
361     {
362         return map.values[index];
363     }
364 
365 
366     /***************************************************************************
367 
368         foreach iterator over the codes and descriptions of the enum.
369 
370     ***************************************************************************/
371 
372     static public int opApply ( scope int delegate ( ref BaseType code, ref istring desc ) dg )
373     {
374         int res;
375         foreach ( enum_code, enum_description; map )
376         {
377             res = dg(enum_code, enum_description);
378         }
379         return res;
380     }
381 
382 
383     /***************************************************************************
384 
385         foreach iterator over the codes and descriptions of the enum and their
386         indices.
387 
388     ***************************************************************************/
389 
390     static public int opApply ( scope int delegate ( ref size_t index, ref BaseType code, ref istring desc ) dg )
391     {
392         int res;
393         foreach ( index, enum_code, enum_description; map )
394         {
395             res = dg(index, enum_code, enum_description);
396         }
397         return res;
398     }
399 }
400 
401 unittest
402 {
403     alias SmartEnumCore!(int) _;
404 }
405 
406 /*******************************************************************************
407 
408     Template to mixin a comma separated list of SmartEnumValues.
409 
410     Params:
411         T = variadic list of one or more SmartEnumValues
412 
413     Generates output of the form:
414 
415     ---
416         first = 1,
417         second = 2,
418         last = 100
419     ---
420 
421 *******************************************************************************/
422 
423 private template EnumValuesList ( T ... )
424 {
425     static if ( T.length == 1 )
426     {
427         static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value);
428     }
429     else
430     {
431         static immutable EnumValuesList = T[0].name ~ "=" ~ CTFE.toString(T[0].value) ~ "," ~ EnumValuesList!(T[1..$]);
432     }
433 }
434 
435 
436 /*******************************************************************************
437 
438     Template to mixin an enum from a list of SmartEnumValues.
439 
440     Params:
441         T = variadic list of one or more SmartEnumValues
442 
443     Generates output of the form:
444 
445     ---
446         enum
447         {
448             first = 1,
449             second = 2,
450             last = 100
451         }
452     ---
453 
454 *******************************************************************************/
455 
456 private template DeclareEnum ( T ... )
457 {
458     static immutable DeclareEnum = "alias " ~ T[0].BaseType.stringof ~ " BaseType; enum : BaseType {" ~ EnumValuesList!(T) ~ "} ";
459 }
460 
461 
462 /*******************************************************************************
463 
464     Template to mixin a series of TwoWayMap value initialisations.
465 
466     Params:
467         T = variadic list of one or more SmartEnumValues
468 
469     Generates output of the form:
470 
471     ---
472         map["first"]=1;
473         map["second"]=2;
474         map["last"]=100;
475     ---
476 
477 *******************************************************************************/
478 
479 private template InitialiseMap ( T ... )
480 {
481     static if ( T.length == 1 )
482     {
483         static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";";
484     }
485     else
486     {
487         static immutable InitialiseMap = `map["` ~ T[0].name ~ `"]=` ~ T[0].name ~ ";" ~ InitialiseMap!(T[1..$]);
488     }
489 }
490 
491 
492 /*******************************************************************************
493 
494     Template to mixin a static constructor which initialises and rehashes a
495     TwoWayMap.
496 
497     Params:
498         T = variadic list of one or more SmartEnumValues
499 
500     Generates output of the form:
501 
502     ---
503         static this ( )
504         {
505             map["first"]=1;
506             map["second"]=2;
507             map["last"]=100;
508 
509             map.rehash;
510         }
511     ---
512 
513 *******************************************************************************/
514 
515 private template StaticThis ( T ... )
516 {
517     static immutable StaticThis = "static this() {" ~ InitialiseMap!(T) ~ "map.rehash;} ";
518 }
519 
520 
521 /*******************************************************************************
522 
523     Template to find the maximum code from a list of SmartEnumValues.
524 
525     Params:
526         T = variadic list of one or more SmartEnumValues
527 
528 *******************************************************************************/
529 
530 private template MaxValue ( T ... )
531 {
532     static if ( T.length == 1 )
533     {
534         static immutable MaxValue = T[0].value;
535     }
536     else
537     {
538         static immutable MaxValue = T[0].value > MaxValue!(T[1..$]) ? T[0].value : MaxValue!(T[1..$]);
539     }
540 }
541 
542 
543 /*******************************************************************************
544 
545     Template to find the minimum code from a list of SmartEnumValues.
546 
547     Params:
548         T = variadic list of one or more SmartEnumValues
549 
550 *******************************************************************************/
551 
552 private template MinValue ( T ... )
553 {
554     static if ( T.length == 1 )
555     {
556         static immutable MinValue = T[0].value;
557     }
558     else
559     {
560         static immutable MinValue = T[0].value < MinValue!(T[1..$]) ? T[0].value : MinValue!(T[1..$]);
561     }
562 }
563 
564 
565 /*******************************************************************************
566 
567     Template to find the length of the longest description in a list of
568     SmartEnumValues.
569 
570     Params:
571         T = variadic list of one or more SmartEnumValues
572 
573 *******************************************************************************/
574 
575 private template LongestName ( T ... )
576 {
577     static if ( T.length == 1 )
578     {
579         static immutable LongestName = T[0].name.length;
580     }
581     else
582     {
583         static immutable LongestName = T[0].name.length > LongestName!(T[1..$]) ? T[0].name.length : LongestName!(T[1..$]);
584     }
585 }
586 
587 
588 /*******************************************************************************
589 
590     Template to find the length of the shortest description in a list of
591     SmartEnumValues.
592 
593     Params:
594         T = variadic list of one or more SmartEnumValues
595 
596 *******************************************************************************/
597 
598 private template ShortestName ( T ... )
599 {
600     static if ( T.length == 1 )
601     {
602         static immutable ShortestName = T[0].name.length;
603     }
604     else
605     {
606         static immutable ShortestName = T[0].name.length < ShortestName!(T[1..$]) ? T[0].name.length : ShortestName!(T[1..$]);
607     }
608 }
609 
610 
611 /*******************************************************************************
612 
613     Template to mixin the declaration of a set of constants.
614 
615     Params:
616         T = variadic list of one or more SmartEnumValues
617 
618     Generates output of the form:
619 
620     ---
621         const length = 3;
622         const min = 1;
623         const max = 100;
624         const min_descr_length = 4;
625         const max_descr_length = 6;
626     ---
627 
628 *******************************************************************************/
629 
630 private template DeclareConstants ( T ... )
631 {
632     static immutable istring DeclareConstants =
633         "enum length = " ~ CTFE.toString(T.length) ~ "; " ~
634         "enum min = " ~ CTFE.toString(MinValue!(T)) ~ "; " ~
635         "enum max = " ~ CTFE.toString(MaxValue!(T)) ~ "; " ~
636         "enum min_descr_length = " ~ CTFE.toString(ShortestName!(T)) ~ "; " ~
637         "enum max_descr_length = " ~ CTFE.toString(LongestName!(T)) ~ "; ";
638 }
639 
640 
641 /*******************************************************************************
642 
643     Template to mixin code for a mixin template declaring the enum class'
644     core members.
645 
646     Params:
647         T = variadic list of one or more SmartEnumValues
648 
649     Generates output of the form:
650 
651     ---
652         mixin SmartEnumCore!(int);
653     ---
654 
655 *******************************************************************************/
656 
657 private template MixinCore ( T ... )
658 {
659     static immutable MixinCore = "mixin SmartEnumCore!(" ~ T[0].BaseType.stringof ~ ");";
660 }
661 
662 
663 /*******************************************************************************
664 
665     Template to mixin a SmartEnum class.
666 
667     Params:
668         Name = name of class
669         T = variadic list of one or more SmartEnumValues
670 
671     Generates output of the form:
672 
673     ---
674         class EnumName : ISmartEnum
675         {
676             // class contents (see templates above)
677         }
678     ---
679 
680 *******************************************************************************/
681 
682 public template SmartEnum ( istring Name, T ... )
683 {
684     static if ( T.length > 0 )
685     {
686         static immutable SmartEnum = "class " ~ Name ~ " : ISmartEnum { " ~ DeclareEnum!(T) ~
687             DeclareConstants!(T) ~ StaticThis!(T) ~ MixinCore!(T) ~ "}";
688     }
689     else
690     {
691         static assert(false, "Cannot create a SmartEnum with no entries!");
692     }
693 }
694 
695 ///
696 unittest
697 {
698     alias SmartEnumValue!(int) Code;
699 
700     mixin (SmartEnum!("Commands",
701         Code("first", 1),
702         Code("second", 2)
703     ));
704 
705     test!("==")(*Commands.description(Commands.first), "first"[]);
706     test!("==")(*Commands.description(1), "first"[]);
707 
708     test!("==")(*Commands.code("first"), Commands.first);
709     test!("==")(*Commands.code("second"), 2);
710 
711     test((1 in Commands) !is null);
712     test((Commands.second in Commands) !is null);
713     test((5 in Commands) is null);
714 
715     test(("first" in Commands) !is null);
716     test(("third" in Commands) is null);
717 
718     size_t count;
719     foreach ( code, descr; Commands )
720         ++count;
721     test!("==")(count, 2);
722 }
723 
724 
725 /*******************************************************************************
726 
727     Template to mixin a SmartEnum class with the codes automatically generated,
728     starting at 0.
729 
730     Params:
731         Name = name of class
732         BaseType = base type of enum
733         Strings = variadic list of descriptions of the enum values (statically
734                   asserted to be istring's)
735 
736 *******************************************************************************/
737 
738 public template AutoSmartEnum ( istring Name, BaseType, Strings ... )
739 {
740     static assert ( is(typeof(Strings[0]) : istring), "AutoSmartEnum - please only give immutable strings as template parameters");
741 
742     static immutable AutoSmartEnum = autoSmartEnumImpl(Name, BaseType.stringof,
743         Strings);
744 }
745 
746 unittest
747 {
748     mixin(AutoSmartEnum!("Name", int, "a", "b", "c"));
749     test!("==")(*Name.code("a"), 0);
750     test!("==")(*Name.code("b"), 1);
751     test!("==")(*Name.code("c"), 2);
752 
753     // Test lookup with mutable values
754     mstring name1 = "a".dup, name2 = "b".dup, name3 = "c".dup;
755     Name n;
756 
757     test!("==")(*Name.code(name1), 0);
758     test!("==")(*Name.code(name2), 1);
759     test!("==")(*Name.code(name3), 2);
760 
761     test!("in")(name1, n);
762     test!("in")(name2, n);
763     test!("in")(name3, n);
764     test!("!in")("NON_EXISTENT".dup, n);
765 }
766 
767 
768 /*******************************************************************************
769 
770     CTFE function to mixin a SmartEnum class with the codes automatically
771     generated, starting at 0.
772 
773     Params:
774         name = name of the class to be created
775         basetype = base type of enum
776         members = variadic list of descriptions of the enum values
777 
778 *******************************************************************************/
779 
780 private istring autoSmartEnumImpl ( istring name, istring basetype,
781     istring[] members ... )
782 {
783     // Create two strings, one to declare the members, and one to populate
784     // the map.
785     // Also determine the minimum and maximum length of each member name
786 
787     istring declstr = null;
788     istring mapstr = null;
789 
790     long maxlen = members[0].length;
791     long minlen = members[0].length;
792 
793     foreach ( i, s ; members )
794     {
795         if ( s.length > maxlen )
796         {
797             maxlen = s.length;
798         }
799         if ( s.length < minlen )
800         {
801             minlen = s.length;
802         }
803 
804         // declstr will be of the form "A=0,B=1,C=2"
805 
806         if ( i > 0 )
807         {
808                 declstr ~= ",";
809         }
810 
811         declstr ~= s ~ "=" ~ CTFE.toString(i);
812 
813         // mapstr will be of the form  `map["A"]=A;map["B"]=B;`
814 
815         mapstr ~= `map["` ~ s ~ `"]=` ~ s ~ `;`;
816     }
817 
818     return  "class " ~ name ~ " : ISmartEnum {"
819         ~ " alias " ~ basetype ~ " BaseType; enum : BaseType {"
820         ~ declstr ~ "} enum length = " ~ CTFE.toString(members.length)
821         ~ "; enum min = 0; enum max = " ~ CTFE.toString(members.length - 1)
822         ~ "; enum min_descr_length = " ~ CTFE.toString(minlen)
823         ~ "; enum max_descr_length = " ~ CTFE.toString(maxlen)
824         ~ "; static this() {" ~ mapstr ~ "map.rehash;} mixin SmartEnumCore!("
825         ~ basetype~ ");}";
826 }
827 
828 unittest
829 {
830     test!("==")(autoSmartEnumImpl("Name", "int", "aa", "bbbb", "c"),
831         `class Name : ISmartEnum { alias int BaseType; enum : BaseType `
832         ~ `{aa=0,bbbb=1,c=2} enum length = 3; enum min = 0; enum max = 2; `
833         ~ `enum min_descr_length = 1; enum max_descr_length = 4; `
834         ~ `static this() {map["aa"]=aa;map["bbbb"]=bbbb;map["c"]=c;`
835         ~ `map.rehash;} mixin SmartEnumCore!(int);}`);
836 }
837 
838 /*******************************************************************************
839 
840     Moved from ocean.core.TwoWayMap
841 
842 *******************************************************************************/
843 
844 
845 
846 import ocean.core.Enforce;
847 
848 import ocean.core.Array : find;
849 
850 /*******************************************************************************
851 
852     Two way map struct template
853 
854     Params:
855         A = key type
856         B = value type
857         Indexed = true to include methods for getting the index of keys or
858             values in the internal arrays
859 
860     Note: 'key' and 'value' types are arbitrarily named, as the mapping goes
861     both ways. They are just named this way for convenience and to present the
862     same interface as the standard associative array.
863 
864 *******************************************************************************/
865 
866 public struct TwoWayMap ( A )
867 {
868     /***************************************************************************
869 
870         Ensure that the template is mapping between two different types. Most of
871         the methods won't compile otherwise.
872 
873     ***************************************************************************/
874 
875     static if ( is(A : cstring) )
876     {
877         static assert(false, "TwoWayMap does not support mapping to a string type");
878     }
879 
880 
881     /***************************************************************************
882 
883         Type aliases.
884 
885     ***************************************************************************/
886 
887     public alias A KeyType;
888     public alias istring ValueType;
889 
890 
891     /***************************************************************************
892 
893         Associative arrays which store the mappings.
894 
895     ***************************************************************************/
896 
897     private ValueType[KeyType] a_to_b;
898     private KeyType[ValueType] b_to_a;
899 
900 
901     /***************************************************************************
902 
903         Dynamic arrays storing all keys and values added to the mappings.
904         Storing these locally avoids calling the associative array .key and
905         .value properties, which cause a memory allocation on each use.
906 
907         TODO: maybe this should be optional, controlled by a template parameter
908 
909     ***************************************************************************/
910 
911     private KeyType[] keys_list;
912     private ValueType[] values_list;
913 
914 
915     /***************************************************************************
916 
917         Optional indices for mapped items.
918 
919     ***************************************************************************/
920 
921     private size_t[KeyType] a_to_index; // A to index in keys_list
922     private size_t[ValueType] b_to_index; // B to index in values_list
923 
924 
925     /***************************************************************************
926 
927         Invariant checking that the length of both mappings should always be
928         identical.
929         Use -debug=TwoWayMapFullConsistencyCheck to check that the indices of
930         mapped items are consistent, too (this check may significantly impact
931         performance).
932 
933     ***************************************************************************/
934 
935     invariant ( )
936     {
937         assert(this.a_to_b.length == this.b_to_a.length);
938 
939         debug ( TwoWayMapFullConsistencyCheck )
940         {
941             foreach ( a, b; this.a_to_b )
942             {
943                 assert(this.a_to_index[a] == this.b_to_index[b]);
944             }
945         }
946     }
947 
948 
949     /***************************************************************************
950 
951         Assigns a set of mappings from an associative array.
952 
953         Params:
954             assoc_array = associative array to assign
955 
956     ***************************************************************************/
957 
958     public void opAssign ( ValueType[KeyType] assoc_array )
959     {
960         this.keys_list.length = 0;
961         this.values_list.length = 0;
962 
963         this.a_to_b = assoc_array;
964 
965         foreach ( a, b; this.a_to_b )
966         {
967             this.b_to_a[b] = a;
968 
969             this.keys_list ~= *(b in this.b_to_a);
970             this.values_list ~= *(a in this.a_to_b);
971         }
972 
973         this.updateIndices();
974     }
975 
976     public void opAssign ( KeyType[ValueType] assoc_array )
977     {
978         this.keys_list.length = 0;
979         this.values_list.length = 0;
980 
981         this.b_to_a = assoc_array;
982 
983         foreach ( b, a; this.b_to_a )
984         {
985             this.a_to_b[a] = b;
986 
987             this.keys_list ~= *(b in this.b_to_a);
988             this.values_list ~= *(a in this.a_to_b);
989         }
990 
991         this.updateIndices();
992     }
993 
994 
995     /***************************************************************************
996 
997         Adds a mapping.
998 
999         Params:
1000             a = item to map to
1001             b = item to map to
1002 
1003     ***************************************************************************/
1004 
1005     public void opIndexAssign ( KeyType a, ValueType b )
1006     out
1007     {
1008         assert(this.a_to_index[a] < this.keys_list.length);
1009         assert(this.b_to_index[b] < this.values_list.length);
1010     }
1011     do
1012     {
1013         auto already_exists = !!(a in this.a_to_b);
1014 
1015         this.a_to_b[a] = b;
1016         this.b_to_a[b] = a;
1017 
1018         if ( !already_exists )
1019         {
1020             this.keys_list ~= *(b in this.b_to_a);
1021             this.values_list ~= *(a in this.a_to_b);
1022         }
1023 
1024         this.updateIndices();
1025     }
1026 
1027     public void opIndexAssign ( ValueType b, KeyType a )
1028     out
1029     {
1030         assert(this.a_to_index[a] < this.keys_list.length);
1031         assert(this.b_to_index[b] < this.values_list.length);
1032     }
1033     do
1034     {
1035         auto already_exists = !!(a in this.a_to_b);
1036 
1037         this.a_to_b[a] = b;
1038         this.b_to_a[b] = a;
1039 
1040         if ( !already_exists )
1041         {
1042             this.keys_list ~= *(b in this.b_to_a);
1043             this.values_list ~= *(a in this.a_to_b);
1044         }
1045 
1046         this.updateIndices();
1047     }
1048 
1049 
1050     /***************************************************************************
1051 
1052         Rehashes the mappings.
1053 
1054     ***************************************************************************/
1055 
1056     public void rehash ( )
1057     {
1058         this.a_to_b.rehash;
1059         this.b_to_a.rehash;
1060 
1061         this.updateIndices();
1062     }
1063 
1064 
1065     /***************************************************************************
1066 
1067         `in` operator - performs a lookup of an item A in the map
1068         corresponding to an item B.
1069 
1070         Params:
1071             b = item to look up
1072 
1073         Returns:
1074             item of type A corresponding to specified item of type B, or null if
1075             no mapping exists
1076 
1077     ***************************************************************************/
1078 
1079     public KeyType* opBinaryRight ( istring op : "in" ) ( cstring b )
1080     {
1081         return b in this.b_to_a;
1082     }
1083 
1084 
1085     /***************************************************************************
1086 
1087         opIn_r operator - performs a lookup of an item B in the map
1088         corresponding to an item A.
1089 
1090         Params:
1091             a = item to look up
1092 
1093         Returns:
1094             item of type B corresponding to specified item of type A, or null if
1095             no mapping exists
1096 
1097     ***************************************************************************/
1098 
1099     public ValueType* opBinaryRight ( istring op : "in" ) ( KeyType a )
1100     {
1101         return a in this.a_to_b;
1102     }
1103 
1104 
1105     /***************************************************************************
1106 
1107         opIndex operator - performs a lookup of an item A in the map
1108         corresponding to an item B.
1109 
1110         Params:
1111             b = item to look up
1112 
1113         Throws:
1114             as per the normal opIndex operator over an associative array
1115 
1116         Returns:
1117             item of type A corresponding to specified item of type B
1118 
1119     ***************************************************************************/
1120 
1121     public KeyType opIndex ( cstring b )
1122     {
1123         return this.b_to_a[b];
1124     }
1125 
1126 
1127     /***************************************************************************
1128 
1129         opIndex operator - performs a lookup of an item B in the map
1130         corresponding to an item A.
1131 
1132         Params:
1133             a = item to look up
1134 
1135         Throws:
1136             as per the normal opIndex operator over an associative array
1137 
1138         Returns:
1139             item of type B corresponding to specified item of type A
1140 
1141     ***************************************************************************/
1142 
1143     public ValueType opIndex ( KeyType a )
1144     {
1145         return this.a_to_b[a];
1146     }
1147 
1148 
1149     /***************************************************************************
1150 
1151         Returns:
1152             number of items in the map
1153 
1154     ***************************************************************************/
1155 
1156     public size_t length ( )
1157     {
1158         return this.a_to_b.length;
1159     }
1160 
1161 
1162     /***************************************************************************
1163 
1164         Returns:
1165             dynamic array containing all map elements of type A
1166 
1167     ***************************************************************************/
1168 
1169     public KeyType[] keys ( )
1170     {
1171         return this.keys_list;
1172     }
1173 
1174 
1175     /***************************************************************************
1176 
1177         Returns:
1178             dynamic array containing all map elements of type B
1179 
1180     ***************************************************************************/
1181 
1182     public ValueType[] values ( )
1183     {
1184         return this.values_list;
1185     }
1186 
1187 
1188     /***************************************************************************
1189 
1190         foreach iterator over the mapping.
1191 
1192         Note that the order of iteration over the map is determined by the
1193         elements of type A (the keys).
1194 
1195     ***************************************************************************/
1196 
1197     public int opApply ( scope int delegate ( ref KeyType a, ref ValueType b ) dg )
1198     {
1199         int res;
1200         foreach ( a, b; this.a_to_b )
1201         {
1202             res = dg(a, b);
1203         }
1204         return res;
1205     }
1206 
1207 
1208     /***************************************************************************
1209 
1210         foreach iterator over the mapping, including each value's index.
1211 
1212         Note that the order of iteration over the map is determined by the
1213         elements of type A (the keys).
1214 
1215     ***************************************************************************/
1216 
1217     public int opApply ( scope int delegate ( ref size_t index, ref KeyType a, ref ValueType b ) dg )
1218     {
1219         int res;
1220         foreach ( a, b; this.a_to_b )
1221         {
1222             auto index = this.indexOf(a);
1223             verify(index !is null);
1224 
1225             res = dg(*index, a, b);
1226         }
1227         return res;
1228     }
1229 
1230 
1231     /***************************************************************************
1232 
1233         Gets the index of an element of type A in the list of all elements of
1234         type A.
1235 
1236         Params:
1237             a = element to look up
1238 
1239         Returns:
1240             pointer to the index of an element of type A in this.keys_list, or
1241             null if the element is not in the map
1242 
1243     ***************************************************************************/
1244 
1245     public size_t* indexOf ( KeyType a )
1246     {
1247         auto index = a in this.a_to_index;
1248         enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map");
1249         return index;
1250     }
1251 
1252 
1253     /***************************************************************************
1254 
1255         Gets the index of an element of type B in the list of all elements of
1256         type B.
1257 
1258         Params:
1259             b = element to look up
1260 
1261         Returns:
1262             pointer to the index of an element of type B in this.values_list,
1263             or null if the element is not in the map
1264 
1265     ***************************************************************************/
1266 
1267     public size_t* indexOf ( cstring b )
1268     {
1269         auto index = b in this.b_to_index;
1270         enforce(index, typeof(this).stringof ~ ".indexOf - element not present in map");
1271         return index;
1272     }
1273 
1274 
1275     /***************************************************************************
1276 
1277         Updates the index arrays when the mapping is altered.
1278 
1279     ***************************************************************************/
1280 
1281     private void updateIndices ( )
1282     {
1283         foreach ( a, b; this.a_to_b )
1284         {
1285             this.a_to_index[a] = this.keys_list.find(a);
1286             this.b_to_index[b] = this.values_list.find(b);
1287         }
1288     }
1289 }