function s = dec2bin(d,numBits)
%MATLAB Code Generation Library Function

%   Limitations:
%   * If numBits is supplied, the output will have a minimum of
%     numBits columns, even if d is empty. If numBits is not supplied,
%     the output will have at least one column, even if it is empty.
%   * For code generation, double and single inputs for d must be in the
%     range intmin('int64') <= d < 2^64.
%   * The output is generally variable-size in the number of columns.
%     However, it is possible to force a fixed number of columns. To do
%     this, supply the numBits input as a constant, and make the value
%     large enough. How large depends on the type of d. It requires at
%     least
%         isa(d,'double')  --> numBits >= 64
%         isa(d,'single')  --> numBits >= 64
%         isa(d,'half')    --> numBits >= 32
%         isa(d,'logical') --> numBits >= 1
%         isa(d,'char')    --> numBits >= 8
%         isa(d,'int8')    --> numBits >= 8
%         isa(d,'uint8')   --> numBits >= 8
%         isa(d,'int16')   --> numBits >= 16
%         isa(d,'uint16')  --> numBits >= 16
%         isa(d,'int32')   --> numBits >= 32
%         isa(d,'uint32')  --> numBits >= 32
%         isa(d,'int64')   --> numBits >= 64
%         isa(d,'uint64')  --> numBits >= 64

%   Copyright 1984-2020 The MathWorks, Inc.
%#codegen

coder.internal.allowHalfInputs;
narginchk(1,2);
ONE = coder.internal.indexInt(1);
coder.internal.assert(coder.internal.isBuiltInNumeric(d) || ...
    isa(d,'half') || islogical(d) || ischar(d), ...
    'MATLAB:dec2bin:InvalidDecimalArg');
coder.internal.assert(isreal(d),'MATLAB:dec2bin:MustBeReal');
coder.internal.errorIf(coder.internal.anyNonFinite(d), ...
    'MATLAB:dec2bin:MustBeFinite');
if nargin < 2
    if ~coder.target('MATLAB') && coder.internal.isConst(d)
        s = coder.const(feval('dec2bin',d));
        return
    end
    nbits = ONE; % Need at least one digit even for 0.
else
    coder.internal.prefer_const(numBits);
    if ~coder.target('MATLAB') && ...
            coder.internal.isConst(d) && ...
            coder.internal.isConst(numBits)
        s = coder.const(feval('dec2bin',d,numBits));
        return
    end
    coder.internal.assert((isnumeric(numBits) || ischar(numBits)) && ...
        isreal(numBits) && isscalar(numBits) && numBits(1) >= 0 && ...
        numBits <= intmax(coder.internal.indexIntClass), ...
        'MATLAB:dec2bin:InvalidBitArg');
    nbits = eml_max(ONE,coder.internal.indexInt(round(numBits(1))));
end
if isa(d,'double') || isa(d,'single')
    if isa(d,'single')
        LO = coder.const(single(intmin('int64')));
        HI = coder.const(pow2(single(64)));
    else
        LO = coder.const(double(intmin('int64')));
        HI = coder.const(pow2(64));
    end
    MUST_BE_GE = @(x)ge(x,LO);
    coder.internal.assert( ...
        coder.internal.vAllOrAny('all',d,MUST_BE_GE), ...
        'MATLAB:dec2bin:NegativeValueOutOfRange');
    MUST_BE_LT = @(x)lt(x,HI);
    coder.internal.assert( ...
        coder.internal.vAllOrAny('all',d,MUST_BE_LT), ...
        'Coder:toolbox:FirstArgumentMustBeLessThan','2^64');
end
maxnbits = coder.const(constNBits(d));
if isempty(d)
    s = char(zeros(0,nbits));
elseif isscalar(d)
    s = decToBinScalar(d(1),nbits);
elseif coder.internal.isConstTrue(nbits >= maxnbits)
    s = decToBinFixedLengthVector(d,nbits);
else
    s = decToBinVariableLengthVector(d,nbits);
end

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

