<div dir="ltr"><div>This looks fine.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Oct 23, 2019 at 3:02 PM Peter Wang <<a href="mailto:novalazy@gmail.com">novalazy@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">library/string.m:<br>
    Add the new predicates.<br>
<br>
tests/hard_coded/Mmakefile:<br>
tests/hard_coded/string_compare_substrings.exp:<br>
tests/hard_coded/string_compare_substrings.m:<br>
    Add test case.<br>
<br>
NEWS:<br>
    Announce additions.<br>
---<br>
 NEWS                                          |  2 +<br>
 library/string.m                              | 65 ++++++++++++++<br>
 tests/hard_coded/Mmakefile                    |  1 +<br>
 .../hard_coded/string_compare_substrings.exp  |  1 +<br>
 tests/hard_coded/string_compare_substrings.m  | 88 +++++++++++++++++++<br>
 5 files changed, 157 insertions(+)<br>
 create mode 100644 tests/hard_coded/string_compare_substrings.exp<br>
 create mode 100644 tests/hard_coded/string_compare_substrings.m<br>
<br>
diff --git a/NEWS b/NEWS<br>
index ba69b8d16..536abd05b 100644<br>
--- a/NEWS<br>
+++ b/NEWS<br>
@@ -427,6 +427,8 @@ Changes to the Mercury standard library:<br>
    - det_remove_prefix/3<br>
    - compare_ignore_case_ascii/3<br>
    - to_rev_char_list/2<br>
+   - compare_substrings/6<br>
+   - unsafe_compare_substrings/6<br>
<br>
   The following procedures in the string module have been deprecated:<br>
<br>
diff --git a/library/string.m b/library/string.m<br>
index ee624c32b..036f1438a 100644<br>
--- a/library/string.m<br>
+++ b/library/string.m<br>
@@ -548,6 +548,30 @@<br>
     %<br>
 :- pred contains_char(string::in, char::in) is semidet.<br>
<br>
+    % compare_substrings(Res, X, StartX, Y, StartY, Length):<br>
+    %<br>
+    % Compare two substrings by code unit order. The two substrings are<br>
+    % the substring of `X' between `StartX' and `StartX + Length', and<br>
+    % the substring of `Y' between `StartY' and `StartY + Length'.<br>
+    % `StartX', `StartY' and `Length' are all in terms of code units.<br>
+    %<br>
+    % Fails if `StartX' or `StartX + Length' are not within [0, length(X)],<br>
+    % or if `StartY' or `StartY + Length' are not within [0, length(Y)],<br>
+    % or if `Length' is negative.<br>
+    %<br>
+:- pred compare_substrings(comparison_result::uo, string::in, int::in,<br>
+    string::in, int::in, int::in) is semidet.<br>
+<br>
+    % unsafe_compare_substrings(Res, X, StartX, Y, StartY, Length):<br>
+    %<br>
+    % Same as compare_between/4 but without range checks.<br>
+    % WARNING: if any of `StartX', `StartY', `StartX + Length' or<br>
+    % `StartY + Length' are out of range, or if `Length' is negative,<br>
+    % then the behaviour is UNDEFINED. Use with care!<br>
+    %<br>
+:- pred unsafe_compare_substrings(comparison_result::uo, string::in, int::in,<br>
+    string::in, int::in, int::in) is det.<br>
+<br>
     % compare_ignore_case_ascii(Res, X, Y):<br>
     %<br>
     % Compare two strings by code point order, ignoring the case of letters<br>
@@ -3289,6 +3313,47 @@ contains_char(Str, Char, I) :-<br>
<br>
 %---------------------%<br>
<br>
+compare_substrings(Res, X, StartX, Y, StartY, Length) :-<br>
+    LengthX = length(X),<br>
+    LengthY = length(Y),<br>
+    ( if<br>
+        Length >= 0,<br>
+        StartX >= 0,<br>
+        StartY >= 0,<br>
+        StartX + Length =< LengthX,<br>
+        StartY + Length =< LengthY<br>
+    then<br>
+        unsafe_compare_substrings(Res, X, StartX, Y, StartY, Length)<br>
+    else<br>
+        fail<br>
+    ).<br>
+<br>
+unsafe_compare_substrings(Res, X, StartX, Y, StartY, Length) :-<br>
+    unsafe_compare_substrings_loop(X, Y, StartX, StartY, Length, Res).<br>
+<br>
+:- pred unsafe_compare_substrings_loop(string::in, string::in,<br>
+    int::in, int::in, int::in, comparison_result::uo) is det.<br>
+<br>
+unsafe_compare_substrings_loop(X, Y, IX, IY, Rem, Res) :-<br>
+    ( if Rem = 0 then<br>
+        Res = (=)<br>
+    else<br>
+        unsafe_index_code_unit(X, IX, CodeX),<br>
+        unsafe_index_code_unit(Y, IY, CodeY),<br>
+        compare(Res0, CodeX, CodeY),<br>
+        (<br>
+            Res0 = (=),<br>
+            unsafe_compare_substrings_loop(X, Y, IX + 1, IY + 1, Rem - 1, Res)<br>
+        ;<br>
+            ( Res0 = (<)<br>
+            ; Res0 = (>)<br>
+            ),<br>
+            Res = Res0<br>
+        )<br>
+    ).<br>
+<br>
+%---------------------%<br>
+<br>
 % XXX ILSEQ unsafe_index_next effectively truncates either or both strings<br>
 % at the first ill-formed sequence.<br>
