diff scripts/specfun/nchoosek.m @ 19251:83b88e20e9c1

nchoosek.m: Overhaul function. * nchoosek.m: Update docstring. Use same variable names in function as in documentation for clarity. Improve input validation. Don't manually clear variables at end of function which will go out of scope anyways and the memory reclaimed. Update built-in self tests.
author Rik <rik@octave.org>
date Fri, 29 Aug 2014 16:30:11 -0700
parents 1514f5337781
children 4197fc428c7d
line wrap: on
line diff
--- a/scripts/specfun/nchoosek.m
+++ b/scripts/specfun/nchoosek.m
@@ -21,7 +21,8 @@
 ## @deftypefn  {Function File} {@var{c} =} nchoosek (@var{n}, @var{k})
 ## @deftypefnx {Function File} {@var{c} =} nchoosek (@var{set}, @var{k})
 ##
-## Compute the binomial coefficient or all combinations of a set of items.
+## Compute the binomial coefficient of @var{n} or list all possible
+## combinations of a @var{set} of items.
 ##
 ## If @var{n} is a scalar then calculate the binomial coefficient
 ## of @var{n} and @var{k} which is defined as
@@ -75,10 +76,10 @@
 ## @end group
 ## @end example
 ##
-## @code{nchoosek} works only for non-negative, integer arguments.  Use
-## @code{bincoeff} for non-integer and negative scalar arguments, or for
-## computing many binomial coefficients at once with vector inputs
-## for @var{n} or @var{k}.
+## Programming Note: When calculating the binomial coefficient @code{nchoosek}
+## works only for non-negative, integer arguments.  Use @code{bincoeff} for
+## non-integer and negative scalar arguments, or for computing many binomial
+## coefficients at once with vector inputs for @var{n} or @var{k}.
 ##
 ## @seealso{bincoeff, perms}
 ## @end deftypefn
@@ -87,16 +88,17 @@
 ## Author: Paul Kienzle <pkienzle@users.sf.net>
 ## Author: Jaroslav Hajek
 
-function A = nchoosek (v, k)
+function C = nchoosek (v, k)
 
   if (nargin != 2
-      || !isnumeric (k) || !isnumeric (v)
-      || !isscalar (k) || ! (isscalar (v) || isvector (v)))
+      || ! (isreal (k) && isscalar (k))
+      || ! (isnumeric (v) && isvector (v)))
     print_usage ();
   endif
-  if (k < 0 || k != fix (k)
-      || (isscalar (v) && (v < k || v < 0 || v != fix (v))))
-    error ("nchoosek: args are non-negative integers with V not less than K");
+  if (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)))
+    error ("nchoosek: N must be a non-negative integer >= K");
   endif
 
   n = length (v);
@@ -104,39 +106,39 @@
   if (n == 1)
     ## Improve precision at next step.
     k = min (k, v-k);
-    A = round (prod ((v-k+1:v)./(1:k)));
-    if (A*2*k*eps >= 0.5)
-      warning ("nchoosek", "nchoosek: possible loss of precision");
+    C = round (prod ((v-k+1:v)./(1:k)));
+    if (C*2*k*eps >= 0.5)
+      warning ("nchoosek: possible loss of precision");
     endif
   elseif (k == 0)
-    A = zeros (1,0);
+    C = zeros (1,0);
   elseif (k == 1)
-    A = v(:);
+    C = v(:);
   elseif (k == n)
-    A = v(:).';
+    C = v(:).';
   elseif (k > n)
-    A = zeros (0, k, class (v));
+    C = zeros (0, k, class (v));
   elseif (k == 2)
     ## Can do it without transpose.
     x = repelems (v(1:n-1), [1:n-1; n-1:-1:1]).';
     y = cat (1, cellslices (v(:), 2:n, n*ones (1, n-1)){:});
-    A = [x, y];
+    C = [x, y];
   elseif (k < n)
     v = v(:).';
-    A = v(k:n);
+    C = v(k:n);
     l = 1:n-k+1;
     for j = 2:k
-      c = columns (A);
-      cA = cellslices (A, l, c*ones (1, n-k+1), 2);
+      c = columns (C);
+      cA = cellslices (C, l, c*ones (1, n-k+1), 2);
       l = c-l+1;
       b = repelems (v(k-j+1:n-j+1), [1:n-k+1; l]);
-      A = [b; cA{:}];
+      C = [b; cA{:}];
       l = cumsum (l);
       l = [1, 1 + l(1:n-k)];
     endfor
-    clear cA b;
-    A = A.';
+    C = C.';
   endif
+
 endfunction
 
 
@@ -145,14 +147,19 @@
 %!assert (size (nchoosek (1:5,0)), [1 0])
 
 %% Test input validation
-%!warning nchoosek (100,45);
+%!error nchoosek ()
+%!error nchoosek (1)
+%!error nchoosek (1,2,3)
+
+%!error nchoosek (100, 2i)
+%!error nchoosek (100, [2 3])
 %!error nchoosek ("100", 45)
-%!error nchoosek (100, "45")
-%!error nchoosek (100, ones (2,2))
-%!error nchoosek (repmat (100, [2 2]), 45)
-%!error nchoosek (100, -45)
-%!error nchoosek (100, 45.5)
-%!error nchoosek (100, 145)
-%!error nchoosek (-100, 45)
-%!error nchoosek (100.5, 45)
+%!error nchoosek (100*ones (2, 2), 45)
+%!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)
+%!error <N must be a non-negative integer .= K> nchoosek (100, 145)
+%!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);