function c = dot(a,b,dim)
%MATLAB Code Generation Library Function

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

coder.internal.assert(nargin > 1,'MATLAB:minrhs');
coder.internal.assert(isa(a,'float') || ischar(a), ...
    'Coder:toolbox:unsupportedClass','dot',class(a));
coder.internal.assert(isa(b,'float') || ischar(b), ...
    'Coder:toolbox:unsupportedClass','dot',class(b));
coder.internal.assert(isequal(size(a),size(b)) ||  ...
    (isvector(a) && isvector(b) && numel(a) == numel(b)), ...
    'MATLAB:dot:InputSizeMismatch');
ONE = coder.internal.indexInt(1);
ZERO = coder.internal.indexInt(0);
cZERO = coder.internal.scalarEg(a,b);
if nargin < 3
    if isvector(a)
        if coder.target('HDL')
            c = vdotp(a,b);
        else
            c = coder.internal.ifConstantFoldingThenElse( ...
                vdotp(a,b), ...
                vdotp(coder.ignoreSize(a),coder.ignoreSize(b)));
        end
        return
    elseif isequal(a,[])
        c = cZERO;
        return
    end
    dim = coder.internal.nonSingletonDim(a);
else
    coder.internal.prefer_const(dim);
    coder.internal.assert(eml_is_const(dim) || eml_option('VariableSizing'), ...
        'Coder:toolbox:dimNotConst');
    coder.internal.assertValidDim(dim);
end
if size(a,dim) == 1 % Covers dim > ndims case.
    sz = size(a);
    % This condition is designed to be always constant-folded.
    if eml_is_const(dim) && dim <= eml_ndims(a)
        % This assignment tells inference that this dimension
        % of the result is static 1. The assignment itself
        % is removed by optimizations once its job is done.
        sz(dim) = 1;
        c = coder.nullcopy(eml_expand(cZERO,sz));
    else
        c = coder.nullcopy(eml_expand(cZERO,sz));
    end
    for j = ONE:numel(c)
        c(j) = coder.internal.conjtimes(a(j),b(j));
    end
    return
end
% This strided algorithm is OK for a few vectors. For a large example, one might
% prefer c = sum(conj(a).*b,dim).
sz = size(a);
sz(dim) = 1;
c = coder.nullcopy(eml_expand(cZERO,sz));
vlen = coder.internal.indexInt(size(a,dim));
vstride = coder.internal.prodsize(a,'below',dim);
vspread = (vlen - 1)*vstride;
npages = coder.internal.prodsize(a,'above',dim);
i2 = ZERO;
ic = ZERO;
for i = 1:npages
    i1 = i2;
    i2 = i2 + vspread;
    for j = 1:vstride
        ic = ic + 1;
        i1 = i1 + 1;
        i2 = i2 + 1;
        c(ic) = vdot(a,b,i1,vstride,i2,vlen);
    end
end

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

function c = vdot(a,b,i1,stride,i2,n)
% Vector dot product:  c = dot(a(i1:stride:i2),b(i1:stride:i2)).
% Dispatches to BLAS for float inputs and does the equivalent of
% sum(conj(a).*b) for non-floats.  The argument n is mathematically
% redundant, given that n = 1 + (i2 - i1)/stride, but i1, stride, and n are
% needed when calling the BLAS with float inputs, while i1, stride, and i2
% are most convenient for handling non-float inputs.
coder.inline('always');
if isa(a,'float') && isa(b,'float')
    c = coder.internal.blas.xdotc(n,a,i1,stride,b,i1,stride);
else
    c = 0;
    for k = i1:stride:i2
        c = c + coder.internal.conjtimes(a(k),b(k));
    end
end

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

function c = vdotp(a,b)
% Reusable kernel function.
% Not coder.inline('always').
ONE = coder.internal.indexInt(1);
n = coder.internal.indexInt(numel(a));
c = vdot(a,b,ONE,ONE,n,n);

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