function [N,C,S] = normalizeArray(A,method,methodType,mtValue,dim,method2,methodType2,mtValue2,AisTabular)
%MATLAB Code Generation Library Function
% Shared normalization computation function for NORMALIZE and GROUPTRANSFORM

%   Copyright 2018-2022 The MathWorks,Inc.
%#codegen

coder.internal.prefer_const(method,methodType,mtValue,dim,method2,methodType2,mtValue2,AisTabular);
checkSupportedArray(A,method,methodType,AisTabular)
if isempty(method2)
    [N,C,S] = normalizeNumeric(A,dim,method,methodType,mtValue);
else
    [N,C,S] = centerScaleNumeric(A,dim,method,methodType,mtValue,method2,methodType2,mtValue2);
end
end

function [n,c,s] = normalizeNumeric(a,dim,method,methodType,mtValue)
coder.internal.prefer_const(dim,method,methodType,mtValue)

if strcmp(method,'zscore')
    if strcmp('std',methodType)
        c = mean(a,dim,'omitnan');
        s = std(a,0,dim,'omitnan');
        mu = bsxfun(@minus,a,c);
        n = bsxfun(@rdivide,mu,s);
    else % "robust"
        c = median(a,dim,'omitnan');
        mu = bsxfun(@minus,a,median(a,dim,'omitnan'));
        s = median(abs(mu),dim,'omitnan');
        n = bsxfun(@rdivide,mu,s);
    end
elseif strcmp(method,'norm')
    if strcmp(methodType,'value')
        % In order to omit NaNs in this case fill NaNs with 0 to compute norms
        fillA = a;
        fillA(isnan(fillA)) = 0;
        coder.internal.assert(mtValue(1) > 0,'MATLAB:normalize:InvalidNormType');
        s = vecnorm(fillA,mtValue(1),dim);
        n = bsxfun(@rdivide,a,s);
    else % p = 'inf'
        % In order to omit NaNs in this case fill NaNs with 0 to compute norms
        fillA = a;
        fillA(isnan(fillA)) = 0;
        s = vecnorm(fillA,methodType,dim);
        n = bsxfun(@rdivide,a,s);
    end
    c = zeros(1,'like',s);
elseif strcmp(method,'center')
    c = centerNumeric(a,dim,methodType,mtValue);
    s = ones(1,'like',c);
    n = bsxfun(@minus,a,c);
elseif strcmp(method,'scale')
    s = scaleNumeric(a,dim,methodType,mtValue);
    c = zeros(1,'like',s);
    n = bsxfun(@rdivide,a,s);
elseif strcmp(method,'range')
    minA = min(a,[],dim);
    maxA = max(a,[],dim);
    if ~isfloat(mtValue)
        left = double(mtValue(1));
        right = double(mtValue(2));
    else
        left = mtValue(1);
        right = mtValue(2);
    end
    if isfloat(a)
        [n,c,s] = rescaleKernel(a,left,right,minA,maxA);
    else
        [n,c,s] = rescaleKernel(a,left,right,double(minA),double(maxA));
    end
else %if strcmp(method,'medianiqr')
    ONE = coder.internal.indexInt(1);
    XTYPE = coder.internal.scalarEg(a,a);
    c = median(a,dim,'omitnan');
    mu = bsxfun(@minus,a,c);
    f = @(x)interQuartileRange(x,[25 75]);
    s = coder.internal.applyVectorFunction(f,a,dim,ONE,XTYPE);
    n = bsxfun(@rdivide,mu,s);
end
end

%--------------------------------------------------------------------------
function [n,c,s] = centerScaleNumeric(a,dim,method,methodType,mtValue,method2,methodType2,mtValue2)
coder.internal.prefer_const(dim,method,methodType,mtValue,method2,methodType2,mtValue2)
% Validate A.
coder.internal.assert(isfloat(a),'MATLAB:normalize:InvalidFirstInput');

if strcmp(method,'center')
    c = centerNumeric(a,dim,methodType,mtValue);
    s = scaleNumeric(a,dim,methodType2,mtValue2);
elseif strcmp(method,'scale')
    c = centerNumeric(a,dim,methodType2,mtValue2);
    s = scaleNumeric(a,dim,methodType,mtValue);
end
mu = bsxfun(@minus,a,c);
n = bsxfun(@rdivide,mu,s);
end

%--------------------------------------------------------------------------
function c = centerNumeric(a,dim,methodType,mtValue)
coder.internal.prefer_const(dim,methodType,mtValue)
if strcmp(methodType,'value') || strcmp(methodType,'table')
    c = mtValue;
elseif strcmp(methodType,'mean')
    c = mean(a,dim,'omitnan');
else %if strcmp(methodType,'median')
    c = median(a,dim,'omitnan');
end
end

%--------------------------------------------------------------------------
function s = scaleNumeric(a,dim,methodType,mtValue)
coder.internal.prefer_const(dim,methodType,mtValue)
if strcmp(methodType,'value') || strcmp(methodType,'table')
    s = mtValue;
elseif strcmp(methodType,'std')
    s = std(a,0,dim,'omitnan');
elseif strcmp(methodType,'mad')
    mu = bsxfun(@minus,a,median(a,dim,'omitnan'));
    s = median(abs(mu),dim,'omitnan');
elseif strcmp(methodType,'first')
    s = findFirst(a,dim);
else % if strcmp(methodType,'iqr')
    ONE = coder.internal.indexInt(1);
    XTYPE = coder.internal.scalarEg(a,a);
    f = @(x)interQuartileRange(x,[25 75]);
    s = coder.internal.applyVectorFunction(f,a,dim,ONE,XTYPE);
end
end
%--------------------------------------------------------------------------

function s = findFirst(x,dim)
coder.internal.prefer_const(dim);
DIM = coder.internal.indexInt(dim);
XTYPE = coder.internal.scalarEg(x,x);
s = coder.internal.applyVectorFunctionNoCopy(@f,x,DIM,coder.internal.indexInt(1),XTYPE);
end

%--------------------------------------------------------------------------

function s = f(v,indToSubFcn,s,indToSubFcnS)
ONE = coder.internal.indexInt(1);
subs = indToSubFcn(ONE);
subsS = indToSubFcnS(ONE);
s(subsS{:}) = v(subs{:});
end

%--------------------------------------------------------------------------

function checkSupportedArray(A,method,methodType,AisTabular)
coder.internal.prefer_const(method,methodType,AisTabular);
% Parse input A
if strcmp('range',method)
    invalidTypeForRange = ~(isnumeric(A) || islogical(A)) || ~isreal(A);
    if AisTabular
        coder.internal.errorIf(invalidTypeForRange, ...
            'MATLAB:normalize:UnsupportedTableVariableRange');
    else
        coder.internal.errorIf(invalidTypeForRange, ...
            'MATLAB:normalize:InvalidFirstInputRange');
    end
elseif strcmp('zscore',method) && strcmp('robust',methodType)
    invalidTypeForRobust = ~isfloat(A) || ~isreal(A);
    if AisTabular
        coder.internal.errorIf(invalidTypeForRobust, ...
            'MATLAB:normalize:UnsupportedTableVariableRobust');
    else
        coder.internal.errorIf(invalidTypeForRobust, ...
            'MATLAB:normalize:InvalidFirstInputRobust');
    end
else
    invalidTypeForNormalize = ~isfloat(A);
    if AisTabular
        coder.internal.errorIf(invalidTypeForNormalize, ...
            'MATLAB:normalize:UnsupportedTableVariable');
    else
        coder.internal.errorIf(invalidTypeForNormalize, ...
            'MATLAB:normalize:InvalidFirstInput');
    end
end
end
