1 /**
2  * Utilities related to D named enumerations.
3  */
4 module iz.enumset;
5 
6 import
7     std.traits, std.range;
8 import
9     iz.types;
10 
11 private enum hasInt128 = is(ucent);
12 
13 /// Container for a EnumSet based on an enum which has up to 8 members.
14 alias Set8 = ubyte;
15 /// Container for a EnumSet based on an enum which has up to 16 members.
16 alias Set16 = ushort;
17 /// Container for a EnumSet based on an enum which has up to 32 members.
18 alias Set32 = uint;
19 /// Container for a EnumSet based on an enum which has up to 64 members.
20 alias Set64 = ulong;
21 /// Container for a EnumSet based on an enum which has up to 128 members (not working, relies on ucent).
22 static if (hasInt128) alias Set128 = ucent;
23 
24 
25 static if (hasInt128)
26     private alias BigestSet = Set128;
27 else
28     private alias BigestSet = Set64;
29 
30 
31 /**
32  * Returns true if the parameter is suitable for being used as a EnumSet container.
33  * Params:
34  * S = a enumset container type.
35  */
36 bool isSetSuitable(S)()
37 {
38     static if (isSigned!S) return false;
39     else static if (is(S==Set8)) return true;
40     else static if (is(S==Set16)) return true;
41     else static if (is(S==Set32)) return true;
42     else static if (is(S==Set64)) return true;
43     else static if (hasInt128 && is(S==Set128)) return true;
44     else return false;
45 }
46 
47 /**
48  * Returns the member count of an enum.
49  * Params:
50  * E = an enum.
51  */
52 size_t enumMemberCount(E)()
53 if (is(E==enum))
54 {
55     static if (isOrderedEnum!E)
56         return 1 + E.max - E.min;
57     else
58     {
59         size_t result;
60         foreach(member; EnumMembers!E) result++;
61         return result;
62     }
63 }
64 
65 /**
66  * Provides the information about the rank of the members of an enum.
67  * The properties are all static and can be retrieved from a template alias.
68  * Params:
69  * E = an enum.
70  */
71 struct EnumRankInfo(E) if (is(E==enum))
72 {
73 
74 private:
75 
76     enum isOrdered = isOrderedEnum!E;
77     static if (!isOrdered)
78     {
79         static immutable size_t[E] _rankLUT;
80         static immutable E[size_t] _membLUT;
81         static immutable size_t _count;
82     }
83 
84 public:
85 
86     static if (!isOrdered)
87     static this() pure nothrow @safe
88     {
89         if (__ctfe){}
90         else foreach(member; EnumMembers!E)
91         {
92             _rankLUT[member] = _count;
93             _membLUT[_count] = member;
94             _count++;
95         }
96     }
97 
98     /// Returns the rank of the last member.
99     static size_t max() pure nothrow @safe @nogc
100     {
101         static if (isOrdered)
102             return E.max - E.min;
103         else
104             return _count-1;
105     }
106 
107     /// Returns the member count. It's always equal to max + 1.
108     static size_t count() pure nothrow @safe @nogc
109     {
110         static if (isOrdered)
111             return 1 + E.max - E.min;
112         else
113             return _count;
114     }
115 
116     /// Always returns 0.
117     static nothrow @safe @nogc  enum min = 0;
118 
119     /// Returns the rank of aMember.
120     static size_t opIndex()(E aMember) pure nothrow @safe @nogc
121     if (isOrdered)
122     {
123         return aMember - E.min;
124     }
125 
126     /// ditto
127     static size_t opIndex()(E aMember) pure nothrow @safe
128     if (!isOrdered)
129     {
130         if (__ctfe)
131         {
132             size_t result;
133             foreach(member; EnumMembers!E)
134             {
135                 if (member == aMember)
136                     return result;
137                 ++result;
138             }
139             assert(0);
140         }
141         else return _rankLUT[aMember];
142     }
143 
144     /// Returns the member at aRank.
145     static E opIndex()(size_t aRank) pure nothrow @safe @nogc
146     if (isOrdered)
147     {
148         return cast(E) (E.min + aRank);
149     }
150 
151     static E opIndex()(size_t aRank) pure nothrow @safe
152     if (!isOrdered)
153     {
154         if (__ctfe)
155         {
156             size_t rank;
157             foreach(member; EnumMembers!E)
158             {
159                 if (rank == aRank)
160                     return member;
161                 ++rank;
162             }
163             assert(0);
164         }
165         else return _membLUT[aRank];
166     }
167 }
168 
169 /**
170  * Indicates if the members of an enum fit in a container.
171  * Params:
172  * E = an enum.
173  * S = a container, either a Set8, a Set16, a Set32 or Set64.
174  */
175 static bool enumFitsInSet(E, S)()
176 if (is(E==enum) && isSetSuitable!S)
177 {
178     S top = S.max;
179     ulong max;
180     foreach(i, member; EnumMembers!E)
181     {
182         max +=  cast(S) 1 << i;
183     }
184     return (max <= top) & (max > 0);
185 }
186 
187 /**
188  * An EnumSet allows to create a bit field using the members of an enum.
189  *
190  * It's designed similarly to the Pascal built-in sets (the "Set Of" construct).
191  * It's also related to the phobos type EnumFlag, except that it has no constraint
192  * related to the enum member values since it's based on the enum members rank.
193  *
194  * It's efficient as function parameter since the size of an EnumSet is
195  * equal to the size of its container (so from 1 to 8 bytes). Since manipulating
196  * an EnumSet set is mostly about making bitwise operations an EnumSet is completly
197  * safe. Another notable characteristic is that an EnumSet is ordered, so if a
198  * range is implemented to allow the usage of std.algorithm.searching functions
199  * it's always more simple and efficient to use the "in" operator.
200  *
201  * There are two ways to use an EnumSet:
202  * * using the C-like operators, "+" and "-" to add or remove members, "^" and "&" to get the difference and the intersection.
203  * * using the Pascal-like intrinsics (here some functions): include(), exclude() and the 'in' operator.
204  *
205  * Params:
206  * S = A Set8, Set16, Set32 or Set64. It must be wide enough to contain all the enum members.
207  * E = An enum, which must have at least one members.
208  *
209  * Example:
210  * ---
211  * enum Employee {jhon, steve, sophia, douglas, clarice, mitch}
212  * alias Team = EnumSet!(Employee, Set8);
213  * auto team1 = Team(Employee.jhon, Employee.sophia, Employee.douglas);
214  * auto team2 = Team(Employee.jhon, Employee.clarice, Employee.mitch);
215  * if (Employee.sophia in team1) writeln("Sophia works in team1");
216  * Team overzealous = team1.intersection(team2);
217  * if (overzealous != 0)
218  * {
219  *     writeln(overzealous, " work(s) too much !");
220  *     team1 -= overzealous;
221  *     assert(team1 == [Employee.sophia, Employee.douglas]);
222  * }
223  * if (team1.memberCount != team2.memberCount)
224  *     writeln("teams are not well balanced !");
225  * ---
226  */
227 struct EnumSet(E, S)
228 if (enumFitsInSet!(E, S))
229 {
230         
231     alias SetType = S;
232     alias EnumSetType = typeof(this);
233 
234     pure: @safe:
235 
236 private:
237 
238     SetType _container;
239     static immutable SetType _max;
240     static immutable EnumRankInfo!E _infs;
241     static immutable SetType _1 = cast(SetType) 1;
242     
243     struct Range
244     {
245         private SetType frontIndex;
246         private SetType backIndex;
247         private EnumSet!(E,S) set;
248         private alias infs = EnumRankInfo!E;
249 
250         private void toNextMember() nothrow @nogc
251         {
252             while (!set[infs[cast(size_t) frontIndex]])
253             {
254                 ++frontIndex;
255                 if (frontIndex == infs.count)
256                     break;
257             }
258         }
259 
260         private void toPrevMember() nothrow @nogc
261         {
262             while (!set[infs[cast(size_t)backIndex]])
263             {
264                 --backIndex;
265                 if (backIndex == 0)
266                     break;
267             }
268         }
269 
270         this(T)(T t)
271         {
272             set = SetType(t);
273             backIndex = cast(set.SetType) infs.count;
274             toNextMember;
275             toPrevMember;
276         }
277 
278         bool empty() nothrow @nogc
279         {
280             return set.none;
281         }
282 
283         E front() nothrow  @nogc
284         in
285         {
286             assert(frontIndex >= 0);
287             assert(frontIndex <= infs.max);
288         }
289         body
290         {
291             return infs[cast(size_t) frontIndex];
292         }
293 
294         void popFront() nothrow @nogc
295         in
296         {
297             assert(frontIndex >= 0);
298             assert(frontIndex <= infs.max);
299         }
300         body
301         {
302             set.exclude(infs[cast(size_t) frontIndex]);
303             toNextMember;
304         }
305 
306         E back() nothrow @nogc
307         in
308         {
309             assert(backIndex >= 0 && backIndex <= infs.max);
310         }
311         body
312         {
313             return infs[cast(size_t) backIndex];
314         }
315 
316         void popBack() nothrow @nogc
317         in
318         {
319             assert(backIndex >= 0 && backIndex <= infs.max);
320         }
321         body
322         {
323             set.exclude(infs[cast(size_t) backIndex]);
324             toPrevMember;
325         }
326 
327         auto save() nothrow @nogc
328         {
329             return Range(set._container);
330         }
331     }    
332 
333 public:
334 
335 // constructors ---------------------------------------------------------------+
336 
337     ///
338     static this() nothrow @nogc
339     {
340         foreach(i, member; EnumMembers!E)
341             _max +=  _1 << i;
342     }
343 
344     /**
345      * Initializes the set with some stuff.
346      * Params:
347      * stuff = either some E member(s), an E input range, an E array
348      * or a string representation.
349      */
350     this(Stuff...)(Stuff stuff)
351     {
352         _container = 0;
353         static if (stuff.length == 1)
354         {
355             alias T = typeof(stuff[0]);
356             static if (is(T == SetType)) _container = stuff[0];
357             else static if (is(T == E)) include(stuff[0]);
358             else static if (is(T == E[])) foreach(s; stuff) include(s);
359             else static if (is(T == string)) fromString(stuff[0]);
360             else static if (isInputRange!T && is(ElementType!T == E))
361                 foreach(E e;stuff[0]) include(e);
362             else static assert(0, "unsupported _ctor argument");
363         }
364         else include(stuff);
365     }
366 // -----------------------------------------------------------------------------
367 // string representation ------------------------------------------------------+
368 
369     /**
370      * Returns the string representation of the set as a binary representation.
371      * Note that the result is preffixed with "0b", as a binary litteral.
372      */
373     string asBitString() const nothrow
374     {
375         static immutable char[2] bitsCh = ['0', '1'];
376         string result = "";
377         foreach_reverse(member; EnumMembers!E)
378             result ~= bitsCh[isIncluded(member)];
379 
380         return "0b" ~ result;
381     }
382 
383     /**
384      * Returns the string representation of the set.
385      * The format is the same as the one used in this() and fromString(),
386      * similar to an array litteral.
387      */
388     string toString() const
389     {
390         import std.conv: to;
391         scope(failure){}
392         string result = "[";
393         bool first;
394         foreach(i, member; EnumMembers!E)
395         {
396             if ((!first) & (isIncluded(member)))
397             {
398                 result ~= to!string(member);
399                 first = true;
400             }
401             else if (isIncluded(member))
402                 result ~= ", " ~ to!string(member);
403         }
404         return result ~ "]";
405     }
406 
407     /**
408      * Defines the set with a string representation.
409      * Params:
410      * str = a string representing one or several E members. It must have the 
411      * form that's similar to an array litteral. Binary litterals are not handled 
412      * by this function.
413      */
414     void fromString(const(char)[] str) nothrow @trusted
415     {
416         import std.conv: to;
417         if (str.length < 2)
418             return;
419 
420         _container = 0;
421         if (str == "[]")
422             return;
423 
424         char[] identifier;
425         const(char)* reader = str.ptr;
426         while(true)
427         {
428             if (*reader != ',' && *reader != '[' && *reader != ']' &&
429                 *reader != ' ') identifier ~= *reader;
430 
431             if (*reader == ',' || *reader == ']')
432             {
433                 try
434                 {
435                     auto member = to!E(identifier);
436                     include(member);
437                 }
438                 catch (Exception e) break;
439                 identifier = identifier.init;
440             }
441 
442             if (reader == str.ptr + str.length)
443                 break;
444 
445             ++reader;
446         }
447     }
448 // -----------------------------------------------------------------------------
449 // operators ------------------------------------------------------------------+
450 
451     /**
452      * Support for the assignment operator.
453      * Params:
454      * rhs = a setXX, an array of E members, an InputRange of E
455      * or an EnumSet with the same type.
456      */
457     void opAssign(const S rhs) nothrow @nogc
458     {
459         _container = (rhs <= _max) ? rhs : _max;
460     }
461 
462     /// ditto
463     void opAssign(const E[] rhs) nothrow
464     {
465         _container = 0;
466         foreach(elem; rhs)
467             include(elem);
468     }
469 
470     /// ditto
471     void opAssign()(auto ref const EnumSetType rhs)
472     {
473         _container = rhs._container;
474     }
475 
476     /// ditto
477     void opAssign(R)(auto ref R rhs)
478     if (!(isArray!R) && isInputRange!R && is(ElementType!R == E))
479     {
480         _container = 0;
481         foreach(E e; rhs) include(e);
482     }
483 
484     /**
485      * Support for the array syntax.
486      * Params:
487      * index = either an unsigned integer or an E member.
488      */
489     bool opIndex(I)(I index) const nothrow
490     {
491         static if (isSigned!I || is(I == SetType))
492             return (_container == (_container | _1 << index));
493         else static if (is(I == E))
494             return isIncluded(index);
495         else static assert(0, "opIndex not implemented when indexer is " ~ I.stringof);
496     }
497 
498     /// Support for the array assignment syntax.
499     void opIndexAssign(bool value, E index) nothrow @nogc
500     {
501         set(index, value);
502     }
503 
504     /**
505      * Support for "+" and "-" operators.
506      * Params:
507      * rhs = either an E member, an E array or another EnumSet (or its container) 
508      * with the same type.
509      */
510     EnumSetType opBinary(string op)(E rhs) nothrow
511     {
512         static if (op == "+")
513         {
514             EnumSetType s = EnumSetType(_container);
515             s.include(rhs);
516             return s;
517         }
518         else static if (op == "-")
519         {
520             EnumSetType s = EnumSetType(_container);
521             s.exclude(rhs);
522             return s;
523         }
524         else static assert(0, "opBinary not implemented for " ~ op);
525     }
526 
527     /// ditto
528     EnumSetType opBinary(string op)(E[] rhs) nothrow
529     {
530         static if (op == "+")
531         {
532             EnumSetType s = EnumSetType(_container);
533             s.include(rhs);
534             return s;
535         }
536         else static if (op == "-")
537         {
538             EnumSetType s = EnumSetType(_container);
539             s.exclude(rhs);
540             return s;
541         }
542         else static assert(0, "opBinary not implemented for " ~ op);
543     }
544 
545     /// ditto
546     EnumSetType opBinary(string op)(EnumSetType rhs) nothrow @nogc
547     {
548         static if (op == "+")
549         {
550             SetType s = _container | rhs._container;
551             return EnumSetType(s);
552         }
553         else static if (op == "-")
554         {
555             SetType s = _container;
556             s &= s ^ rhs._container;
557             return EnumSetType(s);
558         }
559         else static if (op == "^")
560         {
561             return difference(rhs);
562         }
563         else static if (op == "&")
564         {
565             return intersection(rhs);
566         }
567         else static assert(0, "opBinary not implemented for " ~ op);
568     }
569     
570     /// ditto
571     EnumSetType opBinary(string op)(SetType rhs) nothrow @nogc
572     {
573         static if (op == "+")
574         {
575             SetType s = _container | rhs;
576             return EnumSetType(s);
577         }
578         else static if (op == "-")
579         {
580             SetType s = _container;
581             s &= s ^ rhs;
582             return EnumSetType(s);
583         }
584         else static if (op == "^")
585         {
586             return difference(EnumSetType(rhs));
587         }
588         else static if (op == "&")
589         {
590             return intersection(EnumSetType(rhs));
591         }
592         else static assert(0, "opBinary not implemented for " ~ op);
593     }    
594 
595     /**
596      * Support for "+=" and "-=" operators.
597      * Params:
598      * rhs = either some E member(s), an E array or an EnumSet with the same type.
599      */
600     void opOpAssign(string op)(E[] rhs) nothrow
601     {
602         static if (op == "+") include(rhs);
603         else static if (op == "-") exclude(rhs);
604         else static assert(0, "opOpAssign not implemented for " ~ op);
605     }
606 
607     /// ditto
608     void opOpAssign(string op, E...)(E rhs) nothrow
609     {
610         static if (op == "+") include(rhs);
611         else static if (op == "-") exclude(rhs);
612         else static assert(0, "opOpAssign not implemented for " ~ op);
613     }
614 
615     /// ditto
616     void opOpAssign(string op)(EnumSetType rhs) nothrow @nogc
617     {
618         static if (op == "+") _container |= rhs._container;
619         else static if (op == "-") _container &= _container ^ rhs._container;
620         else static assert(0, "opOpAssign not implemented for " ~ op);
621     }
622 
623     // selected for Set8, Set16 and Set32
624     private size_t toHashImpl()(uint x) const nothrow @nogc
625     {
626         x = ((x >> 16) ^ x) * 0x119de1f3U;
627         x = ((x >> 16) ^ x) * 0x119de1f3U;
628         x = (x >> 16) ^ x;
629         return cast(size_t) x;
630     }
631 
632     // selected for Set64
633     private size_t toHashImpl()(ulong x) const nothrow @nogc
634     {
635         x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9UL;
636         x = (x ^ (x >> 27)) * 0x94d049bb133111ebUL;
637         x = x ^ (x >> 31);
638         return cast(size_t) x;
639     }
640 
641     /// Support for built-in AA.
642     size_t toHash() const nothrow @nogc
643     {
644         return toHashImpl(_container);
645     }
646 
647     /// Support for comparison "=" and "!=" operators.
648     bool opEquals(T)(T rhs) const nothrow
649     {
650         static if (is(T == SetType))
651             return (_container == rhs);
652         else static if (isIntegral!T && T.max >= SetType.max)
653             return (_container == rhs);
654         else static if (is(T == EnumSetType))
655             return (_container == rhs._container);
656         else static if (is(T == E[])){
657             auto rhsset = EnumSetType(rhs);
658             return (rhsset._container == _container);
659         }
660         else
661             static assert(0, "opEquals not implemented when rhs is " ~ T.stringof);
662     }
663 
664     /// Support for built-in AA.
665     bool opEquals(ref const EnumSetType rhs) const nothrow @nogc
666     {
667         return (rhs._container == _container);
668     }
669     
670     /// see range()
671     Range opSlice() const nothrow
672     {
673         return Range(_container);
674     }
675 
676     /** 
677      * Support for the in operator.
678      *
679      * Indicates if the right hand side is included in the set.
680      * Params:
681      * rhs = either an E member or a set (or its container) with the same type,
682      * in the last case, calling opIn_r is equivalent to test for greater or equal.
683      */
684     bool opIn_r(T)(T rhs) const nothrow
685     {
686         static if (is(T == E))
687             return isIncluded(rhs);
688         else static if (is(T == EnumSetType))
689             return (_container & rhs._container) >= rhs._container;
690         else static if (is(T == SetType))
691             return (_container & rhs) >= rhs;
692         else
693             static assert(0, "opIn_r not implemented when rhs is " ~ T.stringof);
694     }
695 // -----------------------------------------------------------------------------
696 // set operations -------------------------------------------------------------+
697 
698     /**
699      * Returns a set representing the difference between this set and the argument.
700      * Params:
701      * rhs = either a set with the same type or a set container with the same size.
702      */
703     EnumSetType difference(R)(R rhs) const nothrow
704     if (is(R == EnumSetType) || is(R == SetType))
705     {
706         SetType s;
707         static if (is(R == EnumSetType))
708             s = _container ^ rhs._container;
709         else
710             s = _container ^ rhs;
711         return EnumSetType(s);
712     }
713     
714     /**
715      * Returns a set representing the intersection between this set and the argument.
716      * Params:
717      * rhs = either a set with the same type or a set container with the same size.
718      */
719     EnumSetType intersection(R)(R rhs) const nothrow
720     if (is(R == EnumSetType) || is(R == SetType))
721     {
722         SetType s;
723         static if (is(R == EnumSetType))
724             s = _container & rhs._container;
725         else
726             s = _container & rhs;
727         return EnumSetType(s);
728     }
729 // -----------------------------------------------------------------------------
730 // Pascal-ish primitives ------------------------------------------------------+
731 
732 
733     /**
734      * Sets the bit of a particular member
735      */
736     void set(E member, bool value) @nogc nothrow
737     {
738         _container ^= (ubyte(value * 255) ^ _container) & (1 << _infs[member]);
739     }
740 
741     /**
742      * Includes someMembers in the set.
743      * This is the primitive used for to the operator "+".     
744      * Params:
745      * someMembers = a list of E members or an array of E members
746      */
747     void include(E...)(E someMembers) nothrow
748     {
749         static if (someMembers.length == 1)
750             _container |= _1 << _infs[someMembers];
751         else foreach(member; someMembers)
752             _container |= _1 << _infs[member];
753     }
754 
755     /// ditto
756     void include(E[] someMembers) nothrow
757     {
758         foreach(member; someMembers)
759             _container |= _1 << _infs[member];
760     }
761 
762     /**
763      * Excludes someMembers from the set.
764      * This is the primitive used for to the operator "-".
765      * Params:
766      * someMembers = a list of E members or an array of E members.
767      */
768     void exclude(E...)(E someMembers) nothrow
769     {
770         static if (someMembers.length == 1)
771             _container &= ~(_1 << _infs[someMembers]);
772         else foreach(member; someMembers)
773             _container &= ~(_1 << _infs[member]);
774     }
775 
776     /// ditto
777     void exclude(E[] someMembers) nothrow
778     {
779         foreach(member; someMembers)
780             _container &= _container ^ (_1 << _infs[member]);
781     }
782 
783     /**
784      * Returns true if aMember is in the set.
785      * This is the primitive used for to the operator "in".
786      * Params:
787      * aMember = an E member.
788      */
789     bool isIncluded(E aMember) const nothrow
790     {
791         return (_container >> _infs[aMember]) & 1;
792     }
793 //------------------------------------------------------------------------------
794 // misc helpers ---------------------------------------------------------------+
795 
796     /// Returns a range allowing to iterate for each member included in the set.
797     Range range() const nothrow
798     {
799         return Range(_container);
800     }
801 
802     /// Returns true if the set is empty.
803     bool none() const nothrow @nogc
804     {
805         return _container == 0;
806     }
807 
808     /// Returns true if at least one member is included.
809     bool any() const nothrow @nogc
810     {
811         return _container != 0;
812     }
813 
814     /// Returns true if all the members are included.
815     bool all() const nothrow @nogc
816     {
817         return _container == _max;
818     }
819 
820     /// Returns the maximal value the set can have.
821     static const(S) max() nothrow @nogc
822     {
823         return _max;
824     }
825 
826     /// Returns a lookup table that can be used to retrieve the rank of a member.
827     static ref const(EnumRankInfo!E) rankInfo() nothrow @nogc
828     {
829         return _infs;
830     }
831 
832     /// Returns the enum count
833     static const(S) memberCount() nothrow @nogc
834     {
835         return cast(S) rankInfo.count;
836     }
837 
838     /// Returns the enum count
839     ref const(SetType) container() const nothrow @nogc
840     {
841         return _container;
842     }
843 
844     /// Implements the iz.rtti "text struct" traits to allow the deserialization.
845     void loadFromText(const(char)[] value)
846     {
847         fromString(value);
848     }
849 
850     /// Implements the iz.rtti "text struct" traits to allow the serialization.
851     const(char)[] saveToText()
852     {
853         return toString;
854     }
855 
856 //------------------------------------------------------------------------------
857 }
858 
859 /**
860  * Aliases the smallest set in which E fits.
861  */
862 template SmallestSet(E)
863 if (enumFitsInSet!(E, BigestSet))
864 {
865     static if (enumFitsInSet!(E, Set8))
866         alias SmallestSet = Set8;
867     else static if (enumFitsInSet!(E, Set16))
868         alias SmallestSet = Set16;
869     else static if (enumFitsInSet!(E, Set32))
870         alias SmallestSet = Set32;
871     else static if (enumFitsInSet!(E, Set64))
872         alias SmallestSet = Set64;
873     else static if (hasInt128 && enumFitsInSet!(E, Set128))
874         alias SmallestSet = Set128;
875 }
876 
877 /**
878  * Returns an EnumSet using the smallest container possible.
879  * Params:
880  * E = an enum
881  * a = the parameters passed to the EnumSet constructor.
882  */
883 auto enumSet(E, A...)(A a) @property
884 if (enumFitsInSet!(E, BigestSet))
885 {
886     return EnumSet!(E, SmallestSet!E)(a);
887 }
888 
889 /**
890  * Alias the members of an enum, allowing to use them without the parent name,
891  * and making the use of EnumSet more friendly.
892  *
893  * Params:
894  *      E = The named enum to alias.
895  *      prefix = The prefix of the aliases.
896  */
897 mixin template AliasedEnumMembers(E, string prefix = "")
898 if (is(E == enum))
899 {
900     string genAliasedEnumMembers()
901     {
902         string result;
903         foreach(em; EnumMembers!E)
904         {
905             import std.conv : to;
906             result ~= "alias " ~ prefix ~ to!string(em) ~ " = " ~
907                 E.stringof ~ "." ~ to!string(em) ~ ";";
908         }
909         return result;
910     }
911     mixin(genAliasedEnumMembers());
912 }
913 ///
914 unittest
915 {
916     enum Option {Option1, Option2}
917     mixin AliasedEnumMembers!(Option, "o");
918     static assert(oOption1 is Option.Option1);
919 
920     alias Options = EnumSet!(Option, Set8);
921     Options opts = Options(oOption1);
922 }
923 
924 /// returns true if T and E are suitable for constructing an EnumProcs
925 bool isCallableFromEnum(T, E)()
926 {
927     return ((is(E==enum)) & (isCallable!T));
928 }
929 
930 /**
931  * CallTable based on an enum. It can be compared to an associative array of type E[T].
932  * Additionally an EnumSet can be used to fire a burst of call.
933  * Params:
934  * E = an enum.
935  * T = a callable type.
936  */
937 struct EnumProcs(E,T)
938 if (isCallableFromEnum!(T,E))
939 {
940 
941 @safe nothrow pure:
942 
943 private:
944 
945     static immutable EnumRankInfo!E _infs;
946     alias retT = ReturnType!T;
947     enum procLen = enumMemberCount!E;
948     T[procLen] _procs;
949 
950 public:
951 
952 // constructors ---------------------------------------------------------------+
953 
954     /**
955      * Constructs an EnumProcs with a set of T.
956      * Params:
957      * a = a list of T.
958      */
959     this(A...)(A a)
960     in
961     {
962         static assert(a.length == enumMemberCount!E);
963         foreach(callable; a)
964             static assert(is(A[0] == T));
965     }
966     body
967     {
968         foreach(immutable i, item; a)
969             _procs[i] = item;
970     }
971 
972     /**
973      * Constructs an EnumProcs with an array of T.
974      * Params:
975      * someItems = an array of T.
976      */
977     this(T[] someItems) @nogc
978     in
979     {
980         assert(someItems.length == enumMemberCount!E);
981         assert(is(typeof(someItems[0]) == T));
982     }
983     body
984     {
985         foreach(i, item; someItems)
986         {
987             _procs[i] = someItems[i];
988         }
989     }
990 //------------------------------------------------------------------------------
991 // operators ------------------------------------------------------------------+
992 
993     /**
994      * opIndex allows a more explicit call syntax than opCall.myStuffs[E.member](params).
995      */
996     const(T) opIndex(E aMember) @nogc
997     {
998         return _procs[_infs[aMember]];
999     }
1000 
1001 //------------------------------------------------------------------------------
1002 // call -----------------------------------------------------------------------+
1003 
1004     /**
1005      * Calls the function matching to selector rank.
1006      * Params:
1007      * selector = an E member.
1008      * prms = arguments for calling the function.
1009      * Returns: a value of type ReturnType!T.
1010      */
1011     retT opCall(CallParams...)(E selector, CallParams prms) @nogc
1012     {
1013         return _procs[_infs[selector]](prms);
1014     }
1015 
1016     /**
1017      * Calls the functions matching to a set of selectors.
1018      * Params:
1019      * selectors = a set of E.
1020      * prms = common or selector-sepcific arguments for calling the functions.
1021      * Returns: an array representing the result of each selector, by rank.
1022      */
1023     retT[] opCall(BS,CallParams...)(BS selectors, CallParams prms)
1024     if  (   (is(BS == EnumSet!(E, Set8)))
1025         ||  (is(BS == EnumSet!(E, Set16)))
1026         ||  (is(BS == EnumSet!(E, Set32)))
1027         ||  (is(BS == EnumSet!(E, Set64)))
1028         ||  (is(BS == EnumSet!(E, BigestSet)))
1029         )
1030     {
1031         retT[] result;
1032         result.length = cast(size_t) enumMemberCount!E;
1033 
1034         static if (!CallParams.length)
1035         {
1036             foreach(immutable i; 0 .. selectors.memberCount)
1037             {
1038                 if (selectors[i])
1039                     result[i] = _procs[i]();
1040             }
1041             return result;
1042         }
1043         else static if (!isArray!(CallParams[0]))
1044         {
1045             foreach(immutable i; 0 .. selectors.memberCount)
1046             {
1047                 if (selectors[i])
1048                     result[i] = _procs[i](prms);
1049             }
1050             return result;
1051         }
1052         else
1053         {
1054             foreach(immutable i; 0 .. selectors.memberCount)
1055             {
1056             // Hard to believe it works ! A unittest HAS to show it can fail.
1057                 if (selectors[i])
1058                     result[i] = _procs[i](prms[0][i]); 
1059             }
1060             return result;
1061         }
1062     }
1063 //------------------------------------------------------------------------------
1064 // misc. ----------------------------------------------------------------------+
1065 
1066     /// Returns the array of callable for additional containers operations.
1067     ref T[procLen] procs() @nogc
1068     {
1069         return _procs;
1070     }
1071 //------------------------------------------------------------------------------
1072 
1073 }
1074 
1075 /**
1076  * Encapsulates an array of T and uses the rank of the enum members
1077  * E to perform the actions usually done with integer indexes.
1078  */
1079 struct EnumIndexedArray(E,T,bool staticArray = false)
1080 if (is(E==enum))
1081 {
1082 
1083 pure nothrow @safe:
1084 
1085 private:
1086 
1087     static if (staticArray)
1088     {
1089         enum arrayLen = enumMemberCount!E;
1090         alias arrayT = T[arrayLen];
1091     }
1092     else
1093     {
1094         alias arrayT = T[];
1095     }
1096     arrayT _array;
1097     static immutable EnumRankInfo!E _infs;
1098 
1099 public:
1100 
1101     /// Returns the length of the internal container.
1102     size_t length()
1103     {
1104         return _array.length;
1105     }
1106     
1107     /// Returns the length of the internal container.
1108     size_t opDollar()
1109     {
1110         return length;
1111     }
1112 
1113     /**
1114      * Sets the array length using a standard integer value.
1115      * Unless bounds checking is turned off, the parameter is dynamically
1116      * checked according to E highest rank.
1117      */
1118     static if (!staticArray)
1119     void length(size_t aValue)
1120     in
1121     {
1122         assert(aValue <= _infs.count);
1123     }
1124     body
1125     {
1126         _array.length = aValue;
1127     }
1128 
1129     /**
1130      * Sets the array length according to the value following aMember rank.
1131      */
1132     static if (!staticArray)
1133     void length(E aMember)
1134     {
1135         _array.length = _infs[aMember] + 1;
1136     }
1137 
1138     /**
1139      * Returns the value of the slot indexed by aMember rank.
1140      */
1141     T opIndex(E aMember)
1142     {
1143         return _array[_infs[aMember]];
1144     }
1145 
1146     /**
1147      * Sets the slot indexed by aMember rank to aValue.
1148      */
1149     void opIndexAssign(T aValue,E aMember)
1150     {
1151         _array[_infs[aMember]] = aValue;
1152     }
1153 
1154     /**
1155      * Returns a T slice using loMember and hiMember ranks to define the range.
1156      */
1157     T[] opSlice(E loMember, E hiMember)
1158     in
1159     {
1160         assert(_infs[loMember] <= _infs[hiMember]);
1161     }
1162     body
1163     {
1164         return _array[_infs[loMember].._infs[hiMember]];
1165     }
1166 
1167     /**
1168      * Returns a reference to the the internal container.
1169      */
1170     ref const(arrayT) array()
1171     {
1172         return _array;
1173     }
1174 }
1175 
1176 version(unittest)
1177 {
1178     enum a0;
1179     enum a4     {a0,a1,a2,a3}
1180     enum a8     {a0,a1,a2,a3,a4,a5,a6,a7}
1181     enum a9     {a0,a1,a2,a3,a4,a5,a6,a7,a8}
1182     enum a16    {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15}
1183     enum a17    {a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16}
1184     enum b0     {b0 = 8, b1 = 1, b2 = 17}
1185 
1186     /// Constraints
1187     pure unittest
1188     {
1189         static assert( isSetSuitable!ubyte );
1190         static assert( isSetSuitable!ushort );
1191         static assert( isSetSuitable!uint );
1192         static assert( isSetSuitable!ulong );
1193         static assert( !isSetSuitable!byte );
1194     }
1195 
1196     pure unittest
1197     {
1198         static assert( enumFitsInSet!(a8, Set8));
1199         static assert( !enumFitsInSet!(a9, Set8));
1200         static assert( enumFitsInSet!(a16, Set16));
1201         static assert( !enumFitsInSet!(a17, Set16));
1202         static assert( !enumFitsInSet!(a0, Set64));
1203     }
1204 
1205     /// CTFE
1206     pure @safe unittest
1207     {
1208         static assert(EnumSet!(a8, Set8)(a8.a0,a8.a1) == 0b00000011);
1209         enum set = EnumSet!(a8, Set8)(a8.a0,a8.a1);
1210         static assert(set == 0b011, set);
1211         static assert(EnumRankInfo!a8[0] == a8.a0);
1212         static assert(EnumRankInfo!a8[a8.a1] == 1);
1213         static assert(EnumRankInfo!b0[1] == b0.b1);
1214         static assert(EnumRankInfo!b0[b0.b2] == 2);
1215     }
1216 
1217     /// EnumSet
1218     pure nothrow @safe unittest
1219     {
1220         alias bs8 = EnumSet!(a8, Set8);
1221         bs8 set = bs8(a8.a0,a8.a1,a8.a2,a8.a3,a8.a4,a8.a5,a8.a6,a8.a7);
1222         assert(set == 0b1111_1111);
1223         assert(set.all);
1224         set = set - a8.a0;
1225         assert(set == 0b1111_1110);
1226         set = set - a8.a1;
1227         assert(set == 0b1111_1100);
1228         set = set - a8.a7;
1229         assert(set == 0b0111_1100);
1230         set = 0;
1231         assert(set.none);
1232         assert(set == 0b0000_0000);
1233         set = set + a8.a4;
1234         assert(set == 0b0001_0000);
1235         set = set + a8.a5;
1236         assert(set != 0b0001_0000);
1237         assert(set.any);
1238         set = 0;
1239         assert(set == 0b0000_0000);
1240         set = set + [a8.a0, a8.a1, a8.a3];
1241         assert(set == 0b0000_1011);
1242         set = set - [a8.a1, a8.a3];
1243         assert(set == 0b0000_001);
1244         set = set.max;
1245         set = set - [a8.a5,a8.a6,a8.a7];
1246         assert(set == [a8.a0,a8.a1,a8.a2,a8.a3,a8.a4]);
1247         set = [a8.a0,a8.a2,a8.a4];
1248         assert(set == 0b0001_0101);
1249         set = [a8.a0,a8.a1];
1250         assert(set == 0b0000_0011);
1251         set += [a8.a2,a8.a3];
1252         assert(set == 0b0000_1111);
1253         set -= [a8.a0,a8.a1];
1254         assert(set == 0b0000_1100);
1255         set = 0;
1256         set.exclude([a8.a0,a8.a1,a8.a2,a8.a3,a8.a4]);
1257         assert( set == 0);
1258         set -= a8.a0;
1259         assert( set == 0);
1260 
1261         bs8 set1 = bs8(a8.a0,a8.a1);
1262         bs8 set2 = bs8(a8.a1,a8.a2);
1263         set1 += set2;
1264         assert( set1 == 0b0000_0111);
1265         set1 = bs8(a8.a0,a8.a1,a8.a2);
1266         set2 = bs8(a8.a1,a8.a2,a8.a3);
1267         set1 -= set2;
1268         assert( set1 == 0b0000_0001);
1269 
1270         set1 = bs8(a8.a0);
1271         set2 = bs8(a8.a1);
1272         auto set3 = set1 + set2;
1273         assert(set3 == 0b0000_0011);
1274         assert(set1 == 0b0000_0001);
1275         assert(set2 == 0b0000_0010);
1276         set2.set(a8.a1, false),
1277         assert(set2 == 0b0000_0000);
1278         set2.set(a8.a0, true);
1279         set2.set(a8.a1, true);
1280         set2.set(a8.a3, true);
1281         assert(set2 == 0b0000_1011);
1282         set2.set(a8.a3, false);
1283         assert(set2 == 0b0000_0011);
1284         set2[a8.a4] = true;
1285         assert(set2 == 0b0001_0011);
1286     }
1287 
1288     pure nothrow @safe unittest
1289     {
1290         EnumSet!(a17, Set32) set;
1291         set.include(a17.a8,a17.a9);
1292         assert(!set.isIncluded(a17.a7));
1293         assert(set.isIncluded(a17.a8));
1294         assert(set.isIncluded(a17.a9));
1295         assert(!set.isIncluded(a17.a10));
1296         assert(!(a17.a7 in set));
1297         assert(a17.a8 in set);
1298         assert(a17.a9 in set);
1299         assert(!(a17.a10 in set));
1300         set = 0;
1301         set += [a17.a5, a17.a6, a17.a7];
1302         EnumSet!(a17, Set32) set2;
1303         set2 += [a17.a5,a17.a6];
1304         assert(set2 in set);
1305         set -= [a17.a5];
1306         assert(!(set2 in set));
1307         set2 -= [a17.a5];
1308         assert(set2 in set);
1309     }
1310 
1311     pure nothrow @safe @nogc unittest
1312     {
1313         auto bs = EnumSet!(a17, Set32)(a17.a0, a17.a1, a17.a16);
1314         assert(bs[0]);
1315         assert(bs[1]);
1316         assert(bs[16]);
1317         assert(bs[a17.a0]);
1318         assert(bs[a17.a1]);
1319         assert(bs[a17.a16]);
1320         assert(!bs[8]);
1321         assert(!bs[a17.a8]);
1322     }
1323 
1324     pure @safe unittest
1325     {
1326         EnumSet!(a8, Set8) set = EnumSet!(a8, Set8)(a8.a3, a8.a5);
1327         assert(set == 0b0010_1000);
1328         auto rep = set.toString;
1329         set = 0;
1330         assert(set == 0);
1331         set = EnumSet!(a8, Set8)(rep);
1332         assert(set == 0b0010_1000);
1333 
1334         // test asBitString
1335         auto brep = set.asBitString;
1336         assert( brep == "0b00101000", brep );
1337         set = 0b1111_0000;
1338         brep = set.asBitString;
1339         assert( brep == "0b11110000", brep );
1340 
1341         // err
1342         assert(set != 0);
1343         set.fromString("[56346");
1344         assert(set == 0);
1345 
1346         //set = 0;
1347         //set = to!Set8(brep);
1348         //assert(set == 0b1111_0000);
1349     }
1350 
1351     pure nothrow @safe @nogc unittest
1352     {
1353         auto set = EnumSet!(a17, Set32)(a17.a0);
1354         assert( set.rankInfo[a17.a16] == 16);
1355         assert( set.rankInfo[a17.a15] == 15);
1356     }
1357 
1358     pure nothrow @safe unittest
1359     {
1360         import std.range;
1361 
1362         static assert(isInputRange!((EnumSet!(a17, Set32)).Range));
1363         static assert(isForwardRange!((EnumSet!(a17, Set32)).Range));
1364         static assert(isBidirectionalRange!((EnumSet!(a17, Set32)).Range));
1365 
1366         auto set = EnumSet!(a17, Set32)(a17.a0, a17.a8 , a17.a16);
1367         auto rng = set.range;
1368         assert(rng.front == a17.a0);
1369         rng.popFront;
1370         assert(rng.front == a17.a8);
1371         rng.popFront;
1372         assert(rng.front == a17.a16);
1373         rng.popFront;
1374         assert(rng.empty);
1375 
1376         with (a17) set = [a0, a8, a16, a13];
1377         size_t i;
1378         foreach(a17 a; set.range) {++i;}
1379         assert(i == 4);
1380 
1381         // bidir & forward ranges are not that usefull since an EnumSet is ordered
1382         import std.algorithm;
1383         with (a17) set = [a8, a16, a13];
1384         assert(startsWith(set.range, a17.a8));
1385         with (a17) set += [a2, a4];
1386         assert(startsWith(set[], a17.a2));
1387 
1388         auto set1 = set;
1389         set1 = 0;
1390         assert(set1.none);
1391         set1 = set.range;
1392         assert(set1 == set);
1393 
1394         auto tes = EnumSet!(a17, Set32)(a17.a0, a17.a1, a17.a8 , a17.a16);
1395         auto gnr = tes.range;
1396         assert(gnr.back == a17.a16);
1397         gnr.popBack;
1398         assert(gnr.back == a17.a8);
1399         auto sav = gnr.save;
1400         gnr.popBack;
1401         sav.popBack;
1402         assert(gnr.back == sav.back);
1403         sav.popBack;
1404         assert(gnr.back != sav.back);
1405     }
1406 
1407     pure nothrow @safe @nogc unittest
1408     {
1409         enum E {e1, e2}
1410         alias ESet = EnumSet!(E, Set8);
1411         ESet eSet1 = ESet(E.e1);
1412         ESet eSet2 = ESet(E.e1, E.e2);
1413         assert(eSet1 != eSet2);
1414         eSet2 -= E.e2;
1415         assert(eSet1 == eSet2);
1416     }
1417 
1418     pure nothrow @safe unittest
1419     {
1420         alias Set = EnumSet!(a8, Set8);
1421         Set set1 = Set([a8.a0, a8.a1]);
1422         Set set2 = Set([a8.a1, a8.a3]);
1423         assert(set1.intersection(set2) == Set(a8.a1));
1424         assert(set1.difference(set2) == Set([a8.a0,a8.a3]));
1425         set1 = 0b1010_1010;
1426         assert(set1.intersection(cast(ubyte)0b0000_1010) == 0b0000_1010);
1427         assert(set1.difference(cast(ubyte)0b0000_1010) == 0b1010_0000);
1428         
1429         set1 = Set([a8.a0, a8.a1]);
1430         assert((set1 & cast(Set8)0b1110) == Set(a8.a1));  
1431         assert((set1 ^ cast(Set8)0b1010) == Set([a8.a0,a8.a3]));
1432     }
1433 
1434     pure nothrow @safe unittest
1435     {
1436         enum E {e1, e2}
1437         alias ESet = EnumSet!(E, Set8);
1438 
1439         ESet eSet1;
1440         ESet eSet2 = [E.e1];
1441         ESet eSet3 = [E.e2];
1442         ESet eSet4 = [E.e1, E.e2];
1443 
1444         string[ESet] setDescription;
1445 
1446         setDescription[eSet1] = "empty";
1447         setDescription[eSet2] = "e1";
1448         setDescription[eSet3] = "e2";
1449         setDescription[eSet4] = "e1 and e2";
1450 
1451         // AA with EnumSet as key is about the set value, not the instance.
1452         assert( setDescription[* new ESet] == "empty");
1453         ESet eSet5 = [E.e1, E.e2];
1454         assert( setDescription[eSet5] == "e1 and e2");
1455         eSet5 -= E.e1;
1456         assert( setDescription[eSet5] == "e2");
1457         eSet5 -= E.e2;
1458         assert( setDescription[eSet5] == "empty");
1459         eSet5 -= E.e2; eSet5 -= E.e1;
1460         assert( setDescription[eSet5] == "empty");
1461     }
1462 
1463     /// enumSet
1464     pure nothrow @safe unittest
1465     {
1466         assert( is(typeof(enumSet!a4) == EnumSet!(a4,Set8)) );
1467         assert( is(typeof(enumSet!a8) == EnumSet!(a8,Set8)) );
1468         assert( is(typeof(enumSet!a9) == EnumSet!(a9,Set16))) ;
1469         assert( is(typeof(enumSet!a16) == EnumSet!(a16,Set16)) );
1470         assert( is(typeof(enumSet!a17) == EnumSet!(a17,Set32)) );
1471     }
1472 
1473     /// EnumProcs
1474     pure nothrow @safe unittest
1475     {
1476         enum A {t1=8,t2,t3}
1477         void At1(){}
1478         void At2(){}
1479         void At3(){}
1480 
1481         auto ACaller = EnumProcs!(A, typeof(&At1))(&At1,&At2,&At3);
1482 
1483         int Bt1(int p){return 10 + p;}
1484         int Bt2(int p){return 20 + p;}
1485         int Bt3(int p){return 30 + p;}
1486         auto BCaller = EnumProcs!(A, typeof(&Bt1))([&Bt1,&Bt2,&Bt3]);
1487         assert( BCaller.procs[0]== &Bt1);
1488         assert( BCaller.procs[1]== &Bt2);
1489         assert( BCaller.procs[2]== &Bt3);
1490         assert( BCaller(A.t1, 1) == 11);
1491         assert( BCaller(A.t2, 2) == 22);
1492         assert( BCaller(A.t3, 3) == 33);
1493         assert( BCaller[A.t1](2) == 12);
1494         assert( BCaller[A.t2](3) == 23);
1495         assert( BCaller[A.t3](4) == 34);
1496 
1497         auto bs = EnumSet!(A, Set8)();
1498         bs.include(A.t1,A.t3);
1499 
1500         auto arr0 = BCaller(bs,8);
1501         assert(arr0[0] == 18);
1502         assert(arr0[1] == 0);
1503         assert(arr0[2] == 38);
1504 
1505         bs.include(A.t2);
1506         auto arr1 = BCaller(bs,[4,5,6]);
1507         assert(arr1[0] == 14);
1508         assert(arr1[1] == 25);
1509         assert(arr1[2] == 36);
1510 
1511         int Ct1(int[2] p){return p[0] + p[1];}
1512         int Ct2(int[2] p){return p[0] * p[1];}
1513         int Ct3(int[2] p){return p[0] - p[1];}
1514         auto CCaller = EnumProcs!(A, typeof(&Ct1))(&Ct1,&Ct2,&Ct3);
1515         assert(bs.all);
1516         auto arr2 = CCaller(bs,[cast(int[2])[2,2],cast(int[2])[3,3],cast(int[2])[9,8]]);
1517         assert(arr2[0] == 4);
1518         assert(arr2[1] == 9);
1519         assert(arr2[2] == 1);
1520 
1521         int Dt1(int p, int c, int m){return 1 + p + c + m;}
1522         int Dt2(int p, int c, int m){return 2 + p + c + m;}
1523         int Dt3(int p, int c, int m){return 3 + p + c + m;}
1524         auto DCaller = EnumProcs!(A, typeof(&Dt1))(&Dt1,&Dt2,&Dt3);
1525         assert(bs.all);
1526         auto arr3 = DCaller(bs,1,2,3);
1527         assert(arr3[0] == 7);
1528         assert(arr3[1] == 8);
1529         assert(arr3[2] == 9);
1530     }
1531 
1532     /// EnumRankInfo
1533     pure @safe nothrow unittest
1534     {
1535         enum E
1536         {
1537             e1 = 0.15468,
1538             e2 = 1256UL,
1539             e3 = 'A'
1540         }
1541 
1542         alias infs = EnumRankInfo!E;
1543         assert(infs.min == 0);
1544         assert(infs.max == 2);
1545         assert(infs.count == 3);
1546         assert(infs[2] == 'A');
1547         assert(infs[E.e3] == 2);
1548     }
1549 
1550     /// EnumIndexedArray
1551     pure @safe nothrow unittest
1552     {
1553         enum E {e0 = 1.8,e1,e2,e3 = 888.459,e4,e5,e6,e7}
1554         alias E_Fp_Indexed = EnumIndexedArray!(E,float);
1555         E_Fp_Indexed arr;
1556         assert(arr.length == 0);
1557         arr.length = EnumRankInfo!E.count;
1558 
1559         foreach(i,memb; EnumMembers!E)
1560             arr[memb] = 1.0 + 0.1 * i;
1561 
1562         assert(arr[E.e1] == 1.1f);
1563         assert(arr[E.e0] == 1.0f);
1564 
1565         auto slice = arr[E.e2..E.e4];
1566         assert(slice == [1.2f,1.3f]);
1567     }
1568 
1569     pure @safe nothrow @nogc unittest
1570     {
1571         enum E {e0, e1}
1572         alias Arr = EnumIndexedArray!(E, int, true);
1573         Arr a;
1574         a[E.e0] = 4;
1575         a[E.e1] = 5;
1576         assert(a[E.e0] == 4);
1577         assert(a[E.e1] == 5);
1578     }
1579 }
1580