function myoutput=prior_posterior_statistics_core(myinputs,fpar,B,whoiam, ThisMatlab)
% PARALLEL CONTEXT
% Core functionality for prior_posterior.m function, which can be parallelized.
% See also the comment in posterior_sampler_core.m function.
%
% INPUTS
%   See the comment in posterior_sampler_core.m function.
%
% OUTPUTS
% o myoutput  [struc]
%  Contained OutputFileName_smooth;
%                          _update;
%                          _inno;
%                          _error;
%                          _filter_step_ahead;
%                          _param;
%                          _forc_mean;
%                          _forc_point;
%                          _forc_point_ME;
%                          _filter_covar;
%                          _trend_coeff;
%                          _init_state;
%                          _smoothed_trend;
%                          _smoothed_constant;
%                          _state_uncert;
%                          _init_state_uncert;
%
% ALGORITHM
%   Portion of prior_posterior.m function.
% This file is part of Dynare.
%
% SPECIAL REQUIREMENTS.
%   None.

% Copyright © 2005-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 nargin<4
    whoiam=0;
end
if nargin<5
    ThisMatlab=1;
end

% Reshape 'myinputs' for local computation.
% In order to avoid confusion in the name space, the instruction struct2local(myinputs) is replaced by:
M_=myinputs.M_;
oo_=myinputs.oo_;
options_=myinputs.options_;
estim_params_=myinputs.estim_params_;
Prior=myinputs.Prior;
smoother_info=myinputs.smoother_info;
dataset_info=myinputs.dataset_info;
dataset_=myinputs.dataset_;

type=myinputs.type;
run_smoother=myinputs.run_smoother;
filter_covariance=myinputs.filter_covariance;
updated_covariance=myinputs.updated_covariance;
smoothed_state_uncertainty=myinputs.smoothed_state_uncertainty;
smoothed_shock_variance=myinputs.smoothed_shock_variance;
gend=myinputs.gend;
n_observed_series=dataset_.size(2);
mean_varobs=dataset_info.descriptive.mean;
irun=myinputs.irun;
endo_nbr=myinputs.endo_nbr;
nvn=myinputs.nvn;
naK=myinputs.naK;
horizon=myinputs.horizon;
iendo=myinputs.iendo;
IdObs=myinputs.IdObs; %index of observables
if horizon
    i_last_obs=myinputs.i_last_obs;
    MAX_nforc1=myinputs.MAX_nforc1;
    MAX_nforc2=myinputs.MAX_nforc2;
    if ~isequal(M_.H,0)
        MAX_nforc_ME=myinputs.MAX_nforc_ME;
    end
end
if naK
    MAX_naK=myinputs.MAX_naK;
end
if filter_covariance
    MAX_filter_covariance=myinputs.MAX_filter_covariance;
end
if updated_covariance
    MAX_updated_covariance=myinputs.MAX_updated_covariance;
end
if smoothed_state_uncertainty
    MAX_n_smoothed_state_uncertainty=myinputs.MAX_n_smoothed_state_uncertainty;
end
if smoothed_shock_variance
    MAX_smoothed_shock_variance=myinputs.MAX_smoothed_shock_variance;
end

maxlag=myinputs.maxlag;
MAX_nsmoo=myinputs.MAX_nsmoo;
MAX_ninno=myinputs.MAX_ninno;
MAX_n_smoothed_constant=myinputs.MAX_n_smoothed_constant;
MAX_n_smoothed_trend=myinputs.MAX_n_smoothed_trend;
MAX_n_trend_coeff=myinputs.MAX_n_trend_coeff;
MAX_nerro = myinputs.MAX_nerro;
MAX_nruns=myinputs.MAX_nruns;
ifil=myinputs.ifil;

if ~strcmpi(type,'prior')
    x=myinputs.x;
    if strcmpi(type,'posterior')
        logpost=myinputs.logpost;
    end
else
    options_ = select_qz_criterium_value(options_);
end
if whoiam
    Parallel=myinputs.Parallel;
else
    Parallel=0; %make sure it exists
end

% DirectoryName = myinputs.DirectoryName;
if strcmpi(type,'posterior')
    folder_name=get_posterior_folder_name(options_);
    DirectoryName = CheckPath(folder_name,M_.dname);
elseif strcmpi(type,'gsa')
    if options_.opt_gsa.pprior
        DirectoryName = CheckPath(['gsa',filesep,'prior'],M_.dname);
    else
        DirectoryName = CheckPath(['gsa',filesep,'mc'],M_.dname);
    end
elseif strcmpi(type,'prior')
    DirectoryName = CheckPath('prior',M_.dname);
end

RemoteFlag = 0;
if whoiam
    if Parallel(ThisMatlab).Local==0
        RemoteFlag =1;
    end
    ifil=ifil(:,whoiam);
