function [Mlim, utarget, new_mvoff, new_mdoff, new_yoff, new_myoff, xoff, Bv, Dv] = ...
    mpc_updateFromNominal(IsAdaptive,isQP,Mlim,Mrows,...
    U0,Uscale,old_mvoff,mvindex,old_mdoff,mdindex,utarget,nmv,nmd,...
    Y0,Yscale,old_yoff,old_myoff,myindex,ny,...
    X0,xoff,nxp,DX0,A,Bu,Bv,C,Dv,nCC)
% Update from new nominal values

% Authors:  A. Bemporad, N.L. Ricker, R. Chen
% Copyright 1994-2020 The MathWorks, Inc.

%#codegen

%% Scale new nominal offsets U an Y (not needed for X and DX)
if IsAdaptive
    % LTI
    if isempty(U0)
        U = [];
        isemptyU = true;
    else
        U = U0(:)./Uscale;
        isemptyU = false;
    end
    if isempty(Y0)
        Y = [];
        isemptyY = true;
    else
        Y = Y0(:)./Yscale;
        isemptyY = false;
    end
else
    % LTV
    if isempty(U0)
        U = [];
        isemptyU = true;
    else
        U = U0(:,1)./Uscale;
        isemptyU = false;
    end
    if isempty(Y0)
        Y = [];
        isemptyY = true;
    else
        Y = Y0(:,1)./Yscale;
        isemptyY = false;
    end
end
%% Obtain new nominal offsets for MV and MD
if isemptyU
    new_mvoff = old_mvoff;
    new_mdoff = old_mdoff;
else
    new_mvoff = U(mvindex);
    if nmd>0
        new_mdoff = U(mdindex,1);
    else
        new_mdoff = old_mdoff;
    end
end
%% Obtain new nominal offsets for Y and MY
if isemptyY
    new_yoff = old_yoff;
    new_myoff = old_myoff;
else
    new_yoff = Y;
    new_myoff = Y(myindex);
end
%% Update Mlim
% Mlim is based on the offline design p0 and it does not change size here.
p0 = length(utarget)/nmv;
if isQP
    Mlim = updateMlim(Mlim,Mrows,old_yoff,new_yoff,old_mvoff,new_mvoff,p0,ny,nmv,nCC);
end
%% Update MV target
% utarget is a column vector of p0*nmv with first nmv elements for time k
% and so on.  It does not change size here.   
for ct=1:nmv
    utarget(ct:nmv:((p0-1)*nmv+ct)) = utarget(ct:nmv:((p0-1)*nmv+ct)) + old_mvoff(ct) - new_mvoff(ct);
end
%% Obtain new nominal offsets of X(k) and DX(k) and update xoff/Bv/Dv
if IsAdaptive
    % override scaled nominal offsets for X.
    if ~isempty(X0)
        xoff(1:nxp) = X0(:);
    end
    % special: must update Bv last column because it is already offset free
    if ~isempty(DX0)
        Bv(1:nxp,end,1) = DX0(:);
    end
else
    p1 = size(Bv,3);
    % override scaled nominal offsets for X at time k
    if ~isempty(X0)
        xoff(1:nxp) = X0(:,1);
    end
    % special: update Bv last column based on new DX offset at time k
    % note: we don't need to update Dv(:,end,1) because it is 0 at time k
    if ~isempty(DX0)
        Bv(1:nxp,end,1) = DX0(:,1);
    end
    % update Bv and Dv from time k+1 to k+p based on time varying offsets
    for k=2:p1
        % obtain scaled nominal offsets for MV and MD at any step
        mvoff_k = U0(mvindex,k)./Uscale(mvindex);
        if nmd>0
            mdoff_k = U0(mdindex,k)./Uscale(mdindex);
        end
        % obtain scaled nominal offsets for Y at any step
        yoff_k = Y0(:,k)./Yscale;
        % obtain nominal offsets for X at any step
        xoff_k = X0(:,k);
        % update Bv(1:nxp,end,k) = dx0k + x0k - x0 + Plant.a*(x0 - x0k)  + Plant.b*(u0 - u0k)
        Bv(1:nxp,end,k) = DX0(:,k) + xoff_k-xoff(1:nxp) + A(1:nxp,1:nxp,k)*(xoff(1:nxp)-xoff_k) + Bu(1:nxp,:,k)*(new_mvoff-mvoff_k);
        if nmd>0
            Bv(1:nxp,end,k) = Bv(1:nxp,end,k) + Bv(1:nxp,1:end-1,k)*(new_mdoff-mdoff_k);
        end
        % update Dv(:,end,k) = y0k - y0 + Plant.c*(x0 - x0k) + Plant.d*(u0 - u0k) where Du is 0
        Dv(:,end,k) = (yoff_k-new_yoff) + C(:,1:nxp,k)*(xoff(1:nxp)-xoff_k);
        if nmd>0
            Dv(:,end,k) = Dv(:,end,k) + Dv(:,1:end-1,k)*(new_mdoff-mdoff_k);
        end
    end
end

function Mlim = updateMlim(Mlim,Mrows,old_yoff,new_yoff,old_mvoff,new_mvoff,p,ny,nmv,nCC)
% All possibilities
%  SuJm: p*ny rows: y(1:ny) at p=1, y(1:ny) at p=2, and so on
% -SuJm: p*ny rows: y(1:ny) at p=1, y(1:ny) at p=2, and so on
%  I2Jm: p*nmv rows: u(1:nmv) at p=1, u(1:nmv) at p=2, and so on
% -I2Jm: p*nmv rows: u(1:nmv) at p=1, u(1:nmv) at p=2, and so on
%  I3: nMoves*nmv rows: u(1:nmv) at move=1, u(1:nmv) at move=2, and so on
% -I3: nMoves*nmv rows: u(1:nmv) at move=1, u(1:nmv) at move=2, and so on
% Mlim might contains custom constraints of nCC*(p+1) at the end

% update SuJm, I2Jm constraints but skip I3 constraints because offset
% change has no impact on MV Rate constraints
for ct=1:(length(Mlim)-nCC*(p+1))
    index = Mrows(ct);
    if index<=p*ny
        k = mod(index-1,ny)+1;
        Mlim(ct) = Mlim(ct) + (old_yoff(k) - new_yoff(k));
    elseif index<=2*p*ny
        k = mod(index-p*ny-1,ny)+1;
        Mlim(ct) = Mlim(ct) - (old_yoff(k) - new_yoff(k));
    elseif index<=2*p*ny+p*nmv
        k = mod(index-2*p*ny-1,nmv)+1;
        Mlim(ct) = Mlim(ct) + (old_mvoff(k) - new_mvoff(k));
    elseif index<=2*p*ny+2*p*nmv
        k = mod(index-2*p*ny-p*nmv-1,nmv)+1;
        Mlim(ct) = Mlim(ct) - (old_mvoff(k) - new_mvoff(k));
    end
end
% % Update custom constraints
% if nCC>0
%     MlimCC = E*new_mvoff + F*new_yoff - G;
%     if nmd > 0
%         MlimCC = MlimCC + S*new_mdoff;
%     end
%     MlimCC = -reshape(MlimCC(:,ones(1,p+1)), nCC*(p+1), 1);
%     Mlim(end-length(MlimCC)+1:end) = MlimCC;
% end
% 
%     