function c = mtimes(a0,b0)
% MATLAB Code Generation function the @fi/mtimes operation

% $INCLUDE(DOC) toolbox/eml/lib/fixedpoint/@embedded/@fi/mtimes.m
% Copyright 2002-2014 The MathWorks, Inc.
%#codegen

eml_allow_mx_inputs;
eml_invariant(nargin == 2, 'fixed:coder:notEnoughInputArguments');
coder.extrinsic('emlGetBestPrecForMxArray');
coder.extrinsic('emlGetNTypeForTimes');
coder.extrinsic('emlGetNTypeForMTimes');
coder.extrinsic('eml_fi_math_with_same_types');

eml_property('associative');
if (eml_is_const(isscalar(a0)) && isscalar(a0)) || ...
        (eml_is_const(isscalar(b0)) && isscalar(b0))
    eml_property('commutative');
end

% Check for ambiguous types and return with the correct size output
if eml_ambiguous_types && (isfi(a0) || isfi(b0))
  rowA = size(a0,1); colB = size(b0,2);
  iscoutReal = isreal(a0) && isreal(b0);
  couttemp = zeros(rowA,colB);
  if iscoutReal
      c = coder.ignoreConst(couttemp);
  else
      c = coder.ignoreConst(complex(couttemp,couttemp));
  end
  return;
end

if (isscalar(a0)&&eml_is_const(isscalar(a0))) || (isscalar(b0)&&eml_is_const(isscalar(b0)))
    if ~isfi(b0) % fi * non-fi
        ta = eml_typeof(a0);
        biasA     = coder.const(get(ta,'Bias'));
        non_zero_Bias = (biasA~=0);
        safA      = coder.const(get(ta,'SlopeAdjustmentFactor'));
        non_trivial_SAF = (safA~=1);
    elseif ~isfi(a0) % non-fi * fi
        tb = eml_typeof(b0);
        biasB     = coder.const(get(tb,'Bias'));
        non_zero_Bias = (biasB~=0);
        safB      = coder.const(get(tb,'SlopeAdjustmentFactor'));
        non_trivial_SAF = (safB~=1);
    else
        ta = eml_typeof(a0);
        tb = eml_typeof(b0);
        biasA     = coder.const(get(ta,'Bias'));
        biasB     = coder.const(get(tb,'Bias'));
        safA      = coder.const(get(ta,'SlopeAdjustmentFactor'));
        safB      = coder.const(get(tb,'SlopeAdjustmentFactor'));
        non_zero_Bias = (biasA~=0)||(biasB~=0);
        non_trivial_SAF = (safA~=1)||(safB~=1);
    end
    isslopebias_in = non_zero_Bias||non_trivial_SAF;
    iscomplex_in   = ~isreal(a0) || ~isreal(b0);
      
    eml_invariant(coder.const((isslopebias_in&&iscomplex_in)==0),...
             'fixed:fi:notDefinedForComplexSlopeBiasFi', 'MTIMES');
    c = times(a0,b0);
