function sqrtPd = getSqrtPrecon(problem, x, d, storedb, key)
% Applies the square root of the Hessian preconditioner at x along d.
%
% function sqrtPd = getSqrtPrecon(problem, x, d)
% function sqrtPd = getSqrtPrecon(problem, x, d, storedb)
% function sqrtPd = getSqrtPrecon(problem, x, d, storedb, key)
%
% Returns as sqrtPd the result of applying the square root of the Hessian
% preconditioner to the tangent vector d at point x. The preconditioner is
% supposed to be a symmetric, positive definite approximation of the
% inverse of the Hessian. Its square root must thus be symmetric and
% positive definite itself.
% 
% If no square root of preconditioner is available, sqrtPd = d (identity).
% Note that this may be incompatible with the preconditioner, if that one
% is supplied in the problem description. Always check with canGetPrecon
% and canGetSqrtPrecon.
%
% storedb is a StoreDB object, key is the StoreDB key to point x.
%
% See also: getPrecon canGetPrecon canGetSqrtPrecon getHessian

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, April 3, 2015.
% Contributors: 
% Change log: 

    % Allow omission of the key, and even of storedb.
    if ~exist('key', 'var')
        if ~exist('storedb', 'var')
            storedb = StoreDB();
        end
        key = storedb.getNewKey();
    end

    
    if isfield(problem, 'sqrtprecon')
    %% Apply sqrtprecon for the square root of the preconditioner
    
        % Check whether this function wants to deal with storedb or not.
        switch nargin(problem.sqrtprecon)
            case 2
                sqrtPd = problem.sqrtprecon(x, d);
            case 3
                % Obtain, pass along, and save the store for x.
                store = storedb.getWithShared(key);
                [sqrtPd, store] = problem.sqrtprecon(x, d, store);
                storedb.setWithShared(store, key);
            case 4
                % Pass along the whole storedb (by reference), with key.
                sqrtPd = problem.sqrtprecon(x, d, storedb, key);
            otherwise
                up = MException('manopt:getSqrtPrecon:badsqrtprecon', ...
                    'sqrtprecon should accept 2, 3 or 4 inputs.');
                throw(up);
        end
        
    else
    %% No preconditioner square root provided, so just use the identity.
    
        sqrtPd = d;
        
    end
    
end
