function [filtered_errs, resids, Emat, stateval, error_code, regime_history] = IVF_core(M_,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,options_,err_index,filtered_errs_init,my_obs_list,obs,init_val)
% [filtered_errs, resids, Emat, stateval, error_code] = IVF_core(M_,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,options_,err_index,filtered_errs_init,my_obs_list,obs,init_val)
% Calls the solver to get the shocks explaining the data for the inversion filter
%
% Outputs:
%  - filtered_errs          [T by N_obs]        filtered shocks
%  - resids                 [T by N_obs]        residuals
%  - Emat                   [N by N_obs by T]   response matrix of endogenous variables to shocks at each point in time
%  - stateval               [T by N]            vector of endogenous variables
%  - error_code             [4 by 1]            error code
%
% Inputs
% - M_                      [structure]     MATLAB's structure describing the model (M_).
% - dr                      [structure]     Reduced form model.
% - endo_steady_state       [vector]        steady state value for endogenous variables
% - exo_steady_state        [vector]        steady state value for exogenous variables
% - exo_det_steady_state    [vector]        steady state value for exogenous deterministic variables
% - options_                [structure]     MATLAB's structure describing the current options (options_).
% - err_index               [double]        index of shocks with strictly positive variance in M_.exo_names
% - filtered_errs_init      [T by N_obs]    initial values for the shocks
% - my_obs_list             [cell]          names of observables
% - obs                     [T by N_obs]    observed data
% - init_val                [N by 1]        initial value of endogenous variables

% Original authors: Pablo Cuba-Borda, Luca Guerrieri, Matteo Iacoviello, and Molin Zhong
% Original file downloaded from:
% http://www.lguerrieri.com/jae-replication.zip
% Adapted for Dynare by Dynare Team.
%
% This code is in the public domain and may be used freely.
% However the authors would appreciate acknowledgment of the source by
% citation of any of the following papers:
%
% Pablo Cuba-Borda, Luca Guerrieri, Matteo Iacoviello, and Molin Zhong (2019): "Likelihood evaluation of models
% with occasionally binding constraints", Journal of Applied Econometrics,
% 34(7), 1073-1085


%-------------------------------------
% Filter shocks
%-------------------------------------

[sample_length, n_obs]= size(obs);
nerrs = size(err_index,1);
if nargin<11
    init_val = zeros(M_.endo_nbr,1);
end

resids = zeros(sample_length,nerrs);
stateval = zeros(sample_length,M_.endo_nbr);
Emat = zeros(M_.endo_nbr,nerrs,sample_length);
error_code = zeros(4,1);
%solver options (set locally)
options_.solve_algo = options_.occbin.solver.solve_algo;
options_.jacobian_flag=1;

opts_simul = options_.occbin.simul;
opts_simul.curb_retrench = options_.occbin.likelihood.curb_retrench;
opts_simul.maxit = options_.occbin.likelihood.maxit;
opts_simul.waitbar = false;
opts_simul.periods = options_.occbin.likelihood.periods;
opts_simul.check_ahead_periods = options_.occbin.likelihood.check_ahead_periods;
opts_simul.max_check_ahead_periods = options_.occbin.likelihood.max_check_ahead_periods;
opts_simul.periodic_solution = options_.occbin.likelihood.periodic_solution;
opts_simul.restrict_state_space = options_.occbin.likelihood.restrict_state_space;
opts_simul.piecewise_only = 1;

filtered_errs=zeros(sample_length,n_obs);

if options_.occbin.likelihood.waitbar
    [hh_fig, length_of_old_string] = wait_bar.run(0,[],'IVF_core: Filtering the shocks', options_.console_mode, 0, 'IVF_core: Filtering the shocks.');
end

for this_period=1:sample_length
    if options_.occbin.likelihood.waitbar
        [~,length_of_old_string]=wait_bar.run(this_period/sample_length, hh_fig, sprintf('Period %u of %u', this_period,sample_length),options_.console_mode,length_of_old_string);
    end
    current_obs = obs(this_period,:);
    init_val_old = init_val;

    inan = ~isnan(current_obs);
    current_obs = current_obs(inan);
    obs_list = my_obs_list(inan);
    opts_simul.varobs_id=options_.varobs_id(inan)';
    opts_simul.exo_pos=err_index(inan); %err_index is predefined mapping from observables to shocks
    opts_simul.endo_init = init_val_old;
    opts_simul.SHOCKS = filtered_errs_init(this_period,inan);
    [err_vals_out, exitflag] = dynare_solve(@occbin.match_function, filtered_errs_init(this_period,inan)', options_.occbin.solver.maxit, options_.occbin.solver.solve_tolf, options_.occbin.solver.solve_tolx, options_, obs_list, current_obs, opts_simul, M_, dr, endo_steady_state, exo_steady_state, exo_det_steady_state, options_);

    if exitflag
        filtered_errs=NaN;
        error_code(1) = 304;
        error_code(4) = 1000;
        if this_period == 1
            regime_history(this_period) = [];
        end
        if options_.occbin.likelihood.waitbar; wait_bar.close(hh_fig,options_.console_mode); end
        return
    end
    filtered_errs(this_period,inan)=err_vals_out';

    opts_simul.SHOCKS = err_vals_out;

    [ resids(this_period,inan), ~, stateval(this_period,:), Emat(:,inan,this_period), M_, out] = occbin.match_function(...
        err_vals_out,obs_list,current_obs,opts_simul, M_,dr, endo_steady_state, exo_steady_state, exo_det_steady_state,options_);
    init_val = stateval(this_period,:); %update
    regime_history(this_period) = out.regime_history(1);
    if max(abs(err_vals_out))>1e8
        error_code(1) = 306;
        error_code(4) = max(abs(err_vals_out))/1000;
        filtered_errs=NaN;
        if options_.occbin.likelihood.waitbar; wait_bar.close(hh_fig,options_.console_mode); end
        return
    end
    if max(abs(resids(this_period,:)))>0.001
        disp_verbose('IVF_core: I am stopping because match_function could not find the shocks that',options_.verbosity)
        disp_verbose('IVF_core: solve for the model''s observed variables',options_.verbosity)
        filtered_errs=NaN;
        error_code(1) = 303;
        error_code(4) = max(abs(resids(this_period,:)))*100;
        if options_.occbin.likelihood.waitbar; wait_bar.close(hh_fig,options_.console_mode); end
        return
    end
end
if options_.occbin.likelihood.waitbar
    wait_bar.close(hh_fig,options_.console_mode);
end

end