function y = norm(x,p)
%MATLAB Code Generation Library Function

%   Copyright 2002-2017 The MathWorks, Inc.
%#codegen

eml_invariant(nargin > 0,'MATLAB:minrhs');
eml_invariant(isa(x,'float'), ...
    'Coder:toolbox:unsupportedClass','norm',class(x));
eml_invariant(ndims(x) <= 2,'MATLAB:norm:inputMustBe2D');
eml_invariant(nargin == 1 || ( ...
    (isscalar(p) && coder.internal.isBuiltInNumeric(p)) || ...
    coder.internal.isCharOrScalarString(p)), ...
    'MATLAB:norm:unknownNorm');
if nargin < 2
    p = 2;
else
    coder.internal.prefer_const(p);
    if ~((coder.internal.isCharOrScalarString(p) && ...
            (strcmp(p,'fro') || strcmp(p,'inf'))) || ...
            (coder.internal.isBuiltInNumeric(p) &&  ...
            (isvector(x) || p == 1 || p == 2 || ...
            (isa(p,'float') && p > 0 && eml_isinf(p)))))
        coder.internal.error('Coder:MATLAB:norm_unknownNorm');
        y = coder.internal.nan(class(x));
        return
    end
end
if checkIfNumeric(@isnan,p)
    y = coder.internal.nan('like',real(x));
elseif checkIfNumeric(@eq,p,0)
    y = simplenorm(x,p);
elseif isempty(x)
    y = zeros('like',real(x));
elseif eml_is_const(size(x)) && isscalar(x)
    y = abs(x);
elseif isvector(x)
    y = genpnorm(x,p);
elseif coder.internal.isCharOrScalarString(p) && strcmp(p,'fro')
    y = matFroNorm(x);
elseif coder.internal.isCharOrScalarString(p) || ...
        (isa(p,'float') && p > 0 && eml_isinf(p))
    y = matinfnorm(x);
elseif checkIfNumeric(@eq,p,1)
    y = mat1norm(x);
elseif checkIfNumeric(@eq,p,2)
    y = mat2norm(x);
else
    y = coder.internal.nan(class(x));
end

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

function y = mat2norm(x)
coder.columnMajor;
m = coder.internal.indexInt(size(x,1));
n = coder.internal.indexInt(size(x,2));
% Avoid trying to call SVD with non-finites.
y = zeros('like',real(x));
for j = 1:n
    for i = 1:m
        absx = abs(x(i,j));
        if isnan(absx) || absx > y
            y = absx;
        end
    end
end
if isfinite(y)
    s = coder.internal.svd(x);
    y = s(1);
end


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

function y = mat1norm(x)
% max(sum(abs(x)))
m = size(x,1);
n = size(x,2);
ZERO = zeros('like',real(x));
y = ZERO;
for j = 1:n
    s = ZERO;
    for i = 1:m
        s = s + abs(x(i,j));
    end
    if isnan(s)
        y = coder.internal.nan(class(x));
        return
    elseif s > y
        y = s;
    end
end

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

