function [ stats ] =  spqr_rank_assign_stats(...
    call_from, est_sval_upper_bounds, est_sval_lower_bounds, tol, ...
    numerical_rank, nsvals_small, nsvals_large, stats, stats_ssi, ...
    opts, nargout_call, stats_ssp_N, stats_ssp_NT, start_tic)
%SPQR_RANK_ASSIGN_STATS set flag and other statistics.
%
% Called by spqr_basic and spqr_cod as well as, indirectly, by spqr_pinv and
% spqr_null after these methods have determined estimated bounds on the
% singular values of A. Set the flag which indicates the success of
% and other statistics for spqr_basic, spqr_null, spqr_pinv, spqr_cod.
% Not user callable.
%
% Output:
%    stats - statistics for spqr_basic, spqr_null, spqr_pinv and spqr_cod.
%       including the following fields:
%
%       stats.flag which indicates whether the method has determined the
%          correct numerical rank:
%       stats.flag is 0 if it is likely, although not
%          guaranteed, that stats.rank is the correct numerical rank for
%          tolerance tol (i.e. agrees with the numerical rank
%          determined by the singular values of R).
%       stats.flag is 1 if the calculated numerical rank stats.rank may
%           be correct for the tolerance stats.tol but the estimated error
%           bounds are too large to confirm this.  However stats.rank appears
%           to be correct for an alternate tolerance tol_alt.  More
%           generally stats.rank appears to be correct for any tolerance
%           between stats.est_sval_lower_bounds(nsvals_large) and
%           stats.est_sval_upper_bounds(nsvals_large+1).
%       stats.flag is 2 if the calculated numerical rank stats.numerical
%           may be correct but estimated error bounds are too large to confirm
%           this.  The conditions for stats.flag to be 0 or 1 are not
%           satisfied.
%       stats.flag is 3 if is likely that the numerical rank returned,
%          stats.rank, is too large.
%
%       stats.tol_alt is an alternate tolerance that corresponds to the
%          calculated numerical rank when stats.flag is 1.
%
%       other fields of statistics
%
% Input:
%    call_from ==
%        call_from = 1 indicates a call from spqr_basic
%        call_from = 2 indicates a call from spqr_cod
%    est_sval_upper_bounds -- est_sval_upper_bounds(i) is an
%        estimate of an upper bound on singular value number
%        stats.sval_numbers_for_bounds(i) of A.
%    est_sval_lower_bounds -- stats.est_sval_lower_bounds(i) is an
%        estimate of an lower bound on singular value number
%        sval_numbers_for_bounds(i) of A.
%    tol - the tolerance defining the numerical rank.  The true
%        numerical rank is the number of singular values larger than tol.
%    numerical_rank -- the estimated numerical rank of A
%    nsvals_small -- the number of estimated singular values, from
%        the set that have been estimated, that appear to be smaller
%        than or equal to tol.
%    nsvals_large -- the number of estimated singular values, from
%        the set that have been estimated, that appear to be larger
%        than tol.
%    stats -- the statistics returned by spqr_basic, spqr_null, spqr_pinv
%        and spqr_cod.
%    stats_ssi -- the statistics returned by spqr_ssi.
%    opts -- options for the calls to spqr_function.
%    nargout_call -- the value of nargout in the calling function
%    stats_ssp_N -- stats from spqr_ssp applied to A * N
%    stats_ssp_NT -- stats from spqr_ssp applied to A' * NT
%    start_tic -- tic time for start of calling routine

% spqr_rank, Copyright (c) 2012, Leslie Foster and Timothy A Davis.
% All Rights Reserved.
% SPDX-License-Identifier: BSD-3-clause

get_details = opts.get_details ;

%-------------------------------------------------------------------------------
% determine flag which indicates accuracy of the estimated numerical rank
%-------------------------------------------------------------------------------

if (  ( nsvals_small == 0 || ...
        est_sval_upper_bounds(nsvals_large+1) <= tol ) ) && ...
       ( nsvals_large > 0 && est_sval_lower_bounds(nsvals_large) > tol )
    % numerical rank is correct, assuming estimated error bounds are correct
    flag = 0;
elseif ( nsvals_small > 0 && nsvals_large > 0 ) && ...
       ( est_sval_lower_bounds(nsvals_large) > ...
             est_sval_upper_bounds(nsvals_large + 1) && ...
             est_sval_upper_bounds(nsvals_large + 1) > tol )
    % in this case, assuming that the estimated error bounds are correct,
    % then the numerical rank is correct with a modified tolerance
    flag = 1;
    stats.tol_alt = est_sval_upper_bounds(nsvals_large + 1);
    % Note: satisfactory values of tol_alt are in the range
    %    est_sval_lower_bounds(nsvals_large) > tol_alt
    %    >= est_sval_upper_bounds(nsvals_large + 1)
elseif stats_ssi.flag == 3
    % in this case ssi failed and it is often the case that then
    % calculated numerical rank is too high
    flag = 3;
else
    % in this case, assuming that the estimated error bounds are correct,
    % the errors in the error bounds are too large to determine the
    % numerical rank
    flag = 2;
end


%-------------------------------------------------------------------------------
% return statistics
%-------------------------------------------------------------------------------

stats.flag = flag ;
stats.rank = numerical_rank ;
stats.est_sval_upper_bounds = est_sval_upper_bounds;
stats.est_sval_lower_bounds = est_sval_lower_bounds;
stats.sval_numbers_for_bounds = ...
    numerical_rank - nsvals_large + 1 : ...
    numerical_rank + nsvals_small ;

if call_from == 2
    if ( get_details == 1 || nargout_call >= 3 )
        stats.est_norm_A_times_N = stats_ssp_N.est_svals(1);
    end
    if (get_details == 1)
        stats.est_err_bound_norm_A_times_N = stats_ssp_N.est_error_bounds(1);
    end
end


if ( ( call_from == 1 && nargout_call == 3 ) || ...
        ( call_from == 2 && nargout_call == 4 ) )
    % include estimated norm of A transpose time NT from call to ssp
    stats.est_norm_A_transpose_times_NT = stats_ssp_NT.est_svals(1);
    if get_details == 1
        stats.est_err_bound_norm_A_transpose_times_NT = ...
           stats_ssp_NT.est_error_bounds(1);
    end
end

if stats.tol_alt == -1
    stats = rmfield(stats, 'tol_alt') ;
end

if get_details == 1
    % order the fields of stats in a convenient order (the fields when
    %    get_details is 0 or 2 are already in a good order)
    stats = spqr_rank_order_fields(stats);
end

if (get_details == 1)
    stats.time = toc (start_tic) ;
end