else
  
  eml_invariant(size(a0,2)==size(b0,1), ...
      'fixed:fi:innerDimAgree');
  eml_invariant(ndims(a0)<=2 && ndims(b0)<=2, ...
      'fixed:fi:inputsMustBe2D');
  
  if ( (isfi(a0) && isscaledtype(a0)) || ...
       (isfi(b0) && isscaledtype(b0)) )
    % Fixed FI
    if ~isfi(b0) % fi * non-fi
        fixedOpFloatYieldsFloating = coder.const(strcmpi(get(fimath(a0),'FixedOpFloatingYields'),'Floating'));
        if isa(b0,'double') && fixedOpFloatYieldsFloating
            c = double(a0) * b0;
            return
        elseif isa(b0,'single') && fixedOpFloatYieldsFloating
            c = single(a0) * b0;
            return
        end
        ta = eml_typeof(a0);
        a = a0;
        cHasLocalFimath = coder.const(eml_fimathislocal(a0));
        f = eml_fimath(a0);
        if isinteger(b0)
            % Don't auto-scale built-in integers
            b = fi(b0);
            tb = eml_typeof(b);
        elseif islogical(b0)
            % Don't auto-scale logicals
            b = fi(b0,0,1,0);
            tb = eml_typeof(b);
        else
            % Get best precision numerictype for b
            eml_invariant(eml_is_const(b0), ...
                'fixed:coder:fiNonFiOpForm_NonFiMustBeConstant', '*', '*');
            eml_invariant(isnumeric(b0), 'fixed:coder:inputMustBeNumeric');
            tb = coder.const(emlGetBestPrecForMxArray(b0,ta));
            b = eml_cast(b0,tb,f);
        end
    elseif ~isfi(a0) % non-fi * fi
        fixedOpFloatYieldsFloating = coder.const(strcmpi(get(fimath(b0),'FixedOpFloatingYields'),'Floating'));
        if isa(a0,'double') && fixedOpFloatYieldsFloating
            c = a0 * double(b0);
            return
        elseif isa(a0,'single') && fixedOpFloatYieldsFloating
            c = a0 * single(b0);
            return
        end
        tb = eml_typeof(b0);
        b = b0;
        cHasLocalFimath = coder.const(eml_fimathislocal(b0));
        f = eml_fimath(b0);
        if isinteger(a0)
            % Don't auto-scale built-in integers
            a = fi(a0);
            ta = eml_typeof(a);
        elseif islogical(a0)
            % Don't auto-scale logicals
            a = fi(a0,0,1,0);
            ta = eml_typeof(a);
        else
            % Get best precision numerictype for a
            eml_invariant(eml_is_const(a0), ...
                'fixed:coder:fiNonFiOpForm_NonFiMustBeConstant', '*', '*');
            eml_invariant(isnumeric(a0), 'fixed:coder:inputMustBeNumeric');
            ta = coder.const(emlGetBestPrecForMxArray(a0,tb));
            a = eml_cast(a0,ta,f);
        end
    else % fi * fi
          % Obtain the numerictypes of a0 & b0
          ta = eml_typeof(a0); tb = eml_typeof(b0);
          % Verify that the datatypes are the same
          % - Scaled-type with floating not allowed
          % - Single with Double not allowed
          [errID,a2SD,b2SD,Tsd] = coder.const(@eml_fi_math_with_same_types,ta,tb);
          eml_invariant(isempty(errID), errID);
          % Get the fimaths
          fa = eml_fimath(a0); fb = eml_fimath(b0); 
          
          % Check the fimaths and determine the output fimath
          [f,cHasLocalFimath] = eml_checkfimathforbinaryops(a0,b0);
          
          % Check if a or b have to cast into scaled-doubles
          if a2SD
              a = eml_cast(a0,Tsd,fa);
              b = b0;
          elseif b2SD
              b = eml_cast(b0,Tsd,fb);
              a = a0;
          else
              a = a0; b = b0;
          end
          
   end
      
      % Get the product type tc
      aIsReal = isreal(a); bIsReal = isreal(b);
      [rowsA, colsA] = size(a); colsB = double(size(b,2));

      maxWL = getFixedPointWidthLimit();
      tp = coder.const(emlGetNTypeForTimes(ta,tb,f,true,true,maxWL));
      
      if eml_is_const(size(a))
          tc = coder.const(emlGetNTypeForMTimes(ta,tb,f,aIsReal,bIsReal,colsA,true,maxWL));
      else
          % When input sizes can change at run-time, we only allow SumModes
          % 'KeepLSB' and 'SpecifyPrecision'; For these two modes the output
          % NumericType does not depend on the inner matrix dimension; we pass 2 as a
          % dummy inner matrix dimension argument
          tc = coder.const(emlGetNTypeForMTimes(ta,tb,f,aIsReal,bIsReal,2,false,maxWL));
      end
      % Check for the SlopeBias mode, complex inputs are not supported in this case
      biasA     = coder.const(get(ta,'Bias'));
      biasB     = coder.const(get(tb,'Bias'));
      non_zero_Bias = (biasA~=0)||(biasB~=0);
      
      safA      = coder.const(get(ta,'SlopeAdjustmentFactor'));
      safB      = coder.const(get(tb,'SlopeAdjustmentFactor'));
      non_trivial_SAF = (safA~=1)||(safB~=1);
      
      isslopebias_in = non_zero_Bias||non_trivial_SAF;
      iscomplex_in   = ~isreal(a0) || ~isreal(b0);
      
      eml_invariant(coder.const((isslopebias_in&&iscomplex_in)==0), ...
          'fixed:fi:notDefinedForComplexSlopeBiasFi', 'MTIMES');
      
      fullPrecSum = coder.const(strcmpi(get(f,'SumMode'),'FullPrecision'));
      cb4sum      = coder.const(get(f,'CastBeforeSum'));
      if ~(fullPrecSum || cb4sum)
          eml_invariant(false, ...
              'fixed:coder:fiMathOpsFullPrecisionCastBeforeSum');
      end
      
      
      if aIsReal && bIsReal
          c = eml_cast(zeros(rowsA,colsB),tc,f);
      else
          cd = complex(zeros(rowsA,colsB),zeros(rowsA,colsB));  
          c = eml_cast(cd,tc,f);
      end
      
      for l =1:rowsA
          for m = 1:colsB
              for k = 1:colsA
                  % c(l,m) = c(l,m)+(a(l,k).*b(k,m));
                  % If a & b are real then assignment_type = product_type = tp
                  % If a & b are complex then assignment_type = sum_type:
                  % tc and product_type is tp
                  if aIsReal || bIsReal
                      prodAB = eml_fixpt_times(a(l,k),b(k,m),tp,tp,f);
                  else
                      prodAB = eml_fixpt_times(a(l,k),b(k,m),tc,tp,f);
                  end
                  c(l,m) = eml_plus(c(l,m),prodAB,tc,f); 
              end
          end
      end
      c = eml_fimathislocal(c,cHasLocalFimath);
      
  elseif isfi(a0) && isfloat(a0) && islogical(b0)
      c = fi(double(a0) * b0,'DataType',a0.DataType);
      c = eml_fimathislocal(c,isfimathlocal(a0));

  elseif isfi(b0) && isfloat(b0) && islogical(a0)
      c = fi(a0 * double(b0),'DataType',b0.DataType);
      c = eml_fimathislocal(c,isfimathlocal(b0));

  elseif ( isfi(a0) && isfloat(a0) ) || ...
          ( isfi(b0) && isfloat(b0) )
      % True Double or True Single FI

      % call MATLAB mtimes directly
      check4constNonFI   = false; % non-FI need not be constant
      check4numericData  = true;  % non-FI must be numeric
      check4sameDatatype = true;  % The datatypes of two inputs must be same
      [ain,bin]          = eml_fi_cast_two_inputs(a0,b0,'.*',check4constNonFI,...
                                                  check4numericData,check4sameDatatype);
      [t,f,f_islocal]    = eml_fi_get_numerictype_fimath(a0,b0);
      
      c = eml_cast(ain * bin, t, f);
      c = eml_fimathislocal(c,f_islocal);
      
  else
      % FI datatype not supported
      eml_fi_assert_dataTypeNotSupported('MTIMES');
  end
  
end