end
waitbar_string=['Taking ',type,' subdraws...'];
[h, length_of_old_string] = wait_bar.run(0, [], waitbar_string, options_.console_mode, 0, 'Prior/posterior objects.', whoiam,Parallel(ThisMatlab));

if RemoteFlag==1
    OutputFileName_smooth = {};
    OutputFileName_update = {};
    OutputFileName_inno = {};
    OutputFileName_error = {};
    OutputFileName_filter_step_ahead = {};
    OutputFileName_param = {};
    OutputFileName_forc_mean = {};
    OutputFileName_forc_point = {};
    OutputFileName_forc_point_ME = {};
    OutputFileName_filter_covar ={};
    OutputFileName_updated_covar ={};
    OutputFileName_shock_var ={};
    OutputFileName_trend_coeff = {};
    OutputFileName_smoothed_trend = {};
    OutputFileName_smoothed_constant = {};
    % OutputFileName_moments = {};
end

%initialize arrays
if run_smoother
    stock_init_smooth=NaN(endo_nbr,MAX_n_trend_coeff);
    stock_smooth=NaN(endo_nbr,gend,MAX_nsmoo);
    stock_update=NaN(endo_nbr,gend,MAX_nsmoo);
    stock_innov=NaN(M_.exo_nbr,gend,MAX_ninno);
    stock_smoothed_constant=NaN(endo_nbr,gend,MAX_n_smoothed_constant);
    stock_smoothed_trend=NaN(endo_nbr,gend,MAX_n_smoothed_trend);
    stock_trend_coeff = zeros(endo_nbr,MAX_n_trend_coeff);
    if options_.occbin.smoother.status && ~options_.occbin.smoother.inversion_filter && not(options_.lik_init==2 && options_.Harvey_scale_factor==0)
        % TBD: initialize stock_occbin_regime stock_occbin_realtime_regime
    end
    if horizon
        stock_forcst_mean= NaN(endo_nbr,horizon,MAX_nforc1);
        stock_forcst_point = NaN(endo_nbr,horizon,MAX_nforc2);
        if ~isequal(M_.H,0)
            stock_forcst_point_ME = NaN(n_observed_series,horizon,MAX_nforc_ME);
        end
    end
end
if nvn
    stock_error = NaN(n_observed_series,gend,MAX_nerro);
end
if naK
    stock_filter_step_ahead =NaN(length(options_.filter_step_ahead),endo_nbr,gend+max(options_.filter_step_ahead),MAX_naK);
end
stock_param = NaN(MAX_nruns, length(Prior));
stock_logpo = NaN(MAX_nruns, 1);
stock_ys = NaN(MAX_nruns, endo_nbr);
if filter_covariance
    stock_filter_covariance = zeros(endo_nbr,endo_nbr,gend+1,MAX_filter_covariance);
end
if updated_covariance
    stock_updated_covariance = zeros(endo_nbr,endo_nbr,gend,MAX_updated_covariance);
end
if smoothed_state_uncertainty
    stock_smoothed_uncert = zeros(endo_nbr,endo_nbr,gend,MAX_n_smoothed_state_uncertainty);
    stock_init_smoothed_uncert = zeros(endo_nbr,endo_nbr,MAX_n_smoothed_state_uncertainty);
end
if smoothed_shock_variance
    stock_smoothed_shock_variance = zeros(M_.exo_nbr,M_.exo_nbr,gend,MAX_smoothed_shock_variance);
end

