[m-rev.] for review: Add integer.to_base_string.

Peter Wang novalazy at gmail.com
Mon Feb 16 17:18:57 AEDT 2015


Add integer.to_base_string.

library/integer.m:
	Add to_base_string/2 and to_base_string/3.

tests/general/base_string_to_integer.exp:
tests/general/base_string_to_integer.m:
	Extend test case.

NEWS:
	Announce additions.

diff --git a/NEWS b/NEWS
index bfa7323..d9c8516 100644
--- a/NEWS
+++ b/NEWS
@@ -106,6 +106,8 @@ Changes to the Mercury standard library:
    - from_base_string/3
    - to_int/2
    - det_to_int/1
+   - to_base_string/2
+   - to_base_string/3
 
   The following functions in the integer module have been deprecated:
 
diff --git a/library/integer.m b/library/integer.m
index 49d97f7..ef69f01 100644
--- a/library/integer.m
+++ b/library/integer.m
@@ -50,6 +50,16 @@
     %
 :- func to_string(integer) = string.
 
+    % to_base_string(Integer, Base, String):
+    %
+    % Convert an integer to a string in a given Base.
+    %
+    % Base must be between 2 and 36, both inclusive; if it isn't,
+    % the predicate will throw an exception.
+    %
+:- func to_base_string(integer, int) = string.
+:- pred to_base_string(integer::in, int::in, string::out) is det.
+
     % Convert a string to an integer. The string must contain only digits
     % [0-9], optionally preceded by a plus or minus sign. If the string does
     % not match this syntax then the predicate fails.
@@ -1237,158 +1247,179 @@ string_to_integer_acc([C | Cs], Acc) = Result :-
 % Converting integers to strings.
 %
 
-integer.to_string(i(Sign, Digits)) = SignStr ++ digits_to_string(AbsDigits) :-
+:- type print_info
+    --->    print_info(
+                output_base :: int,
+                print_base  :: int  % output_base^printbase_exponent
+            ).
+
+to_string(Integer) = to_base_string(Integer, 10).
+
+to_base_string(Integer, Base) = String :-
+    to_base_string(Integer, Base, String).
+
+to_base_string(Integer, Base, String) :-
+    ( 2 =< Base, Base =< 36 ->
+        true
+    ;
+        error("integer.to_base_string: invalid base")
+    ),
+    Info = print_info(Base, pow(Base, printbase_exponent)),
+    Integer = i(Sign, Digits),
     ( Sign < 0 ->
-        SignStr = "-",
-        neg_list(Digits, AbsDigits)
+        neg_list(Digits, AbsDigits),
+        String = "-" ++ digits_to_string(Info, AbsDigits)
     ;
-        SignStr = "",
-        Digits = AbsDigits
+        String = digits_to_string(Info, Digits)
     ).
 
-:- func digits_to_string(list(digit)) = string.
+:- func digits_to_string(print_info, list(digit)) = string.
 
-digits_to_string([]) = "0".
-digits_to_string(Digits) = Str :-
+digits_to_string(_Info, []) = "0".
+digits_to_string(Info, Digits) = Str :-
     Digits = [_ | _],
