function S = pad(M, varargin)
%PAD Inserts leading and trailing characters.
%   S = PAD(M)
%   S = PAD(M, WIDTH)
%   S = PAD(M, WIDTH, SIDE)
%   S = PAD(M, WIDTH, SIDE, PadCharacter)
%   
%   See also PAD, CODISTRIBUTED/STRING.
%   


%   Copyright 2016-2020 The MathWorks, Inc.

isArrayOfWidths = false;
% Gather scalar inputs
isScalar = cellfun(@isscalar, varargin);
varargin(isScalar) = distributedutil.CodistParser.gatherElements(varargin(isScalar));
% Shortcut if no codistributed inputs any more, use builtin PAD
if ~iscodistributed(M) && all(cellfun(@(x) ~iscodistributed(x), varargin))
    S = pad(M, varargin{:});
    return
end

% Use builtin PAD to error-check the inputs. This is cheap and helps ensure
% we throw the same errors.
try
    pad("", varargin{:});
catch err
    % If padding width is an array we don't throw the error. We need to
    % check if the dimensions of width are compatible with those of M.
    if nargin > 1 && isnumeric(varargin{1}) && ~isscalar(varargin{1})
        elementWiseOpCheckInputs(M, varargin{1}, ...
            message('MATLAB:string:InvalidArgumentSize','width'));
        isArrayOfWidths = true;
    else
        throw(err);
    end
end

% Valid input datatypes: string or cellstr
if ~(isstring(M) || iscellstr(M))
    firstInput = getString(message('MATLAB:string:FirstInput'));
    error(message('MATLAB:string:MustBeCharCellArrayOrString', firstInput));
end

% If padding width is not specified, we need to determine it from the max
% string length
if nargin<2 || ~isnumeric(varargin{1})
    if isempty(M) || all(ismissing(M), 'all')
        width = 1;
    else
        l = strlength(M);
        width = gather(max(iColonize(l), [], 'omitnan'));
    end
    args = [{width}, varargin];
else
    args = varargin;
end

% Non-string cells or missing string elements can cause one worker to error.
% To avoid stalls we must make sure all workers throw in this case.
if ~isArrayOfWidths
    S = distributedutil.syncOnError( ...
        @() codistributed.pElementwiseUnaryOp(@pad, M, args{:}) );
else % We are given an array of widths
    S = iPadWithArrayOfWidths(M, args{:});
end

function x = iColonize(x)
% Convert an ND array into a column vector
x = reshape(x,numel(x),1);

function S = iPadWithArrayOfWidths(varargin)
% Special path when an array of widths is given

M = varargin{1};
width = varargin{2};
args = varargin(3:end);
% Check for incompatible inputs
distributedutil.CodistParser.verifyNonCodistributedInputs(varargin);
% Note that at this point we know that either M or width is codistributed
[lpM, lpWidth, codistr] = elementWiseOpRedistributeInputs(M, width);
if isempty(lpM)
    lpWidth = 0; % We set lpWidth to 0 if lpM is empty
end
lpS = distributedutil.syncOnError( ...
    pad(lpM, lpWidth, args{:}));
S = codistributed.pDoBuildFromLocalPart(lpS, codistr);
