function approxgrad = getApproxGradient(problem, x, storedb, key)
% Computes an approximation of the gradient of the cost function at x.
%
% function approxgrad = getApproxGradient(problem, x)
% function approxgrad = getApproxGradient(problem, x, storedb)
% function approxgrad = getApproxGradient(problem, x, storedb, key)
%
% Returns an approximation of the gradient at x for the cost function
% described in the problem structure.
%
% storedb is a StoreDB object, key is the StoreDB key to point x.
%
% If no approximate gradient was provided, this call is redirected to
% getGradientFD.
% 
% See also: getGradientFD canGetApproxGradient

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, Nov. 1, 2016.
% 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, 'approxgrad')
    %% Compute the approximate gradient using approxgrad.
        
        % Check whether this function wants to deal with storedb or not.
        switch nargin(problem.approxgrad)
            case 1
                approxgrad = problem.approxgrad(x);
            case 2
                % Obtain, pass along, and save the store for x.
                store = storedb.getWithShared(key);
                [approxgrad, store] = problem.approxgrad(x, store);
                storedb.setWithShared(store, key);
            case 3
                % Pass along the whole storedb (by reference), with key.
                approxgrad = problem.approxgrad(x, storedb, key);
            otherwise
                up = MException('manopt:getApproxGradient:badapproxgrad', ...
                    'approxgrad should accept 1, 2 or 3 inputs.');
                throw(up);
        end
        
    else
    %% Try to fall back to a standard FD approximation.
    
        approxgrad = getGradientFD(problem, x, storedb, key);
        
    end
    
end
