1 // Written in the D programming language.
2 
3 /++
4     Functions which operate on ASCII characters.
5 
6     All of the functions in std.ascii accept Unicode characters but
7     effectively ignore them if they're not ASCII. All `isX` functions return
8     `false` for non-ASCII characters, and all `toX` functions do nothing
9     to non-ASCII characters.
10 
11     For functions which operate on Unicode characters, see
12     $(MREF std, uni).
13 
14 $(SCRIPT inhibitQuickIndex = 1;)
15 $(DIVC quickindex,
16 $(BOOKTABLE,
17 $(TR $(TH Category) $(TH Functions))
18 $(TR $(TD Validation) $(TD
19         $(LREF isAlpha)
20         $(LREF isAlphaNum)
21         $(LREF isASCII)
22         $(LREF isControl)
23         $(LREF isDigit)
24         $(LREF isGraphical)
25         $(LREF isHexDigit)
26         $(LREF isLower)
27         $(LREF isOctalDigit)
28         $(LREF isPrintable)
29         $(LREF isPunctuation)
30         $(LREF isUpper)
31         $(LREF isWhite)
32 ))
33 $(TR $(TD Conversions) $(TD
34         $(LREF toLower)
35         $(LREF toUpper)
36 ))
37 $(TR $(TD Constants) $(TD
38         $(LREF digits)
39         $(LREF fullHexDigits)
40         $(LREF hexDigits)
41         $(LREF letters)
42         $(LREF lowercase)
43         $(LREF lowerHexDigits)
44         $(LREF newline)
45         $(LREF octalDigits)
46         $(LREF uppercase)
47         $(LREF whitespace)
48 ))
49 $(TR $(TD Enums) $(TD
50         $(LREF ControlChar)
51         $(LREF LetterCase)
52 ))
53 ))
54     References:
55         $(LINK2 http://www.digitalmars.com/d/ascii-table.html, ASCII Table),
56         $(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia)
57 
58     License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
59     Authors:   $(HTTP digitalmars.com, Walter Bright) and
60                $(HTTP jmdavisprog.com, Jonathan M Davis)
61     Source:    $(PHOBOSSRC std/ascii.d)
62   +/
63 module std.ascii;
64 
65 immutable fullHexDigits  = "0123456789ABCDEFabcdef";     /// 0 .. 9A .. Fa .. f
66 immutable hexDigits      = fullHexDigits[0 .. 16];         /// 0 .. 9A .. F
67 immutable lowerHexDigits = "0123456789abcdef";           /// 0 .. 9a .. f
68 immutable digits         = hexDigits[0 .. 10];             /// 0 .. 9
69 immutable octalDigits    = digits[0 .. 8];                 /// 0 .. 7
70 immutable letters        = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /// A .. Za .. z
71 immutable uppercase      = letters[0 .. 26];               /// A .. Z
72 immutable lowercase      = letters[26 .. 52];              /// a .. z
73 immutable whitespace     = " \t\v\r\n\f";                /// ASCII _whitespace
74 
75 /++
76     Letter case specifier.
77   +/
78 enum LetterCase : bool
79 {
80     upper, /// Upper case letters
81     lower  /// Lower case letters
82 }
83 
84 ///
85 @safe unittest
86 {
87     import std.conv : to;
88 
89     assert(42.to!string(16, LetterCase.upper) == "2A");
90     assert(42.to!string(16, LetterCase.lower) == "2a");
91 }
92 
93 ///
94 @safe unittest
95 {
96     import std.digest.hmac : hmac;
97     import std.digest : toHexString;
98     import std.digest.sha : SHA1;
99     import std.string : representation;
100 
101     const sha1HMAC = "A very long phrase".representation
102         .hmac!SHA1("secret".representation)
103         .toHexString!(LetterCase.lower);
104     assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
105 }
106 
107 /++
108     All control characters in the ASCII table ($(HTTPS www.asciitable.com, source)).
109 +/
110 enum ControlChar : char
111 {
112     nul = '\x00', /// Null
113     soh = '\x01', /// Start of heading
114     stx = '\x02', /// Start of text
115     etx = '\x03', /// End of text
116     eot = '\x04', /// End of transmission
117     enq = '\x05', /// Enquiry
118     ack = '\x06', /// Acknowledge
119     bel = '\x07', /// Bell
120     bs  = '\x08', /// Backspace
121     tab = '\x09', /// Horizontal tab
122     lf  = '\x0A', /// NL line feed, new line
123     vt  = '\x0B', /// Vertical tab
124     ff  = '\x0C', /// NP form feed, new page
125     cr  = '\x0D', /// Carriage return
126     so  = '\x0E', /// Shift out
127     si  = '\x0F', /// Shift in
128     dle = '\x10', /// Data link escape
129     dc1 = '\x11', /// Device control 1
130     dc2 = '\x12', /// Device control 2
131     dc3 = '\x13', /// Device control 3
132     dc4 = '\x14', /// Device control 4
133     nak = '\x15', /// Negative acknowledge
134     syn = '\x16', /// Synchronous idle
135     etb = '\x17', /// End of transmission block
136     can = '\x18', /// Cancel
137     em  = '\x19', /// End of medium
138     sub = '\x1A', /// Substitute
139     esc = '\x1B', /// Escape
140     fs  = '\x1C', /// File separator
141     gs  = '\x1D', /// Group separator
142     rs  = '\x1E', /// Record separator
143     us  = '\x1F', /// Unit separator
144     del = '\x7F' /// Delete
145 }
146 
147 ///
148 @safe pure nothrow @nogc unittest
149 {
150     import std.algorithm.comparison, std.algorithm.searching, std.range, std.traits;
151 
152     // Because all ASCII characters fit in char, so do these
153     static assert(ControlChar.ack.sizeof == 1);
154 
155     // All control characters except del are in row starting from 0
156     static assert(EnumMembers!ControlChar.only.until(ControlChar.del).equal(iota(32)));
157 
158     static assert(ControlChar.nul == '\0');
159     static assert(ControlChar.bel == '\a');
160     static assert(ControlChar.bs  == '\b');
161     static assert(ControlChar.ff  == '\f');
162     static assert(ControlChar.lf  == '\n');
163     static assert(ControlChar.cr  == '\r');
164     static assert(ControlChar.tab == '\t');
165     static assert(ControlChar.vt  == '\v');
166 }
167 
168 ///
169 @safe pure nothrow unittest
170 {
171     import std.conv;
172     //Control character table can be used in place of hexcodes.
173     with (ControlChar) assert(text("Phobos", us, "Deimos", us, "Tango", rs) == "Phobos\x1FDeimos\x1FTango\x1E");
174 }
175 
176 /// Newline sequence for this system.
177 version (Windows)
178     immutable newline = "\r\n";
179 else version (Posix)
180     immutable newline = "\n";
181 else
182     static assert(0, "Unsupported OS");
183 
184 
185 /++
186     Params: c = The character to test.
187     Returns: Whether `c` is a letter or a number (0 .. 9, a .. z, A .. Z).
188   +/
189 pragma(inline, true)
190 bool isAlphaNum(dchar c) @safe pure nothrow @nogc
191 {
192     const hc = c | 0x20;
193     return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'z');
194 }
195 
196 ///
197 @safe pure nothrow @nogc unittest
198 {
199     assert( isAlphaNum('A'));
200     assert( isAlphaNum('1'));
201     assert(!isAlphaNum('#'));
202 
203     // N.B.: does not return true for non-ASCII Unicode alphanumerics:
204     assert(!isAlphaNum('á'));
205 }
206 
207 @safe unittest
208 {
209     import std.range;
210     foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase))
211         assert(isAlphaNum(c));
212 
213     foreach (c; whitespace)
214         assert(!isAlphaNum(c));
215 }
216 
217 
218 /++
219     Params: c = The character to test.
220     Returns: Whether `c` is an ASCII letter (A .. Z, a .. z).
221   +/
222 pragma(inline, true)
223 bool isAlpha(dchar c) @safe pure nothrow @nogc
224 {
225     // Optimizer can turn this into a bitmask operation on 64 bit code
226     return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
227 }
228 
229 ///
230 @safe pure nothrow @nogc unittest
231 {
232     assert( isAlpha('A'));
233     assert(!isAlpha('1'));
234     assert(!isAlpha('#'));
235 
236     // N.B.: does not return true for non-ASCII Unicode alphabetic characters:
237     assert(!isAlpha('á'));
238 }
239 
240 @safe unittest
241 {
242     import std.range;
243     foreach (c; chain(letters, lowercase, uppercase))
244         assert(isAlpha(c));
245 
246     foreach (c; chain(digits, octalDigits, whitespace))
247         assert(!isAlpha(c));
248 }
249 
250 
251 /++
252     Params: c = The character to test.
253     Returns: Whether `c` is a lowercase ASCII letter (a .. z).
254   +/
255 pragma(inline, true)
256 bool isLower(dchar c) @safe pure nothrow @nogc
257 {
258     return c >= 'a' && c <= 'z';
259 }
260 
261 ///
262 @safe pure nothrow @nogc unittest
263 {
264     assert( isLower('a'));
265     assert(!isLower('A'));
266     assert(!isLower('#'));
267 
268     // N.B.: does not return true for non-ASCII Unicode lowercase letters
269     assert(!isLower('á'));
270     assert(!isLower('Á'));
271 }
272 
273 @safe unittest
274 {
275     import std.range;
276     foreach (c; lowercase)
277         assert(isLower(c));
278 
279     foreach (c; chain(digits, uppercase, whitespace))
280         assert(!isLower(c));
281 }
282 
283 
284 /++
285     Params: c = The character to test.
286     Returns: Whether `c` is an uppercase ASCII letter (A .. Z).
287   +/
288 pragma(inline, true)
289 bool isUpper(dchar c) @safe pure nothrow @nogc
290 {
291     return c <= 'Z' && 'A' <= c;
292 }
293 
294 ///
295 @safe pure nothrow @nogc unittest
296 {
297     assert( isUpper('A'));
298     assert(!isUpper('a'));
299     assert(!isUpper('#'));
300 
301     // N.B.: does not return true for non-ASCII Unicode uppercase letters
302     assert(!isUpper('á'));
303     assert(!isUpper('Á'));
304 }
305 
306 @safe unittest
307 {
308     import std.range;
309     foreach (c; uppercase)
310         assert(isUpper(c));
311 
312     foreach (c; chain(digits, lowercase, whitespace))
313         assert(!isUpper(c));
314 }
315 
316 
317 /++
318     Params: c = The character to test.
319     Returns: Whether `c` is a digit (0 .. 9).
320   +/
321 pragma(inline, true)
322 bool isDigit(dchar c) @safe pure nothrow @nogc
323 {
324     return '0' <= c && c <= '9';
325 }
326 
327 ///
328 @safe pure nothrow @nogc unittest
329 {
330     assert( isDigit('3'));
331     assert( isDigit('8'));
332     assert(!isDigit('B'));
333     assert(!isDigit('#'));
334 
335     // N.B.: does not return true for non-ASCII Unicode numbers
336     assert(!isDigit('0')); // full-width digit zero (U+FF10)
337     assert(!isDigit('4')); // full-width digit four (U+FF14)
338 }
339 
340 @safe unittest
341 {
342     import std.range;
343     foreach (c; digits)
344         assert(isDigit(c));
345 
346     foreach (c; chain(letters, whitespace))
347         assert(!isDigit(c));
348 }
349 
350 
351 /++
352     Params: c = The character to test.
353     Returns: Whether `c` is a digit in base 8 (0 .. 7).
354   +/
355 pragma(inline, true)
356 bool isOctalDigit(dchar c) @safe pure nothrow @nogc
357 {
358     return c >= '0' && c <= '7';
359 }
360 
361 ///
362 @safe pure nothrow @nogc unittest
363 {
364     assert( isOctalDigit('0'));
365     assert( isOctalDigit('7'));
366     assert(!isOctalDigit('8'));
367     assert(!isOctalDigit('A'));
368     assert(!isOctalDigit('#'));
369 }
370 
371 @safe unittest
372 {
373     import std.range;
374     foreach (c; octalDigits)
375         assert(isOctalDigit(c));
376 
377     foreach (c; chain(letters, ['8', '9'], whitespace))
378         assert(!isOctalDigit(c));
379 }
380 
381 
382 /++
383     Params: c = The character to test.
384     Returns: Whether `c` is a digit in base 16 (0 .. 9, A .. F, a .. f).
385   +/
386 pragma(inline, true)
387 bool isHexDigit(dchar c) @safe pure nothrow @nogc
388 {
389     const hc = c | 0x20;
390     return ('0' <= c && c <= '9') || ('a' <= hc && hc <= 'f');
391 }
392 
393 ///
394 @safe pure nothrow @nogc unittest
395 {
396     assert( isHexDigit('0'));
397     assert( isHexDigit('A'));
398     assert( isHexDigit('f')); // lowercase hex digits are accepted
399     assert(!isHexDigit('g'));
400     assert(!isHexDigit('G'));
401     assert(!isHexDigit('#'));
402 }
403 
404 @safe unittest
405 {
406     import std.range;
407     foreach (c; fullHexDigits)
408         assert(isHexDigit(c));
409 
410     foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace))
411         assert(!isHexDigit(c));
412 }
413 
414 
415 /++
416     Params: c = The character to test.
417     Returns: Whether or not `c` is a whitespace character. That includes the
418     space, tab, vertical tab, form feed, carriage return, and linefeed
419     characters.
420   +/
421 pragma(inline, true)
422 bool isWhite(dchar c) @safe pure nothrow @nogc
423 {
424     return c == ' ' || (c >= 0x09 && c <= 0x0D);
425 }
426 
427 ///
428 @safe pure nothrow @nogc unittest
429 {
430     assert( isWhite(' '));
431     assert( isWhite('\t'));
432     assert( isWhite('\n'));
433     assert(!isWhite('1'));
434     assert(!isWhite('a'));
435     assert(!isWhite('#'));
436 
437     // N.B.: Does not return true for non-ASCII Unicode whitespace characters.
438     static import std.uni;
439     assert(std.uni.isWhite('\u00A0'));
440     assert(!isWhite('\u00A0')); // std.ascii.isWhite
441 }
442 
443 @safe unittest
444 {
445     import std.range;
446     foreach (c; whitespace)
447         assert(isWhite(c));
448 
449     foreach (c; chain(digits, letters))
450         assert(!isWhite(c));
451 }
452 
453 
454 /++
455     Params: c = The character to test.
456     Returns: Whether `c` is a control character.
457   +/
458 pragma(inline, true)
459 bool isControl(dchar c) @safe pure nothrow @nogc
460 {
461     return c < 0x20 || c == 0x7F;
462 }
463 
464 ///
465 @safe pure nothrow @nogc unittest
466 {
467     assert( isControl('\0'));
468     assert( isControl('\022'));
469     assert( isControl('\n')); // newline is both whitespace and control
470     assert(!isControl(' '));
471     assert(!isControl('1'));
472     assert(!isControl('a'));
473     assert(!isControl('#'));
474 
475     // N.B.: non-ASCII Unicode control characters are not recognized:
476     assert(!isControl('\u0080'));
477     assert(!isControl('\u2028'));
478     assert(!isControl('\u2029'));
479 }
480 
481 @safe unittest
482 {
483     import std.range;
484     foreach (dchar c; 0 .. 32)
485         assert(isControl(c));
486     assert(isControl(127));
487 
488     foreach (c; chain(digits, letters, [' ']))
489         assert(!isControl(c));
490 }
491 
492 
493 /++
494     Params: c = The character to test.
495     Returns: Whether or not `c` is a punctuation character. That includes
496     all ASCII characters which are not control characters, letters, digits, or
497     whitespace.
498   +/
499 pragma(inline, true)
500 bool isPunctuation(dchar c) @safe pure nothrow @nogc
501 {
502     return c <= '~' && c >= '!' && !isAlphaNum(c);
503 }
504 
505 ///
506 @safe pure nothrow @nogc unittest
507 {
508     assert( isPunctuation('.'));
509     assert( isPunctuation(','));
510     assert( isPunctuation(':'));
511     assert( isPunctuation('!'));
512     assert( isPunctuation('#'));
513     assert( isPunctuation('~'));
514     assert( isPunctuation('+'));
515     assert( isPunctuation('_'));
516 
517     assert(!isPunctuation('1'));
518     assert(!isPunctuation('a'));
519     assert(!isPunctuation(' '));
520     assert(!isPunctuation('\n'));
521     assert(!isPunctuation('\0'));
522 
523     // N.B.: Non-ASCII Unicode punctuation characters are not recognized.
524     assert(!isPunctuation('\u2012')); // (U+2012 = en-dash)
525 }
526 
527 @safe unittest
528 {
529     foreach (dchar c; 0 .. 128)
530     {
531         if (isControl(c) || isAlphaNum(c) || c == ' ')
532             assert(!isPunctuation(c));
533         else
534             assert(isPunctuation(c));
535     }
536 }
537 
538 
539 /++
540     Params: c = The character to test.
541     Returns: Whether or not `c` is a printable character other than the
542     space character.
543   +/
544 pragma(inline, true)
545 bool isGraphical(dchar c) @safe pure nothrow @nogc
546 {
547     return '!' <= c && c <= '~';
548 }
549 
550 ///
551 @safe pure nothrow @nogc unittest
552 {
553     assert( isGraphical('1'));
554     assert( isGraphical('a'));
555     assert( isGraphical('#'));
556     assert(!isGraphical(' ')); // whitespace is not graphical
557     assert(!isGraphical('\n'));
558     assert(!isGraphical('\0'));
559 
560     // N.B.: Unicode graphical characters are not regarded as such.
561     assert(!isGraphical('á'));
562 }
563 
564 @safe unittest
565 {
566     foreach (dchar c; 0 .. 128)
567     {
568         if (isControl(c) || c == ' ')
569             assert(!isGraphical(c));
570         else
571             assert(isGraphical(c));
572     }
573 }
574 
575 
576 /++
577     Params: c = The character to test.
578     Returns: Whether or not `c` is a printable character - including the
579     space character.
580   +/
581 pragma(inline, true)
582 bool isPrintable(dchar c) @safe pure nothrow @nogc
583 {
584     return c >= ' ' && c <= '~';
585 }
586 
587 ///
588 @safe pure nothrow @nogc unittest
589 {
590     assert( isPrintable(' '));  // whitespace is printable
591     assert( isPrintable('1'));
592     assert( isPrintable('a'));
593     assert( isPrintable('#'));
594     assert(!isPrintable('\0')); // control characters are not printable
595 
596     // N.B.: Printable non-ASCII Unicode characters are not recognized.
597     assert(!isPrintable('á'));
598 }
599 
600 @safe unittest
601 {
602     foreach (dchar c; 0 .. 128)
603     {
604         if (isControl(c))
605             assert(!isPrintable(c));
606         else
607             assert(isPrintable(c));
608     }
609 }
610 
611 
612 /++
613     Params: c = The character to test.
614     Returns: Whether or not `c` is in the ASCII character set - i.e. in the
615     range 0 .. 0x7F.
616   +/
617 pragma(inline, true)
618 bool isASCII(dchar c) @safe pure nothrow @nogc
619 {
620     return c <= 0x7F;
621 }
622 
623 ///
624 @safe pure nothrow @nogc unittest
625 {
626     assert( isASCII('a'));
627     assert(!isASCII('á'));
628 }
629 
630 @safe unittest
631 {
632     foreach (dchar c; 0 .. 128)
633         assert(isASCII(c));
634 
635     assert(!isASCII(128));
636 }
637 
638 
639 /++
640     Converts an ASCII letter to lowercase.
641 
642     Params: c = A character of any type that implicitly converts to `dchar`.
643     In the case where it's a built-in type, or an enum of a built-in type,
644     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
645     type, `dchar` is returned.
646 
647     Returns: The corresponding lowercase letter, if `c` is an uppercase
648     ASCII character, otherwise `c` itself.
649   +/
650 auto toLower(C)(C c)
651 if (is(C : dchar))
652 {
653     import std.traits : OriginalType;
654 
655     static if (!__traits(isScalar, C))
656         alias R = dchar;
657     else static if (is(immutable OriginalType!C == immutable OC, OC))
658         alias R = OC;
659 
660     return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c;
661 }
662 
663 ///
664 @safe pure nothrow @nogc unittest
665 {
666     assert(toLower('a') == 'a');
667     assert(toLower('A') == 'a');
668     assert(toLower('#') == '#');
669 
670     // N.B.: Non-ASCII Unicode uppercase letters are not converted.
671     assert(toLower('Á') == 'Á');
672 }
673 
674 @safe pure nothrow unittest
675 {
676 
677     import std.meta;
678     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
679     {
680         foreach (i, c; uppercase)
681             assert(toLower(cast(C) c) == lowercase[i]);
682 
683         foreach (C c; 0 .. 128)
684         {
685             if (c < 'A' || c > 'Z')
686                 assert(toLower(c) == c);
687             else
688                 assert(toLower(c) != c);
689         }
690 
691         foreach (C c; 128 .. C.max)
692             assert(toLower(c) == c);
693 
694         //CTFE
695         static assert(toLower(cast(C)'a') == 'a');
696         static assert(toLower(cast(C)'A') == 'a');
697     }
698 }
699 
700 
701 /++
702     Converts an ASCII letter to uppercase.
703 
704     Params: c = Any type which implicitly converts to `dchar`. In the case
705     where it's a built-in type, or an enum of a built-in type,
706     `Unqual!(OriginalType!C)` is returned, whereas if it's a user-defined
707     type, `dchar` is returned.
708 
709     Returns: The corresponding uppercase letter, if `c` is a lowercase ASCII
710     character, otherwise `c` itself.
711   +/
712 auto toUpper(C)(C c)
713 if (is(C : dchar))
714 {
715     import std.traits : OriginalType;
716 
717     static if (!__traits(isScalar, C))
718         alias R = dchar;
719     else static if (is(immutable OriginalType!C == immutable OC, OC))
720         alias R = OC;
721 
722     return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c;
723 }
724 
725 ///
726 @safe pure nothrow @nogc unittest
727 {
728     assert(toUpper('a') == 'A');
729     assert(toUpper('A') == 'A');
730     assert(toUpper('#') == '#');
731 
732     // N.B.: Non-ASCII Unicode lowercase letters are not converted.
733     assert(toUpper('á') == 'á');
734 }
735 
736 @safe pure nothrow unittest
737 {
738     import std.meta;
739     static foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
740     {
741         foreach (i, c; lowercase)
742             assert(toUpper(cast(C) c) == uppercase[i]);
743 
744         foreach (C c; 0 .. 128)
745         {
746             if (c < 'a' || c > 'z')
747                 assert(toUpper(c) == c);
748             else
749                 assert(toUpper(c) != c);
750         }
751 
752         foreach (C c; 128 .. C.max)
753             assert(toUpper(c) == c);
754 
755         //CTFE
756         static assert(toUpper(cast(C)'a') == 'A');
757         static assert(toUpper(cast(C)'A') == 'A');
758     }
759 }
760 
761 
762 @safe unittest //Test both toUpper and toLower with non-builtin
763 {
764     import std.meta;
765     import std.traits;
766 
767     //User Defined [Char|Wchar|Dchar]
768     static struct UDC {  char c; alias c this; }
769     static struct UDW { wchar c; alias c this; }
770     static struct UDD { dchar c; alias c this; }
771     //[Char|Wchar|Dchar] Enum
772     enum CE :  char {a = 'a', A = 'A'}
773     enum WE : wchar {a = 'a', A = 'A'}
774     enum DE : dchar {a = 'a', A = 'A'}
775     //User Defined [Char|Wchar|Dchar] Enum
776     enum UDCE : UDC {a = UDC('a'), A = UDC('A')}
777     enum UDWE : UDW {a = UDW('a'), A = UDW('A')}
778     enum UDDE : UDD {a = UDD('a'), A = UDD('A')}
779 
780     //User defined types with implicit cast to dchar test.
781     static foreach (Char; AliasSeq!(UDC, UDW, UDD))
782     {
783         assert(toLower(Char('a')) == 'a');
784         assert(toLower(Char('A')) == 'a');
785         static assert(toLower(Char('a')) == 'a');
786         static assert(toLower(Char('A')) == 'a');
787         static assert(toUpper(Char('a')) == 'A');
788         static assert(toUpper(Char('A')) == 'A');
789     }
790 
791     //Various enum tests.
792     static foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
793     {
794         assert(toLower(Enum.a) == 'a');
795         assert(toLower(Enum.A) == 'a');
796         assert(toUpper(Enum.a) == 'A');
797         assert(toUpper(Enum.A) == 'A');
798         static assert(toLower(Enum.a) == 'a');
799         static assert(toLower(Enum.A) == 'a');
800         static assert(toUpper(Enum.a) == 'A');
801         static assert(toUpper(Enum.A) == 'A');
802     }
803 
804     //Return value type tests for enum of non-UDT. These should be the original type.
805     static foreach (T; AliasSeq!(CE, WE, DE))
806     {{
807         alias C = OriginalType!T;
808         static assert(is(typeof(toLower(T.init)) == C));
809         static assert(is(typeof(toUpper(T.init)) == C));
810     }}
811 
812     //Return value tests for UDT and enum of UDT. These should be dchar
813     static foreach (T; AliasSeq!(UDC, UDW, UDD, UDCE, UDWE, UDDE))
814     {
815         static assert(is(typeof(toLower(T.init)) == dchar));
816         static assert(is(typeof(toUpper(T.init)) == dchar));
817     }
818 }