<br>
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile<br>
index 347a93c31..ee9f51179 100644<br>
--- a/tests/hard_coded/Mmakefile<br>
+++ b/tests/hard_coded/Mmakefile<br>
@@ -361,6 +361,7 @@ ORDINARY_PROGS = \<br>
        string_code_unit \<br>
        string_codepoint \<br>
        string_codepoint_offset_ilseq \<br>
+       string_compare_substrings \<br>
        string_count_codepoints_ilseq \<br>
        string_first_char \<br>
        string_fold_ilseq \<br>
diff --git a/tests/hard_coded/string_compare_substrings.exp b/tests/hard_coded/string_compare_substrings.exp<br>
new file mode 100644<br>
index 000000000..70ff8e5af<br>
--- /dev/null<br>
+++ b/tests/hard_coded/string_compare_substrings.exp<br>
@@ -0,0 +1 @@<br>
+done.<br>
diff --git a/tests/hard_coded/string_compare_substrings.m b/tests/hard_coded/string_compare_substrings.m<br>
new file mode 100644<br>
index 000000000..2c7cd81b3<br>
--- /dev/null<br>
+++ b/tests/hard_coded/string_compare_substrings.m<br>
@@ -0,0 +1,88 @@<br>
+%---------------------------------------------------------------------------%<br>
+% vim: ts=4 sw=4 et ft=mercury<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+:- module string_compare_substrings.<br>
+:- interface.<br>
+<br>
+:- import_module io.<br>
+<br>
+:- pred main(io::di, io::uo) is det.<br>
+<br>
+%---------------------------------------------------------------------------%<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+:- implementation.<br>
+<br>
+:- import_module int.<br>
+:- import_module list.<br>
+:- import_module solutions.<br>
+:- import_module string.<br>
+<br>
+%---------------------------------------------------------------------------%<br>
+<br>
+main(!IO) :-<br>
+    S0 = "😀",<br>
+    S1 = string.between(S0, 0, count_code_units(S0) - 1),<br>
+    X = "small" ++ S0 ++ "dog" ++ S1 ++ "cat",<br>
+    Y = "big" ++ S0 ++ "cat" ++ S1 ++ "dog",<br>
+    solutions(generate_params(X, Y), Params),<br>
+    foldl(test_compare_substrings(X, Y), Params, !IO),<br>
+    io.write_string("done.\n", !IO).<br>
+<br>
+:- pred generate_params(string::in, string::in, {int, int, int}::out) is nondet.<br>
+<br>
+generate_params(X, Y, {IX, IY, Len}) :-<br>
+    list.member(IX, -1 .. length(X) + 1),<br>
+    list.member(IY, -1 .. length(Y) + 1),<br>
+    list.member(Len, -1 .. max(length(X), length(Y)) + 1).<br>
+<br>
+:- pred test_compare_substrings(string::in, string::in, {int, int, int}::in,<br>
+    io::di, io::uo) is det.<br>
+<br>
+test_compare_substrings(X, Y, {IX, IY, Len}, !IO) :-<br>
+    ( if compare_substrings(Rel, X, IX, Y, IY, Len) then<br>
+        ( if ref_compare_substrings(RefRel, X, IX, Y, IY, Len) then<br>
+            ( if Rel = RefRel then<br>
+                true<br>
+            else<br>
+                io.write_string(<br>
+                    "error: result does not match reference implementation\n",<br>
+                    !IO)<br>
+            )<br>
+        else<br>
+            io.write_string(<br>
+                "error: succeeded but reference implementation failed\n", !IO)<br>
+        )<br>
+    else<br>
+        ( if ref_compare_substrings(_RelRef, X, IX, Y, IY, Len) then<br>
+            io.write_string(<br>
+                "error: failed but reference implementation succeeded\n", !IO)<br>
+        else<br>
+            true<br>
+        )<br>
+    ).<br>
+<br>
+:- pred ref_compare_substrings(comparison_result::uo, string::in, int::in,<br>
+    string::in, int::in, int::in) is semidet.<br>
+<br>
+ref_compare_substrings(Res, X, StartX, Y, StartY, Length) :-<br>
+    strict_between(X, StartX, StartX + Length, SubX),<br>
+    strict_between(Y, StartY, StartY + Length, SubY),<br>
+    compare(Res, SubX, SubY),<br>
+    /*<br>
+    trace [io(!IO)] (<br>
+        io.print_line({Res, SubX, SubY}, !IO)<br>
+    ),<br>
+    */<br>
+    true.<br>
+<br>
+:- pred strict_between(string::in, int::in, int::in, string::out) is semidet.<br>
+<br>
+strict_between(Str, Start, End, SubStr) :-<br>
+    % string.between adjusts offsets (unfortunately).<br>
+    Start >= 0,<br>
+    End >= Start,<br>
+    Start =< length(Str),<br>
+    End =< length(Str),<br>
+    string.between(Str, Start, End, SubStr).<br>
-- <br>
2.23.0<br>
<br>
_______________________________________________<br>
reviews mailing list<br>
<a href="mailto:reviews@lists.mercurylang.org" target="_blank">reviews@lists.mercurylang.org</a><br>
<a href="https://lists.mercurylang.org/listinfo/reviews" rel="noreferrer" target="_blank">https://lists.mercurylang.org/listinfo/reviews</a><br>
</blockquote></div></div>