changeset 26246:7adb62e4cc39

movfun.m, movslice.m: Use Octave coding conventions. * movfun.m, movslice.m: Use double quotes rather than single quotes for strings. Use '#' rather than '%' for comment character. Use lower case variable names when possible in preference to uppercase or CamelCase. Match variable names in documentation to those in function. Print name of function in all error messages. Use space between function name and parenthesis starting argument list. Use "FIXME" for all instances where there is a question about improving the code. Add titles to all plots in %!demo blocks.
author Rik <rik@octave.org>
date Sun, 16 Dec 2018 12:48:00 -0800
parents af99ea9c325f
children 86217421a37f
files scripts/signal/movfun.m scripts/signal/movslice.m
diffstat 2 files changed, 301 insertions(+), 273 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/signal/movfun.m	Sun Dec 16 11:20:00 2018 +0100
+++ b/scripts/signal/movfun.m	Sun Dec 16 12:48:00 2018 -0800
@@ -20,13 +20,13 @@
 ## Created: 2018-08-09
 
 ## -*- texinfo -*-
-## @deftypefn  {} {@var{y} =} movfun (@var{fun}, @var{x}, @var{wlen})
-## @deftypefnx {} {@var{y} =} movfun (@var{fun}, @var{x}, @var{[@var{nb}, @var{na}}])
+## @deftypefn  {} {@var{y} =} movfun (@var{fcn}, @var{x}, @var{wlen})
+## @deftypefnx {} {@var{y} =} movfun (@var{fcn}, @var{x}, @var{[@var{nb}, @var{na}}])
 ## @deftypefnx {} {@var{y} =} movfun (@dots{}, @var{property}, @var{value})
-## Apply function @var{fun} to a moving window of length @var{wlen} on data
+## Apply function @var{fcn} to a moving window of length @var{wlen} on data
 ## @var{x}.
 ##
-## If @var{wlen} is a scalar, the function @var{fun} is applied to a moving
+## If @var{wlen} is a scalar, the function @var{fcn} is applied to a moving
 ## window of length @var{wlen}.  In this case @var{wlen} must be an odd number.
 ## If @var{wlen} is an array with two elements @w{@code{[@var{nb}, @var{na}]}},
 ## the function is applied to a moving window @code{-@var{nb}:@var{na}}.  This
@@ -35,20 +35,20 @@
 ## The current element is always included.
 ##
 ## During calculations the data input @var{x} is reshaped into a 2-dimensional
-## @var{wlen}-by-@var{N} matrix and @var{fun} is called on this new matrix.
-## Therefore, @var{fun} must accept an array input argument and apply the
+## @var{wlen}-by-@var{N} matrix and @var{fcn} is called on this new matrix.
+## Therefore, @var{fcn} must accept an array input argument and apply the
 ## computation on the columns of that array.
 ##
-## When applied to a column vector of length @var{n}, the function @var{fun}
+## When applied to a column vector of length @var{n}, the function @var{fcn}
 ## must return a @strong{row} vector of length @var{n}.
 ## When applied to an array (possibly multi-dimensional) with @var{n} columns,
-## @var{fun} may return a result in either of two formats: @w{Format 1)}
+## @var{fcn} may return a result in either of two formats: @w{Format 1)}
 ## an array of size 1-by-@var{n}-by-@var{dim3}-by-@dots{}-by-@var{dimN}.  This
 ## is the typical output format from Octave core functions.  Type
 ## @code{demo ("movfun", 5)} for an example of this use case.
 ## @w{Format 2)} a row vector of length
 ## @code{@var{n} * @var{numel_higher_dims}} where @var{numel_higher_dims} is
-## @w{@code{prod (size (@var{x})(3:end))}}.  The output of @var{fun} for the
+## @w{@code{prod (size (@var{x})(3:end))}}.  The output of @var{fcn} for the
 ## i-th input column must be found in the output at indices
 ## @code{i:@var{n}:(@var{n}*@var{numel_higher_dims})}.
 ## This format is useful when concatenating functions into arrays, or when
@@ -73,21 +73,21 @@
 ## @item @qcode{"shrink"}  (default)
 ## The window is truncated at the beginning and end of the array to include
 ## only valid elements.  For example, with a window of length 3,
-## @code{@var{y}(1) = @var{fun} (@var{x}(1:2))}, and
-## @code{@var{y}(end) = @var{fun} (@var{x}(end-1:end))}.
+## @code{@var{y}(1) = @var{fcn} (@var{x}(1:2))}, and
+## @code{@var{y}(end) = @var{fcn} (@var{x}(end-1:end))}.
 ##
 ## @item @qcode{"periodic"}
 ## The window is wrapped around so that
-## @code{@var{y}(1) = @var{fun} ([@var{x}(end-@var{k}:end),
+## @code{@var{y}(1) = @var{fcn} ([@var{x}(end-@var{k}:end),
 ## @var{x}(1:@var{k})])}, where @var{k} is the radius of the window.  For
-## example, with a window of length 3, 
-## @code{@var{y}(1) = @var{fun} ([@var{x}(end-1:end), @var{x}(1)])},
+## example, with a window of length 3,
+## @code{@var{y}(1) = @var{fcn} ([@var{x}(end-1:end), @var{x}(1)])},
 ##
 ## @item @qcode{"zero"}
 ## The array is pre-padded and post-padded with zeros to exactly contain the
 ## window.  For example, with a window of length 3,
