Mercurial > jwe > octave
changeset 30210:23a907b2dbd5
nchoosek.m: Allow "char" and other non-numeric inputs (bug #61119)
* nchoosek.m: Remove "isnumeric" input validation for first input.
Redo input validation to provide more meaningful error messages.
Return Matlab-compatible answers for both size and type in corner
cases of N, K values. Add BIST tests for new input validation and
for new accepted input types.
author | Rik <rik@octave.org> |
---|---|
date | Thu, 23 Sep 2021 10:00:12 -0700 |
parents | 1cd077e9f127 |
children | aaee7b170cb1 |
files | scripts/specfun/nchoosek.m |
diffstat | 1 files changed, 77 insertions(+), 53 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/specfun/nchoosek.m Thu Sep 23 00:00:12 2021 -0400 +++ b/scripts/specfun/nchoosek.m Thu Sep 23 10:00:12 2021 -0700 @@ -92,20 +92,23 @@ function C = nchoosek (v, k) - if (nargin != 2 - || ! (isreal (k) && isscalar (k)) - || ! ((isnumeric (v) || ischar (v)) && isvector (v))) + if (nargin != 2) print_usage (); endif - if (k < 0 || k != fix (k)) + + if (! isvector (v)) + error ("nchoosek: first argument must be a scalar or a vector"); + endif + if (! (isreal (k) && isscalar (k) && k >= 0 && k == fix (k))) error ("nchoosek: K must be an integer >= 0"); - elseif (isscalar (v) && (iscomplex (v) || v < k || v < 0 || v != fix (v))) + endif + if (isscalar (v) && (iscomplex (v) || v < k || v < 0 || v != fix (v))) error ("nchoosek: N must be a non-negative integer >= K"); endif - n = length (v); + n = numel (v); - if (n == 1) + if (n == 1 && isnumeric (v)) ## Improve precision at next step. k = min (k, v-k); C = round (prod ((v-k+1:v)./(1:k))); @@ -113,25 +116,13 @@ warning ("nchoosek: possible loss of precision"); endif elseif (k == 0) - if (is_sq_string (v)) - C = resize ('', 1, 0); - elseif (is_dq_string (v)) - C = resize ("", 1, 0); - else - C = zeros (1, 0, class (v)); - endif + C = v(zeros (1, 0)); # Return 1x0 object for Matlab compatibility elseif (k == 1) C = v(:); elseif (k == n) C = v(:).'; elseif (k > n) - if (is_sq_string (v)) - C = resize ('', 0, k); - elseif (is_dq_string (v)) - C = resize ("", 0, k); - else - C = zeros (0, k, class (v)); - endif + C = v(zeros (0, k)); # return 0xk object for Matlab compatibility elseif (k == 2) ## Can do it without transpose. x = repelems (v(1:n-1), [1:n-1; n-1:-1:1]).'; @@ -156,17 +147,75 @@ endfunction -%!assert (nchoosek (80,10), bincoeff (80,10)) -%!assert (nchoosek (1:5,3), [1:3;1,2,4;1,2,5;1,3,4;1,3,5;1,4,5;2:4;2,3,5;2,4,5;3:5]) -%!assert (size (nchoosek (1:5,0)), [1 0]) +%!assert (nchoosek (80, 10), bincoeff (80, 10)) +%!assert (nchoosek (1:5, 3), +%! [1:3;1,2,4;1,2,5;1,3,4;1,3,5;1,4,5;2:4;2,3,5;2,4,5;3:5]) + +# Test basic behavior for various input types +%!assert (nchoosek ('a':'b', 2), 'ab') +%!assert (nchoosek ("a":"b", 2), "ab") +%!assert (nchoosek ({1,2}, 2), {1,2}) +%!test +%! s(1).a = 1; +%! s(2).a = 2; +%! assert (nchoosek (s, 1), s(:)); +%! assert (nchoosek (s, 2), s); + +# Verify Matlab compatibility of return sizes & types +%!test +%! x = nchoosek (1:2, 0); +%! assert (size (x), [1, 0]); +%! assert (isa (x, "double")); +%! x = nchoosek (1:2, 3); +%! assert (size (x), [0, 3]); +%! assert (isa (x, "double")); + +%!test +%! x = nchoosek (single (1:2), 0); +%! assert (size (x), [1, 0]); +%! assert (isa (x, "single")); +%! x = nchoosek (single (1:2), 3); +%! assert (size (x), [0, 3]); +%! assert (isa (x, "single")); + +%!test +%! x = nchoosek ('a':'b', 0); +%! assert (size (x), [1, 0]); +%! assert (is_sq_string (x)); +%! x = nchoosek ('a':'b', 3); +%! assert (size (x), [0, 3]); +%! assert (is_sq_string (x)); + +%!test +%! x = nchoosek ("a":"b", 0); +%! assert (size (x), [1, 0]); +%! assert (is_dq_string (x)); +%! x = nchoosek ("a":"b", 3); +%! assert (size (x), [0, 3]); +%! assert (is_dq_string (x)); + +%!test +%! x = nchoosek (uint8(1):uint8(2), 0); +%! assert (size (x), [1, 0]); +%! assert (isa (x, "uint8")); +%! x = nchoosek (uint8(1):uint8(2), 3); +%! assert (size (x), [0, 3]); +%! assert (isa (x, "uint8")); + +%!test +%! x = nchoosek ({1, 2}, 0); +%! assert (size (x), [1, 0]); +%! assert (isa (x, "cell")); +%! x = nchoosek ({1, 2}, 3); +%! assert (size (x), [0, 3]); +%! assert (isa (x, "cell")); ## Test input validation %!error <Invalid call> nchoosek () %!error <Invalid call> nchoosek (1) - -%!error nchoosek (100, 2i) -%!error nchoosek (100, [2 3]) -%!error nchoosek (100*ones (2, 2), 45) +%!error <first argument must be a scalar or a vector> nchoosek (ones (3, 3), 1) +%!error <K must be an integer .= 0> nchoosek (100, 2i) +%!error <K must be an integer .= 0> nchoosek (100, [2 3]) %!error <K must be an integer .= 0> nchoosek (100, -45) %!error <K must be an integer .= 0> nchoosek (100, 45.5) %!error <N must be a non-negative integer .= K> nchoosek (100i, 2) @@ -174,28 +223,3 @@ %!error <N must be a non-negative integer .= K> nchoosek (-100, 45) %!error <N must be a non-negative integer .= K> nchoosek (100.5, 45) %!warning <possible loss of precision> nchoosek (100, 45); - -%!assert (nchoosek ('a':'b', 2), 'ab') -%!assert (nchoosek ("a":"b", 2), "ab") - -%!test -%! x = nchoosek ('a':'b', 3); -%! assert (size (x), [0, 3]); -%! assert (is_sq_string (x)); -%! x = nchoosek ('a':'b', 0); -%! assert (size (x), [1, 0]); -%! assert (is_sq_string (x)); -%! -%! x = nchoosek ("a":"b", 3); -%! assert (size (x), [0, 3]); -%! assert (is_dq_string (x)); -%! x = nchoosek ("a":"b", 0); -%! assert (size (x), [1, 0]); -%! assert (is_dq_string (x)); -%! -%! x = nchoosek (uint8(1):uint8(2), 3); -%! assert (size (x), [0, 3]); -%! assert (class (x), "uint8"); -%! x = nchoosek (uint8(1):uint8(2), 0); -%! assert (size (x), [1, 0]); -%! assert (class (x), "uint8");