function nbits = constNBits(d)
% Constant number of bits actually needed to represent any supported
% element of class(d) as a binary number.
if isinteger(d)
    cls = coder.internal.builtinClass(d);
    nbits = coder.internal.indexInt(coder.internal.int_nbits(cls));
elseif ischar(d)
    nbits = coder.internal.indexInt(8);
elseif isa(d,'half')
    nbits = coder.internal.indexInt(32);
elseif islogical(d)
    nbits = coder.internal.indexInt(1);
else
    % The algorithms here work with an arbitrarily large limit, but we
    % limit to 64 for practical purposes so that customers can supply
    % numBits = 64 to get fixed-size outputs instead of the 2048 that
    % would be needed to cover all possible double precision inputs. (2048
    % would be required since the smallest representation of -realmax in
    % twos complement form would be 1025 bits long, and the convention is
    % to return a power-of-two length for results in twos complement form.)
    nbits = coder.internal.indexInt(64);
end

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

function s = decToBinFixedLengthVector(d,nbits)
% Convert a vector d to binary form using precisely nbits bits.
coder.internal.prefer_const(nbits);
nrows = coder.internal.indexInt(numel(d));
s = char(zeros(nrows,nbits));
for k = 1:nrows
    s(k,:) = decToBinScalar(d(k),nbits);
end

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

function s = decToBinVariableLengthVector(d,numBits)
% Convert a vector d to binary form using at least numBits bits.
coder.internal.prefer_const(numBits);
nrows = coder.internal.indexInt(numel(d));
idxmx = coder.internal.indexInt(0);
idxmn = coder.internal.indexInt(0);
[mx,idxmx(1)] = max(d(:));
[mn,idxmn(1)] = min(d(:));
smx = decToBinScalar(mx,numBits);
if idxmx == idxmn
    % Since the max and min are the same, all rows are identical.
    s = repmat(smx,nrows,1);
    return
end
smn = decToBinScalar(mn,numBits);
lenmx = coder.internal.indexInt(length(smx));
lenmn = coder.internal.indexInt(length(smn));
reqLen = eml_max(lenmx,lenmn);
nbits = eml_max(numBits,reqLen);
s = coder.nullcopy(char(zeros(nrows,nbits)));
% Copy the max and min results into s.
npad = nbits - lenmx;
if d(idxmx) < 0
    padval = '1';
else
    padval = '0';
end
for k = 1:npad
    s(idxmx,k) = padval;
end
for k = 1:lenmx
    s(idxmx,npad + k) = smx(k);
end
npad = nbits - lenmn;
if d(idxmn) < 0
    padval = '1';
else
    padval = '0';
end
for k = 1:npad
    s(idxmn,k) = padval;
end
for k = 1:lenmn
    s(idxmn,npad + k) = smn(k);
end
% Convert the rest of the results, and store them in s.
for k = 1:nrows
    if k ~= idxmn && k ~= idxmx
        s(k,:) = decToBinScalar(d(k),nbits);
    end
end

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

function s = decToBinScalar(d,nbits)
% Convert a scalar d to binary form with at least numBits bits.
coder.internal.prefer_const(nbits);
ZERO = coder.internal.indexInt(0);
sfull = toBin(d(1));
ncolsmax = coder.internal.indexInt(length(sfull));
isNegative = d(1) < 0;
[offset,nb] = toBinOffset(sfull,isNegative);
if coder.internal.isConstTrue(nbits >= ncolsmax) || nbits >= nb
    s = coder.nullcopy(char(zeros(1,nbits)));
else
    s = coder.nullcopy(char(zeros(1,nb)));
end
if nb < nbits
    npad = nbits - nb;
    if d < 0
        padval = '1';
    else
        padval = '0';
    end
    for k = 1:npad
        s(1,k) = padval;
    end
else
    npad = ZERO;
end
for k = 1:nb
    s(1,npad + k) = sfull(1,offset + k);
end

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

