function Out = unPivot(In,dim,rmPad)

% UNPIVOT Ungroup a pivot table into a flat dataset (n m by 3 matrix).
% 
%   UNPIVOT(IN) Else by default
%       - In: cell or numeric pivot table (2-D).
%               -- In(1) must be NaN
%               -- Nested cells are not allowed
%   
%   UNPIVOT(IN, DIM) How to sort output
%       - Dim: scalar, 1 or 2. Respectively sort output by column header or by row header.
%
%   UNPIVOT(IN, DIM, RMPAD) Remove padded values
%       - rmPad: specify which values are considered as padded. 
%                NaN, scalars or char 1 by m.
%
%   OUT = ...
%       - Numeric or cell m by 3 matrix. First two columns, header values. 
%         Sorted by the first column (ex-colHead) if DIM == 1 or by the 
%         second column (ex-rowHead) if DIM == 2.
%
% Additional features:
% - <a href="matlab: web('http://www.mathworks.com/matlabcentral/fileexchange/26119-pivotunpivot','-browser')">FEX Pivot/unPivot page</a>
%
% See also PIVOT

% Author: Oleg Komarov (oleg.komarov@hotmail.it)
% 16 dec 2009 - Created
% 20 dec 2009 - Compatibility from 7.1; added link to the submission
% 22 jan 2010 - Fixed column position in Out (the first was switched with the second) 

% ------------------------------------------------------------------------
% CHECK
% ------------------------------------------------------------------------

% #Ninput
error(nargchk(1,3,nargin));

% In 
if isscalar(In) || isvector(In)
    error('unPivot:inFmt','IN is not a pivot table')
elseif iscell(In)
    if ~isnan(In{1}); error('unPivot:inFmt','IN first element must be NaN'); end
    cellIn = true;
    if any(any(cellfun('isclass', In, 'cell')))
        error('unPivot:inFmt','IN nested cells not admitted')
    end
elseif isnumeric(In); cellIn = false;
    if ~isnan(In(1)); error('unPivot:inFmt','IN first element must be NaN'); end
else error('unPivot:inFmt','IN invalid format')
end

% Dim
if nargin == 1 || isempty(dim); dim = 1;
elseif isnumeric(dim)
    if ~(dim == 1 || dim == 2)
        error('unPivot:dimFmt','DIM must be 1 or 2')
    end
else error('unPivot:dimFmt','DIM invalid format')
end

% rmPad
if nargin < 3 || isempty(rmPad) 
    rmPad = NaN;
elseif ~((isnumeric(rmPad) && isscalar(rmPad)) || (ischar(rmPad) && size(rmPad,1) == 1))
    error('unPivot:dimFmt','RMPAD invalid format')
end

% ------------------------------------------------------------------------
% ENGINE
% ------------------------------------------------------------------------

% Extract main parts
rowHead = In(1,2:end); colHead = In(2:end,1); values = In(2:end,2:end); clear In
sz = size(values);

% Index values to unpivot
IDX = 1:numel(values);

% Flip if sorting by rowHeader
if dim == 2
    IDX = reshape(IDX,sz)';  
    values = values';
end

% Row and column indexes
[row, col] = ind2sub(sz,IDX(:));

% Output
Out = [reshape(rowHead(col),[],1), reshape(colHead(row),[],1),values(:)];
clear rowHead colHead values

% Unpad
if cellIn
    Out(cellfun('isempty', Out(:,end)),:) = [];
    IDXc = find(cellfun('isclass', Out(:,end),'char'));   
    if ~isempty(IDXc)
        Chars = Out(IDXc,end); Out(IDXc,end) = {NaN};
    else Chars = [];
    end
    IDXp = false;
    if ischar(rmPad)
        IDXc(strcmp(rmPad,Chars)) = [];
        Chars = Chars(IDXc);
    elseif ~isnan(rmPad)
        IDXp = cellfun(@(x) rmPad == x, Out(:,end));
    end
        IDXnan = cellfun(@isnan, Out(:,end));
        IDXnan(IDXc) = false;
        if ~isempty(IDXc); Out(IDXc,end) = Chars;end
        Out(IDXnan | IDXp,:) = [];
    else
    if ~ischar(rmPad)
        if ~isnan(rmPad); Out(Out(:,end) == rmPad,:) = []; end
        Out(isnan(Out(:,end)),:) = [];
    end
end
end

