function varargout = pca(x,varargin)
% PCA Principal Component Analysis (pca) on raw gpuArray data.
%   COEFF = PCA(X)
%   [COEFF, SCORE] = PCA(X)
%   [COEFF, SCORE, LATENT] = PCA(X)
%   [COEFF, SCORE, LATENT, TSQUARED] = PCA(X)
%   [COEFF, SCORE, LATENT, TSQUARED, EXPLAINED] = PCA(X)
%   [COEFF, SCORE, LATENT, TSQUARED, EXPLAINED, MU] = PCA(X)
%   [...] = PCA(..., 'PARAM1',val1, 'PARAM2',val2, ...)
%
%   Limitations:
%   For improved performance on GPU use PCA(X,"Algorithm","eig").
%   The 'ALS' algorithm is not supported for GPU data. Use 'SVD' (default)
%   or 'EIG' instead.
%
%   See also PCA, PCACOV, GPUARRAY.

%   Copyright 2020 The MathWorks, Inc.

varargout = cell(1,max(1,nargout));

% Ensure only data is on the GPU and check for 'ALS'
if nargin>1
    [varargin{:}] = gather(varargin{:});
    [varargin{:}] = convertStringsToChars(varargin{:});
    
    % Check for ALS algorithm or any other ALS-specific options.
    if strcmpi(iGetAlgorithm(varargin{:}), "ALS")
        error(message("stats:gpu:PcaAls"));
    end
end

% Now flow-through the standard implementation
[varargout{:}] = parallel.internal.array.callToolboxFunction('stats/stats/pca', x, varargin{:});
end

function vAlgorithm = iGetAlgorithm(varargin)
% Find the algorithm (copied from in-memory PCA code)
[vAlgorithm,~,~,setFlag,~] = internal.stats.parseArgs( ...
    {'Algorithm','Coeff0','Score0'}, ...
    {'svd',[],[]}, ...
    varargin{:});

% If Coeff0 or Score0 was supplied the algorithm will be overridden to ALS.
% Do that now so that we get the right error.
if setFlag.Coeff0 || setFlag.Score0
    vAlgorithm = 'als';
else
    % Validate String value for  Algorithm value
    AlgorithmNames = {'svd','eig','als'};
    vAlgorithm = internal.stats.getParamVal(vAlgorithm,AlgorithmNames, '''Algorithm''');
end
end
