function [alphahat_tgT,epsilonhat_tgT,etahat_tgT,a_tgt,P_tgtm1,a_tpkgt,P_tpkgt,decomp,V_tgT, alphahat_tm1gt,etahat_tgt,d,alphahat_0gT,alphahat_0g1,V_0gT,P_tgt,Veta_tgT,Pinf_out,Finf_singular_out,varargout] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux, occbin_, shock_variance_flag)
% [alphahat_tgT,epsilonhat_tgT,etahat_tgT,a_tgt,P_tgtm1,a_tpkgt,P_tpkgt,decomp,V_tgT, alphahat_tm1gt,etahat_tgt,d,alphahat_0gT,alphahat_0g1,V_0gT,P_tgt,Veta_tgT,Pinf_out,Finf_singular_out,varargout] = missing_DiffuseKalmanSmootherH3_Z(a_initial,T,Z,R,Q,H,Pinf1,Pstar1,Y,pp,mm,smpl,data_index,nk,kalman_tol,diffuse_kalman_tol,decomp_flag,state_uncertainty_flag, filter_covariance_flag, smoother_redux, occbin_, shock_variance_flag)
% Computes the diffuse Kalman smoother in the case of a singular var-cov matrix.
% Univariate treatment of multivariate time series.
%
% INPUTS
%    a_initial:mm*1 vector of initial states
%    T:        mm*mm matrix     state transition matrix
%    Z:        pp*mm matrix     selector matrix for observables in augmented state vector
%    R:        mm*rr matrix     second matrix of the state equation relating the structural innovations to the state variables
%    Q:        rr*rr matrix     covariance matrix of structural errors
%    H:        pp*1             vector of variance of measurement errors
%    Pinf1:    mm*mm diagonal matrix with with q ones and m-q zeros
%    Pstar1:   mm*mm variance-covariance matrix with stationary variables
%    Y:        pp*1 vector
%    pp:       number of observed variables
%    mm:       number of state variables
%    smpl:     sample size
%    data_index:                [cell]      1*smpl cell of column vectors of indices.
%    nk:                        number of forecasting periods
%    kalman_tol:                tolerance for zero divider
%    diffuse_kalman_tol:        tolerance for zero divider
%    decomp_flag:               if true, compute filter decomposition
%    state_uncertainty_flag:    if true, compute uncertainty about smoothed
%                               state estimate
%    decomp_flag:               if true, compute filter decomposition
%    filter_covariance_flag:    if true, compute filter covariance
%    smoother_redux:            if true, compute smoother on restricted
%                               state space, recover static variables from this
%    occbin_:   structure for passing OccBin related inputs
%    shock_variance_flag:       if true, compute variance of smoothed shocks (Veta_tgT)

% OUTPUTS
%    alphahat_tgT:   smoothed state variables (a_{t|T})
%    epsilonhat_tgT: measurement errors
%    etahat_tgT:     smoothed shocks
%    a_tgt:          matrix of updated variables (a_{t|t})
%    a_tpkgt:        3D array of k step ahead filtered state variables (a_{t+k|t})
%                    (meaningless for periods 1:d)
%    P_tgtm1:        3D array of one-step ahead forecast error variance
%                    matrices
%    P_tpkgt:        4D array of k-step ahead forecast error variance
%                    matrices (meaningless for periods 1:d)
%    decomp:         decomposition of the effect of shocks on filtered values
%    V_tgT:          3D array of state uncertainty matrices
%    alphahat_tm1gt: filtered states in t-1|t
%    etahat_tgt:     updated shocks in t|t
%    d:              number of diffuse periods
%    alphahat_0gT:   smoothed state in t_0|T
%    alphahat_0g1:   E(alpha_{0}|Y_1) from smoother_redux option,
%                    boundary case of alphahat_tm1gt at t=1
%    V_0gT:          initial state uncertainty
%    P_tgt:          (mm*mm*smpl) 3D array of updated state covariance matrices P_{t|t}
%                      Derived from P: for diffuse periods 1:d, Pstar_{t|t}; for stationary
%                      periods d+1:smpl, P_{t|t} updated in place by the univariate filter
%    Veta_tgT:       (rr*rr*smpl) 3D array of smoothed shock variance matrices
%                    Var(eta_t|Y_{1:T}), DK (2012) eq. 4.69. Computed when
%                    shock_variance_flag is true; empty otherwise.
%    Pinf_out:       (mm*mm*d) 3D array of predicted Pinf matrices for diffuse
%                    periods 1:d (before observation updates). Empty if d=0.
%                    Used by DsgeSmoother for P_tgt reconstruction of static variables.
%    Finf_singular_out: (1*d) logical vector; Finf_singular_out(t)=1 if all observed
%                   Finf(i,t) were zero at time t (no diffuse update for any
%                   observation). Empty if d=0.
%    varargout:     additional OccBin outputs
%       varargout{1} = regimes_;
%       varargout{2} = TTT;
%       varargout{3} = RRR;
%       varargout{4} = CCC;
%       varargout{5} = TT;
%       varargout{6} = RR;
%       varargout{7} = CC;

%
% Notes:
%   Outputs are stored in decision-rule order, i.e. to get variables in order of declaration
%   as in M_.endo_names, ones needs code along the lines of:
%   variables_declaration_order(dr.order_var,:) = alphahat_tgT
%
% Algorithm:
%
%   Uses the univariate filter as described in Durbin/Koopman (2012): "Time
%   Series Analysis by State Space Methods", Oxford University Press,
%   Second Edition, Ch. 6.4 + 7.2.5
%   and
%   Koopman/Durbin (2000): "Fast Filtering and Smoothing for Multivariate State Space
%   Models", in Journal of Time Series Analysis, vol. 21(3), pp. 281-296.
%
% SPECIAL REQUIREMENTS
%   See "Filtering and Smoothing of State Vector for Diffuse State Space
%   Models", S.J. Koopman and J. Durbin (2003), in Journal of Time Series
%   Analysis, vol. 24(1), pp. 85-98.