-## @code{@var{y}(1) = @var{fun} ([0, @var{x}(1:2)])}, and
-## @code{@var{y}(end) = @var{fun} ([@var{x}(end-1:end), 0])}.
+## @code{@var{y}(1) = @var{fcn} ([0, @var{x}(1:2)])}, and
+## @code{@var{y}(end) = @var{fcn} ([@var{x}(end-1:end), 0])}.
 ##
 ## @item @qcode{"same"}
 ## The resulting array @var{y} has the same values as @var{x} at the
@@ -99,23 +99,23 @@
 ## @end table
 ##
 ## Note that for some of these values, the window size at the boundaries is not
-## the same as in the middle part, and @var{fun} must work with these cases.
+## the same as in the middle part, and @var{fcn} must work with these cases.
 ##
 ## @item @qcode{"nancond"}
 ## Controls whether @code{NaN} or @code{NA} values should be excluded (value:
 ## @qcode{"omitnan"}), or included (value: @qcode{"includenan"}) in the
-## arguments passed to @var{fun}.  The default is @qcode{"omitnan"}.
+## arguments passed to @var{fcn}.  The default is @qcode{"omitnan"}.
 ##
 ## @item @qcode{"outdim"}
 ## A row vector that selects which dimensions of the calculation will appear
-## in the output @var{y}.  This is only useful when @var{fun} returns an
+## in the output @var{y}.  This is only useful when @var{fcn} returns an
 ## N-dimensinoal array in @w{Format 1}.  The default is to return all output
 ## dimensions.
 ##
 ## @end table
 ##
 ## Programming Note: The property @qcode{"outdim"} can be used to save memory
-## when the output of @var{fun} has many dimensions, or when a wrapper to the
+## when the output of @var{fcn} has many dimensions, or when a wrapper to the
 ## base function that selects the desired outputs is too costly.  When memory
 ## is not an issue, the easiest way to select output dimensions is to first
 ## calculate the complete result with @code{movfun} and then filter that result
@@ -126,173 +126,186 @@
 ##
 ## @example
 ## @group
-## @var{fun} = @@(x) basefun (x)(:,size(x,2) * (@var{D}-1) + (1:size(x,2)));
-## @var{y} = movfun (@@fun, @dots{});
+## @var{fcn} = @@(x) basefun (x)(:,size(x,2) * (@var{D}-1) + (1:size(x,2)));
+## @var{y} = movfun (@@fcn, @dots{});
 ## @end group
 ## @end example
 ##
 ## @seealso{movslice, prepad, postpad, permute, reshape}
 ## @end deftypefn
 
-## FIXME: variable names in function prototype should match documentation.
-function Y = movfun (func, X, wlen, varargin)
+function y = movfun (fcn, x, wlen, varargin)
+
+  valid_bc = {"shrink", "periodic", "same", "zero", "fill"};
+
   persistent dispatch;
-
-  valid_bc = {'open', 'periodic', 'same', 'zero', 'discard'};
   if (isempty (dispatch))
     dispatch = struct ();
     for k = valid_bc
-      cmd = sprintf ('dispatch.%s = @%s_bc;', k{1}, k{1});
+      cmd = sprintf ("dispatch.%s = @%s_bc;", k{1}, k{1});
       eval (cmd);
     endfor
   endif
 
