function [p,f] = spm_argmax(fun,varargin)
% function minimisation using Gauss-Newton
% FORMAT [P,f] = spm_argmax('fun',varargin,i)
%
% fun      - inline function f - fun(P,varargin)
% varargin - function arguments
% i        - argument to minimise: varargin{i}
%
% P   - optimised argument
% f   - optimised value of fun(P)
%
%--------------------------------------------------------------------------
% spm_argmax uses numerical derivatives and a and adaptive Gauss-Newton 
% scheme: see also spm_argmin.
%__________________________________________________________________________
% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging
 
% Karl Friston
% $Id: spm_argmax.m 5219 2013-01-29 17:07:07Z spm $
 
 
% arguments
%--------------------------------------------------------------------------
x     = varargin(1:end - 1);
i     = varargin{end};

% Gauss-Newton scheme
%==========================================================================
v     = -8;
for j = 1:64
    
    % time
    %----------------------------------------------------------------------
    tic
    
    % get derivatives of function w.r.t. argument
    %----------------------------------------------------------------------
    [dfdpp,dfdp,f] = spm_diff(fun,x{:},[i i]);
    dfdp           = spm_vec(dfdp);
    dfdpp          = spm_cat(dfdpp');
    dfdpp          = -spm_sqrtm(dfdpp'*dfdpp);
    
    % report change in f
    %----------------------------------------------------------------------
    try
        fprintf(' actual: %6.2e (%.2f sec)\n',full(f - P.f),toc)
    catch
        f0   = f;
        p    = spm_vec(x{i});
        P.f  = -Inf;
    end
    
    % If f is decreasing accept current estimates decrease regularization
    %----------------------------------------------------------------------
    if f > P.f
        P.x  = x;
        P.f  = f;
        v    = min(v + 1/8,4);
        v    = max(v,-8);
        str  = 'GN:(+)';
 
    else
        x    = P.x;
        v    = min(v - 2, -4);
        str  = 'GN:(-)';
    end

    % Gradient ascent
    %======================================================================
    dp   = spm_dx(dfdpp,dfdp,{v});
    df   = dfdp'*dp;
      
    % check stability
    %----------------------------------------------------------------------
    while df > 1e4
        v    = v - log(abs(df));
        dp   = spm_dx(dfdpp,dfdp,{v});
        df   = dfdp'*dp;
    end
    
    % update
    %---------------------------------------------------------------------- 
    p    = p + dp;
    x{i} = spm_unvec(p,x{i});
    
    % convergence
    %----------------------------------------------------------------------
    fprintf('%-6s: %i %6s %-6.2e %6s %-6.2e',str,j,'F:',full(P.f - f0),'dF predicted:',full(df))
    if j > 2 && df < 1e-6
        fprintf(' convergence\n')
        break
    end
end

% output
%--------------------------------------------------------------------------
p = x{i};