% Copyright © 2004-2026 Dynare Team
%
% This file is part of Dynare.
%
% Dynare is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% Dynare is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with Dynare.  If not, see <https://www.gnu.org/licenses/>.

if size(H,2)>1
    error('missing_DiffuseKalmanSmootherH3_Z:: H is not a_tgt vector. This must not happen. Please contact the developers.')
end

d = 0;
decomp = [];
Pinf_out = [];
Finf_singular_out = [];
spinf           = size(Pinf1);
spstar          = size(Pstar1(:,:,1));
v               = zeros(pp,smpl);
a_tgt               = zeros(mm,smpl);
a1              = zeros(mm,smpl+1);
if size(a_initial,2)>1
    a_tgt(:,1)          = a_initial(:,2);
    a1(:,1)         = a_initial(:,2);
    ainit = a_initial(:,1);
else
a_tgt(:,1)          = a_initial;
a1(:,1)         = a_initial;
    ainit = a1(:,1);
end
a_tpkgt              = zeros(nk,mm,smpl+nk);

Fstar           = zeros(pp,smpl);
Finf            = zeros(pp,smpl);
Fi              = zeros(pp,smpl);
Ki              = zeros(mm,pp,smpl);
Kstar           = zeros(mm,pp,smpl);
Kinf            = zeros(spstar(1),pp,smpl);
P               = zeros(mm,mm,smpl+1);
P_tgtm1              = P;
if filter_covariance_flag
    P_tpkgt              = zeros(nk,mm,mm,smpl+nk);
else
    P_tpkgt              = [];
end
Pstar           = zeros(spstar(1),spstar(2),smpl);
Pinit = Pstar1(:,:,1);
if size(Pstar1,3)>1
    Pstar(:,:,1)    = Pstar1(:,:,2);
else
Pstar(:,:,1)    = Pstar1;
end
Pinf            = zeros(spinf(1),spinf(2),smpl);
Pinf(:,:,1)     = Pinf1;
Pstar1          = Pstar;
Pinf1           = Pinf;
rr              = size(Q,1); % number of structural shocks
isqvec = false;
if ndims(Q)>2
    Qvec = Q;
    Q=Q(:,:,1);
    isqvec = true;
end
QQ              = R*Q*transpose(R);
QRt             = Q*transpose(R);
alphahat_tgT        = zeros(mm,smpl);
etahat_tgT          = zeros(rr,smpl);
if smoother_redux
    alphahat_tm1gt       = alphahat_tgT;
    etahat_tgt         = etahat_tgT;
else
    alphahat_tm1gt       = [];
    etahat_tgt         = [];
end
epsilonhat_tgT      = zeros(rr,smpl);
r               = zeros(mm,smpl);
compute_N = state_uncertainty_flag || shock_variance_flag;
if state_uncertainty_flag
    if smoother_redux
        V_tgT               = zeros(mm+rr,mm+rr,smpl);
    else
        V_tgT               = zeros(mm,mm,smpl);
    end
else
    V_tgT=[];
end
if compute_N
    N               = zeros(mm,mm,smpl);
end
if shock_variance_flag
    Veta_tgT           = zeros(rr,rr,smpl);
else
    Veta_tgT           = [];
end
alphahat_0gT=[];
alphahat_0g1=[];
V_0gT=[];

C=0;
if ~occbin_.status
    isoccbin = 0;
    TT=[];
    RR=[];
    CC=[];
else
    isoccbin = 1;
    Qt = repmat(Q,[1 1 3]);
    options_=occbin_.info{1};
    dr=occbin_.info{2};
    endo_steady_state=occbin_.info{3};
    exo_steady_state=occbin_.info{4};
    exo_det_steady_state=occbin_.info{5};
    M_=occbin_.info{6};
    occbin_options=occbin_.info{7};
    opts_regime = occbin_options.opts_regime;
    %     first_period_occbin_update = inf;
    if M_.occbin.constraint_nbr==1
        base_regime.regime = 0;
        base_regime.regimestart = 1;
    else
        base_regime.regime1 = 0;
        base_regime.regimestart1 = 1;
        base_regime.regime2 = 0;
        base_regime.regimestart2 = 1;
    end
    if isfield(opts_regime,'regime_history') && ~isempty(opts_regime.regime_history)
        opts_regime.regime_history=[opts_regime.regime_history(1) opts_regime.regime_history];
    else
        opts_regime.binding_indicator=zeros(smpl+2,M_.occbin.constraint_nbr);
    end
    occbin_options.opts_regime = opts_regime;
    [~, ~, ~, regimes_] = occbin.check_regimes([], [], [], opts_regime, M_, options_, dr, endo_steady_state, exo_steady_state, exo_det_steady_state);
    if length(occbin_.info)>7
        if length(occbin_.info)==9 && options_.smoother_redux
            TT=repmat(T,1,1,smpl+1);
            RR=repmat(R,1,1,smpl+1);
            CC=repmat(zeros(mm,1),1,smpl+1);
            T0=occbin_.info{8};
            R0=occbin_.info{9};
        else

            TT=occbin_.info{8};
            RR=occbin_.info{9};
            CC=occbin_.info{10};
            %         TT = cat(3,TT,T);
            %         RR = cat(3,RR,R);
            %         CC = cat(2,CC,zeros(mm,1));
            if options_.smoother_redux
                my_order_var = dr.restrict_var_list;
                CC = CC(my_order_var,:);
                RR = RR(my_order_var,:,:);
                TT = TT(my_order_var,my_order_var,:);
                T0=occbin_.info{11};
                R0=occbin_.info{12};
            end
            if size(TT,3)<(smpl+1)
                TT=repmat(T,1,1,smpl+1);
                RR=repmat(R,1,1,smpl+1);
                CC=repmat(zeros(mm,1),1,smpl+1);
            end
        end

    else
        TT=repmat(T,1,1,smpl+1);
        RR=repmat(R,1,1,smpl+1);
        CC=repmat(zeros(mm,1),1,smpl+1);
    end
    if ~smoother_redux
        T0=T;
        R0=R;

    end
    if ~isinf(occbin_options.first_period_occbin_update)
        % initialize state matrices (otherwise they are set to 0 for
        % t<first_period_occbin_update!)
        TTT=repmat(T0,1,1,smpl+1);
        RRR=repmat(R0,1,1,smpl+1);
        CCC=repmat(zeros(length(T0),1),1,smpl+1);
    end

