function varargout = glmfit(x,y,distr,varargin)
%GLMFIT Fit a generalized linear model
%   B = glmfit(X,Y,DISTR)
%   B = glmfit(X,Y,DISTR,'PARAM1',val1,'PARAM2',val2,...)
%   [B,DEV] = glmfit(...)
%   [B,DEV,STATS] = glmfit(...)
%
%   See also GLMFIT, GPUARRAY.

%   Copyright 2020-2023 The MathWorks, Inc.

if nargin < 2
    error(message('stats:glmfit:TooFewInputs'));
end

if nargin < 3 || isempty(distr)
    distr = 'normal';
else
    distr = lower(convertStringsToChars(distr));
end

if nargin > 3
    [varargin{:}] = convertStringsToChars(varargin{:});
end

% Determine the syntax.
if nargin < 4
    newSyntax = false;
else
    arg = varargin{1};
    if ischar(arg) % either a link name (old syntax), or a parameter name
        try
            validatestring(arg, ...
                {'identity' 'log' 'logit' 'probit' 'comploglog' 'reciprocal' 'logloglink'});
            newSyntax = false;
        catch
            newSyntax = true;
        end
    else % power link exponent, or custom link, but not a parameter name
        newSyntax = false;
    end
end

% Process optional name/value pairs.
if newSyntax
    paramNames = {     'link' 'estdisp' 'offset' 'weights' 'constant' 'rankwarn' 'options' 'b0', 'likelihoodpenalty'};
    paramDflts = {'canonical'     'off'      []        []        'on'      true         []  [], 'none'};
    [~,~,offset,pwts,~,~,~,b0] = ...
        internal.stats.parseArgs(paramNames, paramDflts, varargin{:});
else % the old syntax glmfit(x,y,distr,link,estdisp,offset,pwts,const)
    offset = [];
    pwts = [];
    b0 = [];
    if nargin > 5 && ~isempty(varargin{3}), offset = varargin{3}; end
    if nargin > 6 && ~isempty(varargin{4}), pwts = varargin{4}; end
end

shouldUseGpu = isgpuarray(x) || isgpuarray(y) || isgpuarray(offset) || isgpuarray(pwts) || isgpuarray(b0);
if shouldUseGpu
    x = iSendToDeviceIfPossible(x);
    y = iSendToDeviceIfPossible(y);
end
[varargout{1:nargout}] = parallel.internal.array.callToolboxFunction("stats/stats/glmfit", x,y,distr,varargin{:});
end

function x = iSendToDeviceIfPossible(x)
% Returns the input as a gpuArray if its numeric or logical. Otherwise the
% input is returned as it was received.
if ~isgpuarray(x) && (isnumeric(x) || islogical(x))
    x = gpuArray(x);
end
end