[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