function write(M_)
% write(M_)
% Writes the nonlinear problem to be solved for computing the growth
% rates and levels along the Balanced Growth Path. Note that for
% the variables growing along the BGP, the identified levels are
% meaningless, only the relative levels of the growing variables
% sharing the same trend(s) are relevant.
%
% INPUTS
% - M_     [struct]   Dynare generated structure describing the model
%
% OUTPUTS
% None
%
% REMARKS
% - The trends are assumed to be multiplicative.

% Copyright © 2019-2025 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 M_.maximum_lag  && ~M_.maximum_lead
    i0 = transpose(M_.lead_lag_incidence(1,:)); % Indices of the lagged variables.
    i1 = transpose(M_.lead_lag_incidence(2,:)); % Indices of the current variables.
    i2 = [];                                             % Indices of the leaded variables.
elseif M_.maximum_lag  && M_.maximum_lead
    i0 = transpose(M_.lead_lag_incidence(1,:)); % Indices of the lagged variables.
    i1 = transpose(M_.lead_lag_incidence(2,:)); % Indices of the current variables.
    i2 = transpose(M_.lead_lag_incidence(3,:)); % Indices of the leaded variables.
elseif ~M_.maximum_lag  && M_.maximum_lead
    i0 = [];                                             % Indices of the lagged variables.
    i1 = transpose(M_.lead_lag_incidence(1,:)); % Indices of the current variables.
    i2 = transpose(M_.lead_lag_incidence(2,:)); % Indices of the leaded variables.
else
    error('The model is static. The BGP is trivial.')
end

n0 = length(find(i0)); % Number of lagged variables.
n1 = length(find(i1)); % Number of current variables.
n2 = length(find(i2)); % Number of leaded variables.

purely_backward_model = logical(n0 && n1 && ~n2);
purely_forward_model = logical(~n0 && n1 && n2);

if purely_backward_model
    I0 = i0;
    I1 = i1;
    I2 = [];
elseif purely_forward_model
    I0 = i1;
    I1 = i2;
    I2 = [];
else
    % Model has both leads and lags.
    I0 = i0;
    I1 = i1;
    I2 = i2;
end

% Create function in mod namespace.
fid = fopen(sprintf('+%s/bgpfun.m', M_.fname), 'w');

% Write header.
fprintf(fid, 'function [F, JAC] = bgpfun(z)\n\n');
fprintf(fid, '%% This file has been generated by Dynare (%s).\n\n', datestr(now));

% The function admits a unique vector as input argument. The first
% half of the elements are for the levels of the endogenous
% variables, the second half for the growth factors.
fprintf(fid, 'y = z(1:%u);\n\n', M_.endo_nbr);
fprintf(fid, 'g = z(%u:%u);\n', M_.endo_nbr+1, 2*M_.endo_nbr);

% Define the point where the dynamic model is to be evaluated.
% In period t, then in period t+1
fprintf(fid, 'Y0 = NaN(%u, 1);\n', 3*M_.endo_nbr);
fprintf(fid, 'Y1 = NaN(%u, 1);\n', 3*M_.endo_nbr);
for i=1:length(I0) % period t equations, lagged variables.
    if I0(i)
        fprintf(fid, 'Y0(%u) = y(%u);\n', i+(purely_forward_model*M_.endo_nbr), i);
    end
end
for i=1:length(I1) % period t equations, current variables.
    if I1(i)
        fprintf(fid, 'Y0(%u) = y(%u)*g(%u);\n', i+M_.endo_nbr+(purely_forward_model*M_.endo_nbr), i, i);
    end
end
for i=1:length(I2) % period t equations, leaded variables.
    if I2(i)
        fprintf(fid, 'Y0(%u) = y(%u)*g(%u)*g(%u);\n', i+2*M_.endo_nbr, i, i, i);
    end
end
for i=1:length(I0) % period t+1 equations lagged variables.
    if I0(i)
        fprintf(fid, 'Y1(%u) = y(%u)*g(%u);\n', i+(purely_forward_model*M_.endo_nbr), i, i);
    end
end
for i=1:length(I1) % period t+1 equations current variables.
    if I1(i)
        fprintf(fid, 'Y1(%u) = y(%u)*g(%u)*g(%u);\n', i+M_.endo_nbr+(purely_forward_model*M_.endo_nbr), i, i, i);
    end
end
for i=1:length(I2) % period t+1 equations leaded variables.
    if I2(i)
        fprintf(fid, 'Y1(%u) = y(%u)*g(%u)*g(%u)*g(%u);\n', i+2*M_.endo_nbr, i, i, i, i);
    end
end

fprintf(fid, '\n');

% Define the vector of parameters.
fprintf(fid, 'p = zeros(%u, 1);\n', M_.param_nbr);
for i = 1:M_.param_nbr
    fprintf(fid, 'p(%u) = %16.12f;\n', i, M_.params(i));
end
fprintf(fid, '\n');

