function varargout = ndop(func,varargin);
% NDOP Expand 1-D Array Operation To N-D Array Operation.
% Y = NDOP(@OP,X) is the output of the function @OP applied upon the
% 1-D array X. For 2-D arrays NDOP operates along the columns of X
% by applying @OP on each one, collecting the outputs and arranging
% them the way X is arranged. For N-D arrays @OP is applied to each
% 1-D array along the first non singleton dimension of X.
%
% A function usable in the above NDOP context must accept 1-D arrays
% X1d as input and return 1-D arrays Y1d, where the length of Y must
% remain the same for different 1-D arrays of the same length. NDOP
% works as an arrangment function, the way it calls @OP for each 1-D
% array X1d along the operating dimension of the input N-D array X
% and rearranges the outputs Y1d the way X is arranged.
%
%% The concept of abstraction to N-D array inputs cold be understood
%% simplest by looking at the SUM function. The SUM is a well known
%% function, which could be applied to N-D arrays and supports a dim-
%% ension argument DIM. The functionality of the SUM function is also
%% achievable by the usage of the NDOP function.
%% Just think to a function MYSUM, which just accepts vectors X and
%% calculates the SUM over all elements. One way to expand the input
%% usage of MYSUN to N-D arrays is to modify the code in the MYSUN
%% function. Another, more easier way, is to use the NDOP function
%%
%% Y = ndop(@mysum,X,DIM)
%%
%% As a simple example, look at the built-in DCT function, which sup-
%% ports only operations for matrices and could easily be extended to
%% the usage of N-D arrays by the usage of the NDOP function as stat-
%% ed below
%%
%% Y = ndop(@dct,X);
%%
% Y = NDOP(@OP,X,DIM) sets the operating dimension along the input
% N-D array X. If DIM = [] or unvalid, then the first non singleton
% dimension in X is the operating dimension.
%
%% This way the above example could be extended to the use of the DIM
%% respective operating dimension argument as simple as possible
%%
%% Y = ndop(@dct,X,DIM);
%%
% Y = NDOP(@OP,X,DIM,S1,...,SL) sets input arguments for the eval-
% uation of the @OP function. These arguments are not divided into
% 1-D arrays, they remain unchanged and are therefore suitable for
% passing constants.
%
%% Still using the same example, a parameter of interest while eval-
%% uating the DCT function is the padding respective truncating len-
%% gth N. As easy as before, use
%%
%% Y = ndop(@dct,X,DIM,N);
%%
% [Y1,...,YM] = NDOP(...) are multiple output arguments of the func-
% tion @OP, where each output YI is handled the way as described for
% the single output Y and all 1-D array outputs YI1d of @OP, like
%
% [Y11d,...,YN1d] = feval(@OP,X1d,S1,...,SL);
%
% are pairwise length independent, which i.e. means YI1d may be an
% 1-D array of length 4, wherease YJ1d is only a singleton (1x1).
%
% Y = NDOP(inf,@OP,1,X,DIM,S1,...,SL) is an expansion of the NDOP
% function signature Y = NDOP(@OP,X,DIM,S1,...,SL) the way, that the
% length of the output Y1d for each single call to @OP for different
% 1-D array inputs X1d of the same length is no more restricted to
% the same length of Y1d, but may differ. The outputs are collected
% and rearranged the way, that the longest result Y1d defines the
% size of the operating dimension DIM along the output N-D array Y.
% Any collected outputs, which are of lesser size are padded by the
% datatype typically default value.
%
%% An example, which demonstrates the power of the NDOP function is
%% the UNIQUE function. The outputs of the UNIQUE function depend up-
%% on the inputs and therefore the length of the outputs may vary for
%% different inputs. To overcome this problem, simply use the NDOP
%% function
%%
%% [B,I,J] = ndop(inf,@unique,1,A,DIM);
%%
% [...] = NDOP(inf,@OP,N,X1,...,XN,DIM,S1,...,SL) passes more than
% one argument through the rearrangment algorithm at the same time,
% where all XJ must have the same number of dimensions or be empty.
% As obvious N states the number of inputs XJ, which should be di-
% vided to 1-D arrays and evaluatated through @OP.
%
%% The INTERSECT operation is a binary operation, which requests two
%% input arguments A,B and returns a maximum of three arguments. To
%% extend the INTERSECT operation upon N-D arrays, simply use
%%
%% [C,AI,BI] = NDOP(inf,@intersect,2,A,B,DIM);
%%
% [...] = NDOP(T,@OP,...) sets the trimlength T for each 1-D array
% output Y1d along the calls of the @OP function. T may be either
% set to inf, as already described above or to -inf, then the shor-
% test output Y1d defines the length along the operating dimension
% DIM for Y, all Y1d are truncated. Of course T may be also fixed
% to a positive integer.
%
%% A simple example should demonstrate the usage of the trimlength T
%% for the UNIQUE function
%%
%% X = [1,1,1;1,2,3],
%%
%% unique(X(:,1)) = 1,
%% unique(X(:,1)) = [1,2,3],
%%
%% ndop(inf,@unique,1,X,2) = [1,NaN,NaN;1,2,3],
%% ndop(-inf,@unique,1,X,2) = [1;1],
%% ndop(2,@unique,1,X,2) = [1,NaN;1,2];
%%
% To use up the power range of the NDOP function, the function pas-
% sed by the function_handle should do no input verification or exe-
% cute any other code, that slows down the execution. All type of
% verification or parameter setting should be done in an other func-
% tion, which itsself uses the NDOP to extend the functionality to
% N-D arrays.
%
% See also OPDIM, IPERMUTE.
%
% @author Ingo Lhken, ingo.loehken@gmx.de
% @rev 27/10/2005
% @toolbox DIMEXP, ver 0.1
try;
error(nargchk(2,inf,nargin));
if strcmp(class(func),'function_handle');
[varargout{1:max(1,nargout)}] = uni1dop2unindop(func,varargin{:});
else;
[varargout{1:max(1,nargout)}] = any1dop2anyndop(func,varargin{:});
end;
catch;
error([mfilename,': ',lasterr]);
end;