## Copyright (C) 2020 Adam H. Aitkenhead
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or .
##
## This file is part of Octave.
##
## Octave is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING. If not, see
## .
## -*- texinfo -*-
## @deftypefn {Function File} {[@var{f1}] =} freqspace (@var{n})
## @deftypefnx {Function File} {[@var{f1, f2, f3, ...}] =} freqspace (@var{n})
## @deftypefnx {Function File} {[@var{f1, f2, f3, ...}] =} freqspace ([@var{m, n, o, ...}])
## @deftypefnx {Function File} {[@var{f1}] =} freqspace (@var{n, 'whole'})
## @deftypefnx {Function File} {[@var{f1, f2}] =} freqspace (@var{[m, n], 'meshgrid'})
##
## @code{freqspace} returns a vector @var{f1} of @var{n} evenly spaced values.
##
## If @var{n} is even, the returned vector @var{f1} contains the values: [-1 : 2/n : (n-2)/n].
##
## If @var{n} is odd, the returned vector @var{f1} contains the values: [(1-n)/n : 2/n : (n-1)/n].
##
## If multiple outputs @var{f1,f2,f3...} are requested for a single input @var{n}, then the returned vectors
## @var{f1,f2,f3,...} are all identical.
##
## If multiple outputs @var{f1,f2,f3...} are requested for multiple inputs [@var{m,n,o...}], then each
## returned vector @var{f1,f2,f3...} corresponds to one input @var{m,n,o...}.
##
## If the option 'whole' is applied, the returned vector @var{f1} contains the values: [0 : 2/n : 2*(n-1)/n].
##
## If the option 'meshgrid' is applied, @code{freqspace} applies @code{meshgrid} to the outputs,
## returning @var{f1,f2,f3,...} as arrays rather than vectors. The first two dimensions of the returned
## arrays @var{f1,f2,f3,...} are ordered according to @code{meshgrid}, and not as they would be ordered
## by @code{ndgrid}.
##
## Note that [@var{m,n,o...}] must have integer values, but may be of class single or double.
##
## @seealso{meshgrid}
## @end deftypefn
## Author: Adam H. Aitkenhead
function varargout = freqspace (varargin)
# Check the number of input arguments
if (sum (cellfun (@isnumeric, varargin))!=1)
error ('freqspace: Unsupported number of input arguments')
endif
# Check the number of output arguments
ncount = numel (varargin{1});
if (ncount>1 && nargout!=ncount)
error ('freqspace: Number of output arguments does not match the given inputs')
endif
# Check that the numeric inputs have integer values
if (any (mod (varargin{1}, 1)))
error ('freqspace: Numeric inputs must have integer values')
endif
# Check that all string input arguments are either 'whole' or 'meshgrid'
if (nargin>1) && (!all (ismember (varargin(2:end), {'whole', 'meshgrid'})))
error ('freqspace: Supported string inputs are ''whole'' and ''meshgrid''')
endif
# Generate the outputs F1, F2...
for i = 1:ncount
n = varargin{1}(i);
if (mod (n, 2) == 0) # n is even
varargout{i} = [-1 : 2/n : (n-2)/n];
else # n is odd
varargout{i} = [(1-n)/n : 2/n : (n-1)/n];
endif
endfor
# Duplicate the output arguments if nargout>1 and ncount==1
if (nargout>ncount)
for i = 2:nargout
varargout{i} = varargout{1};
endfor
endif
# Option: 'whole'
if (nargin>1) && ...
(all (cellfun (@ischar, varargin(2:end)))) && ....
(any (cellfun ('strcmp', varargin(2:end), {'whole'} )))
minout = cellfun (@min, varargout);
for i = 1:nargout
varargout{i} = varargout{i} - minout(i);
endfor
endif
# Option: 'meshgrid'
if (nargin>1) && ...
(all (cellfun (@ischar, varargin(2:end)))) && ...
(any (cellfun ('strcmp', varargin(2:end), {'meshgrid'} )))
[varargout{:}] = ndgrid (varargout{:});
for i = 1:nargout
varargout{i} = permute (varargout{i}, ...
[2,1,3:max(3, ndims (varargout{i}))]);
endfor
endif
endfunction
%!test
%! x = 2;
%! [XX, YY] = freqspace (x);
%! assert (numel (XX), x);
%! assert (numel (YY), x);
%!test
%! x = 2;
%! y = 3;
%! [XX, YY] = freqspace ([x, y]);
%! assert (numel (XX), x);
%! assert (numel (YY), y);
%!test
%! x = 2;
%! y = 3;
%! [XX, YY] = freqspace ([x, y], 'meshgrid');
%! assert (size (XX), [3,2]);
%! assert (size (YY), [3,2]);
%!test
%! x = 2;
%! y = 3;
%! [XX, YY] = freqspace ([x, y], 'whole');
%! assert (XX(1), 0);
%! assert (YY(1), 0);
%!test
%! x = 2;
%! y = 3;
%! [XX, YY] = freqspace ([x, y], 'whole', 'meshgrid');
%! assert (size (XX), [3,2]);
%! assert (size (YY), [3,2]);
%! assert (XX(1), 0);
%! assert (YY(1), 0);
## Test input validation
%!error freqspace (2,3);
%!error [XX] = freqspace ([2, 3]);
%!error [XX, YY] = freqspace ([2, 3, 4]);
%!error [XX, YY, ZZ] = freqspace ([2, 3]);
%!error [XX] = freqspace ([2.5])
%!error [XX, YY] = freqspace ([2, 3], 'test')