function c = bitshift(a,k,~)
% MATLAB Code Generation Library function.
%
% BITSHIFT bitwise shift.
%    C = BITSHIFT(A,K) returns a fi object whose stored-integer is equal
%    to the stored-integer of the input fi object A shifted by K bits.
%    A must be a fi object matrix, array, or a scalar fi object (signed or unsigned)
%    K can be any scalar number.
%
% The following cases will not be handled by this function:
%    A is not a fi object
%
% The following cases will error out:
% 1. A is a fi object with DataType of double
% 2. k is a complex number
% 3. k is a vector
%
% Note: Difference from the MATLAB Toolbox BITSHIFT:
%   the MATLAB Toolbox BITSHIFT restricts a to be unsigned (uint8, uint16, uint32, ...). Here we loose the restriction
%   just to be consistent with the Fixed-Point Designer BITSHIFT.
%
%   bitshift(a,K,N) format is not supported to be consistent with the Fixed-Point Designer BITSHIFT.
%

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

% To make sure the number of the input arguments is right
eml_invariant(nargin == 2, ...
    'fixed:coder:fiInvalidMethodSignature', 'BITSHIFT');

if eml_ambiguous_types
    c = coder.ignoreConst(reshape(zeros(size(a)), size(a)));
    return;
end

fnname = 'bitshift';
coder.internal.prefer_const(k);
eml_shift_checks(fnname, a, k); 

if ~isscaledtype(a)
    % FI datatype not supported
    eml_invariant(false, ...
        'fixed:coder:fiFcnDTypeErrorFixPtOnly','BITSHIFT');
end

% Inline the generated code if a is a scalar
if isscalar(a)
    coder.inline('always');
end

if isreal(a)
    c = localBitShift(a,k);
else
    cr = localBitShift(real(a),k);
    ci = localBitShift(imag(a),k);
    c = complex(cr,ci);
end

%--------------------------------------------------------------------------------
function yout = localBitShift(xin,kin)

coder.internal.prefer_const(kin);

coder.inline('always');

Tin = numerictype(xin);
Fin = fimath(xin);
yHasLocalFimath = eml_fimathislocal(xin);

if kin==0
    % Shift by zero is the identity
    yout = xin;
elseif isscaleddouble(xin)
    % For ScaledDouble types, do the arithmetic in double and then cast back.
    % Apply the bit shift to the stored integer part, then scale by slope and add bias back in.
    y = (double(xin)-xin.Bias)./xin.Slope .* pow2(double(kin)) .* xin.Slope + xin.Bias;
    yout = eml_fimathislocal(eml_cast(y, Tin, Fin),yHasLocalFimath);
else
    x = rescale(xin,1,0);% x is the stored integer
    y = x;
    if kin>0 % kin may be a run-time value, test it once per input.
        % LEFT shift (non-negative shift value)
        k = uint8(kin);
        for m = 1 : numel(y)
            y(m) = intLeftShift(x(m),k);
        end
    else
        % RIGHT shift (negative shift value)
        if (isfi(kin) && isfixed(kin) && coder.const(double(realmax(kin)) <= 127))
            % Shift value inside supported range
            k = uint8(-kin);
        else
            % FI or NON-FI shift value: check range
            if kin <= -128
                % Shift value outside supported range
                k = uint8(128);
            else
                % Shift value inside supported range
                k = uint8(-kin);
            end
        end
        for m = 1 : numel(y)
            y(m) = intRightShift(x(m),k);
        end
    end
    yout = eml_fimathislocal(eml_dress(y, Tin, Fin),yHasLocalFimath);
end

%--------------------------------------------------------------------------------
function y = intLeftShift(x,k)

coder.internal.prefer_const(k);

T = eml_typeof(x);
F = eml_fimath(x);

doSaturate = coder.const(isequal(F.OverflowMode,'saturate'));

% Actually performing throw-away shifts (ie, a shift that throws away all
% the bits in the input) is not safe in C.
% isThrowAwayShift = (k >= T.WordLength);
% We don't use the temporary isThrowAwayShift because it generates poor HDL code.

if doSaturate
    zero = eml_fimathislocal(eml_cast(0,T,F),eml_fimathislocal(x));
    one = eml_fimathislocal(eml_cast(1,T,F),eml_fimathislocal(x));

    % We are shifting left and might drop non-zero bits or change the
    % sign-bit.
    % If that happens saturate.
    if (k >= T.WordLength)
        if T.Signed
           % We have not dropped the sign bit.
           droppedBits = eml_lshift(x,uint8(1));
        else
           droppedBits = x;
        end 
        k(1) = 0; % Can be any valid shift amount.
    else
        if T.Signed
            % We have to see if the bit we are going to shift into the sign
            % position is different than the one currently there.
            droppedBits = eml_rshift(x,int8(T.WordLength - k - 1));
        else
            droppedBits = eml_rshift(x,int8(T.WordLength - k));
        end
    end

    % It would be cool to do the saturation case with out any control-flow.
    %  This is possible, but my best attempt so far is too hideous to
    %  contemplate.
    if T.Signed
        signBitVector = eml_rshift(x,uint8(T.WordLength - 1)); % Does sign extension.
        
        % If saturation is needed the result is
        %      if  signBit then 100000 (ie signBit not(signBitVector))
        %      if ~signBit then 011111 (ie signBit not(signBitVector))
        % If saturation is not needed the result is
        %      y << k but msb(y) == signBit

        if droppedBits ~= signBitVector % if saturationNeeded.
            msbone = eml_lshift(one,uint8(T.WordLength - 1));
            y = eml_bitnot(eml_bitxor(msbone,signBitVector));
        else
            y = eml_lshift(x,k);
        end
    else % unsigned case.
        if droppedBits == zero
            y = eml_lshift(x,k);
        else % Saturate.
            y = eml_bitnot(zero);
        end
    end
else %Wrap
    if (k >= T.WordLength)
        y = eml_fimathislocal(eml_cast(0,T,F),eml_fimathislocal(x));
    else
        y = eml_lshift(x,k);
    end
end

%--------------------------------------------------------------------------------
function y = intRightShift(x,k)

coder.inline('always');

T = eml_typeof(x);

if k>=T.WordLength
    if T.Signed        
        % Right shifting preserves the sign-bit.
        y = eml_rshift(x,uint8(T.WordLength - 1));        
    else
        F = fimath(x);
        y = eml_fimathislocal(eml_cast(0,T,F),eml_fimathislocal(x));
    end
else
    y = eml_rshift(x,k);
end
