# HG changeset patch # User Rik # Date 1632416412 25200 # Node ID 23a907b2dbd513d04587518a90f25342e282ee82 # Parent 1cd077e9f127f9ae17c0178204e71f8d41981a46 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. diff -r 1cd077e9f127 -r 23a907b2dbd5 scripts/specfun/nchoosek.m --- 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 nchoosek () %!error nchoosek (1) - -%!error nchoosek (100, 2i) -%!error nchoosek (100, [2 3]) -%!error nchoosek (100*ones (2, 2), 45) +%!error nchoosek (ones (3, 3), 1) +%!error nchoosek (100, 2i) +%!error nchoosek (100, [2 3]) %!error nchoosek (100, -45) %!error nchoosek (100, 45.5) %!error nchoosek (100i, 2) @@ -174,28 +223,3 @@ %!error nchoosek (-100, 45) %!error nchoosek (100.5, 45) %!warning 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");