-  # Parse input arguments
-  parser              = inputParser ();
-  parser.FunctionName = 'movfun';
-  parser.addParamValue ('Endpoints', 'open', ...
+  ## Parse input arguments
+  parser = inputParser ();
+  parser.FunctionName = "movfun";
+  parser.addParamValue ("Endpoints", "shrink", ...
     @(x)any (ismember (tolower (x), valid_bc)));
-  parser.addParamValue ('dim', [], ...
-    @(x) isempty(x) || (isscalar (x) && x > 0 && x <= ndims(X)));
-  parser.addParamValue ('nancond', 'omitnan', ...
-    @(x) any (ismember (x, {'omitnan', 'includenan'})));
-  parser.addParamValue ('outdim', [], ...
+  parser.addParamValue ("dim", [], ...
+    @(x) isempty(x) || (isscalar (x) && x > 0 && x <= ndims(x)));
+  parser.addParamValue ("nancond", "omitnan", ...
+    @(x) any (ismember (x, {"omitnan", "includenan"})));
+  parser.addParamValue ("outdim", [], ...
     @(x) isempty(x) || (isvector (x) && all (x > 0)));
 
-  parser.parse(varargin{:});
-  bc      = parser.Results.Endpoints;   % boundary condition
-  dim     = parser.Results.dim;         % dimension to be used as input
-  nancond = parser.Results.nancond;     % whether nan are ignored or not
-  outdim  = parser.Results.outdim;      % selected output dimension of func
+  parser.parse (varargin{:});
+  bc      = parser.Results.Endpoints;   # boundary condition
+  dim     = parser.Results.dim;         # dimension to be used as input
+  nancond = parser.Results.nancond;     # whether NaN are ignored or not
+  outdim  = parser.Results.outdim;      # selected output dimension of fcn
   clear parser
-  #### End parse input arguments
+  ## End parse input arguments
 
-  # If dim was not provided search the first non-singleton dimension
-  szX  = size (X);
+  ## If dim was not provided search for the first non-singleton dimension
+  szx  = size (x);
   if (isempty (dim))
-    dim  = find (szX > 1, 1, 'first');
+    dim  = find (szx > 1, 1, "first");
   endif
 
-  # Window length is not a vector wlen = [wpre wpos]
+  ## Window length validation
   if (isscalar (wlen))
-    # Check for proper window length
-    # TODO: Matlab accepts even windows
+    ## Check for proper window length
+    ## FIXME: Matlab accepts even windows
     if (mod (wlen, 2) == 0)
-      error ('Octave:invalid-input-arg', 'Window must be of odd length');
+      error ("Octave:invalid-input-arg", "movfun: WLEN must be an odd length");
     endif
     if (wlen == 1)
-      error ('Octave:invalid-input-arg', 'Window must be larger than 1');
+      error ("Octave:invalid-input-arg", "movfun: WLEN must be larger than 1");
     endif
   endif
+  ## FIXME: Need to validate form: wlen = [nb, na]
 
-  # Check that array is longer that wlen at dim. At least one full window must 
-  # fit. Function max is used to include the case when wlen is an array.
-  # TODO: consider bc to decide what to do here
-  if (max (wlen) > szX(dim))
-      error ('Octave:invalid-input-arg', ...
-        'window length (%d) must be shorter than length along dim (%d=%d)', ...
-        wlen, dim, szX(dim));
+  ## Check that array is longer that wlen at dim.  At least one full window
+  ## must fit. Function max is used to include the case when wlen is an array.
+  ## FIXME: Consider using bc to decide what to do here.
+  if (max (wlen) > szx(dim))
+      error ("Octave:invalid-input-arg", ...
+             "movfun: window length (%d) must be shorter than length along DIM (%d=%d)", ...
+        wlen, dim, szx(dim));
   endif
 
-  # Move the desired dim to the 1st dimension
-  dims  = length (szX);                  % number of dimensions
-  dperm = [dim 1:(dim-1) (dim+1):dims];  % permutation of dimensions
-  X     = permute (X, dperm);            % permute dim to first dimensions
-  ncols = prod (szX(dperm(2:end)));      % rest dimensions as single column
-  N     = szX(dperm(1));                 % length of dim
-  X     = reshape (X, N, ncols);         % reshape input
+  ## Move the desired dim to the 1st dimension
+  nd    = length (szx);                  # number of dimensions
+  dperm = [dim, 1:(dim-1), (dim+1):nd];  # permutation of dimensions
+  x     = permute (x, dperm);            # permute dim to first dimensions
+  ncols = prod (szx(dperm(2:end)));      # rest dimensions as single column
+  N     = szx(dperm(1));                 # length of dim
+  x     = reshape (x, N, ncols);         # reshape input
 
-  # obtain function for boundary conditions
+  ## Obtain function for boundary conditions
   bcfunc = dispatch.(tolower (bc));
 
-  # obtain slicer
+  ## Obtain slicer
   [slc, C, Cpre, Cpos, win] = movslice (N, wlen);
 
-  # Check that outdim makes sense
-  tmp     = func (zeros (length (win), 1));  % output for window
-  noutdim = length (tmp);                    % number of output dimensions
-  if (!isempty (outdim))
+  ## Validate that outdim makes sense
+  tmp     = fcn (zeros (length (win), 1));  # output for window
+  noutdim = length (tmp);                   # number of output dimensions
+  if (! isempty (outdim))
     if (max (outdim) > noutdim)
-      error ('Octave:invalid-input-arg', ...
-      'Selected output dimension (%d) is larger than output dimensions (%d)', ...
-        max (outdim), noutdim);
+      error ("Octave:invalid-input-arg", ...
+             "movfun: output dimension OUTDIM (%d) is larger than largest available dimension (%d)", ...
+             max (outdim), noutdim);
     endif
   else
     outdim = 1:noutdim;
   endif
-  soutdim = length (outdim); % length of selected output dimensions
-  # If noutdim is not one then modify function to handle multiple outputs
+  soutdim = length (outdim);  # length of selected output dimensions
+  ## If noutdim is not one then modify function to handle multiple outputs
   if (noutdim > 1)
-    func_ = @(x) reshape (func (x), size (x, 2), noutdim)(:,outdim);
+    fcn_ = @(x) reshape (fcn (x), columns (x), noutdim)(:, outdim);
   else
-    func_ = func;
+    fcn_ = fcn;
   endif
 
-  # apply processing to each column
-  # Is it faster with cellfun? Do not think so
-  # It could be parallel
-  Y       = zeros (N, ncols, soutdim);
+  ## Apply processing to each column
+  ## FIXME: Is it faster with cellfun?  Don't think so, but needs testing.
+  ## It could be parallel
+  y = zeros (N, ncols, soutdim);
   for i = 1:ncols
-    Y(:,i,:) = movfun_oncol (func_, X(:,i), wlen, bcfunc, ...
-          slc, C, Cpre, Cpos, win, soutdim);
+    y(:,i,:) = movfun_oncol (fcn_, x(:,i), wlen, bcfunc,
+                             slc, C, Cpre, Cpos, win, soutdim);
   endfor
 
-  # Restore shape
-  Y = reshape (Y, [szX(dperm), soutdim]);
-  Y = permute (Y, [dperm, dims+1]);
-  Y = squeeze (Y);
+  ## Restore shape
+  y = reshape (y, [szx(dperm), soutdim]);
+  y = permute (y, [dperm, nd+1]);
+  y = squeeze (y);
 
 endfunction
 
-function y = movfun_oncol (func, x, wlen, bcfunc, I, C, Cpre, Cpos, win, odim)
+function y = movfun_oncol (fcn, x, wlen, bcfunc, I, C, Cpre, Cpos, win, odim)
   N = length (x);
   y = NA (N, odim);
-  # process center part
-  y(C,:) = func (x(I));
+
+  ## Process center part
+  y(C,:) = fcn (x(I));
 
-  # process boundaries
-  if !isempty (Cpre) % don't process zero length bkw-window
-    y(Cpre,:) = bcfunc (func, x, Cpre, win, wlen, odim);
+  ## Process boundaries
+  if (! isempty (Cpre))  # don't process zero length bkw-window
+    y(Cpre,:) = bcfunc (fcn, x, Cpre, win, wlen, odim);
   endif
-  if !isempty (Cpos) % don't process zero length fwd-window
-    y(Cpos,:) = bcfunc (func, x, Cpos, win, wlen, odim);
+  if (! isempty (Cpos))  # don't process zero length fwd-window
+    y(Cpos,:) = bcfunc (fcn, x, Cpos, win, wlen, odim);
   endif
+
 endfunction
 
-function y = open_bc (func, x, idxp, win, wlen, odim)
+## Apply "shrink" boundary conditions
+## Function is not applied to any window elements outside the original data.
+function y = shrink_bc (fcn, x, idxp, win, wlen, odim)
   N   = length (x);
   idx = idxp + win;
-  tf  = !((idx < 1) | (idx > N)); % idx inside boundaries
+  tf  = ! ((idx < 1) | (idx > N));  # idx inside boundaries
 
   n   = length (idxp);
   y   = zeros (n, odim);
   for i = 1:n
     k      = idx(tf(:,i),i);
-    y(i,:) = func (x(k));
+    y(i,:) = fcn (x(k));
   endfor
 endfunction
 
-function y = periodic_bc (func, x, idxp, win)
+## Apply "periodic" boundary conditions
+## Data wraps around padding front of window with data from end of array and
+## vice versa.
+function y = periodic_bc (fcn, x, idxp, win)
   N       = length (x);
   idx     = idxp + win;
   tf      = idx < 1;
   idx(tf) = N + idx(tf);
   tf      = idx > N;
   idx(tf) = idx(tf) - N;
-  y       = func (x(idx));
+  y       = fcn (x(idx));
 endfunction
 
-function y = same_bc (func, x, idxp, win)
+## Apply "same" boundary conditions
+## 'y' values outside window are set equal to 'x' values at the window
+## boundary.
+function y = same_bc (fcn, x, idxp, win)
   idx          = idxp + win;
   idx(idx < 1) = 1;
   N            = length (x);
   idx(idx > N) = N;
-  y            = func (x(idx));
+  y            = fcn (x(idx));
 endfunction
 
-function y = zero_bc (func, x, idxp, win, wlen)
-  if isscalar (wlen)
-    wlen = [wlen wlen];
+## Apply "zero" boundary conditions
+## Window is padded at beginning and end with zeros
+function y = zero_bc (fcn, x, idxp, win, wlen)
+  if (isscalar (wlen))
+    wlen = [wlen, wlen];
   endif
   N = length (x);
   if (min (idxp) == 1)
@@ -302,106 +315,113 @@
     x   = postpad (x, N + wlen(2));
     idx = idxp + win;
   endif
-  y = func (x(idx));
+  y = fcn (x(idx));
 endfunction
 
-function y = discard_bc (func, x, idxp, win, wlen, odim)
-  y = nan (length (idxp), odim);
+## Apply "fill" boundary conditions
+## Window is padded at beginning and end with NaN
+function y = fill_bc (fcn, x, idxp, win, wlen, odim)
+  y = NaN (length (idxp), odim);
 endfunction
 
 
 %!demo
+%! clf;
 %! t  = 2 * pi * linspace (0,1,100).';
 %! x  = sin (3 * t);
 %! xn = x + 0.1 * randn (size (x));
-%! x_o = movfun (@mean, xn, 5, 'Endpoints', 'open');
-%! x_p = movfun (@mean, xn, 5, 'Endpoints', 'periodic');
-%! x_f = movfun (@mean, xn, 5, 'Endpoints', 'same');
-%! x_z = movfun (@mean, xn, 5, 'Endpoints', 'zero');
-%! x_d = movfun (@mean, xn, 5, 'Endpoints', 'discard');
+%! x_s = movfun (@mean, xn, 5, "Endpoints", "shrink");
+%! x_p = movfun (@mean, xn, 5, "Endpoints", "periodic");
+%! x_m = movfun (@mean, xn, 5, "Endpoints", "same");
+%! x_z = movfun (@mean, xn, 5, "Endpoints", "zero");
+%! x_f = movfun (@mean, xn, 5, "Endpoints", "fill");
 %!
-%! figure (), clf
-%!    h = plot (
-%!              t, xn, 'o;noisy signal;',
-%!              t, x, '-;true;',
-%!              t, x_o, '-;open;',
-%!              t, x_p, '-;periodic;',
-%!              t, x_f, '-;same;',
-%!              t, x_z, '-;zero;',
-%!              t, x_d, '-;discard;');
-%!    set (h(1), 'markerfacecolor', 'auto');
-%!    set (h(2:end), 'linewidth', 3);
-%!    axis tight
-%!    xlabel ('time')
-%!    ylabel ('signal')
-%! #####
-%! # Moving mean of noisy sinusoidal function with different boundary 
+%! h = plot (t, xn, "o;noisy signal;",
+%!           t, x, "-;true;",
+%!           t, x_s, "-;shrink;",
+%!           t, x_p, "-;periodic;",
+%!           t, x_m, "-;same;",
+%!           t, x_z, "-;zero;",
+%!           t, x_f, "-;fill;");
+%! set (h(1), "markerfacecolor", "auto");
+%! set (h(2:end), "linewidth", 3);
+%! axis tight
+%! xlabel ("time");
+%! ylabel ("signal");
+%! title ("moving mean with different boundary conditions");
+%! #-----------------------------------------------------------------
+%! # Moving mean of noisy sinusoidal function with different boundary
 %! # conditions.
 
 %!demo
+%! clf;
 %! t  = 2 * pi * linspace (0,1,100).';
 %! x  = sin (3 * t);
 %! xn = x + 0.1 * randn (size (x));
-%! nw = 5;
-%! x_ = zeros (size(x,1), nw);
-%! w = 3 + (1:nw) * 4;
-%! for i=1:nw
-%!    x_(:,i) = movfun (@mean, xn, w(i), 'Endpoints', 'periodic');
-%!  endfor
+%! nwin = 5;
+%! x_ = zeros (rows (x), nwin);
+%! wlen = 3 + (1:nwin) * 4;
+%! for i = 1:nwin
+%!   x_(:,i) = movfun (@mean, xn, wlen(i), "Endpoints", "periodic");
+%! endfor
 %!
-%! figure (), clf
-%!    h = plot (
-%!              t, xn, 'o',
-%!              t, x, '-',
-%!              t, x_, '-');
-%!    set (h(1), 'markerfacecolor', 'auto');
-%!    set (h(2:end), 'linewidth', 3);
-%!    axis tight
-%!    xlabel ('time')
-%!    ylabel ('signal')
-%!    legend (h, {'noisy', 'true', strsplit(num2str(w)){:}});
-%! #####
+%! h = plot (t, xn, "o",
+%!           t, x, "-",
+%!           t, x_, "-");
+%! set (h(1), "markerfacecolor", "auto");
+%! set (h(2:end), "linewidth", 3);
+%! axis tight
+%! xlabel ("time");
+%! ylabel ("signal");
+%! title ({'moving mean with "periodic" boundary conditions',
+%!         "and windows of different lengths"});
+%! legend (h, {"noisy", "true", strsplit(num2str(wlen)){:}});
+%! #-----------------------------------------------------------------
 %! # Moving mean of noisy sinusoidal function with periodic boundary conditions
 %! # using windows of different lengths.
 
 %!demo
+%! clf;
 %! t  = linspace (0,1,100).';
-%! X  = exp(-(t - [0.1:0.3:1]).^2/2/0.1^2);
-%! Y  = movfun (@max, X, 15);
-%! figure (), clf
-%!    h = plot (
-%!              t, X, '-',
-%!              t, Y, '--');
-%!    axis tight
-%!    xlabel ('time')
-%!    ylabel ('signal')
-%! #####
-%! # Moving max of different Gaussian functions. Illustrates the application
-%! # on inputs with several columns.
+%! x  = exp (-(t - [0.1:0.3:1]).^2/2/0.1^2);
+%! y  = movfun (@max, x, 15);
+%!
+%! h = plot (t, x, "-",
+%!           t, y, "--");
+%! axis tight
+%! xlabel ("time");
+%! ylabel ("signal");
+%! title ("moving max of several Gaussian functions");
+%! #-----------------------------------------------------------------
+%! # Moving max of different Gaussian functions.
+%! # Illustrates the application of movfun() to inputs with several columns.
 
 %!demo
+%! clf;
 %! t  = linspace (0,1-1e-2,100).';
 %! w  = 2 * pi * 3;
 %! x  = sin (w * t);
 %! y  = cos (w * t);
-%! y_  = movfun (@diff, x, [1 0], 'Endpoints', 'periodic');
-%! # Is the same as y_ = x(2:end) - x(1:end-1);
+%! y_  = movfun (@diff, x, [1 0], "Endpoints", "periodic");
+%! ## Is the same as y_ = x(2:end) - x(1:end-1);
 %! dt = t(2) - t(1);
 %! y_  = y_ / w / dt;
-%! figure (), clf
-%!    h = plot (t, x, '-',
-%!              t, y, '-', 
-%!              t, y_, ':');
-%!    set (h, 'linewidth', 3);
-%!    axis tight
-%!    xlabel ('time')
-%!    ylabel ('signal')
-%!    legend (h, {'sin', 'cos', 'bkw diff'});
-%! #####
-%! # Backward difference of sinusoidal function with periodic boundary 
-%! # conditions. Illustrates the use of asymmetric windows.
+%!
+%! h = plot (t, x, "-",
+%!           t, y, "-",
+%!           t, y_, ":");
+%! set (h, "linewidth", 3);
+%! axis tight
+%! xlabel ("time");
+%! ylabel ("signal");
+%! title ("movfun with periodic boundary conditions and asymmetric window");
+%! legend (h, {"sin", "cos", "[nb, na]"});
+%! #-----------------------------------------------------------------
+%! # Backward diff() of sinusoidal function with periodic boundary conditions.
+%! # Illustrates the use of asymmetric windows.
 
 %!demo
+%! clf;
 %! N    = 1e3;
 %! wlen = 99;
 %! x  = linspace (-1, 1, N).';
@@ -409,25 +429,27 @@
 %! y  = polyval (pp, x);
 %! yn = y + 0.1 * (abs (y) + 0.5) .* exp (randn (N, 1));
 %!
-%! st = movfun (@(y)statistics(y).', yn, wlen);
-%! figure (), clf
-%!    h = plot (x, y, '-',
-%!              x, yn, '.',
-%!              x, st(:,[3 6]), '-', 
-%!              x, st(:,6) + [-1 1].*st(:,7), '-', 
-%!              x, st(:,[1 2 4 5]), '-');
-%!    set (h([1 3:4]), 'linewidth', 3); % mean
-%!    set (h(5:end), 'color', 'k');
-%!    axis tight
-%!    xlabel ('x')
-%!    ylabel ('y')
-%!    legend (h, {'noiseless', 'noisy', 'mean', 'median'})
-%! #####
-%! # Moving window statistics. The plot highlights mean and median. black lines
-%! # show minimum, first quartile, third quartile, and maximum.
-%! # This illustrates the use of functions with multidimensional output.
+%! st = movfun (@(y) (statistics (y)).', yn, wlen);
+%!
+%! h = plot (x, y, "-",
+%!           x, yn, ".",
+%!           x, st(:,[3 6]), "-",
+%!           x, st(:,6) + [-1 1].*st(:,7), "-",
+%!           x, st(:,[1 2 4 5]), "-");
+%! set (h([1 3:4]), "linewidth", 3);  # mean
+%! set (h(5:end), "color", "k");
+%! axis tight
+%! xlabel ("x")
+%! ylabel ("y")
+%! title ("movfun() with Format 1 output data");
+%! legend (h, {"noiseless", "noisy", "mean", "median"})
+%! #-----------------------------------------------------------------
+%! # Moving window statistics.  The plot highlights mean and median.
+%! # Black lines how minimum, first quartile, third quartile, and maximum.
+%! # Demo illustrates the use of functions with multidimensional output.
 
 %!demo
+%! clf;
 %! N    = 1e2;
 %! wlen = 9;
 %! x  = linspace (-1, 1, N).';
@@ -436,98 +458,104 @@
 %! y(:,2) = y + 0.1 * (abs (y) + 0.5) .* exp (randn (N, 1));
 %! y(:,1) = -y(:,1) + 0.1 * randn (N, 1);
 %!
-%! func = @(y)[min(y) max(y)];
-%! st = movfun (func, y, wlen);
-%! figure (), clf
-%!    h = plot (x, y, 'o',
-%!              x, squeeze (st(:,1,:)), '-', 
-%!              x, squeeze (st(:,2,:)), '-');
-%!    axis tight
-%!    set (h(3:4), 'color', get(h(1), 'color'));
-%!    set (h(5:6), 'color', get(h(2), 'color'));
-%!    xlabel ('x')
-%!    ylabel ('y')
-%!    legend (h(1:2), {'data1', 'data2'})
-%! #####
-%! # Moving window bounds.
-%! # This illustrates the use of functions with flat multidimensional output.
-
-%!error(movfun(@min, [0;0], 1)) % wlen == 1
-%!error(movfun(@min, [0;0], 2)) % odd wlen
-%!error(movfun(@min, [0;0], 3)) % wlen larger than data
-%!error(movfun(@min, [0;0;0], [1 4])) % wlen larger than data
-%!error(movfun(@min, [0;0;0], [4 1])) % wlen larger than data
+%! fcn = @(y) [min(y), max(y)];
+%! st = movfun (fcn, y, wlen);
+%!
+%! h = plot (x, y, "o",
+%!           x, squeeze (st(:,1,:)), "-",
+%!           x, squeeze (st(:,2,:)), "-");
+%! axis tight
+%! set (h(3:4), "color", get (h(1), "color"));
+%! set (h(5:6), "color", get (h(2), "color"));
+%! xlabel ("x")
+%! ylabel ("y")
+%! title ("movfun() with Format 2 output data");
+%! legend (h(1:2), {"data1", "data2"})
+%! #-----------------------------------------------------------------
+%! # Moving min() and max() on the same window.
+%! # Demo illustrates the use of functions with flat multidimensional output.
 
 %!test
-%! X = (1:10).' + [-3, 0, 4];
-%! ctrfun = @(x)x(2,:);
-%! valid_bc = {'same', 'periodic', 'zero'};
+%! x = (1:10).' + [-3, 0, 4];
+%! ctrfun = @(x) x(2,:);
+%! valid_bc = {"same", "periodic", "zero"};
 %! for bc = valid_bc
-%!   assert (movfun(ctrfun, X, 3, 'Endpoints', bc{1}), X);
+%!   assert (movfun (ctrfun, x, 3, "Endpoints", bc{1}), x);
 %! endfor
-%! X_ = X; X_([1 end],:) = nan;
-%! assert (movfun(ctrfun, X, 3, 'Endpoints', 'discard'), X_);
-%! X_ = X; X_([1 end],:) = X([2 end],:);
-%! assert (movfun(ctrfun, X, 3, 'Endpoints', 'open'), X_);
-
-%!test % dim == 2 same as transpose
-%! X = randi (10, 3);
-%! ctrfun = @(x)x(2,:);
-%! valid_bc = {'same', 'periodic', 'zero'};
-%! for bc = valid_bc
-%!   assert (movfun(ctrfun, X.', 3, 'Endpoints', bc{1}, 'dim', 2), X.');
-%! endfor
-%! X_ = X; X_([1 end],:) = nan;
-%! assert (movfun(ctrfun, X.', 3, 'Endpoints', 'discard', 'dim', 2), X_.');
-%! X_ = X; X_([1 end],:) = X([2 end],:);
-%! assert (movfun(ctrfun, X.', 3, 'Endpoints', 'open', 'dim', 2), X_.');
+%! x_ = x; x_([1 end],:) = NaN;
+%! assert (movfun (ctrfun, x, 3, "Endpoints", "fill"), x_);
+%! x_ = x; x_([1 end],:) = x([2 end],:);
+%! assert (movfun (ctrfun, x, 3, "Endpoints", "shrink"), x_);
 
 %!test
-%! X = randi (10, 3, 10, 2);
-%! Y = movfun (@(x)x(2,:), X, 3, 'Endpoints', 'same', 'dim', 2);
-%! assert (X, Y);
+%! ## dim == 2, same as transpose
+%! x = randi (10, 3);
+%! ctrfun = @(x) x(2,:);
+%! valid_bc = {"same", "periodic", "zero"};
+%! for bc = valid_bc
+%!   assert (movfun (ctrfun, x.', 3, "Endpoints", bc{1}, "dim", 2), x.');
+%! endfor
+%! x_ = x; x_([1 end],:) = NaN;
+%! assert (movfun (ctrfun, x.', 3, "Endpoints", "fill", "dim", 2), x_.');
+%! x_ = x; x_([1 end],:) = x([2 end],:);
+%! assert (movfun (ctrfun, x.', 3, "Endpoints", "shrink", "dim", 2), x_.');
 
-%!test # bad zero_bc
-%! X = ones (10, 1);
-%! Y = X; Y(1:2) = Y([end end-1]) = [0.6;0.8];
-%! assert (movfun (@mean, X, 5, 'Endpoints', 'zero'), Y);
+%!test
+%! x = randi (10, 3, 10, 2);
+%! y = movfun (@(x) x(2,:), x, 3, "Endpoints", "same", "dim", 2);
+%! assert (x, y);
+
+%!test
+%! ## bad zero_bc
+%! x = ones (10, 1);
+%! y = x; y(1:2) = y([end end-1]) = [0.6;0.8];
+%! assert (movfun (@mean, x, 5, "Endpoints", "zero"), y);
 
 ## Asymmetric windows
-%!shared X,wlen,wlen0b,wlen0f,ctrfun,Xd,UNO,UNOd0b,UNOd0f
-%! X = (1:10).' + [-3, 0, 4];
+%!shared x,wlen,wlen0b,wlen0f,ctrfun,xd,UNO,UNOd0b,UNOd0f
+%! x = (1:10).' + [-3, 0, 4];
 %! wlen = [2 1];
 %! wlen0b = [0 2];
 %! wlen0f = [2 0];
-%! ctrfun = @(x)x(wlen(1)+1,:);
-%! Xd = X; Xd([1:2 end],:) = nan;
+%! ctrfun = @(x) x(wlen(1)+1,:);
+%! xd = x; xd([1:2 end],:) = NaN;
 %! UNO = ones (7,1);
-%! UNOd0b = UNOd0f = UNO; 
-%! UNOd0b(end-1:end,:) = nan;
-%! UNOd0f(1:2,:) = nan;
+%! UNOd0b = UNOd0f = UNO;
+%! UNOd0b(end-1:end,:) = NaN;
+%! UNOd0f(1:2,:) = NaN;
 
-%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'same'), X);
-%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'discard'), Xd);
-%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'periodic'), X);
-%!assert (movfun(ctrfun, X, wlen, 'Endpoints', 'zero'), X);
-%!error movfun(ctrfun, X, wlen, 'Endpoints', 'open'); % for shorter x, indexing fails
+%!assert (movfun (ctrfun, x, wlen, "Endpoints", "same"), x)
+%!assert (movfun (ctrfun, x, wlen, "Endpoints", "fill"), xd)
+%!assert (movfun (ctrfun, x, wlen, "Endpoints", "periodic"), x)
+%!assert (movfun (ctrfun, x, wlen, "Endpoints", "zero"), x)
+## for shorter x, indexing fails
+%!error movfun (ctrfun, x, wlen, "Endpoints", "shrink")
 
-%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'same'), UNO);
-%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'same'), UNO);
+%!assert (movfun (@min, UNO, wlen0b, "Endpoints", "same"), UNO)
+%!assert (movfun (@min, UNO, wlen0f, "Endpoints", "same"), UNO)
 
