function yreturn = abs(xfi, var1, var2)
%ABS    Fixed-point abs function for MATLAB Code Generation
%
%   ABS(A) will return the absolute value of real or complex input A

% $INCLUDE(DOC) toolbox/eml/lib/fixedpoint/@embedded/@fi/abs.m $
% Copyright 2005-2014 The MathWorks, Inc.
%#codegen
% This function accepts mxArray input argument

coder.extrinsic('eml_fiabs_helper');
coder.extrinsic('strcmpi');
coder.extrinsic('emlGetNTypeForTimes');
coder.extrinsic('emlGetNTypeForPlus');
eml_allow_mx_inputs;      
eml_invariant(~strcmp(coder.target(),'hdl') || isreal(xfi), ...
    'fixed:hdlcoder:complexAbsHDLCodeGenUnsupported');
if eml_ambiguous_types
    yreturn = coder.ignoreConst(zeros(size(xfi)));
    return;
end

eml_invariant(nargin >= 1 && nargin <= 3, ...
    'fixed:coder:incorrectNumberOfInputs');

if isfloat(xfi)
    
    fimathSpecified = false;
    
    % Parse the inputs
    switch nargin
        
      case 1 % abs(x)
        Ty = coder.const(eml_typeof(xfi));
        Fy = coder.const(eml_fimath(xfi));
        
      case 2 % abs(x,T) or abs(x,F) 
        eml_invariant(eml_is_const(var1), ...
            'fixed:coder:abs_X_Var1_Var1MustBeConstant');
        if isnumerictype(var1)
            Ty = localGetAbsNumericType(xfi,var1);
            Fy = coder.const(eml_fimath(xfi));
        elseif isfimath(var1)
            Ty = coder.const(eml_typeof(xfi));
            Fy = coder.const(var1);
        else
            eml_invariant(false, ...
                'fixed:fi:invalidSyntax','ABS');
        end
        
      case 3 % abs(x,T,F) or abs(x,F,T)
        eml_invariant(eml_is_const(var1) && eml_is_const(var2),...
            'fixed:coder:abs_X_Var1_Var2_VarsMustBeConstant');
        if isnumerictype(var1)&&isfimath(var2) % abs(x,T,F)
            Ty = localGetAbsNumericType(xfi,var1);
            Fy = coder.const(var2);            
        elseif isfimath(var1)&&isnumerictype(var2) % abs(x,F,T)   
            Ty = localGetAbsNumericType(xfi,var2);
            Fy = coder.const(var1);
        else 
            eml_invariant(false, ...
                'fixed:fi:invalidSyntax','ABS');
        end
        
    end
    
    if localAbsIsDouble(Ty)||localAbsIsSingle(Ty)
        xfi_type = eml_cast(0,Ty);
        dType = eml_fi_getDType(xfi_type);
    else
        dType  = eml_fi_getDType(xfi);
    end
    
    xDType = eml_cast(xfi,dType);
    yDType = abs(xDType);
    y      = eml_cast(yDType,Ty,Fy);      
    