function y = matinfnorm(x)
% max(sum(abs(x')))
% Same as mat1norm(x') but without creating a temporary for x'.
m = size(x,1);
n = size(x,2);
ZERO = zeros('like',real(x));
y = ZERO;
for i = 1:m
    s = ZERO;
    for j = 1:n
        s = s + abs(x(i,j));
    end
    if isnan(s)
        y = coder.internal.nan(class(x));
        return
    elseif s > y
        y = s;
    end
end

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

function y = vecpospnorm(x,p)
% Vector p-norm for finite p > 0.
coder.internal.prefer_const(p);
n = numel(x);
y = zeros('like',real(x));
scale = zeros('like',real(x));
for k = 1:n
    absx = abs(x(k));
    if isnan(absx)
        y = coder.internal.nan(class(x));
        return
    elseif isinf(absx)
        y = coder.internal.inf(class(x));
        for j = k+1:n
            if isnan(x(j))
                y = coder.internal.nan(class(x));
                return
            end
        end
        return
    elseif absx > 0
        if scale < absx
            y = 1 + y*(scale./absx).^p;
            scale = absx;
        else
            y = y + (absx./scale).^p;
        end
    end
end
if y > 0 && ~isinf(y)
    y = scale * y.^(1./p);
end

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

function y = vecnegpnorm(x,p)
% Vector p-norm for finite p < 0.
coder.internal.prefer_const(p);
n = numel(x);
y = zeros('like',real(x));
scale = coder.internal.inf(class(x));
for k = 1:n
    absx = abs(x(k));
    if isnan(absx)
        y = coder.internal.nan(class(x));
        return
    elseif absx == 0
        y = zeros('like',real(x));
        for j = k+1:n
            if isnan(x(j))
                y = coder.internal.nan(class(x));
                return
            end
        end
        return
    elseif absx < scale
        y = 1 + y*(scale./absx).^p;
        scale = absx;
    elseif ~isinf(absx)
        y = y + (absx./scale).^p;
    end
end
if y > 0 && ~isinf(y)
    y = scale * y.^(1./p);
end

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

function y = genpnorm(x,p)
% Vector p-norm with special cases for various p.
% Assumes that if coder.internal.isCharOrScalarString(p), p = 'fro' or p = 'inf'.
coder.internal.prefer_const(p);
ONE = ones(coder.internal.indexIntClass);
if (coder.internal.isCharOrScalarString(p) && strcmp(p,'fro')) || ...
        (checkIfNumeric(@eq,p,2))
    y = coder.internal.blas.xnrm2(coder.internal.indexInt(numel(x)),x,ONE,ONE);
elseif coder.internal.isCharOrScalarString(p) || ...
        (isa(p,'float') && p > 0 && eml_isinf(p))
    % y = max(abs(x(:))) except nans are not ignored.
    y = zeros('like',real(x));
    for k = 1:numel(x)
        absx = abs(x(k));
        if isnan(absx) || absx > y
            y = absx;
        end
    end
elseif checkIfNumeric(@eq,p,1)
    % y = sum(abs(x(:)));
    y = zeros('like',real(x));
    for k = 1:numel(x)
        y = y + abs(x(k));
    end
elseif isa(p,'float') && p < 0 && eml_isinf(p)
    % y = min(abs(x(:))) except that nans are not ignored.
    y = coder.internal.inf(class(x));
    for k = 1:numel(x)
        absx = abs(x(k));
        if isnan(absx) || absx < y
            y = absx;
        end
    end
elseif checkIfNumeric(@gt,p,0)
    y = vecpospnorm(x,cast(p,'like',real(x)));
elseif checkIfNumeric(@lt,p,0)
    y = vecnegpnorm(x,cast(p,'like',real(x)));
elseif checkIfNumeric(@eq,p,0)
    y = simplenorm(x,p);
else
    y = coder.internal.nan(class(x));
end

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

function y = simplenorm(x,p)
% Simplistic vector p-norm intended for use only when p == 0. Note that p
% can be -0 as well as +0.
y = zeros('like',real(x));
for k = 1:numel(x)
    y = y + abs(x(k)).^p;
end
y = y.^(1./p);

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

function p = checkIfNumeric(f,x,varargin)
if coder.internal.isBuiltInNumeric(x)
    p = f(x,varargin{:});
else
    p = false;
end

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

function y = matFroNorm(x)
if coder.internal.avoidArrayFlattening
    m = coder.internal.indexInt(size(x,1));
    n = coder.internal.indexInt(size(x,2));
    y = zeros('like',real(x));
    scale = coder.const(eps(class(x))*sqrt(realmin(class(x))));
    if coder.isRowMajor
        for i = 1:m
            for j = 1:n
                [y,scale] = yupdate(abs(real(x(i,j))),y,scale);
                if ~isreal(x)
                    [y,scale] = yupdate(abs(imag(x(i,j))),y,scale);
                end
            end
        end
    else
        for j = 1:n
            for i = 1:m
                [y,scale] = yupdate(abs(real(x(i,j))),y,scale);
                if ~isreal(x)
                    [y,scale] = yupdate(abs(imag(x(i,j))),y,scale);
                end
            end
        end
    end
    y = scale*sqrt(y);
else
    ONE = ones(coder.internal.indexIntClass);
    y = coder.internal.blas.xnrm2(coder.internal.indexInt(numel(x)),x,ONE,ONE);
end

function [y,scale] = yupdate(absx,y,scale)
coder.inline('always');
if isnan(absx)
    y = absx;
elseif isinf(absx)
    y = y + absx; % If y is NaN, preserve the NaN.
elseif absx > scale
    t = scale/absx;
    y = 1 + y*(t*t);
    scale = absx;
else
    t = absx/scale;
    y = y + t*t;
end

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