-%!assert (movfun(@min, UNO, wlen, 'Endpoints', 'open'), UNO);
-%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'open'), UNO);
-%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'open'), UNO);
+%!assert (movfun (@min, UNO, wlen, "Endpoints", "shrink"), UNO)
+%!assert (movfun (@min, UNO, wlen0b, "Endpoints", "shrink"), UNO)
+%!assert (movfun (@min, UNO, wlen0f, "Endpoints", "shrink"), UNO)
 
-%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'discard'), UNOd0b);
-%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'discard'), UNOd0f);
+%!assert (movfun (@min, UNO, wlen0b, "Endpoints", "fill"), UNOd0b)
+%!assert (movfun (@min, UNO, wlen0f, "Endpoints", "fill"), UNOd0f)
 
-%!assert (movfun(@min, UNO, wlen0b, 'Endpoints', 'periodic'), UNO);
-%!assert (movfun(@min, UNO, wlen0f, 'Endpoints', 'periodic'), UNO);
+%!assert (movfun (@min, UNO, wlen0b, "Endpoints", "periodic"), UNO)
+%!assert (movfun (@min, UNO, wlen0f, "Endpoints", "periodic"), UNO)
 
-%!assert (movfun(@max, UNO, wlen0b, 'Endpoints', 'zero'), UNO);
-%!assert (movfun(@max, UNO, wlen0f, 'Endpoints', 'zero'), UNO);
+%!assert (movfun (@max, UNO, wlen0b, "Endpoints", "zero"), UNO)
+%!assert (movfun (@max, UNO, wlen0f, "Endpoints", "zero"), UNO)
 
 ## Multidimensional output