elseif isscaledtype(xfi)
    
    if isslopebiasscaled(xfi.numerictype)
        eml_invariant(false, 'fixed:fi:binaryPointOnlyFi_NT','ABS');
    end
    
    switch nargin

      case 1 % abs(x) 
        if eml_is_const(xfi)
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,xfi);
        else % ~eml_is_const(x)
            data = eml_cast(0,numerictype(xfi),fimath(xfi));
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,data);
        end

      case 2 % abs(x,T) or abs(x,F) 
        eml_invariant(eml_is_const(var1), ...
            'fixed:coder:abs_X_Var1_Var1MustBeConstant');
        if eml_is_const(xfi)
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,xfi,var1);
        else % ~eml_is_const(x)
            data = eml_cast(0,numerictype(xfi),fimath(xfi));
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,data,var1);
        end

      case 3 % abs(x,T,F) or abs(x,F,T)
        eml_invariant(eml_is_const(var1) && eml_is_const(var2),...
            'fixed:coder:abs_X_Var1_Var2_VarsMustBeConstant');
        if eml_is_const(xfi)
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,xfi,var1,var2);
        else % ~eml_is_const(x)
            data = eml_cast(0,numerictype(xfi),fimath(xfi));
            [Ty,Fy,fimathSpecified] = coder.const(@eml_fiabs_helper,data,var1,var2);
        end

    end
    
    if isreal(xfi)
        % compute abs of real input
        if localAbsIsDouble(Ty)||localAbsIsSingle(Ty)
            % handle the case where input is of datatype fixed but inferred
            % numerictype is of datatype float
            y = localAbsToFloat(xfi,Ty,Fy);            
           
        else
            Tx = numerictype(xfi);
            if isscaleddouble(Tx) || isscaleddouble(Ty)
                y = eml_cast(zeros(size(xfi)), numerictype(Ty,'DataType','scaleddouble'), Fy);
            else 
                y = eml_cast(zeros(size(xfi)), Ty, Fy);
            end
            
            for k = 1:numel(xfi)
                if xfi(k) < 0
                    y(k) = eml_uminus(xfi(k), numerictype(y), Fy);
                else
                    y(k) = eml_cast(xfi(k), numerictype(y), Fy);
                end
            end
        end

    else
        % compute abs of complex input
        if isboolean(xfi)||coder.const(strcmpi(get(Ty, 'DataType'), 'boolean'))
            eml_invariant(false, ...
                'fixed:fi:unsupportedComplexBoolean');
        end
        
        fullPrecSum = coder.const(strcmpi(get(Fy,'SumMode'),'FullPrecision'));
        cb4sum      = coder.const(get(Fy,'CastBeforeSum'));
        if ~(fullPrecSum || cb4sum)
            eml_invariant(false, ...
                'fixed:coder:fiMathOpsFullPrecisionCastBeforeSum');
        end
        if localAbsIsDouble(Ty)||localAbsIsSingle(Ty)
            % handle the case where input is of datatype fixed but inferred
            % numerictype is of datatype float
            y = localAbsToFloat(xfi,Ty,Fy);            
            
        else
            % handle the case where input and inferred numerictype are of
            % datatype fixed 
            tin = numerictype(xfi);
            xfi_re = fi(real(xfi),tin,Fy);
            xfi_im = fi(imag(xfi),tin, Fy);
            
            maxWL = getFixedPointWidthLimit();
            tp = coder.const(emlGetNTypeForTimes(tin,tin,Fy,true,true,maxWL));
            
            xfi_re_sq = eml_times(xfi_re,xfi_re,tp,Fy);
            xfi_im_sq = eml_times(xfi_im,xfi_im,tp,Fy);
            if issigned(xfi)
                tpu = numerictype(false,tp.WordLength,tp.FractionLength);
                tpu = numerictype(tpu,'DataType',tp.DataType);
                
                if isfixed(xfi)
                    xfi_re_sqU = eml_reinterpretcast(xfi_re_sq,tpu);
                    xfi_im_sqU = eml_reinterpretcast(xfi_im_sq,tpu);
                else
                    xfi_re_sqU = eml_cast(xfi_re_sq,tpu);
                    xfi_im_sqU = eml_cast(xfi_im_sq,tpu);
                end

                ts = coder.const(emlGetNTypeForPlus(tpu,tpu,Fy,maxWL));   
                xfi_abs_sq = eml_plus(xfi_re_sqU,xfi_im_sqU,ts,Fy);    

            else
                ts = coder.const(emlGetNTypeForPlus(tp,tp,Fy,maxWL));   
                xfi_abs_sq = eml_plus(xfi_re_sq,xfi_im_sq,ts,Fy);     
            end
            y = sqrt(xfi_abs_sq,Ty,Fy);
       
        end
    end    
else
    eml_fi_assert_dataTypeNotSupported('ABS'); % FI datatype not supported
end

% If the input fi does not have an attached fimath, then the output doesn't either
if coder.const(~eml_fimathislocal(xfi)) || fimathSpecified
    yreturn = eml_fimathislocal(y,false);
else
    yreturn = eml_cast(eml_fimathislocal(y,true),numerictype(y),fimath(xfi));
end

%--------------------------------------------------------------------------------
function Ty = localGetAbsNumericType(x,T)
% Returns numerictype of output. This local function should
% be used only when input x is a float fi. The output, Ty is const.

eml_transient;

if (coder.const(isnumerictype(T)) && (localAbsIsSingle(T) || localAbsIsDouble(T)))
    % NumericType is float (double/single)
    Ty = coder.const(T);
else
    Ty = coder.const(eml_typeof(x));
end

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

function bOut = localAbsIsSingle(T)
coder.extrinsic('strcmpi');
eml_transient;

bOut = coder.const(strcmpi(get(T, 'DataType'), 'Single'));

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

function bOut = localAbsIsDouble(T)
coder.extrinsic('strcmpi');
eml_transient;

bOut = coder.const(strcmpi(get(T, 'DataType'), 'Double'));

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

function y = localAbsToFloat(xfi,Ty,Fy)

xfi_type = eml_cast(0,Ty);
dType = eml_fi_getDType(xfi_type);
xDType = eml_cast(xfi,dType);
yDType = abs(xDType);
y = eml_cast(yDType,Ty,Fy);      