opts_local = options_;
for b=fpar:B
    is_successful_draw = true;
    if strcmpi(type,'prior')
        iter=1;
        logpo=[];
        while (isempty(logpo) || isinf(logpo)) && iter<1000
            deep = Prior.draw();
            logpo = evaluate_posterior_kernel(deep, M_, estim_params_, oo_, options_, Prior);
            iter=iter+1;
        end
        if iter==1000
            fprintf('\nprior_posterior_statistics: unable to get a valid draw in 1000 tries. Giving up.')
        end
    else
        deep = x(b,:);
        if strcmpi(type,'posterior')
            logpo = logpost(b);
        else
            logpo = evaluate_posterior_kernel(deep',M_,estim_params_,oo_,options_,Prior);
        end
    end
    M_ = set_all_parameters(deep,estim_params_,M_);

    if run_smoother
        [dr,info,M_.params] =compute_decision_rules(M_,opts_local,oo_.dr, oo_.steady_state, oo_.exo_steady_state, oo_.exo_det_steady_state);
        if ismember(info(1),[3,4])
            opts_local.qz_criterium = 1 + (opts_local.qz_criterium-1)*10; %loosen tolerance, useful for big models where BK conditions were tested on restricted state space
            [dr,info,M_.params] =compute_decision_rules(M_,opts_local,oo_);
        end
        if info(1)
            message=get_error_message(info,opts_local);
            fprintf('\nprior_posterior_statistics: One of the draws failed with the error:\n%s\n',message)
            fprintf('prior_posterior_statistics: This should not happen. Please contact the developers.\n')
        end
        if options_.occbin.smoother.status
            opts_local.occbin.smoother.debug = false;
            opts_local.occbin.smoother.plot = false;
            opts_local.occbin.smoother.store_results = false;
            opts_local.occbin.simul.waitbar=0;
            opts_local.occbin.smoother.waitbar = false;
            opts_local.occbin.smoother.linear_smoother=false; % speed-up
            if options_.occbin.smoother.inversion_filter
                [~, info, ~, ~, ~, ~, smoother_results, regime_history] = ...
                    occbin.IVF_posterior(deep,dataset_,[],options_,M_,estim_params_,Prior,prior_bounds(Prior,options_.prior_trunc),dr, oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state);
                if info(1)
                    message=get_error_message(info,opts_local);
                    fprintf('\nprior_posterior_statistics: IVF smoother failed for one of the draws:\n%s\n',message)
                    is_successful_draw = false;
                else
                    stock_occbin_regime(:,irun(5))=regime_history;
                    stock_occbin_realtime_regime(:,irun(5))=regime_history;
                end
            else % PKF
                opts_local.verbosity=0;
                [smoother_results, occbin_info] = ...
                    occbin.DSGE_smoother(deep,M_,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,opts_local,estim_params_,dataset_,dataset_info,smoother_info);
                occbin_error_flag = occbin_info.error_flag;
                occbin_regime_history = occbin_info.regime_history;
                if occbin_error_flag(1)
                    message=get_error_message(occbin_error_flag,opts_local);
                    fprintf('\nprior_posterior_statistics: One of the draws failed with the error:\n%s\n',message)
                    is_successful_draw = false;
                end
                if is_successful_draw && options_.smoothed_state_uncertainty && not(opts_local.lik_init==2 && opts_local.Harvey_scale_factor==0)
                    % Extract only the fields needed before/inside the loop
                    state_uncertainty0 = smoother_results.smoothed.V_0gT;
                    a0T = smoother_results.smoothed.alphahat0;
                    SteadyState = smoother_results.model.SteadyState;

                    state_uncertainty1 = state_uncertainty0(dr.inv_order_var,dr.inv_order_var);
                    alphahat0 = a0T(dr.inv_order_var);
                    alphahat1 = alphahat0(dr.state_var);
                    state_uncertainty1 = state_uncertainty1(dr.state_var,dr.state_var);
                    [U,X] = svd(0.5*(state_uncertainty1+state_uncertainty1'));
                    % P= U*X*U'; % symmetric matrix!
                    is = find(diag(X)>options_.kalman_tol);
                    StateVectorVarianceSquareRoot = chol(X(is,is))';

                    % Get the rank of StateVectorVarianceSquareRoot
                    state_variance_rank = size(StateVectorVarianceSquareRoot,2);
                    U = U(:,is);

                    if not(isempty(is))
                        alphahat01 = U(:,is)*StateVectorVarianceSquareRoot*randn(state_variance_rank,1)+alphahat1;
                    else
                        alphahat01 = alphahat1;
                    end
                    error_indicator=true;
                    M_local = M_;
                    % set direct assignment of initial states
                    M_local.endo_initial_state.status = true;
                    M_local.endo_initial_state.values = zeros(M_.endo_nbr,1);
                    opts_local1 = opts_local;
                    opts_local1.lik_init = 2;
                    opts_local1.Harvey_scale_factor = 0;
                    niter=0;
                    while error_indicator && niter<10
                        niter=niter+1;
                        M_local.endo_initial_state.values(dr.state_var) = alphahat01 + SteadyState(dr.state_var);
                        [smoother_results, occbin_info] = ...
                            occbin.DSGE_smoother(deep,M_local,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,opts_local1,estim_params_,dataset_,dataset_info,smoother_info);
                        occbin_error_flag = occbin_info.error_flag;
                        occbin_regime_history = occbin_info.regime_history;
                        SteadyState = smoother_results.model.SteadyState;
                        if occbin_error_flag(1)
                            if not(isempty(is)) && niter==1
                                % first check if smoother mean works
                                M_local.endo_initial_state.values(dr.state_var) = alphahat1;
                                [smoother_results, occbin_info] = ...
                                    occbin.DSGE_smoother(deep,M_local,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,opts_local1,estim_params_,dataset_,dataset_info,smoother_info);
                                occbin_error_flag = occbin_info.error_flag;
                                occbin_regime_history = occbin_info.regime_history;
                                SteadyState = smoother_results.model.SteadyState;
                            end
                            if occbin_error_flag(1) && (niter==1 || niter==10)
                                % use smoother ?
                                [smoother_results, occbin_info] = ...
                                    occbin.DSGE_smoother(deep,M_,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,opts_local,estim_params_,dataset_,dataset_info,smoother_info);
                                occbin_error_flag = occbin_info.error_flag;
                                occbin_regime_history = occbin_info.regime_history;
                                SteadyState = smoother_results.model.SteadyState;
                            else
                                % try another draw from smoother distribution
                                alphahat01 = U(:,is)*StateVectorVarianceSquareRoot*randn(state_variance_rank,1)+alphahat1;
                                occbin_error_flag(1) = 1;
                            end
                        end
                        error_indicator = any(occbin_error_flag(1));
                    end
                end
                if occbin_error_flag(1)
                    message=get_error_message(occbin_error_flag,opts_local);
                    fprintf('\nprior_posterior_statistics: One of the draws failed with the error:\n%s\n',message)
                    is_successful_draw = false;
                else
                    stock_occbin_regime(:,irun(5))=occbin_regime_history;
                    stock_occbin_realtime_regime(:,irun(5))=occbin_info.realtime_regime_history;
                end
            end
        else
            smoother_results = DsgeSmoother(deep,dataset_,dataset_info,M_,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,opts_local,estim_params_,smoother_info);
        end
        % Unpack frequently-used fields from smoother_results
        SteadyState = smoother_results.model.SteadyState;
        trend_coeff = smoother_results.model.trend_coeff;
        trend_addition = smoother_results.model.trend_addition;

        if is_successful_draw
            stock_trend_coeff(options_.varobs_id,irun(9))=trend_coeff;
            stock_smoothed_trend(IdObs,:,irun(11))=trend_addition;
            if options_.loglinear %reads values from smoother results, which are in dr-order and put them into declaration order
                constant_part=repmat(log(SteadyState(dr.order_var)),1,gend);
                stock_smooth(dr.order_var,:,irun(1)) = smoother_results.smoothed.alphahat(1:endo_nbr,:)+ ...
                    constant_part;
                stock_update(dr.order_var,:,irun(1)) = smoother_results.filtered.ahat(1:endo_nbr,:)+ ...
                    constant_part;
            else
                constant_part=repmat(SteadyState(dr.order_var),1,gend);
                stock_smooth(dr.order_var,:,irun(1)) = smoother_results.smoothed.alphahat(1:endo_nbr,:)+ ...
                    constant_part;
                stock_update(dr.order_var,:,irun(1)) = smoother_results.filtered.ahat(1:endo_nbr,:)+ ...
                    constant_part;
            end
            if ~(options_.occbin.smoother.status && options_.occbin.smoother.inversion_filter)
                stock_init_smooth(dr.order_var,irun(9)) = smoother_results.smoothed.alphahat0(1:endo_nbr)+constant_part(:,1);
            end
            stock_smoothed_constant(dr.order_var,:,irun(10))=constant_part;
            %% Compute constant for observables
            if options_.prefilter == 1 %as mean is taken after log transformation, no distinction is needed here
                constant_part=repmat(mean_varobs',1,gend);
            elseif options_.prefilter == 0 && options_.loglinear %logged steady state must be used
                constant_part=repmat(log(SteadyState(IdObs)),1,gend);
            elseif options_.prefilter == 0 && ~options_.loglinear %unlogged steady state must be used
                constant_part=repmat(SteadyState(IdObs),1,gend);
            end
            %add trend to observables
            if options_.prefilter
                %do correction for prefiltering for observed variables
                if options_.loglinear
                    mean_correction=-repmat(log(SteadyState(IdObs)),1,gend)+constant_part;
                else
                    mean_correction=-repmat(SteadyState(IdObs),1,gend)+constant_part;
                end
                stock_smoothed_constant(IdObs,:,irun(10))=stock_smoothed_constant(IdObs,:,irun(10))+mean_correction;
                %smoothed variables are E_T(y_t) so no trend shift is required
                stock_smooth(IdObs,:,irun(1))=stock_smooth(IdObs,:,irun(1))+trend_addition+mean_correction;
                stock_init_smooth(IdObs,irun(9))=stock_init_smooth(IdObs,irun(9))+trend_addition(:,1)+mean_correction(:,1);
                %updated variables are E_t(y_t) so no trend shift is required
                stock_update(IdObs,:,irun(1))=stock_update(IdObs,:,irun(1))+trend_addition+mean_correction;
            else
                stock_init_smooth(IdObs,irun(9))=stock_init_smooth(IdObs,irun(9))+trend_addition(:,1);
                stock_smooth(IdObs,:,irun(1))=stock_smooth(IdObs,:,irun(1))+trend_addition;
                stock_update(IdObs,:,irun(1))=stock_update(IdObs,:,irun(1))+trend_addition;
            end
            stock_innov(:,:,irun(2))  = smoother_results.smoothed.etahat;
            if nvn
                stock_error(:,:,irun(3))  = smoother_results.smoothed.epsilonhat;
            end
            if naK
                %filtered variable E_t(y_t+k) requires to shift trend by k periods
                %write variables into declaration order
                if options_.loglinear %reads values from smoother results, which are in dr-order and put them into declaration order
                    constant_part=repmat(log(SteadyState(dr.order_var))',[length(options_.filter_step_ahead),1,gend+max(options_.filter_step_ahead)]);
                else
                    constant_part=repmat(SteadyState(dr.order_var)',[length(options_.filter_step_ahead),1,gend+max(options_.filter_step_ahead)]);
                end
                stock_filter_step_ahead(:,dr.order_var,:,irun(4)) = smoother_results.filtered.aK(options_.filter_step_ahead,1:endo_nbr,:) + constant_part;
                %now add trend to observables
                for ii=1:length(options_.filter_step_ahead)
                    if options_.prefilter
                        zdim = size(stock_filter_step_ahead(ii,IdObs,:,irun(4)));
                        squeezed = reshape(stock_filter_step_ahead(ii,IdObs,:,irun(4)), [zdim(2:end) 1]);
                        stock_filter_step_ahead(ii,IdObs,:,irun(4)) = squeezed ...
                            +repmat(mean_correction(:,1),1,gend+max(options_.filter_step_ahead)) ... %constant correction
                            +[trend_addition repmat(trend_addition(:,end),1,max(options_.filter_step_ahead))+trend_coeff*(1:max(options_.filter_step_ahead))]; %trend
                    else
                        zdim = size(stock_filter_step_ahead(ii,IdObs,:,irun(4)));
                        squeezed = reshape(stock_filter_step_ahead(ii,IdObs,:,irun(4)), [zdim(2:end) 1]);
                        stock_filter_step_ahead(ii,IdObs,:,irun(4)) = squeezed ...
                            +[trend_addition repmat(trend_addition(:,end),1,max(options_.filter_step_ahead))+trend_coeff*(1:max(options_.filter_step_ahead))]; %trend
                    end
                end
            end
            if horizon
                yyyy = smoother_results.smoothed.alphahat(iendo,i_last_obs);
                if options_.occbin.smoother.status
                    M_.endo_histval=yyyy(dr.inv_order_var);
                    options_.occbin.forecast.replic=0;
                    options_.occbin.simul.waitbar=false;
                    [~, error_flag, yf] = occbin.forecast(options_,M_,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,8);
                else
                    yf = simulate_posterior_forecasts(yyyy,dr,horizon,false,M_.Sigma_e,1);
                end
                if options_.prefilter
                    % add mean
                    yf(:,IdObs) = yf(:,IdObs)+repmat(mean_varobs, ...
                        horizon+maxlag,1);
                    % add trend, taking into account that last point of sample is still included in forecasts and only cut off later
                    yf(:,IdObs) = yf(:,IdObs)+((options_.first_obs-1)+gend+(1-maxlag:horizon)')*trend_coeff'-...
                        repmat(mean(trend_coeff*(options_.first_obs:options_.first_obs+gend-1),2)',length(1-maxlag:horizon),1); %center trend
                else
                    % add trend, taking into account that last point of sample is still included in forecasts and only cut off later
                    yf(:,IdObs) = yf(:,IdObs)+((options_.first_obs-1)+gend+(1-maxlag:horizon)')*trend_coeff';
                end
                if options_.loglinear
                    yf = yf+repmat(log(SteadyState'),horizon+maxlag,1);
                else
                    yf = yf+repmat(SteadyState',horizon+maxlag,1);
                end
                if options_.occbin.smoother.status
                    options_.occbin.forecast.replic=1;
                    options_.occbin.forecast.qmc=0; % make sure we draw randn
                    options_.occbin.forecast.waitbar=false;
                    [~, error_flag, yf1] = occbin.forecast(options_,M_,dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state,8);
                else
                    yf1 = simulate_posterior_forecasts(yyyy,dr,horizon,true,M_.Sigma_e,1);
                end
                if options_.prefilter == 1
                    % add mean
                    yf1(:,IdObs,:) = yf1(:,IdObs,:)+ ...
                        repmat(mean_varobs,[horizon+maxlag,1,1]);
                    % add trend, taking into account that last point of sample is still included in forecasts and only cut off later
                    yf1(:,IdObs) = yf1(:,IdObs)+((options_.first_obs-1)+gend+(1-maxlag:horizon)')*trend_coeff'-...
                        repmat(mean(trend_coeff*(options_.first_obs:options_.first_obs+gend-1),2)',length(1-maxlag:horizon),1); %center trend
                else
                    % add trend, taking into account that last point of sample is still included in forecasts and only cut off later
                    yf1(:,IdObs,:) = yf1(:,IdObs,:)+repmat(((options_.first_obs-1)+gend+(1-maxlag:horizon)')* ...
                        trend_coeff',[1,1,1]);
                end
                if options_.loglinear
                    yf1 = yf1 + repmat(log(SteadyState'),[horizon+maxlag,1,1]);
                else
                    yf1 = yf1 + repmat(SteadyState',[horizon+maxlag,1,1]);
                end

                stock_forcst_mean(:,:,irun(6)) = yf(maxlag+1:end,:)';
                stock_forcst_point(:,:,irun(7)) = yf1(maxlag+1:end,:)';
                if ~isequal(M_.H,0)
                    ME_shocks=zeros(n_observed_series,horizon);
                    i_exo_var = setdiff(1:n_observed_series,find(diag(M_.H) == 0));
                    nxs = length(i_exo_var);
                    chol_H = chol(M_.H(i_exo_var,i_exo_var));
                    if ~isempty(M_.H)
                        ME_shocks(i_exo_var,:) = chol_H*randn(nxs,horizon);
                    end
                    stock_forcst_point_ME(:,:,irun(12)) = yf1(maxlag+1:end,IdObs)'+ME_shocks;
                end
            end
            if filter_covariance
                stock_filter_covariance(dr.order_var,dr.order_var,:,irun(8)) = smoother_results.filtered.P;
            end
            if updated_covariance
                stock_updated_covariance(dr.order_var,dr.order_var,:,irun(14)) = smoother_results.filtered.Ptt;
            end
            if smoothed_state_uncertainty
                stock_smoothed_uncert(dr.order_var,dr.order_var,:,irun(13)) = smoother_results.smoothed.V_tgT;
                stock_init_smoothed_uncert(dr.order_var,dr.order_var,irun(13)) = smoother_results.smoothed.V_0gT;
            end
            if smoothed_shock_variance
                stock_smoothed_shock_variance(:,:,:,irun(15)) = smoother_results.smoothed.V_eta;
            end
        end
    else
        [~,~,SteadyState] = dynare_resolve(M_,options_,oo_.dr,oo_.steady_state,oo_.exo_steady_state,oo_.exo_det_steady_state);
    end
    if is_successful_draw
        stock_param(irun(5),:) = deep;
        stock_logpo(irun(5),1) = logpo;
        stock_ys(irun(5),:) = SteadyState';


        irun = irun +  ones(15,1);
    end

    if run_smoother && (irun(1) > MAX_nsmoo || b == B)
        stock = stock_smooth(:,:,1:irun(1)-1);
        ifil(1) = ifil(1) + 1;
        save([DirectoryName '/' M_.fname '_smooth' int2str(ifil(1)) '.mat'],'stock');

        stock = stock_update(:,:,1:irun(1)-1);
        save([DirectoryName '/' M_.fname '_update' int2str(ifil(1)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_smooth = [OutputFileName_smooth; {[DirectoryName filesep], [M_.fname '_smooth' int2str(ifil(1)) '.mat']}];
            OutputFileName_update = [OutputFileName_update; {[DirectoryName filesep], [M_.fname '_update' int2str(ifil(1)) '.mat']}];
        end
        irun(1) = 1;
    end

    if run_smoother && (irun(2) > MAX_ninno || b == B)
        stock = stock_innov(:,:,1:irun(2)-1);
        ifil(2) = ifil(2) + 1;
        save([DirectoryName '/' M_.fname '_inno' int2str(ifil(2)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_inno = [OutputFileName_inno; {[DirectoryName filesep], [M_.fname '_inno' int2str(ifil(2)) '.mat']}];
        end
        irun(2) = 1;
    end

    if run_smoother && nvn && (irun(3) > MAX_nerro || b == B)
        stock = stock_error(:,:,1:irun(3)-1);
        ifil(3) = ifil(3) + 1;
        save([DirectoryName '/' M_.fname '_error' int2str(ifil(3)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_error = [OutputFileName_error; {[DirectoryName filesep], [M_.fname '_error' int2str(ifil(3)) '.mat']}];
        end
        irun(3) = 1;
    end

    if run_smoother && naK && (irun(4) > MAX_naK || b == B)
        stock = stock_filter_step_ahead(:,:,:,1:irun(4)-1);
        ifil(4) = ifil(4) + 1;
        save([DirectoryName '/' M_.fname '_filter_step_ahead' int2str(ifil(4)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_filter_step_ahead = [OutputFileName_filter_step_ahead; {[DirectoryName filesep], [M_.fname '_filter_step_ahead' int2str(ifil(4)) '.mat']}];
        end
        irun(4) = 1;
    end

    if irun(5) > MAX_nruns || b == B
        stock = stock_param(1:irun(5)-1,:);
        stock_logpo = stock_logpo(1:irun(5)-1);
        stock_ys = stock_ys(1:irun(5)-1,:);
        ifil(5) = ifil(5) + 1;
        save([DirectoryName '/' M_.fname '_param' int2str(ifil(5)) '.mat'],'stock','stock_logpo','stock_ys');
        if RemoteFlag==1
            OutputFileName_param = [OutputFileName_param; {[DirectoryName filesep], [M_.fname '_param' int2str(ifil(5)) '.mat']}];
        end
        if options_.occbin.smoother.status
            stock = stock_occbin_regime(:,1:irun(5)-1);
            save([DirectoryName '/' M_.fname '_occbin_regime' int2str(ifil(5)) '.mat'],'stock');
            stock = stock_occbin_realtime_regime(:,1:irun(5)-1);
            save([DirectoryName '/' M_.fname '_occbin_realtime_regime' int2str(ifil(5)) '.mat'],'stock');
            if RemoteFlag==1
                OutputFileName_param = [OutputFileName_param; {[DirectoryName filesep], [M_.fname '_occbin_regime' int2str(ifil(5)) '.mat']}];
                OutputFileName_param = [OutputFileName_param; {[DirectoryName filesep], [M_.fname '_occbin_realtime_regime' int2str(ifil(5)) '.mat']}];
            end
        end
        irun(5) = 1;
    end

    if run_smoother && horizon && (irun(6) > MAX_nforc1 || b == B)
        stock = stock_forcst_mean(:,:,1:irun(6)-1);
        ifil(6) = ifil(6) + 1;
        save([DirectoryName '/' M_.fname '_forc_mean' int2str(ifil(6)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_forc_mean = [OutputFileName_forc_mean; {[DirectoryName filesep], [M_.fname '_forc_mean' int2str(ifil(6)) '.mat']}];
        end
        irun(6) = 1;
    end

    if run_smoother && horizon && (irun(7) > MAX_nforc2 ||  b == B)
        stock = stock_forcst_point(:,:,1:irun(7)-1);
        ifil(7) = ifil(7) + 1;
        save([DirectoryName '/' M_.fname '_forc_point' int2str(ifil(7)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_forc_point = [OutputFileName_forc_point; {[DirectoryName filesep], [M_.fname '_forc_point' int2str(ifil(7)) '.mat']}];
        end
        irun(7) = 1;
    end

    if run_smoother && filter_covariance && (irun(8) > MAX_filter_covariance || b == B)
        stock = stock_filter_covariance(:,:,:,1:irun(8)-1);
        ifil(8) = ifil(8) + 1;
        save([DirectoryName '/' M_.fname '_filter_covar' int2str(ifil(8)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_filter_covar = [OutputFileName_filter_covar; {[DirectoryName filesep], [M_.fname '_filter_covar' int2str(ifil(8)) '.mat']}];
        end
        irun(8) = 1;
    end

    if run_smoother && updated_covariance && (irun(14) > MAX_updated_covariance || b == B)
        stock = stock_updated_covariance(:,:,:,1:irun(14)-1);
        ifil(14) = ifil(14) + 1;
        save([DirectoryName '/' M_.fname '_updated_covar' int2str(ifil(14)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_updated_covar = [OutputFileName_updated_covar; {[DirectoryName filesep], [M_.fname '_updated_covar' int2str(ifil(14)) '.mat']}];
        end
        irun(14) = 1;
    end

    irun_index=9;
    if run_smoother && (irun(irun_index) > MAX_n_trend_coeff || b == B)
        ifil(irun_index) = ifil(irun_index) + 1;
        stock = stock_trend_coeff(:,1:irun(irun_index)-1);
        save([DirectoryName '/' M_.fname '_trend_coeff' int2str(ifil(irun_index)) '.mat'],'stock');
        stock = stock_init_smooth(:,1:irun(irun_index)-1);
        save([DirectoryName '/' M_.fname '_init_state' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_trend_coeff = [OutputFileName_trend_coeff; {[DirectoryName filesep], [M_.fname '_trend_coeff' int2str(ifil(irun_index)) '.mat']}];
            OutputFileName_init_state = [OutputFileName_init_state; {[DirectoryName filesep], [M_.fname '_init_state' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end

    irun_index=10;
    if run_smoother && (irun(irun_index) > MAX_n_smoothed_constant || b == B)
        stock = stock_smoothed_constant(:,:,1:irun(irun_index)-1);
        ifil(irun_index) = ifil(irun_index) + 1;
        save([DirectoryName '/' M_.fname '_smoothed_constant' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_smoothed_constant = [OutputFileName_smoothed_constant; {[DirectoryName filesep], [M_.fname '_smoothed_constant' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end

    irun_index=11;
    if run_smoother && (irun(irun_index) > MAX_n_smoothed_trend || b == B)
        stock = stock_smoothed_trend(:,:,1:irun(irun_index)-1);
        ifil(irun_index) = ifil(irun_index) + 1;
        save([DirectoryName '/' M_.fname '_smoothed_trend' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_smoothed_trend = [OutputFileName_smoothed_trend; {[DirectoryName filesep], [M_.fname '_smoothed_trend' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end

    irun_index=12;
    if run_smoother && horizon && ~isequal(M_.H,0) && (irun(irun_index) > MAX_nforc_ME ||  b == B)
        stock = stock_forcst_point_ME(:,:,1:irun(irun_index)-1);
        ifil(irun_index) = ifil(irun_index) + 1;
        save([DirectoryName '/' M_.fname '_forc_point_ME' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_forc_point_ME = [OutputFileName_forc_point_ME; {[DirectoryName filesep], [M_.fname '_forc_point_ME' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end

    irun_index=13;
    if run_smoother && smoothed_state_uncertainty && (irun(irun_index) > MAX_n_smoothed_state_uncertainty || b == B)
        ifil(irun_index) = ifil(irun_index) + 1;
        stock = stock_smoothed_uncert(:,:,:,1:irun(irun_index)-1);
        save([DirectoryName '/' M_.fname '_state_uncert' int2str(ifil(irun_index)) '.mat'],'stock');
        stock = stock_init_smoothed_uncert(:,:,1:irun(irun_index)-1);
        save([DirectoryName '/' M_.fname '_init_state_uncert' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_state_uncert = [OutputFileName_state_uncert; {[DirectoryName filesep], [M_.fname '_state_uncert' int2str(ifil(irun_index)) '.mat']}];
            OutputFileName_init_state_uncert = [OutputFileName_init_state_uncert; {[DirectoryName filesep], [M_.fname '_init_state_uncert' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end

    irun_index=15;
    if run_smoother && smoothed_shock_variance && (irun(irun_index) > MAX_smoothed_shock_variance || b == B)
        stock = stock_smoothed_shock_variance(:,:,:,1:irun(irun_index)-1);
        ifil(irun_index) = ifil(irun_index) + 1;
        save([DirectoryName '/' M_.fname '_shock_var' int2str(ifil(irun_index)) '.mat'],'stock');
        if RemoteFlag==1
            OutputFileName_shock_var = [OutputFileName_shock_var; {[DirectoryName filesep], [M_.fname '_shock_var' int2str(ifil(irun_index)) '.mat']}];
        end
        irun(irun_index) = 1;
    end
    if mod(b-fpar+1, 5)==0
        [~,length_of_old_string]=wait_bar.run((b-fpar+1)/(B-fpar+1),h,waitbar_string, options_.console_mode, length_of_old_string,[],whoiam,Parallel(ThisMatlab));
    end
end

myoutput.ifil=ifil;
if RemoteFlag==1
    myoutput.OutputFileName = [OutputFileName_smooth;
                        OutputFileName_update;
                        OutputFileName_inno;
                        OutputFileName_error;
                        OutputFileName_filter_step_ahead;
                        OutputFileName_param;
                        OutputFileName_forc_mean;
                        OutputFileName_forc_point;
                        OutputFileName_forc_point_ME;
                        OutputFileName_filter_covar;
                        OutputFileName_updated_covar;
                        OutputFileName_trend_coeff;
                        OutputFileName_init_state;
                        OutputFileName_smoothed_trend;
                        OutputFileName_smoothed_constant;
                        OutputFileName_state_uncert;
                        OutputFileName_init_state_uncert;
                        OutputFileName_shock_var];
end

wait_bar.close(h,options_.console_mode);


function yf=simulate_posterior_forecasts(y0,dr,horizon,stochastic_indicator,Sigma_e,n)
% function yf=simulate_posterior_forecasts(y0,horizon,dr,n)
%
% computes forecasts based on first order model solution, given shocks
% drawn from the shock distribution, but not including measurement error
% Inputs:
%   - y0                    [endo_nbr by maximum_endo_lag]      matrix of starting values
%   - dr                    [structure]                         structure with Dynare decision rules
%   - horizon               [scalar]                            number of forecast periods
%   - stochastic_indicator  [boolean]                           indicator whether to consider stochastic shocks
%   - Sigma_e               [integer]                           covariance matrix of shocks
%   - n                     [scalar]                            number of repetitions
%
% Outputs:
%   - yf        [horizon+ykmin_ by endo_nbr by n]   array of forecasts

if nargin< 4
    stochastic_indicator=false;
    n=1;
end
%select states
k2 = dr.inv_order_var(dr.state_var);

if stochastic_indicator
    % eliminate shocks with 0 variance
    i_exo_var = setdiff(1:length(Sigma_e),find(diag(Sigma_e) == 0));
    nxs = length(i_exo_var);

    chol_S = chol(Sigma_e(i_exo_var,i_exo_var));

    if ~isempty(Sigma_e)
        e = randn(nxs,n,horizon);
    end

    B1 = dr.ghu(:,i_exo_var)*chol_S';
end
endo_nbr=length(y0);

yf = zeros(endo_nbr,1+horizon,n);
yf(:,1,:,:) = repmat(y0,[1,1,n]);

for iter=1:horizon
    if stochastic_indicator
        yf(:,iter+1,:) = dr.ghx*squeeze(yf(k2,iter,:))+B1*squeeze(e(:,:,iter));
    else
        yf(:,iter+1,:) = dr.ghx*squeeze(yf(k2,iter,:));
    end
end

yf(dr.order_var,:,:) = yf;
yf=permute(yf,[2 1 3]);
