function M1 = mtimes(M1,M2)
%MTIMES  Multiplies two input/output models together.
%
%   M = MTIMES(M1,M2) performs the multiplication M = M1 * M2. For dynamic
%   systems, this is equivalent to connecting M1 and M2 in series as follows:
%
%      u ----> M2 ----> M1 ----> y
%
%   If M1 and M2 are arrays of models, their product is a model array of 
%   the same size where the k-th system is obtained by
%      M(:,:,k) = M1(:,:,k) * M2(:,:,k) .
%
%   See also SERIES, INPUTOUTPUTMODEL/MLDIVIDE, INPUTOUTPUTMODEL/MRDIVIDE, 
%   INPUTOUTPUTMODEL/INV, INPUTOUTPUTMODEL.

%   Author(s): P. Gahinet
%   Copyright 1986-2011 The MathWorks, Inc.
try
   if isnumeric(M1) && all(size(M1,[1 2])==1) && hasCustomScalarMultiply_(M2)
      % Bypass for SCALAR * M2 (e.g., 3 * @pid, 3 * @mechss)
      M1 = M2.leftMultiplyByScalar_(M1);
      
   elseif isnumeric(M2) && all(size(M2,[1 2])==1) && hasCustomScalarMultiply_(M1)
      % Bypass for M1 * SCALAR
      M1 = M1.rightMultiplyByScalar_(M2);
      
   else
      % Harmonize types
      if ~ltipack.hasMatchingType('mtimes',M1,M2)
         [M1,M2] = ltipack.matchType('mtimes',M1,M2);
      end
         
      % Both operands are now of the same type
      % Check I/O dimensions and handle scalar multiplication
      sizes1 = M1.IOSize_;
      sizes2 = M2.IOSize_;
      ScalarFlags = false(1,2);
      if all(sizes1(1:2)==1) && sizes2(1)~=1
         % M1 is SISO (scalar multiplication)
         if any(sizes2==0)
            % Scalar * Empty = Empty
            M1 = M2;   return
         elseif sizes2(1)<=sizes2(2)
            % Evaluate as (M1 * eye(ny2)) * M2
            ScalarFlags(1) = true;
         else
            % Evaluate as M2 * (M1 * eye(nu2))
            tmp = M2;  M2 = M1;  M1 = tmp;
            ScalarFlags(2) = true;
         end
      elseif all(sizes2(1:2)==1) && sizes1(2)~=1
         % M2 is SISO (scalar multiplication)
         if any(sizes1==0)
            % Scalar * Empty = Empty
            return
         elseif sizes1(1)>=sizes1(2)
            % Evaluate as M1 * (M2 * eye(nu1))
            ScalarFlags(2) = true;
         else
            % Evaluate as (M2 * eye(ny1)) * M1
            tmp = M1;  M1 = M2;  M2 = tmp;
            ScalarFlags(1) = true;
         end
      elseif ~any(ScalarFlags) && sizes1(2)~=sizes2(1)
         ctrlMsgUtils.error('Control:combination:IncompatibleIODims')
      end
      
      % Combine data and metadata
      M1 = mtimes_(M1,M2,ScalarFlags);% overloadable since M1,M2 are of the same class
      M1 = mtimesMetaData(M1,M2,ScalarFlags);      
      if ScalarFlags(1)
         M1.IOSize_ = M2.IOSize_;
      elseif ~ScalarFlags(2)
         M1.IOSize_ = [M1.IOSize_(1) M2.IOSize_(2)];
      end
      % Consistency checks (e.g., consistent block definition in LFT arrays)
      checkModelArray_(M1)
   end
catch E
   throw(E)
end

