[m-rev.] diff: add integer.(det_)from_base_string/2

Julien Fischer juliensf at csse.unimelb.edu.au
Thu May 10 13:51:57 AEST 2007


The following additions were requested by Nick.

Estimated hours taken: 1.5
Branches: main

library/integer.m:
 	Add the functions integer.from_base_string/2 and
 	integer.det_from_base_string/2.

NEWS:
 	Announce the new functions.

tests/general/Mmakefile:
tests/general/.cvsignore:
tests/general/base_string_to_integer.{m,exp}:
 	Test the new functions.

Julien.

Index: NEWS
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/NEWS,v
retrieving revision 1.457
diff -u -r1.457 NEWS
--- NEWS	1 May 2007 01:13:09 -0000	1.457
+++ NEWS	10 May 2007 02:19:22 -0000
@@ -17,6 +17,10 @@

  Changes to the Mercury standard library:

+* The following functions have been added to the integer module:
+	integer.from_base_string/2
+	integer.det_from_base_string/2
+
  * Predicates and functions which create strings from lists of characters
    now fail, throw an exception or return an error value if a null character
    is found.  Unexpected null characters in strings are a potential source of
Index: library/integer.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/integer.m,v
retrieving revision 1.27
diff -u -r1.27 integer.m
--- library/integer.m	23 Oct 2006 00:32:57 -0000	1.27
+++ library/integer.m	10 May 2007 02:06:04 -0000
@@ -42,6 +42,18 @@

  :- func integer.det_from_string(string) = integer.

+    % Convert a string in the specified base (2-36) to an integer.
+    % The string must contain one or more digits in the specified base,
+    % optionally preceded by a plus or minus sign.  For bases > 10, digits
+    % 10 to 35 are represented by the letters A-z or a-z.  If the string
+    % does not match this syntax then the function fails.
+    %
+:- func integer.from_base_string(int, string) = integer is semidet.
+
+    % As above but throws an exception rather than failing.
+    %
+:- func integer.det_from_base_string(int, string) = integer.
+
  :- func '+'(integer) = integer.

  :- func '-'(integer) = integer.
@@ -1197,4 +1209,42 @@
          printbase_mul_by_digit(X, Y)), Y).

  %-----------------------------------------------------------------------------%
+
+integer.from_base_string(Base0, String) = Integer :-
+    string.index(String, 0, Char),
+    Base = integer(Base0),
+    Len = string.length(String),
+    ( Char = ('-') ->
+        Len > 1,
+        string.foldl_substring(accumulate_integer(Base), String, 1, Len - 1,
+            integer.zero, N),
+        Integer = -N
+    ; Char = ('+') ->
+        Len > 1,
+        string.foldl_substring(accumulate_integer(Base), String, 1, Len - 1,
+            integer.zero, N),
+        Integer = N
+    ;
+        string.foldl_substring(accumulate_integer(Base), String, 0, Len,
+            integer.zero, N),
+        Integer = N
+    ).
+
+:- pred accumulate_integer(integer::in, char::in, integer::in, integer::out)
+    is semidet.
+
+accumulate_integer(Base, Char, !N) :-
+    char.digit_to_int(Char, Digit0),
+    Digit = integer(Digit0),
+    Digit < Base,
+    !:N = (Base * !.N) + Digit.
+
+integer.det_from_base_string(Base, String) = Integer :-
+    ( Integer0 = integer.from_base_string(Base, String) ->
+        Integer = Integer0
+    ;
+        error("integer.det_from_base_string")
+    ).
+
+%-----------------------------------------------------------------------------%
  %-----------------------------------------------------------------------------%
Index: tests/general/.cvsignore
===================================================================
RCS file: /home/mercury/mercury1/repository/tests/general/.cvsignore,v
retrieving revision 1.16
diff -u -r1.16 .cvsignore
--- tests/general/.cvsignore	30 Sep 1998 04:23:25 -0000	1.16
+++ tests/general/.cvsignore	10 May 2007 03:47:36 -0000
@@ -13,3 +13,4 @@
  mode_inf
  intermod_type
  CLEAN
+base_string_to_integer
Index: tests/general/Mmakefile
===================================================================
RCS file: /home/mercury/mercury1/repository/tests/general/Mmakefile,v
retrieving revision 1.54
diff -u -r1.54 Mmakefile
--- tests/general/Mmakefile	14 Nov 2006 04:10:22 -0000	1.54
+++ tests/general/Mmakefile	10 May 2007 03:47:22 -0000
@@ -10,6 +10,7 @@
  		arithmetic \
  		array_test \
  		base_string_to_int_test \
+		base_string_to_integer \
  		commit_bug \
  		commit_bug_2 \
  		complex_failure \
