function irf(nlags,identification)
% function irf(nlags,identification)
% builds IRFs for a BVAR model
%
% INPUTS
%    nlags            [integer]     number of lags for the bvar
%    identification   [string]      identification scheme ('Cholesky' or 'SquareRoot')
%
% OUTPUTS
%    none
%
% SPECIAL REQUIREMENTS
%    none

% Copyright © 2007-2023 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/>.

global options_ oo_ M_

if nargin==1
    identification = 'Cholesky';
end

[ny, nx, posterior] = bvar.toolbox(nlags);

S_inv_upper_chol = chol(inv(posterior.S));

% Option 'lower' of chol() not available in old versions of
% Matlab, so using transpose
XXi_lower_chol = chol(posterior.XXi)';

k = ny*nlags+nx;

% Declaration of the companion matrix
Companion_matrix = diag(ones(ny*(nlags-1),1),-ny);

% Number of explosive VAR models sampled
p = 0;

% Initialize a four dimensional array.
sampled_irfs = NaN(ny, ny, options_.irf, options_.bvar_replic);

for draw=1:options_.bvar_replic

    % Get a covariance matrix from an inverted Wishart distribution.
    Sigma = rand_inverse_wishart(ny, posterior.df, S_inv_upper_chol);
    Sigma_upper_chol = chol(Sigma);
    Sigma_lower_chol = transpose(Sigma_upper_chol);

    % Get the Autoregressive matrices from a matrix variate distribution.
    Phi = rand_matrix_normal(k, ny, posterior.PhiHat, Sigma_lower_chol, XXi_lower_chol);

    % Form the companion matrix.
    Companion_matrix(1:ny,:) = transpose(Phi(1:ny*nlags,:));

    % All the eigenvalues of the companion matrix have to be on or
    % inside the unit circle to rule out explosive time series.
    test = (abs(eig(Companion_matrix)));
    if any(test>1.0000000000001)
        p = p+1;
    end

    if strcmpi(identification,'Cholesky')
        StructuralMat = Sigma_lower_chol;
    elseif strcmpi(identification,'SquareRoot')
        StructuralMat = sqrtm(Sigma);
    else
        error('Unknown identification option. Valid choices are ''Cholesky'' and ''SquareRoot''.')
    end

    % Build the IRFs...
    lags_data = zeros(ny,ny*nlags) ;
    sampled_irfs(:,:,1,draw) = StructuralMat ;
    lags_data(:,1:ny) = StructuralMat ;
    for t=2:options_.irf
        sampled_irfs(:,:,t,draw) = lags_data(:,:)*Phi(1:ny*nlags,:) ;
        lags_data(:,ny+1:end) = lags_data(:,1:end-ny) ;
        lags_data(:,1:ny) = sampled_irfs(:,:,t,draw) ;
    end

end

if p > 0
    skipline()
    disp('Some of the VAR models sampled from the posterior distribution')
    disp(['were found to be explosive (' int2str(p) ' samples).'])
    skipline()
end

posterior_mean_irfs = mean(sampled_irfs,4);
posterior_variance_irfs = var(sampled_irfs, 1, 4);

sorted_irfs = sort(sampled_irfs,4);
sort_idx = round((0.5 + [-options_.bvar.conf_sig, options_.bvar.conf_sig, .0]/2) * options_.bvar_replic);

posterior_down_conf_irfs = sorted_irfs(:,:,:,sort_idx(1));
posterior_up_conf_irfs = sorted_irfs(:,:,:,sort_idx(2));
posterior_median_irfs = sorted_irfs(:,:,:,sort_idx(3));

number_of_columns = fix(sqrt(ny));
number_of_rows = ceil(ny / number_of_columns) ;

% Plots of the IRFs
for shock=1:ny
    figure('Name',['Posterior BVAR Impulse Responses (shock in equation ' int2str(shock) ').']);
    for variable=1:ny
        subplot(number_of_rows,number_of_columns,variable);
        h1 = area(1:options_.irf,squeeze(posterior_up_conf_irfs(shock,variable,:)),'FaceColor',[.9 .9 .9],'BaseValue',min([min(posterior_up_conf_irfs(shock,variable,:)),min(posterior_down_conf_irfs(shock,variable,:))]));
        hold on
        h2 = area(1:options_.irf,squeeze(posterior_down_conf_irfs(shock,variable,:)),'FaceColor',[1 1 1],'BaseValue',min([min(posterior_up_conf_irfs(shock,variable,:)),min(posterior_down_conf_irfs(shock,variable,:))]));
        plot(1:options_.irf,squeeze(posterior_median_irfs(shock,variable,:)),'-k','linewidth',2)
        axis tight
        hold off
    end
end

% Save intermediate results
DirectoryName = [ M_.dname '/bvar_irf' ];
if ~isfolder(DirectoryName)
    mkdir('.',DirectoryName);
end
save([ DirectoryName '/simulations.mat'], 'sampled_irfs');

% Save results in oo_
for i=1:ny
    shock_name = options_.varobs{i};
    for j=1:ny
        variable_name = options_.varobs{j};
        oo_.bvar.irf.Mean.(variable_name).(shock_name) = posterior_mean_irfs(j,i,:);
        oo_.bvar.irf.Median.(variable_name).(shock_name) = posterior_median_irfs(j,i,:);
        oo_.bvar.irf.Var.(variable_name).(shock_name) = posterior_variance_irfs(j,i,:);
        oo_.bvar.irf.Upper_bound.(variable_name).(shock_name) = posterior_up_conf_irfs(j,i,:);
        oo_.bvar.irf.Lower_bound.(variable_name).(shock_name) = posterior_down_conf_irfs(j,i,:);
    end
end