% Initialize the vector holding the residuals over the two periods.
fprintf(fid, 'F = NaN(%u, 1);\n', 2*M_.endo_nbr);

% Set vector of exogenous variables to 0.
fprintf(fid, 'x = zeros(1, %u);\n\n', M_.exo_nbr);

% Evaluate the residuals and Jacobian of the dynamic model in periods t and t+1.
fprintf(fid, '[F(1:%u), T0_order, T0] = %s.dynamic_resid(Y0, x, p, y);\n', M_.endo_nbr, M_.fname);
fprintf(fid, '[F(%u:%u), T1_order, T1] = %s.dynamic_resid(Y1, x, p, y);\n', M_.endo_nbr+1, 2*M_.endo_nbr, M_.fname);
fprintf(fid, 'if nargout>1\n');
fprintf(fid, '    sparse_rowval = [');
fprintf(fid, '%u ', M_.dynamic_g1_sparse_rowval);
fprintf(fid, '];\n');
fprintf(fid, '    sparse_colval = [');
fprintf(fid, '%u ', M_.dynamic_g1_sparse_colval);
fprintf(fid, '];\n');
fprintf(fid, '    sparse_colptr = [');
fprintf(fid, '%u ', M_.dynamic_g1_sparse_colptr);
fprintf(fid, '];\n');
fprintf(fid, '    J0 = %s.dynamic_g1(Y0, x, p, y, sparse_rowval, sparse_colval, sparse_colptr, T0_order, T0);\n', M_.fname);
fprintf(fid, '    J1 = %s.dynamic_g1(Y1, x, p, y, sparse_rowval, sparse_colval, sparse_colptr, T1_order, T1);\n', M_.fname);

% Transform back the Jacobians J0 and J1 in the legacy format (non-sparse)
% NB: it is probably possible to simplify the rest of this file, but maintaining
% decent performance does not seem straightforward.
lli = find(M_.lead_lag_incidence');
if purely_forward_model
    lli = lli + M_.endo_nbr;
end
fprintf(fid, '    lli = [');
fprintf(fid, '%u ', lli);
fprintf(fid, '];\n');

fprintf(fid, '    J = zeros(%u, %u);\n', 2*M_.endo_nbr, n0+n1+n2+M_.endo_nbr);
fprintf(fid, '    J(1:%u,1:%u) = full(J0(:,lli));\n', M_.endo_nbr, n0+n1+n2);
fprintf(fid, '    J(%u:%u,1:%u) = full(J1(:,lli));\n', M_.endo_nbr+1, 2*M_.endo_nbr, n0+n1+n2);

fprintf(fid, '    JAC = zeros(%u,%u);\n', 2*M_.endo_nbr, 2*M_.endo_nbr);

% Compute the derivatives of the first block of equations (period t)
% with respect to the endogenous variables.
if purely_backward_model || purely_forward_model
    for i=1:M_.eq_nbr
        for j=1:M_.endo_nbr
            if I1(j)
                if I0(j)
                    fprintf(fid, '    JAC(%u,%u) = J(%u,%u)+J(%u,%u)*g(%u);\n', i, j, i, I0(j), i, I1(j), j);
                else
                    fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u);\n', i, j, i, I1(j), j);
                end
            else
                if I0(j)
                    fprintf(fid, '    JAC(%u,%u) = J(%u,%u);\n', i, j, i, I0(j));
                end
            end
        end
    end
else
    for i=1:M_.eq_nbr
        for j=1:M_.endo_nbr
            if I2(j)
                if I1(j)
                    if I0(j)
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)+J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I0(j), i, I1(j), j, i, I2(j), j, j);
                    else
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I1(j), j, i, I2(j), j, j);
                    end
                else
                    if I0(j)
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)+J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I0(j), i, I2(j), j, j);
                    else
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I2(j), j, j);
                    end
                end
            else
                if I1(j)
                    if I0(j)
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)+J(%u,%u)*g(%u);\n', i, j, i, I0(j), i, I1(j), j);
                    else
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u);\n', i, j, i, I1(j), j);
                    end
                else
                    if I0(j)
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u);\n', i, j, i, I0(j));
                    end
                end
            end
        end
    end
end

% Compute the derivatives of the second block of equations (period t+1)
% with respect to the endogenous variables.
if purely_backward_model || purely_forward_model
    for i=M_.eq_nbr+1:2*M_.eq_nbr
        for j=1:M_.endo_nbr
            if I1(j)
                if I0(j)
                    fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I0(j), j, i, I1(j), j, j);
                else
                    fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I1(j), j, j);
                end
            else
                if I0(j)
                    fprintf(fid, '    JAC(%u, %u) = J(%u, %u)*g(%u);\n', i, j, i, I0(j), j);
                end
            end
        end
    end
