function [X, info] = cycle_reduction(A0, A1, A2, cvg_tol, max_it, ch)

%@info:
%! @deftypefn {Function File} {[@var{X}, @var{info}] =} cycle_reduction (@var{A0},@var{A1},@var{A2},@var{cvg_tol},@var{ch})
%! @anchor{cycle_reduction}
%! @sp 1
%! Solves the quadratic matrix equation A2*X^2 + A1*X + A0 = 0.
%! @sp 2
%! @strong{Inputs}
%! @sp 1
%! @table @ @var
%! @item A0
%! Square matrix of doubles, n*n.
%! @item A1
%! Square matrix of doubles, n*n.
%! @item A2
%! Square matrix of doubles, n*n.
%! @item cvg_tol
%! Scalar double, tolerance parameter.
%! @item ch
%! Any MATLAB object, if not empty the solution is checked.
%! @end table
%! @sp 1
%! @strong{Outputs}
%! @sp 1
%! @table @ @var
%! @item X
%! Square matrix of doubles, n*n, solution of the matrix equation.
%! @item info
%! Scalar integer, if nonzero the algorithm failed in finding the solution of the matrix equation.
%! @end table
%! @sp 2
%! @strong{This function is called by:}
%! @sp 2
%! @strong{This function calls:}
%! @sp 2
%! @strong{References:}
%! @sp 1
%! D.A. Bini, G. Latouche, B. Meini (2002), "Solving matrix polynomial equations arising in queueing problems", Linear Algebra and its Applications 340, pp. 222-244
%! @sp 1
%! D.A. Bini, B. Meini (1996), "On the solution of a nonlinear matrix equation arising in queueing problems", SIAM J. Matrix Anal. Appl. 17, pp. 906-926.
%! @sp 2
%! @end deftypefn
%@eod:

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

it = 0;
info = 0;
X = [];
crit = Inf;
A0_0 = A0;
Ahat1 = A1;
if (nargin == 6 && ~isempty(ch) )
    A1_0 = A1;
    A2_0 = A2;
end
n = length(A0);
id0 = 1:n;
id2 = id0+n;

cont = 1;
while cont
    tmp = ([A0; A2]/A1)*[A0 A2];
    A1 = A1 - tmp(id0,id2) - tmp(id2,id0);
    A0 = -tmp(id0,id0);
    A2 = -tmp(id2,id2);
    Ahat1 = Ahat1 -tmp(id2,id0);
    crit = norm(A0,1);
    if crit < cvg_tol
        % keep iterating until condition on A2 is met
        if norm(A2,1) < cvg_tol
            cont = 0;
        end
    elseif it == max_it
        info(1) = 401;
        info(2) = log(norm(A1,1));
        return
    elseif isnan(crit)
        info(1) = 402;
        info(2) = log(norm(A1,1));
        return
    end
    it = it + 1;
end

X = -Ahat1\A0_0;

if (nargin == 6 && ~isempty(ch) )
    %check the solution
    res = norm(A0_0 + A1_0 * X + A2_0 * X * X, 1);
    if (res > cvg_tol)
        info(1) = 403;
        info(2) = log(res);
        dprintf('The norm of the residual is %s whereas the tolerance criterion is %s', num2str(res), num2str(cvg_tol));
    end
end

return % --*-- Unit tests --*--

%@test:1

t = zeros(3,1);

% Set the dimension of the problem to be solved.
n = 500;

% Set the equation to be solved
A = eye(n);
B = diag(30*ones(n,1)); B(1,1) = 20; B(end,end) = 20; B = B - diag(10*ones(n-1,1),-1); B = B - diag(10*ones(n-1,1),1);
C = diag(15*ones(n,1)); C = C - diag(5*ones(n-1,1),-1); C = C - diag(5*ones(n-1,1),1);

% Solve the equation with the cycle reduction algorithm
try
    tic; X1 = cycle_reduction(C,B,A,1e-7,100); elapsedtime = toc;
    disp(['Elapsed time for cycle reduction algorithm is: ' num2str(elapsedtime) ' (n=' int2str(n) ').'])
    t(1) = 1;
catch
    % nothing to do here.
end

% Solve the equation with the logarithmic reduction algorithm
try
    tic; X2 = logarithmic_reduction(A,B,C,1e-16,100); elapsedtime = toc;
    disp(['Elapsed time for logarithmic reduction algorithm is: ' num2str(elapsedtime) ' (n=' int2str(n) ').'])
    t(2) = 1;
catch
    % nothing to do here.
end

% Check the results.
if t(1) && t(2)
    t(3) = dassert(X1,X2,1e-12);
end

T = all(t);
%@eof:1