function [offset,nbits] = toBinOffset(sfull,isNegative)
% Find the offset to the actual bits we will return from the canonical
% representation sfull.
coder.inline('always');
ONE = coder.internal.indexInt(1);
FOUR = coder.internal.indexInt(4);
EIGHT = coder.internal.indexInt(8);
lastIdx = coder.internal.indexInt(numel(sfull));
if isNegative
    % Length s must be a power of 2, no less than 4.
    % Since sfull is in twos complement form, sfull(1) == '1'.
    % Find the first 0.
    idx = findFirst('0',sfull);
    if idx == 0
        % This is d == -1, just need the last 8 bits.
        offset = lastIdx - EIGHT;
        nbits = EIGHT;
    else
        % For negative numbers, we use a power-of-two number of bits
        % (unless the result would be longer than sfull).
        idx = idx - ONE;
        minlen = eml_max(FOUR,lastIdx - idx + 1);
        nbits = eml_min(lastIdx,eml_lshift(ONE,nextpow2(minlen)));
        offset = lastIdx - nbits;
    end
else
    idx = findFirst('1',sfull);
    if idx == 0
        % This is d == 0, just need the last bit.
        idx = lastIdx;
    end
    offset = idx - ONE;
    nbits = lastIdx - offset;
end
assert(nbits <= lastIdx); %<HINT>

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

function idx = findFirst(c,s)
coder.internal.prefer_const(c);
idx = coder.internal.indexInt(0);
ns = coder.internal.indexInt(numel(s));
for k = 1:ns
    if s(k) == c
        idx = k;
        break
    end
end

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

function s = toBin(d)
% Convert scalar d to binary form, a char array of '0' and '1' values.
% The output s is fixed-size.
eml_assert(coder.internal.isConstTrue(isscalar(d)), ...
    'Private function toBin only supports scalar inputs.');
nbits = coder.const(constNBits(d));
if isfloat(d)
    if d < 0
        s = fdec2bin(-d,nbits);
        s = tc(s,nbits);
    else
        s = fdec2bin(d,nbits);
    end
elseif islogical(d)
    if d
        s = '1';
    else
        s = '0';
    end
elseif ischar(d)
    s = idec2bin(uint8(d),nbits);
else
    if d < 0
        if d == intmin(class(d))
            s = repmat('1',1,nbits);
        else
            s = idec2bin(-d,nbits);
            s = tc(s,nbits);
        end
    else
        s = idec2bin(d,nbits);
    end
end

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

function y = tc(b,nbits)
% Returns the twos complement of the binary representation b.
coder.internal.prefer_const(nbits);
nx = coder.internal.indexInt(length(b));
y = repmat('1',1,nbits);
xoffset = nbits - nx;
% Perform (2^n - 1) - x.
for k = 1:nx
    if b(k) == '1'
        y(k + xoffset) = '0';
    end
end
% Add 1 to reach 2^n - x.
carry = true;
for k = nbits:-1:1
    if carry
        if y(k) == '1'
            y(k) = '0';
        else
            y(k) = '1';
            carry = false;
        end
    end
end

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

function s = idec2bin(d,nb)
% DEC2BIN for non-negative integer and char inputs.
coder.inline('always');
coder.internal.prefer_const(nb);
ucls = coder.internal.unsignedClass(class(d));
s = repmat('0',1,nb);
dk = eml_cast(d,ucls,'to zero','spill');
UONE = ones(ucls);
UZERO = zeros(ucls);
for k = 1:nb
    mask = eml_lshift(UONE,nb - k);
    if eml_bitand(dk,mask) ~= UZERO
        s(k) = '1';
    end
end

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

function s = fdec2bin(d,nb)
% DEC2BIN for non-negative floating point inputs.
coder.inline('always');
coder.internal.prefer_const(nb);
ONE = coder.internal.indexInt(1);
HDL = coder.target('HDL');
s = repmat('0',1,nb);
for j = nb:-1:ONE
    olddi = d;
    d = floor(d/2);
    if 2*d < olddi
        s(j) = '1';
    end
    if ~HDL && ~(d > 0)
        break
    end
end

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