end

t = 0;
icc=0;
if ~isempty(Pinf(:,:,1))
    newRank = rank(Z*Pinf(:,:,1)*Z',diffuse_kalman_tol);
else
    newRank = rank(Pinf(:,:,1),diffuse_kalman_tol);
end
if newRank
    % add this to get smoothed states in period 0
    Pinf_init = Pinf(:,:,1);
    Pstar_init = Pstar(:,:,1);
    ainit = a1(:,1);
    if isoccbin && length(occbin_.info)>6
        if isqvec
            QQ = RR(:,:,1)*Qvec(:,:,1)*transpose(RR(:,:,1));
        else
            QQ = RR(:,:,1)*Q*transpose(RR(:,:,1));
        end
        T = TT(:,:,1);
        C = CC(:,1);
        a1(:,1) = T*a_tgt(:,1)+C;                                                 %transition according to (6.14) in DK (2012)
    end
    Pstar(:,:,1)  = T*Pstar(:,:,1)*T' + QQ;
end

while newRank && t < smpl
    t = t+1;
    a_tgt(:,t) = a1(:,t);
    Pstar1(:,:,t) = Pstar(:,:,t);
    Pinf1(:,:,t) = Pinf(:,:,t);
    di = data_index{t}';
    for i=di
        Zi = Z(i,:);
        v(i,t)      = Y(i,t)-Zi*a_tgt(:,t);                                     % nu_{t,i} in 6.13 in DK (2012)
        Fstar(i,t)  = Zi*Pstar(:,:,t)*Zi' +H(i);                            % F_{*,t} in 5.7 in DK (2012), relies on H being diagonal
        Finf(i,t)   = Zi*Pinf(:,:,t)*Zi';                                   % F_{\infty,t} in 5.7 in DK (2012)
        Kstar(:,i,t) = Pstar(:,:,t)*Zi';                                    % KD (2000), eq. (15)
        if Finf(i,t) > diffuse_kalman_tol && newRank                        % F_{\infty,t,i} = 0, use upper part of bracket on p. 175 DK (2012) for w_{t,i}
            icc=icc+1;
            Kinf(:,i,t)       = Pinf(:,:,t)*Zi';                            % KD (2000), eq. (15)
            Kinf_Finf         = Kinf(:,i,t)/Finf(i,t);
            a_tgt(:,t)            = a_tgt(:,t) + Kinf_Finf*v(i,t);                  % KD (2000), eq. (16)
            Pstar(:,:,t)      = Pstar(:,:,t) + ...
                Kinf(:,i,t)*Kinf_Finf'*(Fstar(i,t)/Finf(i,t)) - ...
                Kstar(:,i,t)*Kinf_Finf' - ...
                Kinf_Finf*Kstar(:,i,t)';                                    % KD (2000), eq. (16)
            Pinf(:,:,t)       = Pinf(:,:,t) - Kinf(:,i,t)*Kinf(:,i,t)'/Finf(i,t); % KD (2000), eq. (16)
        elseif Fstar(i,t) > kalman_tol
            a_tgt(:,t)            = a_tgt(:,t) + Kstar(:,i,t)*v(i,t)/Fstar(i,t);    % KD (2000), eq. (17)
            Pstar(:,:,t)      = Pstar(:,:,t) - Kstar(:,i,t)*Kstar(:,i,t)'/Fstar(i,t);   % KD (2000), eq. (17)
            % Pinf is passed through unaltered, see eq. (17) of
            % Koopman/Durbin (2000)
        else
            % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see
            % p. 157, DK (2012)
        end
    end
    % After the observation loop, Pstar(:,:,t) holds Pstar_{t|t} (the updated covariance).
    % It will be copied into P(:,:,1:d) after the diffuse phase ends.
    if newRank
        if ~isempty(Pinf(:,:,t))
            oldRank = rank(Z*Pinf(:,:,t)*Z',diffuse_kalman_tol);
        else
            oldRank = rank(Pinf(:,:,t),diffuse_kalman_tol);
        end
    else
        oldRank = 0;
    end
    if isoccbin
        if length(occbin_.info)>9
            if isqvec
                QQ = RR(:,:,t+1)*Qvec(:,:,t+1)*transpose(RR(:,:,t+1));
            else
                QQ = RR(:,:,t+1)*Q*transpose(RR(:,:,t+1));
            end
            T = TT(:,:,t+1);
            C = CC(:,t+1);
        else
            TT(:,:,t+1)=  T;
            RR(:,:,t+1)=  R;
        end
    end
    a1(:,t+1) = T*a_tgt(:,t)+C;
    a_tpkgt(1,:,t+1) = a1(:,t+1);
    for jnk=2:nk
        a_tpkgt(jnk,:,t+jnk) = T*dynare_squeeze(a_tpkgt(jnk-1,:,t+jnk-1));
    end
    if isqvec
        QQ = R*Qvec(:,:,t+1)*transpose(R);
    end
    Pstar(:,:,t+1) = T*Pstar(:,:,t)*T'+ QQ;
    Pinf(:,:,t+1) = T*Pinf(:,:,t)*T';
    if newRank
        if ~isempty(Pinf(:,:,t+1))
            newRank = rank(Z*Pinf(:,:,t+1)*Z',diffuse_kalman_tol);
        else
            newRank = rank(Pinf(:,:,t+1),diffuse_kalman_tol);
        end
    end
    if oldRank ~= newRank
        disp('univariate_diffuse_kalman_filter:: T does influence the rank of Pinf!')
        disp('This may happen for models with order of integration >1.')
    end
end

d = t;
if isoccbin
    first_period_occbin_update = occbin_options.first_period_occbin_update;
    if d>0
        first_period_occbin_update = max(t+2,occbin_options.first_period_occbin_update);
        % Kalman update is not yet robust to accommodate diffuse steps
    end
    if occbin_options.opts_simul.waitbar && first_period_occbin_update<smpl
        [hh_fig, length_of_old_string] = wait_bar.run(0,[],'OccBin: Piecewise Kalman Filter', options_.console_mode, 0, 'OccBin: Piecewise Kalman Filter.');
        waitbar_indicator=1;
    else
        waitbar_indicator=0;
    end
else
    first_period_occbin_update = inf;
    waitbar_indicator=0;
end
P(:,:,d+1) = Pstar(:,:,d+1);
% For diffuse periods t=1:d, the univariate filter updates Pstar in place.
% Copy updated Pstar_{t|t} into P so that P(:,:,1:smpl) uniformly stores P_{t|t}.
P(:,:,1:d) = Pstar(:,:,1:d);
% Populate P_tgtm1 for diffuse periods: Pstar1(:,:,t) stores Pstar_{t|t-1} (predicted,
% before observation updates), matching P in the multivariate smoother (H1_Z).
P_tgtm1(:,:,1:d) = Pstar1(:,:,1:d);
% Return predicted Pinf and Finf_singular flags for diffuse P_tgt reconstruction
% in DsgeSmoother. Pinf1(:,:,t) stores the predicted Pinf before observation
% updates. Finf_singular_out(t)=1 if no observation at time t had nonzero Finf.
Pinf_out  = Pinf1(:,:,1:d);                                                 % predicted Pinf for diffuse periods
Finf_singular_out = zeros(1,d);
for k_diffuse=1:d
    Finf_singular_out(k_diffuse) = (max(Finf(:,k_diffuse)) <= diffuse_kalman_tol);
end
Fstar = Fstar(:,1:d);
Finf = Finf(:,1:d);
Kstar = Kstar(:,:,1:d);
Pstar = Pstar(:,:,1:d);
Pinf  = Pinf(:,:,1:d);
Pstar1 = Pstar1(:,:,1:d);
Pinf1  = Pinf1(:,:,1:d);
lik=zeros(1,smpl);
while t<smpl
    t = t+1;
    a_tgt(:,t) = a1(:,t);
    P_tgtm1(:,:,t) = P(:,:,t);
    di = data_index{t}';
    if t>=first_period_occbin_update
        if waitbar_indicator
            [~, length_of_old_string] = wait_bar.run(t/smpl, hh_fig, sprintf('Period %u of %u', t,smpl),options_.console_mode,length_of_old_string);
        end
        occbin_options.opts_simul.waitbar=0;
        if t==1
            if isqvec
                Qt = cat(3,Q,Qvec(:,:,t:t+1));
            end
            a0 = a_tgt(:,1);
            a10 = [a0 a_tgt(:,1)];
            P0 = P(:,:,1);
            P10 = P_tgtm1(:,:,[1 1]);
            data_index0{1}=[];
            data_index0(2)=data_index(1);
            v0(:,2)=v(:,1);
            Y0(:,2)=Y(:,1);
            Y0(:,1)=nan;
            Fi0 = Fi(:,1);
            Ki0 = Ki(:,:,1);
            TT01 = cat(3,T,TT(:,:,1));
            RR01 = cat(3,R,RR(:,:,1));
            CC01 = zeros(size(CC,1),2);
            CC01(:,2) = CC(:,1);
            [ax, a1x, Px, P1x, vx, Tx, Rx, Cx, tmp, error_flag, M_, likx, etaha, aha, V_tgT, Fix, Kix, TTx,RRx,CCx] = occbin.kalman_update_engine(a0, a10, P0, P10, t, data_index0, Z, v0, Y0, H, Qt, T0, R0, TT01, RR01, CC01, regimes_(t:t+1), base_regime, di, M_, dr,endo_steady_state,exo_steady_state,exo_det_steady_state, options_, occbin_options, ...
                Fi0,Ki0,kalman_tol,nk);
        else
            if isqvec
                Qt = Qvec(:,:,t-1:t+1);
            end
            [ax, a1x, Px, P1x, vx, Tx, Rx, Cx, tmp, error_flag, M_, likx, etaha, aha, V_tgT, Fix, Kix,TTx,RRx,CCx] = occbin.kalman_update_engine(a_tgt(:,t-1),a1(:,t-1:t),P(:,:,t-1),P_tgtm1(:,:,t-1:t),t,data_index(t-1:t),Z,v(:,t-1:t), Y(:,t-1:t), H, Qt, T0, R0, TT(:,:,t-1:t),RR(:,:,t-1:t),CC(:,t-1:t), regimes_(t:t+1), base_regime, di, M_, dr,endo_steady_state,exo_steady_state,exo_det_steady_state, options_, occbin_options, ...
                Fi(:,t-1),Ki(:,:,t-1),kalman_tol,nk);
        end
        if ~error_flag
            regimes_(t:t+2)=tmp;
            lik(t)=likx;
        else
            P_tgt = [];
            varargout{1} = [];
            varargout{2} = [];
            varargout{3} = [];
            varargout{4} = [];
            varargout{5} = [];
            varargout{6} = [];
            varargout{7} = [];
            return
        end

        if smoother_redux && t>1
            alphahat_tm1gt(:,t-1) = aha(:,1);
        end
        etahat_tgt(:,t) = etaha(:,2);
        a_tgt(:,t) = ax(:,1);
        a1(:,t) = a1x(:,2);
        a1(:,t+1) = ax(:,2);
        v(di,t) = vx(di,2);
        Fi(di,t) = Fix(di,2);
        Ki(:,di,t) = Kix(:,di,2);
        TT(:,:,t:t+1) = Tx(:,:,1:2);
        RR(:,:,t:t+1) = Rx(:,:,1:2);
        CC(:,t:t+1) = Cx(:,1:2);
        TTT(:,:,t)=TTx;
        RRR(:,:,t)=RRx;
        CCC(:,t)=CCx;
        P(:,:,t) = Px(:,:,1);                                        %P(:,:,t) now holds P_{t|t} from OccBin update
        P_tgtm1(:,:,t) = P1x(:,:,2);
        P(:,:,t+1) = Px(:,:,2);
        a_tpkgt(1,:,t+1) = a1(:,t+1);
        for jnk=1:nk
            P_tpkgt(jnk,:,:,t+jnk) = Px(:,:,1+jnk);
            a_tpkgt(jnk,:,t+jnk) = ax(:,1+jnk);
        end
    else
        if isoccbin && t==1
            if isqvec
                QQ = RR(:,:,t)*Qvec(:,:,t)*transpose(RR(:,:,t));
            else
                QQ = RR(:,:,t)*Q*transpose(RR(:,:,t));
            end
            T = TT(:,:,t);
            C = CC(:,t);
            a1(:,t) = T*a_tgt(:,t)+C;                                                 %transition according to (6.14) in DK (2012)
            a_tgt(:,t) = a1(:,t);
            P(:,:,t) = T*P(:,:,t)*T' + QQ;                                        %transition according to (6.14) in DK (2012)
            P_tgtm1(:,:,t) = P(:,:,t);
        end
        for i=di
            Zi = Z(i,:);
            v(i,t)  = Y(i,t) - Zi*a_tgt(:,t);                                       % nu_{t,i} in 6.13 in DK (2012)
            Fi(i,t) = Zi*P(:,:,t)*Zi' + H(i);                                   % F_{t,i} in 6.13 in DK (2012), relies on H being diagonal
            Ki(:,i,t) = P(:,:,t)*Zi';                                           % K_{t,i}*F_(i,t) in 6.13 in DK (2012)
            if Fi(i,t) > kalman_tol
                a_tgt(:,t) = a_tgt(:,t) + Ki(:,i,t)*v(i,t)/Fi(i,t);                     %filtering according to (6.13) in DK (2012)
                P(:,:,t) = P(:,:,t) - Ki(:,i,t)*Ki(:,i,t)'/Fi(i,t);             %filtering according to (6.13) in DK (2012)
                P(:,:,t) = (P(:,:,t)+P(:,:,t)')/2;
            else
                % do nothing as a_{t,i+1}=a_{t,i} and P_{t,i+1}=P_{t,i}, see
                % p. 157, DK (2012)
            end
        end
        % After the observation loop, P(:,:,t) holds P_{t|t} (updated in place by the univariate filter).
        if isqvec
            QQ = R*Qvec(:,:,t)*transpose(R);
        end
        if smoother_redux
            ri=zeros(mm,1);
            for st=t:-1:max(d+1,t-1) %use smoother recursions backwards in time from t to t-1 to get objects required for smoother_redux, see missing_DiffuseKalmanSmootherH3_Z.m
                di = flipud(data_index{st})';
                for i = di
                    if Fi(i,st) > kalman_tol
                        Li = eye(mm)-Ki(:,i,st)*Z(i,:)/Fi(i,st);
                        ri = Z(i,:)'/Fi(i,st)*v(i,st)+Li'*ri;                             % DK (2012), 6.15, equation for r_{t,i-1}
                    end
                end
                if st==t-1
                    alphahat_tm1gt(:,st) = a1(:,st) + P_tgtm1(:,:,st)*ri;
                else
                    if isoccbin
                        if isqvec
                            QRt = Qvec(:,:,st)*transpose(RR(:,:,st));
                        else
                            QRt = Q*transpose(RR(:,:,st));
                        end
                        T = TT(:,:,st);
                    else
                        if isqvec
                            QRt = Qvec(:,:,st)*transpose(R);
                        end
                    end
                    etahat_tgt(:,st) = QRt*ri;
                end
                ri = T'*ri;                                                             % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}}
            end
            if t==1
                alphahat_0g1 = P_tgtm1(:,:,st)*ri;
            end
        end
        if isoccbin
            if isqvec
                QQ = RR(:,:,t+1)*Qvec(:,:,t+1)*transpose(RR(:,:,t+1));
            else
                QQ = RR(:,:,t+1)*Q*transpose(RR(:,:,t+1));
            end
            T = TT(:,:,t+1);
            C = CC(:,t+1);
        else
            if isqvec
                QQ = R*Qvec(:,:,t+1)*transpose(R);
            end
        end
        a1(:,t+1) = T*a_tgt(:,t)+C;                                                 %transition according to (6.14) in DK (2012)
        P(:,:,t+1) = T*P(:,:,t)*T' + QQ;                                        %transition according to (6.14) in DK (2012)
        P(:,:,t+1) = (P(:,:,t+1)+P(:,:,t+1)')/2;
        if filter_covariance_flag
            Pf          = P(:,:,t+1);
        end
        a_tpkgt(1,:,t+1) = a1(:,t+1);
        if ~isempty(nk) && nk>1 && isoccbin && (t>=first_period_occbin_update || isinf(first_period_occbin_update))
            opts_simul = occbin_options.opts_simul;
            opts_simul.SHOCKS = zeros(nk,M_.exo_nbr);
            if smoother_redux
                tmp=zeros(M_.endo_nbr,1);
                tmp(dr.restrict_var_list)=a_tgt(:,t);
                opts_simul.endo_init = tmp(dr.inv_order_var);
            else
                opts_simul.endo_init = a_tgt(dr.inv_order_var,t);
            end
            opts_simul.init_regime = []; %regimes_(t);
            opts_simul.waitbar=0;
            options_.occbin.simul=opts_simul;
            [~, out] = occbin.solver(M_,options_,dr,endo_steady_state,exo_steady_state,exo_det_steady_state);
            if out.error_flag
                error('missing_DiffuseKalmanSmootherH3_Z:: OccBin solver did not solve the model in period %u.',t)
            end
        end
        for jnk=1:nk
            if filter_covariance_flag
                if jnk>1
                    Pf = T*Pf*T' + QQ;
                end
                P_tpkgt(jnk,:,:,t+jnk) = Pf;
            end
            if jnk>1
                if isoccbin && (t>=first_period_occbin_update || isinf(first_period_occbin_update))
                    if smoother_redux
                        a_tpkgt(jnk,:,t+jnk) = out.piecewise(jnk,dr.order_var(dr.restrict_var_list)) - out.ys(dr.order_var(dr.restrict_var_list))';
                    else
                        a_tpkgt(jnk,dr.inv_order_var,t+jnk) = out.piecewise(jnk,:) - out.ys';
                    end
                else
                    a_tpkgt(jnk,:,t+jnk) = T*dynare_squeeze(a_tpkgt(jnk-1,:,t+jnk-1));
                end
            end
        end
    end
end
if waitbar_indicator
    wait_bar.close(hh_fig,options_.console_mode);
end

P_tgtm1(:,:,t+1) = P(:,:,t+1);
% P(:,:,1:smpl) now uniformly stores P_{t|t} for all periods:
%   - Diffuse periods 1:d: copied from Pstar_{t|t} after the diffuse phase
%   - Stationary periods d+1:smpl: updated in place by the univariate filter
% Trim off the (smpl+1) slice which holds P_{T+1|T} (one-step ahead predicted).
P_tgt = P(:,:,1:smpl);

if ~isinf(first_period_occbin_update) && isoccbin
    regimes_ = regimes_(1:smpl+1);
else
    regimes_ = struct();
    TTT=TT;
    RRR=RR;
    CCC=CC;
    %     return
end
varargout{1} = regimes_;
varargout{2} = TTT;
varargout{3} = RRR;
varargout{4} = CCC;
varargout{5} = TT;
varargout{6} = RR;
varargout{7} = CC;

%% do backward pass
ri=zeros(mm,1);
if compute_N
    Ni=zeros(mm,mm);
end
t = smpl+1;
while t > d+1
    t = t-1;
    di = flipud(data_index{t})';
    for i = di
        if Fi(i,t) > kalman_tol
            Li = eye(mm)-Ki(:,i,t)*Z(i,:)/Fi(i,t);
            ri = Z(i,:)'/Fi(i,t)*v(i,t)+Li'*ri;                             % DK (2012), 6.15, equation for r_{t,i-1}
            if compute_N
                Ni = Z(i,:)'/Fi(i,t)*Z(i,:)+Li'*Ni*Li;                      % KD (2000), eq. (23)
            end
        end
    end
    r(:,t) = ri;                                                            % DK (2012), below 6.15, r_{t-1}=r_{t,0}
    alphahat_tgT(:,t) = a1(:,t) + P_tgtm1(:,:,t)*r(:,t);
    if isoccbin
        if isqvec
            QRt = Qvec(:,:,t)*transpose(RR(:,:,t));
        else
            QRt = Q*transpose(RR(:,:,t));
        end
        R = RR(:,:,t);
        T = TT(:,:,t);
    else
        if isqvec
            QRt             = Qvec(:,:,t)*transpose(R);
        end
    end
    etahat_tgT(:,t) = QRt*r(:,t);
    ri = T'*ri;                                                             % KD (2003), eq. (23), equation for r_{t-1,p_{t-1}}
    if compute_N
        N(:,:,t) = Ni;                                                          % DK (2012), below 6.15, N_{t-1}=N_{t,0}
    end
    if state_uncertainty_flag
        if smoother_redux
            ptmp = [P_tgtm1(:,:,t) R*Q; (R*Q)' Q];
            ntmp = [N(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)];
            V_tgT(:,:,t)    = ptmp - ptmp*ntmp*ptmp;
        else
            V_tgT(:,:,t) = P_tgtm1(:,:,t)-P_tgtm1(:,:,t)*N(:,:,t)*P_tgtm1(:,:,t);                      % KD (2000), eq. (7) with N_{t-1} stored in N(:,:,t)
        end
    end
    if shock_variance_flag
        if isqvec
            Veta_tgT(:,:,t) = Qvec(:,:,t) - QRt*N(:,:,t)*QRt';                        %DK (2012), eq. 4.69
        else
            Veta_tgT(:,:,t) = Q - QRt*N(:,:,t)*QRt';                                   %DK (2012), eq. 4.69
        end
    end
    if compute_N
        Ni = T'*Ni*T;                                                           % KD (2000), eq. (23), equation for N_{t-1,p_{t-1}}
    end
end

if d==0 % recover states in period t=0
    a0 = ainit;
    r0 = ri;
    P0 = Pinit;
    % if OCCBIN, P_tgtm1 in t=1 must be consistent with the regime in 1
    alphahat_0gT = a0 + P0*r0;

    % we do NOT need eps(0)!
    % alphahat_tgT is smoothed state in t=0, so that S(1)=T*s(0)+R*eps(1);
    if state_uncertainty_flag
        N0 = Ni;                                                          % DK (2012), below 6.15, N_{t-1}=N_{t,0}
        if smoother_redux
            ptmp = [P0 R*Q; (R*Q)' Q];
            ntmp = [N0 zeros(mm,rr); zeros(rr,mm+rr)];
            V_0gT    = ptmp - ptmp*ntmp*ptmp;
        else
            V_0gT = P0-P0*N0*P0;                      % KD (2000), eq. (7) with N_{t-1} stored in N(:,:,t)
        end
    end

else % diffuse filter
    r0 = zeros(mm,d);
    r0(:,d) = ri;
    r1 = zeros(mm,d);
    if compute_N
        %N_0 at (d+1) is N(d+1), so we can use N for continuing and storing N_0-recursion
        N_0=zeros(mm,mm,d);   %set N_1_{d}=0, below  KD (2000), eq. (24)
        N_0(:,:,d) = Ni;
        N_1=zeros(mm,mm,d);   %set N_1_{d}=0, below  KD (2000), eq. (24)
        N_2=zeros(mm,mm,d);   %set N_2_{d}=0, below  KD (2000), eq. (24)
    end
    for t = d:-1:1
        di = flipud(data_index{t})';
        for i = di
            if Finf(i,t) > diffuse_kalman_tol
                % recursions need to be from highest to lowest term in order to not
                % overwrite lower terms still needed in this step
                Linf    = eye(mm) - Kinf(:,i,t)*Z(i,:)/Finf(i,t);
                L0      = (Kinf(:,i,t)*(Fstar(i,t)/Finf(i,t))-Kstar(:,i,t))*Z(i,:)/Finf(i,t);
                r1(:,t) = Z(i,:)'*v(i,t)/Finf(i,t) + ...
                    L0'*r0(:,t) + ...
                    Linf'*r1(:,t);   % KD (2000), eq. (25) for r_1
                r0(:,t) = Linf'*r0(:,t);   % KD (2000), eq. (25) for r_0
                if compute_N
                    N_2(:,:,t)=-Z(i,:)'/Finf(i,t)^2*Z(i,:)*Fstar(i,t) ...
                        + Linf'*N_2(:,:,t)*Linf...
                        + Linf'*N_1(:,:,t)*L0...
                        + L0'*N_1(:,:,t)'*Linf...
                        + L0'*N_0(:,:,t)*L0;                                    % DK (2012), eq. 5.29
                    N_1(:,:,t)=Z(i,:)'/Finf(i,t)*Z(i,:)+Linf'*N_1(:,:,t)*Linf...
                        +L0'*N_0(:,:,t)*Linf;                                   % DK (2012), eq. 5.29; note that, compared to DK (2003) this drops the term (L_1'*N(:,:,t+1)*Linf(:,:,t))' in the recursion due to it entering premultiplied by Pinf when computing V_tgT, and Pinf*Linf'*N=0
                    N_0(:,:,t)=Linf'*N_0(:,:,t)*Linf;                           % DK (2012), eq. 5.19, noting that L^(0) is named Linf
                end
            elseif Fstar(i,t) > kalman_tol % step needed whe Finf == 0
                L_i=eye(mm) - Kstar(:,i,t)*Z(i,:)/Fstar(i,t);
                r0(:,t) = Z(i,:)'/Fstar(i,t)*v(i,t)+L_i'*r0(:,t);           % propagate r0 and keep r1 fixed
                if compute_N
                    N_0(:,:,t)=Z(i,:)'/Fstar(i,t)*Z(i,:)+L_i'*N_0(:,:,t)*L_i;   % propagate N_0 and keep N_1 and N_2 fixed
                end
            end
        end
        alphahat_tgT(:,t) = a1(:,t) + Pstar1(:,:,t)*r0(:,t) + Pinf1(:,:,t)*r1(:,t); % KD (2000), eq. (26)
        r(:,t)        = r0(:,t);
        if isoccbin
            if isqvec
                QRt = Qvec(:,:,t)*transpose(RR(:,:,t));
            else
                QRt = Q*transpose(RR(:,:,t));
            end
            R = RR(:,:,t);
            T = TT(:,:,t);
        else
            if isqvec
                QRt             = Qvec(:,:,t)*transpose(R);
            end
        end
        etahat_tgT(:,t)   = QRt*r(:,t);                                         % KD (2000), eq. (27)
        if state_uncertainty_flag
            % DK (2012), eq. 5.30 uses the *predicted* covariances Pstar_{t|t-1}
            % and Pinf_{t|t-1}, stored in Pstar1 and Pinf1 (the univariate filter
            % updates Pstar/Pinf in place, so they hold t|t after the obs loop).
            if smoother_redux
                pstmp = [Pstar1(:,:,t) R*Q; (R*Q)' Q];
                pitmp = [Pinf1(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)];
                ntmp0 = [N_0(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)];
                ntmp1 = [N_1(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)];
                ntmp2 = [N_2(:,:,t) zeros(mm,rr); zeros(rr,mm+rr)];
                V_tgT(:,:,t)    = pstmp - pstmp*ntmp0*pstmp...
                    -(pitmp*ntmp1*pstmp)'...
                    - pitmp*ntmp1*pstmp...
                    - pitmp*ntmp2*pitmp;                                   % DK (2012), eq. 5.30

            else
                V_tgT(:,:,t)=Pstar1(:,:,t)-Pstar1(:,:,t)*N_0(:,:,t)*Pstar1(:,:,t)...
                    -(Pinf1(:,:,t)*N_1(:,:,t)*Pstar1(:,:,t))'...
                    - Pinf1(:,:,t)*N_1(:,:,t)*Pstar1(:,:,t)...
                    - Pinf1(:,:,t)*N_2(:,:,t)*Pinf1(:,:,t);                     % DK (2012), eq. 5.30
            end
        end
        if shock_variance_flag
            if isqvec
                Veta_tgT(:,:,t) = Qvec(:,:,t) - QRt*N_0(:,:,t)*QRt';              %DK (2012), eq. 4.69 (N_0 is the diffuse N)
            else
                Veta_tgT(:,:,t) = Q - QRt*N_0(:,:,t)*QRt';                         %DK (2012), eq. 4.69 (N_0 is the diffuse N)
            end
        end
        if t > 1
            r0(:,t-1) = T'*r0(:,t);                                         % KD (2000), below eq. (25) r_{t-1,p_{t-1}}=T'*r_{t,0}
            r1(:,t-1) = T'*r1(:,t);                                         % KD (2000), below eq. (25) r_{t-1,p_{t-1}}=T'*r_{t,0}
            if compute_N
                N_0(:,:,t-1)= T'*N_0(:,:,t)*T;                                % KD (2000), below eq. (25) N_{t-1,p_{t-1}}=T'*N_{t,0}*T
                N_1(:,:,t-1)= T'*N_1(:,:,t)*T;                                % KD (2000), below eq. (25) N^1_{t-1,p_{t-1}}=T'*N^1_{t,0}*T
                N_2(:,:,t-1)= T'*N_2(:,:,t)*T;                                % KD (2000), below eq. (25) N^2_{t-1,p_{t-1}}=T'*N^2_{t,0}*T
            end
        else
            r00 = T'*r0(:,t);                                         % KD (2000), below eq. (25) r_{t-1,p_{t-1}}=T'*r_{t,0}
            r10 = T'*r1(:,t);                                         % KD (2000), below eq. (25) r_{t-1,p_{t-1}}=T'*r_{t,0}
            if compute_N
                N_00= T'*N_0(:,:,t)*T;                                % KD (2000), below eq. (25) N_{t-1,p_{t-1}}=T'*N_{t,0}*T
                N_10= T'*N_1(:,:,t)*T;                                % KD (2000), below eq. (25) N^1_{t-1,p_{t-1}}=T'*N^1_{t,0}*T
                N_20= T'*N_2(:,:,t)*T;                                % KD (2000), below eq. (25) N^2_{t-1,p_{t-1}}=T'*N^2_{t,0}*T
            end
        end
    end
    % get smoothed states in t=0
    alphahat_0gT = ainit + Pstar_init*r00 + Pinf_init*r10; % KD (2000), eq. (26)
    if state_uncertainty_flag
        if smoother_redux
            pstmp = [Pstar_init R*Q; (R*Q)' Q];
            pitmp = [Pinf_init zeros(mm,rr); zeros(rr,mm+rr)];
            ntmp0 = [N_00 zeros(mm,rr); zeros(rr,mm+rr)];
            ntmp1 = [N_10 zeros(mm,rr); zeros(rr,mm+rr)];
            ntmp2 = [N_20 zeros(mm,rr); zeros(rr,mm+rr)];
            V_0gT    = pstmp - pstmp*ntmp0*pstmp...
                -(pitmp*ntmp1*pstmp)'...
                - pitmp*ntmp1*pstmp...
                - pitmp*ntmp2*pitmp;                                   % DK (2012), eq. 5.30

        else
            V_0gT=Pstar_init-Pstar_init*N_00*Pstar_init...
                -(Pinf_init*N_10*Pstar_init)'...
                - Pinf_init*N_10*Pstar_init...
                - Pinf_init*N_20*Pinf_init;                       % DK (2012), eq. 5.30
        end
    end
end

if decomp_flag
    decomp = zeros(nk,mm,rr,smpl+nk);
    ZRQinv = inv(Z*QQ*Z');
    for t = max(d,1):smpl
        ri_d = zeros(mm,1);
        di = flipud(data_index{t})';
        for i = di
            if Fi(i,t) > kalman_tol
                ri_d = Z(i,:)'/Fi(i,t)*v(i,t)+ri_d-Ki(:,i,t)'*ri_d/Fi(i,t)*Z(i,:)';
            end
        end

        % calculate eta_tm1t
        if isoccbin
            if isqvec
                QRt = Qvec(:,:,t)*transpose(RR(:,:,t));
            else
                QRt = Q*transpose(RR(:,:,t));
            end
            R = RR(:,:,t);
            T = TT(:,:,t);
        else
            if isqvec
                QRt = Qvec(:,:,t)*transpose(R);
            end
        end
        eta_tm1t = QRt*ri_d;
        % calculate decomposition
        Ttok = eye(mm,mm);
        AAA = P_tgtm1(:,:,t)*Z'*ZRQinv*Z*R;
        for h = 1:nk
            BBB = Ttok*AAA;
            for j=1:rr
                decomp(h,:,j,t+h) = eta_tm1t(j)*BBB(:,j);
            end
            Ttok = T*Ttok;
        end
    end
end

epsilonhat_tgT = Y - Z*alphahat_tgT;

if (d==smpl)
    warning('missing_DiffuseKalmanSmootherH3_Z:: The diffuse filter never exited the diffuse state. There isn''t enough information to estimate the initial conditions of the nonstationary variables');
    return
end
