# HG changeset patch
# User Abdallah-Elshamy <abdallah.k.elshamy@gmail.com>
# Date 1582478012 -7200
#      Sun Feb 23 19:13:32 2020 +0200
# Node ID 2214d3597d8bf497edaab1b4f79e9f9063da0399
# Parent  865f8ddb2c78154f5a5832c6cd31345bea4b5a88
New functions: startsWith and endsWith (bug #57041)

* scripts/strings : Add startsWith.m and endsWith.m
* scripts/strings/module.mk : Add references to startsWith.m and endsWith.m
* doc/interpreter/strings.txi : Add two docstring lines
* NEWS : Add the two functions to the list of new functions

diff -r 865f8ddb2c78 -r 2214d3597d8b NEWS
--- a/NEWS	Fri Feb 21 11:52:08 2020 -0500
+++ b/NEWS	Sun Feb 23 19:13:32 2020 +0200
@@ -44,7 +44,9 @@ functional form---parentheses and '=' fo
 
 ### Alphabetical list of new functions added in Octave 7
 
+* `endsWith`
 * `rng`
+* `startsWith`
 
 ### Deprecated functions and properties
 
diff -r 865f8ddb2c78 -r 2214d3597d8b doc/interpreter/strings.txi
--- a/doc/interpreter/strings.txi	Fri Feb 21 11:52:08 2020 -0500
+++ b/doc/interpreter/strings.txi	Sun Feb 23 19:13:32 2020 +0200
@@ -416,6 +416,10 @@ corresponding functions for case-insensi
 
 @DOCSTRING(strncmpi)
 
+@DOCSTRING(startsWith)
+
+@DOCSTRING(endsWith)
+
 @node Manipulating Strings
 @section Manipulating Strings
 
diff -r 865f8ddb2c78 -r 2214d3597d8b scripts/strings/endsWith.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/strings/endsWith.m	Sun Feb 23 19:13:32 2020 +0200
@@ -0,0 +1,205 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {@var{retval} =} endsWith (@var{str}, @var{pattern})
+## @deftypefnx {} {@var{retval} =} endsWith (@var{str}, @var{pattern}, "IgnoreCase", @var{confirm_ignore})
+## Checks that a cell array of strings ends with a pattern
+##
+## Return an array of logical values that indicates which strings in the cell
+## array of strings -or the string- @var{str} ends with a string in the cell
+## array of strings -or the string- @var{pattern}
+##
+## The input @var{str} is the strings you want to check.
+## The input @var{pattern} is the patterns that ends the strings.
+## The input @var{confirm_ignore} is a logical that confirms IgnoreCase.
+##
+## If the parameter @qcode{"IgnoreCase"} is set to true, then the function
+## will ignore the cases of @var{str} and @var{pattern}.
+##
+## Reconstruct the answer by using @var{str} @{size (@var{retval})@}
+##
+## Examples:
+##
+## @example
+## ## one string and one pattern while considering case
+## endsWith ("hello", "lo")
+##       @result{}  1
+##
+## ## one string and one pattern while ignoring case
+## endsWith ("hello", "LO", "IgnoreCase", true)
+##       @result{}  1
+##
+## ## multiple strings and multiple patterns while considering case
+## endsWith (@{"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"@},
+##           @{".docx", ".odt", ".txt"@})
+##       @result{}  1  1  0  0
+##
+## ## multiple strings and one pattern while considering case
+## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@},
+##           ".txt", "IgnoreCase", false)
+##       @result{}  0  0  1  0
+##
+## ## multiple strings and one pattern while ignoring case
+## endsWith (@{"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"@},
+##           ".txt", "IgnoreCase", true)
+##       @result{}  1  0  1  0
+## @end example
+##
+## @xseealso{startswith}
+## @end deftypefn
+
+function retval = endsWith (str, pattern, IgnoreCase, confirm_ignore)
+  if (! (nargin == 2 || nargin == 4))
+    print_usage ();
+  endif
+
+  ## checks the input types (only string or  Cell Arrays of Strings are allowed)
+  if (! (iscellstr (str) || ischar (str))
+      || ! (iscellstr (pattern) || ischar (pattern)))
+    error ("endsWith: Arguments must be a string or Cell Arrays of Strings")
+  else
+    str = cellstr (str);
+    pattern = cellstr (pattern);
+  endif
+
+  retval = zeros (size (str));
+
+  for j = 1:numel (pattern)
+    pattern{j} = flip (pattern{j});
+  endfor
+
+  for j = 1:numel (str)
+    str{j} = flip (str{j});
+  endfor
+
+  if (nargin == 2)
+    for j = 1:numel (pattern)
+      retval |= strncmp (str, pattern{j}, length (pattern{j}));
+    endfor
+  endif
+
+  if (nargin == 4)
+    ## make checks on the third parameter
+    if (! ischar (IgnoreCase))
+      error (["endsWith: Invalid argument \"%d\". ", ...
+              "Argument must be IgnoreCase"], IgnoreCase);
+    elseif (length (IgnoreCase) == 0)
+      error (["endsWith: Invalid argument \"%s\". ", ...
+              "Argument must be IgnoreCase"], IgnoreCase);
+    else
+      ## to be compatible with MATLAB
+      ## (MATLAB accepts the command with "I", "i", "Ig", "IG" ..etc )
+      for i = 1:length (IgnoreCase)
+        if (! (IgnoreCase(i) == "ignorecase"(i)
+               || IgnoreCase(i) == "IGNORECASE"(i)))
+          error (["endsWith: Invalid argument \"%s\". ", ...
+                  "Argument must be IgnoreCase"], IgnoreCase);
+        endif
+      endfor
+    endif
+
+    if (numel (confirm_ignore) != 1)
+      error ("endsWith: 'IgnoreCase' value must be a logical scalar")
+    endif
+
+    ## to be compatible with MATLAB
+    ## (MATLAB accepts the command with true, 5, 1 ..etc  )
+    try
+      IgnoreFlag = logical (confirm_ignore);
+    catch
+      error ("endsWith: 'IgnoreCase' value must be a logical scalar")
+    end_try_catch
+
+    if (IgnoreFlag)
+      for j = 1:numel (pattern)
+        retval |= strncmpi (str, pattern{j}, length (pattern{j}));
+      endfor
+    else
+      for j = 1:numel (pattern)
+        retval |= strncmp (str, pattern{j}, length (pattern{j}));
+      endfor
+    endif
+
+  endif
+endfunction
+
+
+## Test simple use with one string and one pattern
+%!assert (endsWith ("hello", "lo"))
+%!assert (endsWith ("hello", "LO", "i", 5)) #  check compatibility with MATLAB
+%!assert (! endsWith ("hello", "no"))
+
+## Test multiple strings with a single pattern
+%!test
+%!  str = {"myFile.odt", "results.ppt", "myCode.m"; ...
+%!         "data-analysis.ppt", "foundations.txt", "data.odt"};
+%!  pattern = ".odt";
+%!  correct_ans = [true, false, false; false, false, true];
+%!  assert (endsWith (str, pattern), correct_ans)
+
+## Test multiple strings with multiple patterns
+%!test
+%!  str = {"tests.txt", "mydoc.odt", "myFunc.m", "results.pptx"};
+%!  pattern = {".docx", ".odt", ".txt"};
+%!  correct_ans = [true, true, false, false];
+%!  assert (endsWith (str, pattern), correct_ans)
+
+## Test IgnoreCase
+%!test
+%!  str = {"TESTS.TXT", "mydoc.odt", "result.txt", "myFunc.m"};
+%!  pattern = ".txt";
+%!  correct_ans_with_ignore = [true, false, true, false];
+%!  correct_ans_without_ignore = [false, false, true, false];
+%!  assert (endsWith (str, pattern, "IgnoreCase", true), ...
+%!          correct_ans_with_ignore)
+%!  assert (endsWith (str, pattern, "IgnoreCase", false), ...
+%!          correct_ans_without_ignore)
+%!  assert (endsWith (str, pattern, "I", 500), ...
+%!          correct_ans_with_ignore) #  check compatibility with MATLAB
+%!  assert (endsWith (str, pattern, "iG", 0), ...
+%!          correct_ans_without_ignore) #  check compatibility with MATLAB
+
+## Test error detection
+%!error <endsWith: Arguments must be a string or Cell Arrays of Strings> ...
+%!      endsWith (152, "hi")
+
+%!error <endsWith: Arguments must be a string or Cell Arrays of Strings> ...
+%!      endsWith ("hello", 152)
+
+%!error <endsWith: 'IgnoreCase' value must be a logical scalar> ...
+%!      endsWith ("hello", "hi", "i", "true")
+
+%!error <endsWith: 'IgnoreCase' value must be a logical scalar> ...
+%!      endsWith ("hello", "hi", "i", [1, 2])
+
+%!error <endsWith: Invalid argument "ignire". Argument must be IgnoreCase> ...
+%!      endsWith ("hello", "he", "ignire", 1)
+
+%!error <endsWith: Invalid argument "". Argument must be IgnoreCase> ...
+%!      endsWith ("hello", "he", "", 1)
+
+%!error <endsWith: Invalid argument "5". Argument must be IgnoreCase> ...
+%!      endsWith ("hello", "he", 5, 1)
diff -r 865f8ddb2c78 -r 2214d3597d8b scripts/strings/module.mk
--- a/scripts/strings/module.mk	Fri Feb 21 11:52:08 2020 -0500
+++ b/scripts/strings/module.mk	Sun Feb 23 19:13:32 2020 +0200
@@ -9,6 +9,7 @@ FCN_FILE_DIRS += %reldir%
   %reldir%/dec2base.m \
   %reldir%/dec2bin.m \
   %reldir%/dec2hex.m \
+  %reldir%/endsWith.m \
   %reldir%/erase.m \
   %reldir%/hex2dec.m \
   %reldir%/index.m \
@@ -20,6 +21,7 @@ FCN_FILE_DIRS += %reldir%
   %reldir%/ostrsplit.m \
   %reldir%/regexptranslate.m \
   %reldir%/rindex.m \
+  %reldir%/startsWith.m \
   %reldir%/str2num.m \
   %reldir%/strcat.m \
   %reldir%/strchr.m \
diff -r 865f8ddb2c78 -r 2214d3597d8b scripts/strings/startsWith.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/strings/startsWith.m	Sun Feb 23 19:13:32 2020 +0200
@@ -0,0 +1,197 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {@var{retval} =} startsWith (@var{str}, @var{pattern})
+## @deftypefnx {} {@var{retval} =} startsWith (@var{str}, @var{pattern}, "IgnoreCase", @var{confirm_ignore})
+## Checks that a cell array of strings starts with a pattern
+##
+## Return an array of logical values that indicates which strings in the cell
+## array of strings -or the string- @var{str} starts with a string in the cell
+## array of strings -or the string- @var{pattern}
+##
+## The input @var{str} is the strings you want to check.
+## The input @var{pattern} is the patterns that starts the strings.
+## The input @var{confirm_ignore} is a logical that confirms IgnoreCase.
+##
+## If the parameter @qcode{"IgnoreCase"} is set to true, then the function
+## will ignore the cases of @var{str} and @var{pattern}.
+##
+## Reconstruct the answer by using @var{str} @{size (@var{retval})@}
+##
+## Examples:
+##
+## @example
+## ## one string and one pattern while considering case
+## startsWith ("hello", "he")
+##       @result{}  1
+##
+## ## one string and one pattern while ignoring case
+## startsWith ("hello", "HE", "IgnoreCase", true)
+##       @result{}  1
+##
+## ## multiple strings and multiple patterns while considering case
+## startsWith (@{"lab work.pptx", "myFile.odt", "data.txt", "foundations.ppt"@},
+##             @{"lab", "data"@})
+##       @result{}  1  0  1  0
+##
+## ## multiple strings and one pattern while considering case
+## startsWith (@{"DATASHEET.ods", "myFile.odt", "data.txt", "foundations.ppt"@},
+##             "data", "IgnoreCase", false)
+##       @result{}  0  0  1  0
+##
+## ## multiple strings and one pattern while ignoring case
+## startsWith (@{"DATASHEET.ods", "myFile.odt", "data.txt", "foundations.ppt"@},
+##             "data", "IgnoreCase", true)
+##       @result{}  1  0  1  0
+## @end example
+##
+## @xseealso{endswith}
+## @end deftypefn
+
+function retval = startsWith (str, pattern, IgnoreCase, confirm_ignore)
+  if (! (nargin == 2 || nargin == 4))
+    print_usage ();
+  endif
+
+  ## checks the input types (only string or  Cell Arrays of Strings are allowed)
+  if (! (iscellstr (str) || ischar (str))
+      || ! (iscellstr (pattern) || ischar (pattern)))
+    error ("startsWith: Arguments must be a string or cell Arrays of Strings")
+  else
+    str = cellstr (str);
+    pattern = cellstr (pattern);
+  endif
+
+  retval = zeros (size (str));
+
+  if (nargin == 2)
+    for j = 1:numel (pattern)
+      retval |= strncmp (str, pattern{j}, length (pattern{j}));
+    endfor
+  endif
+
+  if (nargin == 4)
+    ## make checks on the third parameter
+    if (! ischar (IgnoreCase))
+      error (["startsWith: Invalid argument \"%d\". ", ...
+              "Argument must be IgnoreCase"], IgnoreCase);
+    elseif (length (IgnoreCase) == 0)
+      error (["startsWith: Invalid argument \"%s\". ", ...
+              "Argument must be IgnoreCase"], IgnoreCase);
+    else
+      ## to be compatible with MATLAB
+      ## (MATLAB accepts the command with "I", "i", "Ig", "IG" ..etc )
+      for i = 1:length (IgnoreCase)
+        if (! (IgnoreCase(i) == "ignorecase"(i)
+               || IgnoreCase(i) == "IGNORECASE"(i)))
+          error (["startsWith: Invalid argument \"%s\". ", ...
+                  "Argument must be IgnoreCase"], IgnoreCase);
+        endif
+      endfor
+    endif
+
+    if (numel (confirm_ignore) != 1)
+      error ("startsWith: 'IgnoreCase' value must be a logical scalar")
+    endif
+
+    ## to be compatible with MATLAB
+    ## (MATLAB accepts the command with true, 5, 1 ..etc  )
+    try
+      IgnoreFlag = logical (confirm_ignore);
+    catch
+      error ("startsWith: 'IgnoreCase' value must be a logical scalar")
+    end_try_catch
+
+    if (IgnoreFlag)
+      for j = 1:numel (pattern)
+        retval |= strncmpi (str, pattern{j}, length (pattern{j}));
+      endfor
+    else
+      for j = 1:numel (pattern)
+        retval |= strncmp (str, pattern{j}, length (pattern{j}));
+      endfor
+    endif
+
+  endif
+endfunction
+
+
+## Test simple use with one string and one pattern
+%!assert (startsWith ("hello", "he"))
+%!assert (startsWith ("hello", "HE", "i", 5)) #  check compatibility with MATLAB
+%!assert (! startsWith ("hello", "no"))
+
+## Test multiple strings with a single pattern
+%!test
+%!  str = {"data science", "dataSheet.ods", "myFunc.m"; "foundations.ppt", ...
+%!         "results.txt", "myFile.odt"};
+%!  pattern = "data";
+%!  correct_ans = [true, true, false; false, false, false];
+%!  assert (startsWith (str, pattern), correct_ans)
+
+## Test multiple strings with multiple patterns
+%!test
+%!  str = {"lab work.pptx", "myFile.odt", "data.txt", "foundations.ppt"};
+%!  pattern = {"lab", "data"};
+%!  correct_ans = [true, false, true, false];
+%!  assert (startsWith (str, pattern), correct_ans)
+
+## Test IgnoreCase
+%!test
+%!  str = {"DATASHEET.ods", "myFile.odt", "data.txt", "foundations.ppt"};
+%!  pattern = "data";
+%!  correct_ans_with_ignore = [true, false, true, false];
+%!  correct_ans_without_ignore = [false, false, true, false];
+%!  assert (startsWith (str, pattern, "IgnoreCase", true), ...
+%!          correct_ans_with_ignore)
+%!  assert (startsWith (str, pattern, "IgnoreCase", false), ...
+%!          correct_ans_without_ignore)
+%!  assert (startsWith (str, pattern, "I", 500), ...
+%!          correct_ans_with_ignore)  #  check compatibility with MATLAB
+%!  assert (startsWith (str, pattern, "iG", 0), ...
+%!          correct_ans_without_ignore) #  check compatibility with MATLAB
+
+## Test error detection
+%!error <startsWith: Arguments must be a string or cell Arrays of Strings> ...
+%!      startsWith (152, "hi")
+
+%!error <startsWith: Arguments must be a string or cell Arrays of Strings> ...
+%!      startsWith ("hello", 152)
+
+%!error <startsWith: 'IgnoreCase' value must be a logical scalar> ...
+%!      startsWith ("hello", "hi", "i", "true")
+
+%!error <startsWith: 'IgnoreCase' value must be a logical scalar> ...
+%!      startsWith ("hello", "hi", "i", [1, 2])
+
+%!error <startsWith: Invalid argument "ignire". Argument must be IgnoreCase> ...
+%!      startsWith ("hello", "he", "ignire", 1)
+
+%!error <startsWith: Invalid argument "". Argument must be IgnoreCase> ...
+%!      startsWith ("hello", "he", "", 1)
+
+%!error <startsWith: Invalid argument "5". Argument must be IgnoreCase> ...
+%!      startsWith ("hello", "he", 5, 1)