else
    for i=M_.eq_nbr+1:2*M_.eq_nbr
        for j=1:M_.endo_nbr
            if I2(j)
               if I1(j)
                   if I0(j)
                       fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u)+J(%u,%u)*g(%u)*g(%u)*g(%u);\n', i, j, i, I0(j), j, i, I1(j), j, j, i, I2(j), j, j, j);
                   else
                       fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)*g(%u)+J(%u,%u)*g(%u)*g(%u)*g(%u);\n', i, j, i, I1(j), j, j, i, I2(j), j, j, j);
                   end
               else
                   if I0(j)
                       fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u)*g(%u);\n', i, j, i, I0(j), j, i, I2(j), j, j, j);
                   else
                       fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)*g(%u)*g(%u);\n', i, j, i, I2(j), j, j, j);
                   end
               end
            else
                if I1(j)
                    if I0(j)
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)+J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I0(j), j, i, I1(j), j, j);
                    else
                        fprintf(fid, '    JAC(%u,%u) = J(%u,%u)*g(%u)*g(%u);\n', i, j, i, I1(j), j, j);
                    end
                else
                    if I0(j)
                        fprintf(fid, '    JAC(%u, %u) = J(%u, %u)*g(%u);\n', i, j, i, I0(j), j);
                    end
                end
            end
        end
    end
end

% Compute the derivatives of the first block of equations (period t)
% with respect to the growth factors.
if purely_backward_model || purely_forward_model
    for i=1:M_.eq_nbr
        for j=1:M_.endo_nbr
            if I1(j)
                fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u);\n', i, n0+n1+n2+j, i, I1(j), j);
            end
        end
    end
else
    for i=1:M_.eq_nbr
        for j=1:M_.endo_nbr
            if I2(j)
                if I1(j)
                    fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u)+J(%u,%u)*2*g(%u)*y(%u);\n', i, n0+n1+n2+j, i, I1(j), j, i, I2(j), j, j);
                else
                    fprintf(fid, '    J(%u,%u) = J(%u,%u)*2*g(%u)*y(%u);\n', i, n0+n1+n2+j, i, I2(j), j, j);
                end
            else
                if I1(j)
                    fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u);\n', i, n0+n1+n2+j, i, I1(j), j);
                end
            end
        end
    end
end

% Compute the derivatives of the second block of equations (period t+1)
% with respect to the growth factors.
if purely_backward_model || purely_forward_model
    for i=M_.eq_nbr+1:2*M_.eq_nbr
        for j=1:M_.endo_nbr
            if I0(j)
                fprintf(fid, '    J(%u,%u) = J(%u,%u)+J(%u,%u)*y(%u);\n', i, n0+n1+n2+j, i, n0+n1+n2+j, i, I0(j), j);
            end
            if I1(j)
                fprintf(fid, '    J(%u,%u) = J(%u,%u)+2*J(%u,%u)*y(%u)*g(%u);\n', i, n0+n1+n2+j, i, n0+n1+n2+j, i, I1(j), j, j);
            end
        end
    end
else
    for i=M_.eq_nbr+1:2*M_.eq_nbr
        for j=1:M_.endo_nbr
            if I2(j)
                if I1(j)
                    if I0(j)
                        fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u)+2*J(%u,%u)*y(%u)*g(%u)+3*J(%u,%u)*y(%u)*g(%u)*g(%u);\n', i, n0+n1+n2+j, i, I0(j), j, i, I1(j), j, j, i, I2(j), j, j, j);
                    else
                        fprintf(fid, '    J(%u,%u) = 2*J(%u,%u)*y(%u)*g(%u)+3*J(%u,%u)*y(%u)*g(%u)*g(%u);\n', i, n0+n1+n2+j, i, I1(j), j, j, i, I2(j), j, j, j);
                    end
                else
                    if I0(j)
                        fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u)+3*J(%u,%u)*y(%u)*g(%u)*g(%u);\n', i, n0+n1+n2+j, i, I0(j), j, i, I2(j), j, j, j);
                    else
                        fprintf(fid, '    J(%u,%u) = 3*J(%u,%u)*y(%u)*g(%u)*g(%u);\n', i, n0+n1+n2+j, i, I2(j), j, j, j);
                    end
                end
            else
                if I1(j)
                    if I0(j)
                        fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u)+2*J(%u,%u)*y(%u)*g(%u);\n', i, n0+n1+n2+j, i, I0(j), j, i, I1(j), j, j);
                    else
                        fprintf(fid, '    J(%u,%u) = 2*J(%u,%u)*y(%u)*g(%u);\n', i, n0+n1+n2+j, i, I1(j), j, j);
                    end
                else
                    if I0(j)
                        fprintf(fid, '    J(%u,%u) = J(%u,%u)*y(%u);\n', i, n0+n1+n2+j, i, I0(j), j);
                    end
                end
            end
        end
    end
end

fprintf(fid, '    JAC(:,%u:%u) = J(:,%u:%u);\n', M_.endo_nbr+1, 2*M_.endo_nbr, n0+n1+n2+1, n0+n1+n2+M_.endo_nbr);

fprintf(fid,'end\n');
fclose(fid);