Index: tests/general/base_string_to_integer.exp
===================================================================
RCS file: tests/general/base_string_to_integer.exp
diff -N tests/general/base_string_to_integer.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/general/base_string_to_integer.exp	10 May 2007 03:46:00 -0000
@@ -0,0 +1,43 @@
+0 (base 2) = 0
+1 (base 2) = 1
+01 (base 2) = 1
+-000001 (base 2) = -1
+11 (base 2) = 3
+111 (base 2) = 7
+11111 (base 2) = 31
+101010 (base 2) = 42
+10000000000000000000000000000000 (base 2) = 2147483648
+1000000000000000000000000000000000000000000000000000000000000000 (base 2) = 9223372036854775808
+10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 (base 2) = 170141183460469231731687303715884105728
+0 (base 8) = 0
+1 (base 8) = 1
+-1 (base 8) = -1
+10 (base 8) = 8
+-10 (base 8) = -8
+76543210 (base 8) = 16434824
+-76543210 (base 8) = -16434824
+7777777777777777777777777 (base 8) = 37778931862957161709567
+0 (base 10) = 0
+1 (base 10) = 1
+10 (base 10) = 10
+11 (base 10) = 11
+1234567890 (base 10) = 1234567890
+-1 (base 10) = -1
+-10 (base 10) = -10
+-1234567890 (base 10) = -1234567890
+1234567891234567891234567890 (base 10) = 1234567891234567891234567890
+-1234567891234567891234567890 (base 10) = -1234567891234567891234567890
+0 (base 16) = 0
+1 (base 16) = 1
+-1 (base 16) = -1
+10 (base 16) = 16
+A (base 16) = 10
+-A (base 16) = -10
+a (base 16) = 10
+-a (base 16) = -10
+F (base 16) = 15
+-F (base 16) = -15
+fedcba0987654321 (base 16) = 18364757930599072545
+-fedcba0987654321 (base 16) = -18364757930599072545
+fffffffffffffffffffffffffffffffffff (base 16) = 1393796574908163946345982392040522594123775
+-fffffffffffffffffffffffffffffffffff (base 16) = -1393796574908163946345982392040522594123775
Index: tests/general/base_string_to_integer.m
===================================================================
RCS file: tests/general/base_string_to_integer.m
diff -N tests/general/base_string_to_integer.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/general/base_string_to_integer.m	10 May 2007 03:46:00 -0000
@@ -0,0 +1,98 @@
+% vim: ft=mercury ts=4 sw=4 et
+% Test converting strings to arbitrary precision integers.
+:- module base_string_to_integer.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+:- import_module integer.
+:- import_module list.
+:- import_module require.
+:- import_module string.
+
+main(!IO) :-
+	Base2 = [
+        "0", 
+		"1", 
+		"01",
+        "-000001",
+        "11",
+		"111",
+		"11111",
+		"101010",
+		"10000000000000000000000000000000",
+		"1000000000000000000000000000000000000000000000000000000000000000",
+		"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+	],
+    check_base_valid(2, Base2, !IO),
+    Base8 = [
+        "0",
+        "1",
+        "-1",
+        "10",
+        "-10",
+        "76543210",
+        "-76543210",
+        "7777777777777777777777777"
+    ],
+    check_base_valid(8, Base8, !IO),
+    Base10 = [
+        "0",
+        "1",
+        "10",
+        "11",
+        "1234567890",
+        "-1",
+        "-10",
+        "-1234567890",
+        "1234567891234567891234567890",
+        "-1234567891234567891234567890"
+    ],
+    check_base_valid(10, Base10, !IO),
+    Base16 = [
+        "0",
+        "1",
+        "-1",
+        "10",
+        "A",
+        "-A",
+        "a",
+        "-a",
+        "F",
+        "-F",
+        "fedcba0987654321",
+        "-fedcba0987654321",
+        "fffffffffffffffffffffffffffffffffff",
+        "-fffffffffffffffffffffffffffffffffff"
+    ],
+    check_base_valid(16, Base16, !IO),
+    InvalidBase2 = ["3", "4", "5", "6", "a", "A", "z", "13"],
+    check_base_invalid(2, InvalidBase2, !IO),
+    InvalidBase10 = ["abc", "-123a", "ZZZ"],
+    check_base_invalid(10, InvalidBase10, !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) :-
+	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),
+	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) ->
+        NumStr = integer.to_string(Num),
+        string.format("ERROR: %s (base %d) = %s\n",
+            [s(String), i(Base), s(NumStr)], ErrorMsg),
+        error(ErrorMsg)
+    ;
+        check_base_invalid(Base, Strings, !IO)
+    ).

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list