-%!error(movfun(@(x)[min(x) max(x)], (1:10).', 3, 'Outdim', 3)) % outdim > dim
-%!assert(size(movfun(@(x)[min(x) max(x)], (1:10).', 3)), [10 2])
-%!assert(size(movfun(@(x)[min(x) max(x)], cumsum(ones(10,5),2), 3)), [10 5 2])
+%!assert(size(movfun (@(x)[min(x) max(x)], (1:10).', 3)), [10 2])
+%!assert(size(movfun (@(x)[min(x) max(x)], cumsum(ones(10,5),2), 3)), [10 5 2])
+## outdim > dim
+%!error (movfun (@(x) [min(x), max(x)], (1:10).', 3, "Outdim", 3))
+
+## Test input validation
+%!error (movfun (@min, [0;0], 1))  # wlen == 1
+%!error (movfun (@min, [0;0], 2))  # odd wlen
+%!error (movfun (@min, [0;0], 3))  # wlen larger than data
+%!error (movfun (@min, [0;0;0], [1 4]))  # wlen larger than data
+%!error (movfun (@min, [0;0;0], [4 1]))  # wlen larger than data
--- a/scripts/signal/movslice.m	Sun Dec 16 11:20:00 2018 +0100
+++ b/scripts/signal/movslice.m	Sun Dec 16 12:48:00 2018 -0800
@@ -21,31 +21,31 @@
 
 ## -*- texinfo -*-
 ## @deftypefn  {} {@var{slcidx} =} movslice (@var{N}, @var{wlen})
-## @deftypefnx {} {[@var{slcidx}, @var{c}, @var{pre}, @var{pos}, @var{w}] =} movslice (@dots{})
+## @deftypefnx {} {[@var{slcidx}, @var{C}, @var{Cpre}, @var{Cpost}, @var{win}] =} movslice (@dots{})
 ## Generate indices to slice a vector of length @var{N} in to windows
 ## of length @var{wlen}.
 ##
 ## FIXME: Document inputs N, wlen
 ##
-## FIXME: Document outputs slcidx, c, pre, pos, w
+## FIXME: Document outputs slcidx, C, Cpre, Cpost, win.
 ## @seealso{movfun}
 ## @end deftypefn
 
-## FIXME: variable names in function prototype should match documentation.
-function [slcidx, C, Cpre, Cpos, win] = movslice (N, wlen)
+function [slcidx, C, Cpre, Cpost, win] = movslice (N, wlen)
 
   ## FIXME: Input validation for N, wlen
+
   ## FIXME: Eventually add asymmetric window
   if (isscalar (wlen))
     hwlen = (wlen - 1) / 2;
     wlen = [hwlen hwlen];
   endif
 
-  Cpre = 1:wlen(1);               # centers that can't fit the pre-window
-  Cnf  = N - wlen(2) + 1;         # first center that can't fit the post-window
-  Cpos = Cnf:N;                   # centers that can't fit post-window
-  C    = (wlen(1) + 1):(Cnf - 1);
-  win  = (-wlen(1):wlen(2)).';
+  Cpre  = 1:wlen(1);              # centers that can't fit the pre-window
+  Cnf   = N - wlen(2) + 1;        # first center that can't fit the post-window
+  Cpost = Cnf:N;                  # centers that can't fit post-window
+  C     = (wlen(1) + 1):(Cnf - 1);
+  win   = (-wlen(1):wlen(2)).';
   slcidx = C + win;
 
 endfunction