[m-rev.] for review: fix problems with the char module
Julien Fischer
jfischer at opturion.com
Wed Sep 10 17:36:38 AEST 2014
For review by anyone.
----------------------
Fix problems with the char module.
(1) The behaviour of digit_to_int/2 was inconsistent with that of is_digit/2.
The former succeeds for all of 0-9, a-z and A-Z while the latter succeeds only
for 0-9 (i.e. it was possible for digit_to_int/2 to succeed for non-decimal
characters, which is not what was intended in many of it uses).
(2) Predicates involving hexadecimal digits were inconsistently named, they
were "hex digits" in one predicate name, "hex chars" in another.
This change ensures that the following operations are supported for binary,
octal, decimal and hexadecimal digits and that we use a consistent naming
scheme for the predicates that implement them:
- testing if a character is a digit of the given base
- conversion to an int
- conversion from an int
In addition, we also add predicates for supporting these operations for user
defined bases, ranging from 2-36.
library/char.m:
Add the predicate is_decimal_digit/1, which is a synonym for is_digit/1.
Add the predicate is_base_digit/2.
Add the predicates int_to_{binary,octal,decimal,hex}_digit/2 and
base_int_to_digit/3.
Add the predicates {binary,octal,decimal,hex}_digit_to_int/2 and
base_digit_to_int/3.
Add det function versions of the above.
Delete the function det_digit_to_int/1 that I added the other day.
Mark the following as obsolete:
- is_hex_digit/2
- int_to_hex_char/2
- int_to_digit/2
- det_int_to_digit/1
- det_int_to_digit/2
Avoid redundant module qualification in the implementation.
Mark some C foreign_procs as not modifying the trail.
Re-order some declarations according to how the coding standard says they
should be ordered.
library/bitmap.m:
library/integer.m:
library/parsing_utils.m:
library/string.m:
compiler/prog_rep_tables.m:
Replace calls to obsolete predicates or functions.
NEWS:
Announce the above changes.
Add note advising users of digit_to_int/2 to check their code for the
problem described above.
tests/hard_coded/Mmakefile:
tests/hard_coded/test_char_digits.m:
tests/hard_coded/test_char_digits.exp:
Add a systematic test for the above predicates.
diff --git a/NEWS b/NEWS
index 3e699d5..8fc219b 100644
--- a/NEWS
+++ b/NEWS
@@ -35,7 +35,34 @@ Changes to the Mercury standard library:
* Procedures in the store module no longer acquire the global lock.
-* We have added the function det_digit_to_int/1 to the char module.
+* The following predicates and functions have been added to the char module:
+
+ - is_decimal_digit/1
+ - is_base_digit/2
+ - int_to_binary_digit/2
+ - int_to_octal_digit/2
+ - int_to_decimal_digit/2
+ - int_to_hex_digit/2
+ - base_int_to_digit/3, det_base_int_to_digit/2
+ - binary_digit_to_int/2, det_binary_digit_to_int/1
+ - octal_digit_to_int/2, det_octal_digit_to_int/1
+ - decimal_digit_to_int/2, det_decimal_digit_to_int/1
+ - hex_digit_to_int/2, det_hex_digit_to_int/1
+ - base_digit_to_int/3, det_base_digit_to_int/2
+
+ The following predicates in the char module have been deprecated and will
+ either be removed or have their semantics changed in a future release.
+
+ - is_hex_digit/2
+ - int_to_hex_char/2
+ - digit_to_int/2
+ - int_to_digit/2
+ - det_int_to_digit/1, det_int_to_digit/2
+
+ NOTE: existing code that calls the predicate char.digit_to_int/2 and assumes
+ that the call will succeed only for those characters for which
+ char.is_digit/1 is true may be broken. char.digit_to_int/2 succeeds for
+ the characters 0-9, a-z and A-Z, not just 0-9.
Changes to the Mercury compiler:
diff --git a/compiler/prog_rep_tables.m b/compiler/prog_rep_tables.m
index 30a7d96..75ce864 100644
--- a/compiler/prog_rep_tables.m
+++ b/compiler/prog_rep_tables.m
@@ -207,7 +207,7 @@ find_number_suffix(String, BeforeNum, Num) :-
int::in, int::out, list(char)::out) is semidet.
rev_find_number_suffix([RevHead | RevTail], !Num, !Scale, RevRest) :-
- ( char.digit_to_int(RevHead, Digit) ->
+ ( char.decimal_digit_to_int(RevHead, Digit) ->
!:Num = !.Num + (!.Scale * Digit),
!:Scale = !.Scale * 10,
rev_find_number_suffix(RevTail, !Num, !Scale, RevRest)
diff --git a/library/bitmap.m b/library/bitmap.m
index 24e1379..3a606f2 100644
--- a/library/bitmap.m
+++ b/library/bitmap.m
@@ -1396,9 +1396,9 @@ to_string_chars(Index, BM, !Chars) :-
Byte = BM ^ unsafe_byte(Index),
Mask = n_bit_mask(4),
( if
- char.int_to_hex_char((Byte `unchecked_right_shift` 4) /\ Mask,
+ char.int_to_hex_digit((Byte `unchecked_right_shift` 4) /\ Mask,
HighChar),
- char.int_to_hex_char(Byte /\ Mask, LowChar)
+ char.int_to_hex_digit(Byte /\ Mask, LowChar)
then
!:Chars = [HighChar, LowChar | !.Chars],
to_string_chars(Index - 1, BM, !Chars)
@@ -1433,8 +1433,8 @@ hex_chars_to_bitmap(Str, Index, End, ByteIndex, !BM) :-
% Each byte of the bitmap should have mapped to a pair of characters.
fail
else
- char.is_hex_digit(Str ^ unsafe_elem(Index), HighNibble),
- char.is_hex_digit(Str ^ unsafe_elem(Index + 1), LowNibble),
+ char.hex_digit_to_int(Str ^ unsafe_elem(Index), HighNibble),
+ char.hex_digit_to_int(Str ^ unsafe_elem(Index + 1), LowNibble),
Byte = (HighNibble `unchecked_left_shift` 4) \/ LowNibble,
!:BM = !.BM ^ unsafe_byte(ByteIndex) := Byte,
hex_chars_to_bitmap(Str, Index + 2, End, ByteIndex + 1, !BM)
diff --git a/library/char.m b/library/char.m
index 40edc1c..4af004d 100644
--- a/library/char.m
+++ b/library/char.m
@@ -68,8 +68,8 @@
% Converts an integer to its corresponding character. Aborts
% if there isn't one.
%
-:- pred char.det_from_int(int::in, char::out) is det.
:- func char.det_from_int(int) = char.
+:- pred char.det_from_int(int::in, char::out) is det.
% Returns the maximum numerical character code.
%
@@ -141,39 +141,125 @@
%
:- pred char.is_binary_digit(char::in) is semidet.
- % True iff the character is a octal digit (0-7) in the ASCII range.
+ % True iff the character is an octal digit (0-7) in the ASCII range.
%
:- pred char.is_octal_digit(char::in) is semidet.
- % True iff the character is a hexadecimal digit (0-9, a-f, A-F)
- % in the ASCII range.
+ % True iff the character is a decimal digit (0-9) in the ASCII range.
+ % Synonym for char.is_digit/1.
+ %
+:- pred char.is_decimal_digit(char::in) is semidet.
+
+ % True iff the character is a hexadecimal digit (0-9, a-f, A-F) in the
+ % ASCII range.
%
:- pred char.is_hex_digit(char::in) is semidet.
+
+ % is_base_digit(Base, Digit):
+ % True iff Digit is a digit in the given Base (0-9, a-z, A-Z).
+ % Throws an exception if Base < 2 or Base > 36.
+ %
+:- pred char.is_base_digit(int::in, char::in) is semidet.
+
+ % Convert an integer in the range 0-1 to a binary digit (0 or 1) in the
+ % ASCII range.
+ %
+:- pred char.int_to_binary_digit(int::in, char::out) is semidet.
+
+ % Convert an integer 0-7 to an octal digit (0-7) in the ASCII range.
+ %
+:- pred char.int_to_octal_digit(int::in, char::out) is semidet.
+
+ % Convert an integer 0-9 to a decimal digit (0-9) in the ASCII range.
+ %
+:- pred char.int_to_decimal_digit(int::in, char::out) is semidet.
+
+ % Convert an integer 0-15 to a hexadecimal digit (0-9, A-F) in the ASCII
+ % range.
+ %
+:- pred char.int_to_hex_digit(int::in, char::out) is semidet.
+
+ % base_int_to_digit(Base, Int, DigitChar):
+ % True iff `DigitChar' is a decimal digit (0-9) or an uppercase letter
+ % (A-Z) representing the value `Int' (0-35) in the given base.
+ % Throws an exception if `Base' < 2 or `Base' > 36.
+ %
+:- pred char.base_int_to_digit(int::in, int::in, char::out) is semidet.
+
+ % As above, but throw an exception instead of failing.
+ %
+:- func char.det_base_int_to_digit(int, int) = char.
+ % True iff char is a binary digit (0 or 1).
+ % Returns the character's value as a digit (0-1).
+ %
+:- pred char.binary_digit_to_int(char::in, int::out) is semidet.
+
+ % As above, but throws an exception instead of failing.
+ %
+:- func char.det_binary_digit_to_int(char) = int.
+
+ % True iff char is an octal digit (0-7).
+ % Returns the character's value as a digit (0-7).
+ %
+:- pred char.octal_digit_to_int(char::in, int::out) is semidet.
+
+ % As above, but throws an exception instead of failing.
+ %
+:- func char.det_octal_digit_to_int(char) = int.
+
+ % True iff char is a decimal digit (0-9).
+ % Returns the character's value as a digit (0-9).
+ %
+:- pred char.decimal_digit_to_int(char::in, int::out) is semidet.
+
+ % As above, but throws an exception instead of failing.
+ %
+:- func char.det_decimal_digit_to_int(char) = int.
+
+ % True iff char is a hexadecimal digit (0-9, a-z or A-F).
+ % Returns the character's value as a digit (0-15).
+ %
+:- pred char.hex_digit_to_int(char::in, int::out) is semidet.
+
+ % As above, but throws an exception instead of failing.
+ %
+:- func char.det_hex_digit_to_int(char) = int.
+
+ % base_digit_to_int(Base, DigitChar, Int):
+ % True iff `DigitChar' is a decimal digit (0-9) or a letter (a-z, A-Z)
+ % representing the value `Int' (0-35) in the given base.
+ % Throws an exception if `Base' < 2 or `Base' > 36.
+ %
+:- pred char.base_digit_to_int(int::in, char::in, int::out) is semidet.
+
+ % As above, but throws an exception instead of failing.
+ %
+:- func char.det_base_digit_to_int(int, char) = int.
+
+:- pragma obsolete(char.is_hex_digit/2).
:- pred char.is_hex_digit(char, int).
:- mode char.is_hex_digit(in, out) is semidet.
- % Convert an integer 0-15 to a hexadecimal digit (0-9, A-F)
- % in the ASCII range.
+ % Convert an integer 0-15 to a hexadecimal digit (0-9, A-F) in the ASCII
+ % range.
%
+:- pragma obsolete(char.int_to_hex_char/2).
:- pred char.int_to_hex_char(int, char).
:- mode char.int_to_hex_char(in, out) is semidet.
% Succeeds if char is a decimal digit (0-9) or letter (a-z or A-Z).
% Returns the character's value as a digit (0-9 or 10-35).
%
+:- pragma obsolete(char.digit_to_int/2).
:- pred char.digit_to_int(char::in, int::out) is semidet.
- % As above, but calls error/1 instead of failing.
+ % char.int_to_digit(Int, DigitChar):
%
-:- func char.det_digit_to_int(char) = int.
-
- % char.int_to_uppercase_digit(Int, DigitChar):
- %
- % True iff `Int' is an integer in the range 0-35 and
- % `DigitChar' is a decimal digit or uppercase letter
- % whose value as a digit is `Int'.
+ % True iff `Int' is an integer in the range 0-35 and `DigitChar' is a
+ % decimal digit or uppercase letter whose value as a digit is `Int'.
%
+:- pragma obsolete(char.int_to_digit/2).
:- pred char.int_to_digit(int, char).
:- mode char.int_to_digit(in, out) is semidet.
:- mode char.int_to_digit(out, in) is semidet.
@@ -181,7 +267,9 @@
% Returns a decimal digit or uppercase letter corresponding to the value.
% Calls error/1 if the integer is not in the range 0-35.
%
+:- pragma obsolete(char.det_int_to_digit/1).
:- func char.det_int_to_digit(int) = char.
+:- pragma obsolete(char.det_int_to_digit/2).
:- pred char.det_int_to_digit(int::in, char::out) is det.
% Convert a char to a pretty_printer.doc for formatting.
@@ -227,14 +315,14 @@
% The information here is duplicated in lookup_token_action in lexer.m.
% If you update this; you will also need update that.
-char.is_whitespace(' ').
-char.is_whitespace('\t').
-char.is_whitespace('\n').
-char.is_whitespace('\r').
-char.is_whitespace('\f').
-char.is_whitespace('\v').
-
-char.is_alpha(Char) :-
+is_whitespace(' ').
+is_whitespace('\t').
+is_whitespace('\n').
+is_whitespace('\r').
+is_whitespace('\f').
+is_whitespace('\v').
+
+is_alpha(Char) :-
( char.is_lower(Char) ->
true
; char.is_upper(Char) ->
@@ -243,7 +331,7 @@ char.is_alpha(Char) :-
fail
).
-char.is_alnum(Char) :-
+is_alnum(Char) :-
( char.is_alpha(Char) ->
true
; char.is_digit(Char) ->
@@ -252,7 +340,7 @@ char.is_alnum(Char) :-
fail
).
-char.is_alpha_or_underscore(Char) :-
+is_alpha_or_underscore(Char) :-
( Char = '_' ->
true
;
@@ -262,7 +350,7 @@ char.is_alpha_or_underscore(Char) :-
% We explicitly enumerate here for efficiency.
% (The information here and in some of the following predicates,
% e.g. char.lower_upper, is duplicated in lookup_token_action in lexer.m.)
-char.is_alnum_or_underscore(Char) :-
+is_alnum_or_underscore(Char) :-
( Char = '0'
; Char = '1'
; Char = '2'
@@ -334,30 +422,30 @@ char.is_alnum_or_underscore(Char) :-
% char.is_alpha_or_underscore(Char)
% ).
-char.is_lower(Lower) :-
+is_lower(Lower) :-
char.lower_upper(Lower, _).
-char.is_upper(Upper) :-
+is_upper(Upper) :-
( char.lower_upper(_, Upper) ->
true
;
fail
).
-char.to_lower(C1) = C2 :-
+to_lower(C1) = C2 :-
char.to_lower(C1, C2).
-char.to_lower(Char, Lower) :-
+to_lower(Char, Lower) :-
( char.lower_upper(LowerChar, Char) ->
Lower = LowerChar
;
Lower = Char
).
-char.to_upper(C1) = C2 :-
+to_upper(C1) = C2 :-
char.to_upper(C1, C2).
-char.to_upper(Char, Upper) :-
+to_upper(Char, Upper) :-
( char.lower_upper(Char, UpperChar) ->
Upper = UpperChar
;
@@ -372,71 +460,228 @@ char.to_upper(Char, Upper) :-
% but these versions are very portable.
%-----------------------------------------------------------------------------%
+%
+% Digit classification.
+%
+
+is_binary_digit('0').
+is_binary_digit('1').
+
+is_octal_digit('0').
+is_octal_digit('1').
+is_octal_digit('2').
+is_octal_digit('3').
+is_octal_digit('4').
+is_octal_digit('5').
+is_octal_digit('6').
+is_octal_digit('7').
+
+is_decimal_digit('0').
+is_decimal_digit('1').
+is_decimal_digit('2').
+is_decimal_digit('3').
+is_decimal_digit('4').
+is_decimal_digit('5').
+is_decimal_digit('6').
+is_decimal_digit('7').
+is_decimal_digit('8').
+is_decimal_digit('9').
+
+is_digit(D) :- is_decimal_digit(D).
+
+is_hex_digit('0').
+is_hex_digit('1').
+is_hex_digit('2').
+is_hex_digit('3').
+is_hex_digit('4').
+is_hex_digit('5').
+is_hex_digit('6').
+is_hex_digit('7').
+is_hex_digit('8').
+is_hex_digit('9').
+is_hex_digit('a').
+is_hex_digit('b').
+is_hex_digit('c').
+is_hex_digit('d').
+is_hex_digit('e').
+is_hex_digit('f').
+is_hex_digit('A').
+is_hex_digit('B').
+is_hex_digit('C').
+is_hex_digit('D').
+is_hex_digit('E').
+is_hex_digit('F').
+
+is_base_digit(Base, Digit) :-
+ ( ( Base < 2 ; Base > 36) ->
+ error("char.is_base_digit: invalid base")
+ ;
+ base_digit_to_int(Base, Digit, _Int)
+ ).
+
+%-----------------------------------------------------------------------------%
+%
+% Digit to integer conversion.
+%
+
+binary_digit_to_int('0', 0).
+binary_digit_to_int('1', 1).
+
+det_binary_digit_to_int(Digit) = Int :-
+ ( binary_digit_to_int(Digit, Int0) ->
+ Int = Int0
+ ;
+ error("char.binary_digit_to_int failed")
+ ).
+
+octal_digit_to_int('0', 0).
+octal_digit_to_int('1', 1).
+octal_digit_to_int('2', 2).
+octal_digit_to_int('3', 3).
+octal_digit_to_int('4', 4).
+octal_digit_to_int('5', 5).
+octal_digit_to_int('6', 6).
+octal_digit_to_int('7', 7).
+
+det_octal_digit_to_int(Digit) = Int :-
+ ( octal_digit_to_int(Digit, Int0) ->
+ Int = Int0
+ ;
+ error("char.octal_digit_to_int failed")
+ ).
-char.is_binary_digit('0').
-char.is_binary_digit('1').
-
-char.is_octal_digit('0').
-char.is_octal_digit('1').
-char.is_octal_digit('2').
-char.is_octal_digit('3').
-char.is_octal_digit('4').
-char.is_octal_digit('5').
-char.is_octal_digit('6').
-char.is_octal_digit('7').
-
-char.is_digit('0').
-char.is_digit('1').
-char.is_digit('2').
-char.is_digit('3').
-char.is_digit('4').
-char.is_digit('5').
-char.is_digit('6').
-char.is_digit('7').
-char.is_digit('8').
-char.is_digit('9').
-
-char.is_hex_digit(X) :- char.is_hex_digit(X, _).
-
-char.is_hex_digit('0', 0).
-char.is_hex_digit('1', 1).
-char.is_hex_digit('2', 2).
-char.is_hex_digit('3', 3).
-char.is_hex_digit('4', 4).
-char.is_hex_digit('5', 5).
-char.is_hex_digit('6', 6).
-char.is_hex_digit('7', 7).
-char.is_hex_digit('8', 8).
-char.is_hex_digit('9', 9).
-char.is_hex_digit('a', 10).
-char.is_hex_digit('b', 11).
-char.is_hex_digit('c', 12).
-char.is_hex_digit('d', 13).
-char.is_hex_digit('e', 14).
-char.is_hex_digit('f', 15).
-char.is_hex_digit('A', 10).
-char.is_hex_digit('B', 11).
-char.is_hex_digit('C', 12).
-char.is_hex_digit('D', 13).
-char.is_hex_digit('E', 14).
-char.is_hex_digit('F', 15).
-
-char.int_to_hex_char(0, '0').
-char.int_to_hex_char(1, '1').
-char.int_to_hex_char(2, '2').
-char.int_to_hex_char(3, '3').
-char.int_to_hex_char(4, '4').
-char.int_to_hex_char(5, '5').
-char.int_to_hex_char(6, '6').
-char.int_to_hex_char(7, '7').
-char.int_to_hex_char(8, '8').
-char.int_to_hex_char(9, '9').
-char.int_to_hex_char(10, 'A').
-char.int_to_hex_char(11, 'B').
-char.int_to_hex_char(12, 'C').
-char.int_to_hex_char(13, 'D').
-char.int_to_hex_char(14, 'E').
-char.int_to_hex_char(15, 'F').
+decimal_digit_to_int('0', 0).
+decimal_digit_to_int('1', 1).
+decimal_digit_to_int('2', 2).
+decimal_digit_to_int('3', 3).
+decimal_digit_to_int('4', 4).
+decimal_digit_to_int('5', 5).
+decimal_digit_to_int('6', 6).
+decimal_digit_to_int('7', 7).
+decimal_digit_to_int('8', 8).
+decimal_digit_to_int('9', 9).
+
+det_decimal_digit_to_int(Digit) = Int :-
+ ( decimal_digit_to_int(Digit, Int0) ->
+ Int = Int0
+ ;
+ error("char.decimal_digit_to_int failed")
+ ).
+
+is_hex_digit(Digit, Int) :-
+ hex_digit_to_int(Digit, Int).
+
+hex_digit_to_int('0', 0).
+hex_digit_to_int('1', 1).
+hex_digit_to_int('2', 2).
+hex_digit_to_int('3', 3).
+hex_digit_to_int('4', 4).
+hex_digit_to_int('5', 5).
+hex_digit_to_int('6', 6).
+hex_digit_to_int('7', 7).
+hex_digit_to_int('8', 8).
+hex_digit_to_int('9', 9).
+hex_digit_to_int('a', 10).
+hex_digit_to_int('b', 11).
+hex_digit_to_int('c', 12).
+hex_digit_to_int('d', 13).
+hex_digit_to_int('e', 14).
+hex_digit_to_int('f', 15).
+hex_digit_to_int('A', 10).
+hex_digit_to_int('B', 11).
+hex_digit_to_int('C', 12).
+hex_digit_to_int('D', 13).
+hex_digit_to_int('E', 14).
+hex_digit_to_int('F', 15).
+
+det_hex_digit_to_int(Digit) = Int :-
+ ( hex_digit_to_int(Digit, Int0) ->
+ Int = Int0
+ ;
+ error("char.hex_digit_to_int failed")
+ ).
+
+base_digit_to_int(Base, Digit, Int) :-
+ ( ( Base < 2 ; Base > 36 ) ->
+ error("char.base_digit_to_int: invalid base")
+ ;
+ ( char.lower_upper(Digit, Upper) ->
+ int_to_extended_digit(Int, Upper)
+ ;
+ int_to_extended_digit(Int, Digit)
+ ),
+ Int < Base
+ ).
+
+det_base_digit_to_int(Base, Digit) = Int :-
+ ( base_digit_to_int(Base, Digit, Int0) ->
+ Int = Int0
+ ;
+ error("char.base_digit_to_int failed")
+ ).
+
+%-----------------------------------------------------------------------------%
+%
+% Integer to digit conversion.
+%
+
+int_to_binary_digit(0, '0').
+int_to_binary_digit(1, '1').
+
+int_to_octal_digit(0, '0').
+int_to_octal_digit(1, '1').
+int_to_octal_digit(2, '2').
+int_to_octal_digit(3, '3').
+int_to_octal_digit(4, '4').
+int_to_octal_digit(5, '5').
+int_to_octal_digit(6, '6').
+int_to_octal_digit(7, '7').
+
+int_to_decimal_digit(0, '0').
+int_to_decimal_digit(1, '1').
+int_to_decimal_digit(2, '2').
+int_to_decimal_digit(3, '3').
+int_to_decimal_digit(4, '4').
+int_to_decimal_digit(5, '5').
+int_to_decimal_digit(6, '6').
+int_to_decimal_digit(7, '7').
+int_to_decimal_digit(8, '8').
+int_to_decimal_digit(9, '9').
+
+int_to_hex_digit(0, '0').
+int_to_hex_digit(1, '1').
+int_to_hex_digit(2, '2').
+int_to_hex_digit(3, '3').
+int_to_hex_digit(4, '4').
+int_to_hex_digit(5, '5').
+int_to_hex_digit(6, '6').
+int_to_hex_digit(7, '7').
+int_to_hex_digit(8, '8').
+int_to_hex_digit(9, '9').
+int_to_hex_digit(10, 'A').
+int_to_hex_digit(11, 'B').
+int_to_hex_digit(12, 'C').
+int_to_hex_digit(13, 'D').
+int_to_hex_digit(14, 'E').
+int_to_hex_digit(15, 'F').
+
+int_to_hex_char(Int, Char) :-
+ int_to_hex_digit(Int, Char).
+
+base_int_to_digit(Base, Int, Digit) :-
+ ( ( Base < 2 ; Base > 36) ->
+ error("char.base_int_to_digit: invalid base")
+ ;
+ Int < Base,
+ int_to_extended_digit(Int, Digit)
+ ).
+
+det_base_int_to_digit(Base, Int) = Digit :-
+ ( base_int_to_digit(Base, Int, Digit0) ->
+ Digit = Digit0
+ ;
+ error("char.base_int_to_digit failed")
+ ).
%-----------------------------------------------------------------------------%
@@ -444,91 +689,91 @@ char.det_int_to_digit(N) = C :-
char.det_int_to_digit(N, C).
char.det_int_to_digit(Int, Digit) :-
- ( char.int_to_digit(Int, Digit1) ->
+ ( int_to_extended_digit(Int, Digit1) ->
Digit = Digit1
;
error("char.int_to_digit failed")
).
-char.int_to_digit(0, '0').
-char.int_to_digit(1, '1').
-char.int_to_digit(2, '2').
-char.int_to_digit(3, '3').
-char.int_to_digit(4, '4').
-char.int_to_digit(5, '5').
-char.int_to_digit(6, '6').
-char.int_to_digit(7, '7').
-char.int_to_digit(8, '8').
-char.int_to_digit(9, '9').
-char.int_to_digit(10, 'A').
-char.int_to_digit(11, 'B').
-char.int_to_digit(12, 'C').
-char.int_to_digit(13, 'D').
-char.int_to_digit(14, 'E').
-char.int_to_digit(15, 'F').
-char.int_to_digit(16, 'G').
-char.int_to_digit(17, 'H').
-char.int_to_digit(18, 'I').
-char.int_to_digit(19, 'J').
-char.int_to_digit(20, 'K').
-char.int_to_digit(21, 'L').
-char.int_to_digit(22, 'M').
-char.int_to_digit(23, 'N').
-char.int_to_digit(24, 'O').
-char.int_to_digit(25, 'P').
-char.int_to_digit(26, 'Q').
-char.int_to_digit(27, 'R').
-char.int_to_digit(28, 'S').
-char.int_to_digit(29, 'T').
-char.int_to_digit(30, 'U').
-char.int_to_digit(31, 'V').
-char.int_to_digit(32, 'W').
-char.int_to_digit(33, 'X').
-char.int_to_digit(34, 'Y').
-char.int_to_digit(35, 'Z').
+:- pred int_to_extended_digit(int, char).
+:- mode int_to_extended_digit(in, out) is semidet.
+:- mode int_to_extended_digit(out, in) is semidet.
+
+int_to_extended_digit(0, '0').
+int_to_extended_digit(1, '1').
+int_to_extended_digit(2, '2').
+int_to_extended_digit(3, '3').
+int_to_extended_digit(4, '4').
+int_to_extended_digit(5, '5').
+int_to_extended_digit(6, '6').
+int_to_extended_digit(7, '7').
+int_to_extended_digit(8, '8').
+int_to_extended_digit(9, '9').
+int_to_extended_digit(10, 'A').
+int_to_extended_digit(11, 'B').
+int_to_extended_digit(12, 'C').
+int_to_extended_digit(13, 'D').
+int_to_extended_digit(14, 'E').
+int_to_extended_digit(15, 'F').
+int_to_extended_digit(16, 'G').
+int_to_extended_digit(17, 'H').
+int_to_extended_digit(18, 'I').
+int_to_extended_digit(19, 'J').
+int_to_extended_digit(20, 'K').
+int_to_extended_digit(21, 'L').
+int_to_extended_digit(22, 'M').
+int_to_extended_digit(23, 'N').
+int_to_extended_digit(24, 'O').
+int_to_extended_digit(25, 'P').
+int_to_extended_digit(26, 'Q').
+int_to_extended_digit(27, 'R').
+int_to_extended_digit(28, 'S').
+int_to_extended_digit(29, 'T').
+int_to_extended_digit(30, 'U').
+int_to_extended_digit(31, 'V').
+int_to_extended_digit(32, 'W').
+int_to_extended_digit(33, 'X').
+int_to_extended_digit(34, 'Y').
+int_to_extended_digit(35, 'Z').
+
+int_to_digit(Int, Digit) :-
+ int_to_extended_digit(Int, Digit).
char.digit_to_int(Digit, Int) :-
( char.lower_upper(Digit, Upper) ->
- char.int_to_digit(Int, Upper)
- ;
- char.int_to_digit(Int, Digit)
- ).
-
-char.det_digit_to_int(Digit) = Int :-
- ( digit_to_int(Digit, Int0) ->
- Int = Int0
+ int_to_extended_digit(Int, Upper)
;
- error("char.digit_to_int failed")
+ int_to_extended_digit(Int, Digit)
).
%-----------------------------------------------------------------------------%
-char.lower_upper('a', 'A').
-char.lower_upper('b', 'B').
-char.lower_upper('c', 'C').
-char.lower_upper('d', 'D').
-char.lower_upper('e', 'E').
-char.lower_upper('f', 'F').
-char.lower_upper('g', 'G').
-char.lower_upper('h', 'H').
-char.lower_upper('i', 'I').
-char.lower_upper('j', 'J').
-char.lower_upper('k', 'K').
-char.lower_upper('l', 'L').
-char.lower_upper('m', 'M').
-char.lower_upper('n', 'N').
-char.lower_upper('o', 'O').
-char.lower_upper('p', 'P').
-char.lower_upper('q', 'Q').
-char.lower_upper('r', 'R').
-char.lower_upper('s', 'S').
-char.lower_upper('t', 'T').
-char.lower_upper('u', 'U').
-char.lower_upper('v', 'V').
-char.lower_upper('w', 'W').
-char.lower_upper('x', 'X').
-char.lower_upper('y', 'Y').
-char.lower_upper('z', 'Z').
+lower_upper('a', 'A').
+lower_upper('b', 'B').
+lower_upper('c', 'C').
+lower_upper('d', 'D').
+lower_upper('e', 'E').
+lower_upper('f', 'F').
+lower_upper('g', 'G').
+lower_upper('h', 'H').
+lower_upper('i', 'I').
+lower_upper('j', 'J').
+lower_upper('k', 'K').
+lower_upper('l', 'L').
+lower_upper('m', 'M').
+lower_upper('n', 'N').
+lower_upper('o', 'O').
+lower_upper('p', 'P').
+lower_upper('q', 'Q').
+lower_upper('r', 'R').
+lower_upper('s', 'S').
+lower_upper('t', 'T').
+lower_upper('u', 'U').
+lower_upper('v', 'V').
+lower_upper('w', 'W').
+lower_upper('x', 'X').
+lower_upper('y', 'Y').
+lower_upper('z', 'Z').
%-----------------------------------------------------------------------------%
@@ -566,7 +811,7 @@ char.to_int(C) = N :-
:- pragma foreign_proc("C",
char.to_int(Character::out, Int::in),
- [will_not_call_mercury, promise_pure, thread_safe,
+ [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness],
"
Character = Int;
@@ -653,7 +898,7 @@ char.max_char_value = N :-
:- pragma foreign_proc("C",
char.max_char_value(Max::out),
- [will_not_call_mercury, promise_pure, thread_safe,
+ [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
does_not_affect_liveness],
"
Max = 0x10ffff;
diff --git a/library/integer.m b/library/integer.m
index c9a6a0a..a277401 100644
--- a/library/integer.m
+++ b/library/integer.m
@@ -608,7 +608,7 @@ int_to_integer(D) = Int :-
Int = i(-1, [D])
;
( int.min_int(D) ->
- % were we to call int.abs, int overflow might occur.
+ % Were we to call int.abs, int overflow might occur.
Int = integer(D + 1) - integer.one
;
Int = big_sign(D, pos_int_to_digits(int.abs(D)))
@@ -1263,9 +1263,8 @@ printbase_pos_mul_list([X|Xs], Carry, Y) =
%-----------------------------------------------------------------------------%
-integer.from_base_string(Base0, String) = Integer :-
+integer.from_base_string(Base, String) = Integer :-
string.index(String, 0, Char),
- Base = integer(Base0),
Len = string.length(String),
( Char = ('-') ->
Len > 1,
@@ -1283,14 +1282,13 @@ integer.from_base_string(Base0, String) = Integer :-
Integer = N
).
-:- pred accumulate_integer(integer::in, char::in, integer::in, integer::out)
+:- pred accumulate_integer(int::in, char::in, integer::in, integer::out)
is semidet.
accumulate_integer(Base, Char, !N) :-
- char.digit_to_int(Char, Digit0),
+ char.base_digit_to_int(Base, Char, Digit0),
Digit = integer(Digit0),
- Digit < Base,
- !:N = (Base * !.N) + Digit.
+ !:N = (integer(Base) * !.N) + Digit.
integer.det_from_base_string(Base, String) = Integer :-
( Integer0 = integer.from_base_string(Base, String) ->
diff --git a/library/parsing_utils.m b/library/parsing_utils.m
index d1135ed..5ead0e2 100644
--- a/library/parsing_utils.m
+++ b/library/parsing_utils.m
@@ -883,8 +883,7 @@ int_literal(Src, Int, !PS) :-
digits(Base, Src, unit, !PS) :-
next_char(Src, C, !PS),
- char.digit_to_int(C, D),
- D < Base,
+ char.is_base_digit(Base, C),
digits_2(Base, Src, _, !PS).
:- pred digits_2(int::in, src::in, unit::out, ps::in, ps::out) is semidet.
@@ -892,11 +891,10 @@ digits(Base, Src, unit, !PS) :-
digits_2(Base, Src, unit, !PS) :-
( if
next_char(Src, C, !PS),
- char.digit_to_int(C, D),
- D < Base
- then
+ char.is_base_digit(Base, C)
+ then
digits_2(Base, Src, _, !PS)
- else
+ else
true
).
diff --git a/library/string.m b/library/string.m
index 03354cc..89442bd 100644
--- a/library/string.m
+++ b/library/string.m
@@ -1193,8 +1193,7 @@ string.base_string_to_int(Base, String, Int) :-
:- pred accumulate_int(int::in, char::in, int::in, int::out) is semidet.
accumulate_int(Base, Char, N0, N) :-
- char.digit_to_int(Char, M),
- M < Base,
+ char.base_digit_to_int(Base, Char, M),
N = (Base * N0) + M,
( N0 =< N ; Base \= 10 ). % Fail on overflow for base 10 numbers.
@@ -1202,8 +1201,7 @@ accumulate_int(Base, Char, N0, N) :-
int::in, int::out) is semidet.
accumulate_negative_int(Base, Char, N0, N) :-
- char.digit_to_int(Char, M),
- M < Base,
+ char.base_digit_to_int(Base, Char, M),
N = (Base * N0) - M,
( N =< N0 ; Base \= 10 ). % Fail on underflow for base 10 numbers.
@@ -1518,12 +1516,12 @@ string.int_to_base_string_1(N, Base, Str) :-
string.int_to_base_string_2(NegN, Base, !RevChars) :-
( NegN > -Base ->
N = -NegN,
- char.det_int_to_digit(N, DigitChar),
+ DigitChar = char.det_base_int_to_digit(Base, N),
!:RevChars = [DigitChar | !.RevChars]
;
NegN1 = NegN // Base,
N10 = (NegN1 * Base) - NegN,
- char.det_int_to_digit(N10, DigitChar),
+ DigitChar = char.det_base_int_to_digit(Base, N10),
string.int_to_base_string_2(NegN1, Base, !RevChars),
!:RevChars = [DigitChar | !.RevChars]
).
@@ -1609,12 +1607,12 @@ string.int_to_base_string_group_2(NegN, Base, Curr, Period, Sep, Str) :-
;
( NegN > -Base ->
N = -NegN,
- char.det_int_to_digit(N, DigitChar),
+ DigitChar = char.det_base_int_to_digit(Base, N),
string.char_to_string(DigitChar, Str)
;
NegN1 = NegN // Base,
N10 = (NegN1 * Base) - NegN,
- char.det_int_to_digit(N10, DigitChar),
+ DigitChar = char.det_base_int_to_digit(Base, N10),
string.char_to_string(DigitChar, DigitString),
string.int_to_base_string_group_2(NegN1, Base, Curr + 1, Period,
Sep, Str1),
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 3263b70..687fb47 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -306,6 +306,7 @@ ORDINARY_PROGS= \
term_io_test \
term_to_univ_test \
test234_sorted_insert \
+ test_char_digits \
test_cord \
test_cord2 \
test_imported_no_tag \
diff --git a/tests/hard_coded/test_char_digits.exp b/tests/hard_coded/test_char_digits.exp
new file mode 100644
index 0000000..32fa5e2
--- /dev/null
+++ b/tests/hard_coded/test_char_digits.exp
@@ -0,0 +1,2574 @@
+==== Testing binary digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': no : * : *
+'3': no : * : *
+'4': no : * : *
+'5': no : * : *
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing octal digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing decimal digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== testing hex digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-2 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': no : * : *
+'3': no : * : *
+'4': no : * : *
+'5': no : * : *
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-3 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': no : * : *
+'4': no : * : *
+'5': no : * : *
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-4 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': no : * : *
+'5': no : * : *
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-5 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': no : * : *
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-6 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': no : * : *
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-7 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': no : * : *
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-8 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': no : * : *
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-9 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': no : * : *
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-10 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': no : * : *
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': no : * : *
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-11 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': no : * : *
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': no : * : *
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-12 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': no : * : *
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': no : * : *
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-13 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': no : * : *
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': no : * : *
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-14 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': no : * : *
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': no : * : *
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-15 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': no : * : *
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': no : * : *
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-16 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': no : * : *
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': no : * : *
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-17 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': no : * : *
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': no : * : *
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-18 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': no : * : *
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': no : * : *
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-19 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': no : * : *
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': no : * : *
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-20 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': no : * : *
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': no : * : *
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-21 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': no : * : *
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': no : * : *
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-22 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': no : * : *
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': no : * : *
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-23 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': no : * : *
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': no : * : *
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-24 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': no : * : *
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': no : * : *
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-25 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': no : * : *
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': no : * : *
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-26 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': no : * : *
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': no : * : *
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-27 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': no : * : *
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': no : * : *
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-28 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': no : * : *
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': no : * : *
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-29 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': no : * : *
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': no : * : *
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-30 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': no : * : *
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': no : * : *
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-31 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': no : * : *
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': no : * : *
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-32 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': yes : 31 : 'V'
+'w': no : * : *
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': yes : 31 : 'V'
+'W': no : * : *
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-33 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': yes : 31 : 'V'
+'w': yes : 32 : 'W'
+'x': no : * : *
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': yes : 31 : 'V'
+'W': yes : 32 : 'W'
+'X': no : * : *
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-34 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': yes : 31 : 'V'
+'w': yes : 32 : 'W'
+'x': yes : 33 : 'X'
+'y': no : * : *
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': yes : 31 : 'V'
+'W': yes : 32 : 'W'
+'X': yes : 33 : 'X'
+'Y': no : * : *
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-35 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': yes : 31 : 'V'
+'w': yes : 32 : 'W'
+'x': yes : 33 : 'X'
+'y': yes : 34 : 'Y'
+'z': no : * : *
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': yes : 31 : 'V'
+'W': yes : 32 : 'W'
+'X': yes : 33 : 'X'
+'Y': yes : 34 : 'Y'
+'Z': no : * : *
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
+==== Testing base-36 digits ====
+'0': yes : 0 : '0'
+'1': yes : 1 : '1'
+'2': yes : 2 : '2'
+'3': yes : 3 : '3'
+'4': yes : 4 : '4'
+'5': yes : 5 : '5'
+'6': yes : 6 : '6'
+'7': yes : 7 : '7'
+'8': yes : 8 : '8'
+'9': yes : 9 : '9'
+'a': yes : 10 : 'A'
+'b': yes : 11 : 'B'
+'c': yes : 12 : 'C'
+'d': yes : 13 : 'D'
+'e': yes : 14 : 'E'
+'f': yes : 15 : 'F'
+'g': yes : 16 : 'G'
+'h': yes : 17 : 'H'
+'i': yes : 18 : 'I'
+'j': yes : 19 : 'J'
+'k': yes : 20 : 'K'
+'l': yes : 21 : 'L'
+'m': yes : 22 : 'M'
+'n': yes : 23 : 'N'
+'o': yes : 24 : 'O'
+'p': yes : 25 : 'P'
+'q': yes : 26 : 'Q'
+'r': yes : 27 : 'R'
+'s': yes : 28 : 'S'
+'t': yes : 29 : 'T'
+'u': yes : 30 : 'U'
+'v': yes : 31 : 'V'
+'w': yes : 32 : 'W'
+'x': yes : 33 : 'X'
+'y': yes : 34 : 'Y'
+'z': yes : 35 : 'Z'
+'A': yes : 10 : 'A'
+'B': yes : 11 : 'B'
+'C': yes : 12 : 'C'
+'D': yes : 13 : 'D'
+'E': yes : 14 : 'E'
+'F': yes : 15 : 'F'
+'G': yes : 16 : 'G'
+'H': yes : 17 : 'H'
+'I': yes : 18 : 'I'
+'J': yes : 19 : 'J'
+'K': yes : 20 : 'K'
+'L': yes : 21 : 'L'
+'M': yes : 22 : 'M'
+'N': yes : 23 : 'N'
+'O': yes : 24 : 'O'
+'P': yes : 25 : 'P'
+'Q': yes : 26 : 'Q'
+'R': yes : 27 : 'R'
+'S': yes : 28 : 'S'
+'T': yes : 29 : 'T'
+'U': yes : 30 : 'U'
+'V': yes : 31 : 'V'
+'W': yes : 32 : 'W'
+'X': yes : 33 : 'X'
+'Y': yes : 34 : 'Y'
+'Z': yes : 35 : 'Z'
+'!': no : * : *
+'@': no : * : *
+'?': no : * : *
diff --git a/tests/hard_coded/test_char_digits.m b/tests/hard_coded/test_char_digits.m
new file mode 100644
index 0000000..26bd592
--- /dev/null
+++ b/tests/hard_coded/test_char_digits.m
@@ -0,0 +1,90 @@
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
+%-----------------------------------------------------------------------------%
+%
+% Test the predicates for classifying various kinds of digits as well
+% as conversion from digits to integers and back again.
+%
+%-----------------------------------------------------------------------------%
+
+:- module test_char_digits.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module string.
+
+main(!IO) :-
+ do_test("Testing binary digits", is_binary_digit, binary_digit_to_int,
+ int_to_binary_digit, !IO),
+ do_test("Testing octal digits", is_octal_digit, octal_digit_to_int,
+ int_to_octal_digit, !IO),
+ do_test("Testing decimal digits", is_decimal_digit, decimal_digit_to_int,
+ int_to_decimal_digit, !IO),
+ do_test("testing hex digits", is_hex_digit, hex_digit_to_int,
+ int_to_hex_digit, !IO),
+ int.fold_up(test_base, 2, 36, !IO).
+
+:- pred do_test(
+ string::in,
+ pred(char)::in(pred(in) is semidet),
+ pred(char, int)::in(pred(in, out) is semidet),
+ pred(int, char)::in(pred(in, out) is semidet),
+ io::di, io::uo) is det.
+
+do_test(Header, IsDigit, ToInt, FromInt, !IO) :-
+ io.format("==== %s ====\n", [s(Header)], !IO),
+ list.foldl(do_test_char(IsDigit, ToInt, FromInt), digits, !IO).
+
+:- pred do_test_char(
+ pred(char)::in(pred(in) is semidet),
+ pred(char, int)::in(pred(in, out) is semidet),
+ pred(int, char)::in(pred(in, out) is semidet),
+ char::in, io::di, io::uo) is det.
+
+do_test_char(IsDigit, ToInt, FromInt, Char, !IO) :-
+ io.format("'%c': ", [c(Char)], !IO),
+ ( if IsDigit(Char)
+ then io.write_string("yes : ", !IO)
+ else io.write_string("no : ", !IO)
+ ),
+ ( if ToInt(Char, Int) then
+ io.format("%d : ", [i(Int)], !IO),
+ ( if FromInt(Int, CharPrime) then
+ io.format("'%c'", [c(CharPrime)], !IO)
+ else
+ io.write_string("*", !IO)
+ )
+ else
+ io.write_string("* : *", !IO)
+ ),
+ io.nl(!IO).
+
+:- pred test_base(int::in, io::di, io::uo) is det.
+
+test_base(Base, !IO) :-
+ string.format("Testing base-%d digits", [i(Base)], Header),
+ do_test(Header, is_base_digit(Base), base_digit_to_int(Base),
+ base_int_to_digit(Base), !IO).
+
+:- func digits = list(char).
+
+digits = [
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', '!', '@', '?'].
+
+%-----------------------------------------------------------------------------%
+:- end_module test_char_digits.
+%-----------------------------------------------------------------------------%
More information about the reviews
mailing list