function [xparam1, estim_params_, lb, ub, M_]=set_prior(estim_params_, M_, options_)
% [xparam1,estim_params_,lb,ub,M_]=set_prior(estim_params_, M_, options_)
% -------------------------------------------------------------------------
% Populates the estimation-parameter counts, builds the optimization bounds
% and initial value vector, and writes the prior definition to disk
% (at `M_.dname/prior/definition.mat`) for use by prior_sampler and
% posterior_sampler_initialization.
%
% INPUTS
%    o estim_params_    [structure] characterizing parameters to be estimated.
%    o M_               [structure] characterizing the model.
%    o options_         [structure] characterizing the options.
%
% OUTPUTS
%    o xparam1          [double]    vector of parameters to be estimated (initial values)
%    o estim_params_    [structure] characterizing parameters to be estimated
%    o lb               [double]    vector of lower bounds for the estimated parameters.
%    o ub               [double]    vector of upper bounds for the estimated parameters.
%    o M_               [structure] characterizing the model.
%
% SPECIAL REQUIREMENTS
%    None

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

estim_params_.nvx = size(estim_params_.var_exo,1);    % number of estimated stderr parameters for structural shocks
estim_params_.nvn = size(estim_params_.var_endo,1);   % number of estimated stderr parameters for measurement errors
estim_params_.ncx = size(estim_params_.corrx,1);      % number of estimated corr parameters for structural shocks
estim_params_.ncn = size(estim_params_.corrn,1);      % number of estimated corr parameters for measurement errors
estim_params_.nsx = size(estim_params_.skew_exo,1);   % number of estimated skew parameters for structural shocks
estim_params_.np  = size(estim_params_.param_vals,1); % number of estimated structural parameters
if isfield(estim_params_,'endo_init_vals') && ~isempty(estim_params_.endo_init_vals)
    estim_params_.nendoinit  = size(estim_params_.endo_init_vals,1); % number of estimated initial states
else
    estim_params_.nendoinit = 0;
    estim_params_.endo_init_vals = zeros(0,10);
end

xparam1 = [];
ub = []; % Upper bound imposed for optimization.
lb = []; % Lower bound imposed for optimization.

if estim_params_.nvx % estimated stderr parameters for structural shocks (ordered first in xparam1)
    xparam1 = [xparam1; estim_params_.var_exo(:,2)];
    ub = [ub; estim_params_.var_exo(:,4)];
    lb = [lb; estim_params_.var_exo(:,3)];
end

if estim_params_.nvn % estimated stderr parameters for measurement errors (ordered second in xparam1)
    estim_params_.nvn_observable_correspondence=NaN(estim_params_.nvn,1); % stores number of corresponding observable
    if isequal(M_.H,0) %if no previously set measurement error, initialize H
        nvarobs = length(options_.varobs);
        M_.H = zeros(nvarobs,nvarobs);
        M_.Correlation_matrix_ME = eye(nvarobs);
    end
    for i=1:estim_params_.nvn
        obsi_ = strmatch(M_.endo_names{estim_params_.var_endo(i,1)}, options_.varobs, 'exact');
        if isempty(obsi_)
            error(['The variable ' M_.endo_names{estim_params_.var_endo(i,1)} ' has to be declared as observable since you assume a measurement error on it.'])
        end
        estim_params_.nvn_observable_correspondence(i,1)=obsi_;
    end
    xparam1 = [xparam1; estim_params_.var_endo(:,2)];
    ub = [ub; estim_params_.var_endo(:,4)];
    lb = [lb; estim_params_.var_endo(:,3)];
end

if estim_params_.ncx % estimated corr parameters for structural shocks (ordered third in xparam1)
    xparam1 = [xparam1; estim_params_.corrx(:,3)];
    ub = [ub; max(min(estim_params_.corrx(:,5),1),-1)];
    lb = [lb; min(max(estim_params_.corrx(:,4),-1),1)];
end

if estim_params_.ncn % estimated corr parameters for measurement errors (ordered fourth in xparam1)
    estim_params_.corrn_observable_correspondence=NaN(estim_params_.ncn,2);
    if isequal(M_.H,0)
        nvarobs = length(options_.varobs);
        M_.H = zeros(nvarobs,nvarobs);
        M_.Correlation_matrix_ME = eye(nvarobs);
    end
    xparam1 = [xparam1; estim_params_.corrn(:,3)];
    ub = [ub; max(min(estim_params_.corrn(:,5),1),-1)];
    lb = [lb; min(max(estim_params_.corrn(:,4),-1),1)];
    for i=1:estim_params_.ncn
        k1 = estim_params_.corrn(i,1);
        k2 = estim_params_.corrn(i,2);
        obsi1 = strmatch(M_.endo_names{k1}, options_.varobs, 'exact');
        obsi2 = strmatch(M_.endo_names{k2}, options_.varobs, 'exact');
        estim_params_.corrn_observable_correspondence(i,:)=[obsi1, obsi2];
    end
end

if estim_params_.nsx % estimated skew parameters for structural shocks (ordered fifth in xparam1)
    xparam1 = [xparam1; estim_params_.skew_exo(:,2)];
    sn_skewness_bound = abs((sqrt(2)*(pi-4))/(pi-2)^(3/2));
    ub = [ub; max(min(estim_params_.skew_exo(:,4),sn_skewness_bound),-sn_skewness_bound)];
    lb = [lb; min(max(estim_params_.skew_exo(:,3),-sn_skewness_bound),sn_skewness_bound)];
end

if estim_params_.nendoinit % estimated endo init state (ordered sixth in xparam1)
    xparam1 = [xparam1; estim_params_.endo_init_vals(:,2)];
    ub = [ub; estim_params_.endo_init_vals(:,4)];
    lb = [lb; estim_params_.endo_init_vals(:,3)];
end

if estim_params_.np % estimated structural parameters (ordered last in xparam1)
    xparam1 = [xparam1; estim_params_.param_vals(:,2)];
    ub = [ub; estim_params_.param_vals(:,4)];
    lb = [lb; estim_params_.param_vals(:,3)];
end

% Build the prior object via Path 2 (bulk construction from estim_params_).
Prior = dprior(estim_params_, M_, 0);

% I create subfolder M_.dname/prior if needed.
CheckPath('prior',M_.dname);

% I save the prior definition if the prior has changed.
definition_path = [M_.dname '/prior/definition.mat'];
if isfile(definition_path)
    loaded_old = load(definition_path);
    if isfield(loaded_old, 'Prior')
        old = loaded_old.Prior;
    elseif isfield(loaded_old, 'bayestopt_')
        old = dprior(loaded_old.bayestopt_, 0);
    else
        old = [];
    end
    if isempty(old) || length(Prior.p1) ~= length(old.p1) ...
            || any(Prior.p1-old.p1) || any(Prior.p2-old.p2) ...
            || any(Prior.p3-old.p3) || any(Prior.p4-old.p4) ...
            || any(Prior.p5(:)-old.p5(:)) ...
            || any(Prior.p6-old.p6) || any(Prior.p7-old.p7)
        delete(definition_path);
        save(definition_path,'Prior');
    end
else
    save(definition_path,'Prior');
end
