changeset 19093: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 13fa7b5375a9
children edf5d63c82e1
files doc/interpreter/tips.txi scripts/specfun/nchoosek.m
diffstat 2 files changed, 59 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/tips.txi	Fri Aug 29 13:27:36 2014 -0700
+++ b/doc/interpreter/tips.txi	Fri Aug 29 16:30:11 2014 -0700
@@ -500,7 +500,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
@@ -554,10 +555,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
@@ -566,7 +567,7 @@
 @noindent
 which demonstrates most of the concepts discussed above.
 @iftex
-This documentation string renders as
+This documentation string renders in Info format as
 @c Note: use the actual output of info below, rather than try and 
 @c reproduce it here to prevent it looking different from how it would
 @c appear with info.
@@ -574,8 +575,8 @@
 @example
  -- Function File: C = nchoosek (N, K)
  -- Function File: C = nchoosek (SET, K)
-     Compute the binomial coefficient or all combinations of a set of
-     items.
+     Compute the binomial coefficient of N or list all possible
+     combinations of a SET of items.
 
      If N is a scalar then calculate the binomial coefficient of N and
      K which is defined as
@@ -608,32 +609,31 @@
                  1   3
                  2   3
 
+     Programming Note: When calculating the binomial coefficient
      `nchoosek' works only for non-negative, integer arguments.  Use
      `bincoeff' for non-integer and negative scalar arguments, or for
      computing many binomial coefficients at once with vector inputs
      for N or K.
 
-     See also: bincoeff, perms
+     See also: bincoeff, perms.
 @end example
 @noindent
-using info, whereas in a printed documentation using @TeX{} it will
-appear as
+whereas in printed documentation using @TeX{} it will appear as
 
 @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
-
 @tex
 $$
  {n \choose k} = {n (n-1) (n-2) \cdots (n-k+1) \over k!}
                = {n! \over k! (n-k)!}
 $$
 @end tex
-
 @noindent
 This is the number of combinations of @var{n} items taken in groups of
 size @var{k}.
@@ -665,10 +665,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
--- a/scripts/specfun/nchoosek.m	Fri Aug 29 13:27:36 2014 -0700
+++ b/scripts/specfun/nchoosek.m	Fri Aug 29 16:30:11 2014 -0700
@@ -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);