function C = char(D)
%CHAR Convert a codistributed array to a codistributed character array (string)
%   S = CHAR(X)
%   
%   The syntax S = CHAR(T1,T2,T3, ...) is not supported.
%   
%   Example:
%   spmd
%       N = 1000;
%       D = 65*codistributed.ones(N,'uint16');
%       C = char(D)
%       classD = underlyingType(D)
%       classC = underlyingType(C)
%   end
%   
%   converts the N-by-N codistributed uint16 matrix D into a
%   codistributed char array C.
%   classD is 'uint16' while classC is 'char'.
%   
%   See also CHAR, CODISTRIBUTED, CODISTRIBUTED/ONES.


%   Copyright 2006-2023 The MathWorks, Inc.

% Operation is elementwise for all standard MATLAB types. For custom types
% (string, datetime, etc.) each element can expand in different ways.
% e.g.
% * 2x2 datetime array -> 4xN char array
% * 2x2 string array -> 2xNx2 char array
% (where N is the longest text element)

switch underlyingType(D)
    case "string"
        if iscolumn(D) % 2D and 2nd dimension is already 1
            % Make sure we don't try and expand the distribution dimension
            D = redistribute(D, codistributor1d(1));
        else
            % Insert a unit second dimension to be expanded. This also ensures
            % that we aren't trying to expand the distribution dimension.
            sz = size(D);
            D = reshape(D, [sz(1) 1 sz(2:end)]);
        end
        C = iExpandInSecondDim(D, 'right');
        
    case {"categorical", "datetime"}
        % Convert to column then expand second dimension, padding at the
        % end
        D = colonize(D);
        C = iExpandInSecondDim(D, 'right');
        
    case {"duration", "calendarDuration"}
        % Convert to column then expand second dimension, padding at the
        % beginning
        D = colonize(D);
        C = iExpandInSecondDim(D, 'left');
        
    otherwise
        % All other types are elementwise
        C = codistributed.pElementwiseUnaryOp(@char, D);
end
end


function C = iExpandInSecondDim(D, padDirection)
% Helper to expand each element of D as a char row vector.

% Convert the local part
oldLP = getLocalPart(D);
LP = char(oldLP);

% String can insert an extra dimension instead of replacing the unit second
% dimension. If so, remove the third (unit) dimension.
if ndims(LP)>ndims(oldLP) && size(LP,3)==1
    sz = size(LP);
    sz(3) = [];
    LP = reshape(LP, sz);
end

% Work out the longest char vector length across all local parts, then pad
% the local parts to have identical width
maxLength = spmdReduce(@max, size(LP,2));
sz = size(LP);
if isempty(LP) && sz(2) ~= 0
    sz(2) = maxLength;
    LP = char.empty(sz);
elseif ~isempty(LP)
    LP = char(pad(string(LP), maxLength, padDirection)); % (Pad doesn't support char arrays!)
end
outSz = size(D);
outSz(2) = maxLength;

% Output is distributed exactly as per the input except with a bigger
% second dimension.
inCodistr = getCodistributor(D);
if inCodistr.Dimension == 2
    outCodistr = inCodistr.hGetNewForSize(outSz);
else
    outCodistr = inCodistr.hGetCompleteForSize(outSz);
end

% Build output
C = codistributed.pDoBuildFromLocalPart(LP, outCodistr);
end
