function tX = validateVector(tX, err)
%validateVector Possibly deferred check for vector attribute
%   TX = validateVector(TX,ERR) validates that TX is a vector. If TX is not a vector, the specified error will be thrown. ERR can be: 
%     * A fully constructed error nessage, e.g. message(ERRID,ARG1,...)
%     * An error ID string "ERRID"
%     * A cell array of arguments to pass to message {"ERRID", ARG1, ...}
%     * A nullary function that will throw on call
%
%   This requires a full pass of TX.

% Copyright 2017-2018 The MathWorks, Inc.

% This prevents this frame and anything below it being added to the gather
% error stack.
frameMarker = matlab.bigdata.internal.InternalStackFrame(); %#ok<NASGU>

narginchk(2, 2);
assert(~istall(err), 'Assertion failed: validateVector expects ERR not to be tall.');
assert(nargout >= 1, 'Assertion failed: validateVector expects output to be captured.');
errFcn = matlab.bigdata.internal.util.getErrorFunction(err);

try
    adaptor = matlab.bigdata.internal.adaptors.getAdaptor(tX);
    if ~istall(tX)
        adaptor = setTallSize(adaptor, size(tX, 1));
    end
    if adaptor.isKnownNotVector()
         errFcn();
    end
    if adaptor.isKnownVector()
        return;
    end
    if adaptor.isTallSizeGuaranteedNonUnity()
        % Can optimize the check because this tall array cannot be a row.
        tX = tall.validateColumn(tX, errFcn);
        return;
    end

    % Must be a tall array to reach here.
    tSz = partitionfun(@(info, x) iValidatePartition(info, x, errFcn), tX);
    tSz = reducefun(@(sz) iValidateBetweenPartition(sz, errFcn, true), tSz);
    tSz = clientfun(@(sz) iValidateBetweenPartition(sz, errFcn, false), tSz);
    % The framework will assume tSz is partition dependent because it is
    % derived from partitionfun. It is not, so we must correct this.
    tSz = copyPartitionIndependence(tSz, tX);
    tX = slicefun(@(x, ~) x, tX, tSz);
    if isnan(adaptor.NDims)
        adaptor = setSmallSizes(adaptor, NaN);
    end
    tX.Adaptor = adaptor;
catch err
    throwAsCaller(err);
end

function [isFinished, sz] = iValidatePartition(info, x, errFcn)
% Validate that a partition is a row or column. The output is the size of
% the partition if it is a row or empty. This will also exit early if the
% array is a column.
sz = size(x);
if iscolumn(x)
    isFinished = true;
    return;
end

isFinished = info.IsLastChunk;
if ~ismatrix(x) || sz(1) + info.RelativeIndexInPartition - 1 > 1
    errFcn();
end

function sz = iValidateBetweenPartition(sz, errFcn, allowEmpty)
% Validate that the size between partitions indicate a row or column.
sz = [sum(sz(:, 1)), sz(1, 2)];
if sz(2) == 1
    return;
end

if (sz(1) > 1) || (~allowEmpty && (sz(1) == 0))
    errFcn();
end
