function samples = mh(varargin)
% res = mh(y,x0,@genfunc,[LB,UB,params])
%
% Compulsory Parameters
%   y        = data (Nx1)
%   x0       = initial parameters (Px1)
%   @genfunc = generative model
%
%   Output is nsamples*P where nsamples=njumps/sampleevery
%
% Example:
%   % define a forward model (here y=exp(-a*x)+b)
%   myfun=@(x,c)(exp(-x(1)*c)+x(2));
%   % generate some noisy data
%   true_x = [1;2];
%   c=linspace(1,10,100);
%   y=myfun(true_x,c) + .05*randn(1,100);
%   % estimate parameters
%   x0=[1;2]; % you can get x0 using nonlinear opt
%   samples=mh(y,x0,@(x)(myfun(x,c)));
%   figure,plot(samples)
%
% Other Parameters
%   LB = lower bounds on x (default=[-inf]*ones(size(x0)))
%   UB = upper bounds on x (default=[+inf]*ones(size(x0)))
%   params.burnin = #burnin iterations (default=1000)
%   params.njumps = #jumps (default=5000)
%   params.sampleevery = keep every n jumps (default=10)
%   params.update = update proposal every n jumps (default=20)
%
%
% S. Jbabdi 01/12


[y,x0,genfunc,LB,UB,params]=parseinputargs(varargin,nargin);
N=length(y);

p=x0;
s=genfunc(x0);
e=N/2*log(norm(y-s));

acc=zeros(1,length(p));
rej=zeros(1,length(p));
keepp=zeros(params.njumps,length(p));
prop=ones(1,length(p));
for i=1:params.njumps+params.burnin
    for k=1:length(p)
        oldp=p;
        p(k)=p(k)+randn*prop(k);
        if(p(k)<LB(k) || p(k)>UB(k))
            p(k)=oldp(k);
            rej(k)=rej(k)+1;
        else
            s=genfunc(p);
            olde=e;
            e=N/2*log(norm(y-s));
            if(exp(olde-e)>rand)
                acc(k)=acc(k)+1;
            else
                p(k)=oldp(k);
                rej(k)=rej(k)+1;
                e=olde;
            end
        end
    end
    keepp(i,:)=p;
    if(rem(i,params.update)==0)
        prop=prop.*sqrt((1+acc)./(1+rej));
        acc=0*acc;
        rej=0*rej;
    end
end
samples=keepp(params.burnin+1:params.sampleevery:end,:);

function [y,x0,genfunc,LB,UB,params]=parseinputargs(varargin,nargin)
y=varargin{1};
x0=varargin{2};
genfunc=varargin{3};
if(nargin>3);LB=varargin{4};else LB=-inf*ones(size(x0));end
if(nargin>4);UB=varargin{5};else UB=+inf*ones(size(x0));end
if(nargin>5);
    params=varargin{6};
else
    params=struct('burnin',1000,'njumps',5000,'sampleevery',10,'update',20);
end
if(~isfield(params,'burnin'));params.burnin=1000;end
if(~isfield(params,'njumps'));params.njumps=5000;end
if(~isfield(params,'sampleevery'));params.sampleevery=10;end
if(~isfield(params,'update'));params.update=20;end




