function subgrad = getSubgradient(problem, x, tol, storedb, key)
% Computes a subgradient of the cost function at x, up to a tolerance
%
% function subgrad = getSubgradient(problem, x)
% function subgrad = getSubgradient(problem, x, tol)
% function subgrad = getSubgradient(problem, x, tol, storedb)
% function subgrad = getSubgradient(problem, x, tol, storedb, key)
%
% Returns a subgradient at x of the cost function described in the problem
% structure. A tolerance tol ( >= 0 ) can also be specified. By default,
% tol = 0.
%
% storedb is a StoreDB object, key is the StoreDB key to point x.
%
% See also: getDirectionalDerivative canGetGradient

% This file is part of Manopt: www.manopt.org.
% Original author: Nicolas Boumal, July 20, 2017.
% 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
    
    % Default tolerance is 0
    if ~exist('tol', 'var') || isempty(tol)
        tol = 0;
    end

    
    if isfield(problem, 'subgrad')
    %% Compute a subgradient using subgrad.
    
        % Check whether this function wants to deal with storedb or not.
        switch nargin(problem.subgrad)
            case 1
                warning('manopt:subgradient', ...
                       ['problem.subgrad normally admits a second\n' ...
                        'parameter, tol >= 0, as a tolerance.\n']);
                subgrad = problem.subgrad(x); % tol is not passed here
            case 2
                subgrad = problem.subgrad(x, tol);
            case 3
                % Obtain, pass along, and save the store for x.
                store = storedb.getWithShared(key);
                [subgrad, store] = problem.subgrad(x, tol, store);
                storedb.setWithShared(store, key);
            case 4
                % Pass along the whole storedb (by reference), with key.
                subgrad = problem.subgrad(x, tol, storedb, key);
            otherwise
                up = MException('manopt:getSubgradient:badsubgrad', ...
                    'subgrad should accept 1, 2, 3 or 4 inputs.');
                throw(up);
        end
    
    elseif canGetGradient(problem)
    %% The gradient is a subgradient.
        
        subgrad = getGradient(problem, x, storedb, key);
    
    else
    %% Abandon
        
        up = MException('manopt:getSubgradient:fail', ...
            ['The problem description is not explicit enough to ' ...
             'compute a subgradient.']);
        throw(up);
        
    end
    
end