-    printbase_rep(printbase_pos_int_to_digits(base),
+    printbase_rep(Info, printbase_pos_int_to_digits(Info, base),
         Digits, i(_, DigitsInPrintBase)),
     (
         DigitsInPrintBase = [Head | Tail],
-        string.int_to_string(Head, SHead),
-        digits_to_strings(Tail, Ss, []),
+        Info = print_info(OutputBase, _),
+        string.int_to_base_string(Head, OutputBase, SHead),
+        digits_to_strings(Info, Tail, Ss, []),
         string.append_list([SHead | Ss], Str)
     ;
         DigitsInPrintBase = [],
         unexpected($module, $pred, "empty list")
     ).
 
-:- pred digits_to_strings(list(digit)::in, list(string)::out,
+:- pred digits_to_strings(print_info::in, list(digit)::in, list(string)::out,
     list(string)::in) is det.
 
-digits_to_strings([]) --> [].
-digits_to_strings([H | T]) -->
-    { digit_to_string(H, S) },
+digits_to_strings(_Info, []) --> [].
+digits_to_strings(Info, [H | T]) -->
+    { digit_to_string(Info, H, S) },
     [ S ],
-    digits_to_strings(T).
+    digits_to_strings(Info, T).
 
-:- pred printbase_rep(integer::in, list(digit)::in, integer::out) is det.
+:- pred printbase_rep(print_info::in, integer::in, list(digit)::in,
+    integer::out) is det.
 
-printbase_rep(Base, Digits, printbase_rep_1(Digits, Base, integer.zero)).
+printbase_rep(Info, Base, Digits, Result) :-
+    Result = printbase_rep_1(Info, Digits, Base, integer.zero).
 
-:- func printbase_rep_1(list(digit), integer, integer) = integer.
+:- func printbase_rep_1(print_info, list(digit), integer, integer) = integer.
 
-printbase_rep_1([], _Base, Carry) = Carry.
-printbase_rep_1([X|Xs], Base, Carry) =
-    printbase_rep_1(Xs, Base,
-        printbase_pos_plus(printbase_pos_mul(Base, Carry),
-        printbase_pos_int_to_digits(X))).
+printbase_rep_1(_Info, [], _Base, Carry) = Carry.
+printbase_rep_1(Info, [X | Xs], Base, Carry) =
+    printbase_rep_1(Info, Xs, Base,
+        printbase_pos_plus(Info,
+            printbase_pos_mul(Info, Base, Carry),
+            printbase_pos_int_to_digits(Info, X))).
 
-:- pred digit_to_string(digit::in, string::out) is det.
+:- pred digit_to_string(print_info::in, digit::in, string::out) is det.
 
-digit_to_string(D, S) :-
-    string.int_to_string(D, S1),
-    string.pad_left(S1, '0', log10printbase, S).
+digit_to_string(Info, D, S) :-
+    Info = print_info(OutputBase, _),
+    string.int_to_base_string(D, OutputBase, S1),
+    string.pad_left(S1, '0', printbase_exponent, S).
 
 %---------------------------------------------------------------------------%
 %
 % Essentially duplicated code to work in base `printbase' follows
 %
 
-:- func printbase = int.
-
-printbase = 10000.
-
-:- func log10printbase = int.
+:- func printbase_exponent = int.
 
-log10printbase = 4.
+printbase_exponent = 4.
 
-:- func printbase_pos_int_to_digits(int) = integer.
+:- func printbase_pos_int_to_digits(print_info, int) = integer.
 
-printbase_pos_int_to_digits(D) =
-    printbase_pos_int_to_digits_2(D, integer.zero).
+printbase_pos_int_to_digits(Info, D) =
+    printbase_pos_int_to_digits_2(Info, D, integer.zero).
 
-:- func printbase_pos_int_to_digits_2(int, integer) = integer.
+:- func printbase_pos_int_to_digits_2(print_info, int, integer) = integer.
 
-printbase_pos_int_to_digits_2(D, Tail) = Result :-
+printbase_pos_int_to_digits_2(Info, D, Tail) = Result :-
     ( D = 0 ->
         Result = Tail
     ;
         Tail = i(Length, Digits),
-        printbase_chop(D, Div, Mod),
-        Result = printbase_pos_int_to_digits_2(Div,
+        printbase_chop(Info, D, Div, Mod),
+        Result = printbase_pos_int_to_digits_2(Info, Div,
             i(Length + 1, [Mod | Digits]))
     ).
 
-:- pred printbase_chop(int::in, digit::out, digit::out) is det.
+:- pred printbase_chop(print_info::in, int::in, digit::out, digit::out) is det.
 
-printbase_chop(N, Div, Mod) :-
-    Mod = N mod printbase,
-    Div = N div printbase.
+printbase_chop(Info, N, Div, Mod) :-
+    Info = print_info(_, Base),
+    Mod = N mod Base,
+    Div = N div Base.
 
-:- func printbase_mul_by_digit(digit, integer) = integer.
+:- func printbase_mul_by_digit(print_info, digit, integer) = integer.
 
-printbase_mul_by_digit(D, i(Len, Ds)) = Out :-
-    printbase_mul_by_digit_2(D, Div, Ds, DsOut),
+printbase_mul_by_digit(Info, D, i(Len, Ds)) = Out :-
+    printbase_mul_by_digit_2(Info, D, Div, Ds, DsOut),
     Out = ( Div = 0 -> i(Len, DsOut) ; i(Len + 1, [Div | DsOut]) ).
 
-:- pred printbase_mul_by_digit_2(digit::in, digit::out,
+:- pred printbase_mul_by_digit_2(print_info::in, digit::in, digit::out,
     list(digit)::in, list(digit)::out) is det.
 
-printbase_mul_by_digit_2(_, 0, [], []).
-printbase_mul_by_digit_2(D, Div, [X | Xs], [Mod | NewXs]) :-
-    printbase_mul_by_digit_2(D, DivXs, Xs, NewXs),
-    printbase_chop(D * X + DivXs, Div, Mod).
+printbase_mul_by_digit_2(_Info, _, 0, [], []).
+printbase_mul_by_digit_2(Info, D, Div, [X | Xs], [Mod | NewXs]) :-
+    printbase_mul_by_digit_2(Info, D, DivXs, Xs, NewXs),
+    printbase_chop(Info, D * X + DivXs, Div, Mod).
 
-:- func printbase_pos_plus(integer, integer) = integer.
+:- func printbase_pos_plus(print_info, integer, integer) = integer.
 
-printbase_pos_plus(i(L1, D1), i(L2, D2)) = Out :-
-    printbase_add_pairs(Div, i(L1, D1), i(L2, D2), Ds),
+printbase_pos_plus(Info, i(L1, D1), i(L2, D2)) = Out :-
+    printbase_add_pairs(Info, Div, i(L1, D1), i(L2, D2), Ds),
     Len = ( L1 > L2 -> L1 ; L2 ),
     Out = ( Div = 0 -> i(Len, Ds) ; i(Len + 1, [Div | Ds]) ).
 
-:- pred printbase_add_pairs(digit::out, integer::in, integer::in,
-    list(digit)::out) is det.
+:- pred printbase_add_pairs(print_info::in, digit::out, integer::in,
+    integer::in, list(digit)::out) is det.
 
-printbase_add_pairs(Div, i(L1, D1), i(L2, D2), Ds) :-
+printbase_add_pairs(Info, Div, i(L1, D1), i(L2, D2), Ds) :-
     ( L1 = L2 ->
-        printbase_add_pairs_equal(Div, D1, D2, Ds)
+        printbase_add_pairs_equal(Info, Div, D1, D2, Ds)
     ; L1 < L2, D2 = [H2 | T2] ->
-        printbase_add_pairs(Div1, i(L1, D1), i(L2 - 1, T2), Ds1),
-        printbase_chop(H2 + Div1, Div, Mod),
+        printbase_add_pairs(Info, Div1, i(L1, D1), i(L2 - 1, T2), Ds1),
+        printbase_chop(Info, H2 + Div1, Div, Mod),
         Ds = [Mod | Ds1]
     ; L1 > L2, D1 = [H1 | T1] ->
-        printbase_add_pairs(Div1, i(L1 - 1, T1), i(L2, D2), Ds1),
-        printbase_chop(H1 + Div1, Div, Mod),
+        printbase_add_pairs(Info, Div1, i(L1 - 1, T1), i(L2, D2), Ds1),
+        printbase_chop(Info, H1 + Div1, Div, Mod),
         Ds = [Mod | Ds1]
     ;
         unexpected($module, $pred, "integer.printbase_add_pairs")
     ).
 
-:- pred printbase_add_pairs_equal(digit::out, list(digit)::in, list(digit)::in,
-    list(digit)::out) is det.
+:- pred printbase_add_pairs_equal(print_info::in, digit::out, list(digit)::in,
+    list(digit)::in, list(digit)::out) is det.
 
-printbase_add_pairs_equal(0, [], _, []).
-printbase_add_pairs_equal(0, [_ | _], [], []).
-printbase_add_pairs_equal(Div, [X | Xs], [Y | Ys], [Mod | TailDs]) :-
-    printbase_add_pairs_equal(DivTail, Xs, Ys, TailDs),
-    printbase_chop(X + Y + DivTail, Div, Mod).
+printbase_add_pairs_equal(_, 0, [], _, []).
+printbase_add_pairs_equal(_, 0, [_ | _], [], []).
+printbase_add_pairs_equal(Info, Div, [X | Xs], [Y | Ys], [Mod | TailDs]) :-
+    printbase_add_pairs_equal(Info, DivTail, Xs, Ys, TailDs),
+    printbase_chop(Info, X + Y + DivTail, Div, Mod).
 
-:- func printbase_pos_mul(integer, integer) = integer.
+:- func printbase_pos_mul(print_info, integer, integer) = integer.
 
-printbase_pos_mul(i(L1, Ds1), i(L2, Ds2)) =
+printbase_pos_mul(Info, i(L1, Ds1), i(L2, Ds2)) =
     ( L1 < L2 ->
-        printbase_pos_mul_list(Ds1, integer.zero, i(L2, Ds2))
+        printbase_pos_mul_list(Info, Ds1, integer.zero, i(L2, Ds2))
     ;
-        printbase_pos_mul_list(Ds2, integer.zero, i(L1, Ds1))
+        printbase_pos_mul_list(Info, Ds2, integer.zero, i(L1, Ds1))
     ).
 
-:- func printbase_pos_mul_list(list(digit), integer, integer) = integer.
+:- func printbase_pos_mul_list(print_info, list(digit), integer, integer)
+    = integer.
 
-printbase_pos_mul_list([], Carry, _Y) = Carry.
-printbase_pos_mul_list([X|Xs], Carry, Y) =
-    printbase_pos_mul_list(Xs, printbase_pos_plus(mul_base(Carry),
-        printbase_mul_by_digit(X, Y)), Y).
+printbase_pos_mul_list(_Info, [], Carry, _Y) = Carry.
+printbase_pos_mul_list(Info, [X | Xs], Carry, Y) =
+    printbase_pos_mul_list(Info, Xs,
+        printbase_pos_plus(Info, mul_base(Carry),
+            printbase_mul_by_digit(Info, X, Y)), Y).
 
 %---------------------------------------------------------------------------%
 
diff --git a/tests/general/base_string_to_integer.exp b/tests/general/base_string_to_integer.exp
index 0e2e9af..7f2531f 100644
--- a/tests/general/base_string_to_integer.exp
+++ b/tests/general/base_string_to_integer.exp
@@ -1,43 +1,171 @@
 0 (base 2) = 0
+to_base_string produces "0"
+
 1 (base 2) = 1
+to_base_string produces "1"
+
 01 (base 2) = 1
+to_base_string produces "1"
+
 -000001 (base 2) = -1
+to_base_string produces "-1"
+
 11 (base 2) = 3
+to_base_string produces "11"
+
 111 (base 2) = 7
+to_base_string produces "111"
+
 11111 (base 2) = 31
+to_base_string produces "11111"
+
 101010 (base 2) = 42
+to_base_string produces "101010"
+
 10000000000000000000000000000000 (base 2) = 2147483648
+to_base_string produces "10000000000000000000000000000000"
+
 1000000000000000000000000000000000000000000000000000000000000000 (base 2) = 9223372036854775808
+to_base_string produces "1000000000000000000000000000000000000000000000000000000000000000"
+
 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 (base 2) = 170141183460469231731687303715884105728
+to_base_string produces "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+
 0 (base 8) = 0
+to_base_string produces "0"
+
 1 (base 8) = 1
+to_base_string produces "1"
+
 -1 (base 8) = -1
+to_base_string produces "-1"
+
 10 (base 8) = 8
+to_base_string produces "10"
+
 -10 (base 8) = -8
+to_base_string produces "-10"
+
 76543210 (base 8) = 16434824
+to_base_string produces "76543210"
+
 -76543210 (base 8) = -16434824
+to_base_string produces "-76543210"
+
 7777777777777777777777777 (base 8) = 37778931862957161709567
+to_base_string produces "7777777777777777777777777"
+
 0 (base 10) = 0
+to_base_string produces "0"
+
 1 (base 10) = 1
+to_base_string produces "1"
+
 10 (base 10) = 10
+to_base_string produces "10"
+
 11 (base 10) = 11
+to_base_string produces "11"
+
 1234567890 (base 10) = 1234567890
+to_base_string produces "1234567890"
+
 -1 (base 10) = -1
+to_base_string produces "-1"
+
 -10 (base 10) = -10
+to_base_string produces "-10"
+
 -1234567890 (base 10) = -1234567890
+to_base_string produces "-1234567890"
+
 1234567891234567891234567890 (base 10) = 1234567891234567891234567890
+to_base_string produces "1234567891234567891234567890"
+
 -1234567891234567891234567890 (base 10) = -1234567891234567891234567890
+to_base_string produces "-1234567891234567891234567890"
+
 0 (base 16) = 0
+to_base_string produces "0"
+
 1 (base 16) = 1
+to_base_string produces "1"
+
 -1 (base 16) = -1
+to_base_string produces "-1"
+
 10 (base 16) = 16
+to_base_string produces "10"
+
 A (base 16) = 10
+to_base_string produces "A"
+
 -A (base 16) = -10
+to_base_string produces "-A"
+
 a (base 16) = 10
+to_base_string produces "A"
+
 -a (base 16) = -10
+to_base_string produces "-A"
+
 F (base 16) = 15
+to_base_string produces "F"
+
 -F (base 16) = -15
+to_base_string produces "-F"
+
 fedcba0987654321 (base 16) = 18364757930599072545
+to_base_string produces "FEDCBA0987654321"
+
 -fedcba0987654321 (base 16) = -18364757930599072545
+to_base_string produces "-FEDCBA0987654321"
+
 fffffffffffffffffffffffffffffffffff (base 16) = 1393796574908163946345982392040522594123775
+to_base_string produces "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+
 -fffffffffffffffffffffffffffffffffff (base 16) = -1393796574908163946345982392040522594123775
+to_base_string produces "-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+
+0 (base 36) = 0
+to_base_string produces "0"
+
+1 (base 36) = 1
+to_base_string produces "1"
+
+-1 (base 36) = -1
+to_base_string produces "-1"
+
+10 (base 36) = 36
+to_base_string produces "10"
+
+A (base 36) = 10
+to_base_string produces "A"
+
+-A (base 36) = -10
+to_base_string produces "-A"
+
+a (base 36) = 10
+to_base_string produces "A"
+
+-a (base 36) = -10
+to_base_string produces "-A"
+
+Z (base 36) = 35
+to_base_string produces "Z"
+
+-Z (base 36) = -35
+to_base_string produces "-Z"
+
+zyxwvutsrqponmlkjihgfedcba0987654321 (base 36) = 106300512100105327644605138221229898724868848283284886905
+to_base_string produces "ZYXWVUTSRQPONMLKJIHGFEDCBA0987654321"
+
+-zyxwvutsrqponmlkjihgfedcba0987654321 (base 36) = -106300512100105327644605138221229898724868848283284886905
+to_base_string produces "-ZYXWVUTSRQPONMLKJIHGFEDCBA0987654321"
+
+zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz (base 36) = 2955204414547681244658707659790455381671329323051646975
+to_base_string produces "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+
+-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz (base 36) = -2955204414547681244658707659790455381671329323051646975
+to_base_string produces "-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
+
diff --git a/tests/general/base_string_to_integer.m b/tests/general/base_string_to_integer.m
index ea0738a..742d784 100644
--- a/tests/general/base_string_to_integer.m
+++ b/tests/general/base_string_to_integer.m
@@ -73,6 +73,23 @@ main(!IO) :-
         "-fffffffffffffffffffffffffffffffffff"
     ],
     check_base_valid(16, Base16, !IO),
+    Base36 = [
+        "0",
+        "1",
+        "-1",
+        "10",
+        "A",
+        "-A",
+        "a",
+        "-a",
+        "Z",
+        "-Z",
+        "zyxwvutsrqponmlkjihgfedcba0987654321",
+        "-zyxwvutsrqponmlkjihgfedcba0987654321",
+        "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
+        "-zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
+    ],
+    check_base_valid(36, Base36, !IO),
     InvalidBase2 = ["3", "4", "5", "6", "a", "A", "z", "13"],
     check_base_invalid(2, InvalidBase2, !IO),
     InvalidBase10 = ["abc", "-123a", "ZZZ"],
@@ -81,18 +98,22 @@ main(!IO) :-
 :- pred check_base_valid(int::in, list(string)::in, io::di, io::uo) is det.
 
 check_base_valid(_, [], !IO).
-check_base_valid(Base, [ String | Strings ], !IO) :-
+check_base_valid(Base, [String | Strings], !IO) :-
     Num = integer.det_from_base_string(Base, String),
     NumStr = integer.to_string(Num),
-    io.format("%s (base %d) = %s\n", [s(String), i(Base), s(NumStr)],
-        !IO),
+    io.format("%s (base %d) = %s\n", [s(String), i(Base), s(NumStr)], !IO),
+
+    integer.to_base_string(Num, Base, StringB),
+    io.format("to_base_string produces \"%s\"\n", [s(StringB)], !IO),
+    io.nl(!IO),
+
     check_base_valid(Base, Strings, !IO).
 
 :- pred check_base_invalid(int::in, list(string)::in, io::di, io::uo) is det.
 
 check_base_invalid(_, [], !IO).
-check_base_invalid(Base, [ String | Strings ], !IO) :-
-    ( Num = integer.from_base_string(Base, String) ->
+check_base_invalid(Base, [String | Strings], !IO) :-
+    ( integer.from_base_string(Base, String, Num) ->
         NumStr = integer.to_string(Num),
         string.format("ERROR: %s (base %d) = %s\n",
             [s(String), i(Base), s(NumStr)], ErrorMsg),
-- 
2.1.2




More information about the reviews mailing list