function [y,ev] = dmrg_eigb(a,k,eps,varargin)
%Find several minimal eigenvalues of a TT-matrix using DMRG method
%   [Y,EV]=DMRG_EIGB(A,K,EPS,OPTIONS) Attempts to find K minimal
%   eigenvalues of a TT-matrix A with accuracy EPS. We use minimization of
%   Block-Rayleigh quotient to do this. The solution is returned a block
%   TT-tensor (i.e, r(d+1) is equal to K).
%   Options are provided in form
%   'PropertyName1',PropertyValue1,'PropertyName2',PropertyValue2 and so
%   on. The parameters are set to default (in brackets in the following)
%   The list of option names and default values are:
%       o y0 - initial approximation [random rank-5 tensor]
%       o rmax - maximal  TT-rank of the (block) solution [2500]
%       o nswp - maximal number of sweeps [4]
%       o kick_rank - stabilization parameter, the larger, the better
%       accuracy but the higher complexity [5]
%       o verb - print debug info [ {true} | false ]
%       o msize - the size of local matrix when the iterative solver is
%       used
%   Example:
%       d=8; f=3;
%       mat=tt_qlaplace_dd(d*ones(1,f)); %Laplace in the QTT-format
%       [v,ev]=dmrg_eigb(mat,5,1e-6); %5 lowest eigenvalues
%
%
% TT-Toolbox 2.2, 2009-2012
%
%This is TT Toolbox, written by Ivan Oseledets et al.
%Institute of Numerical Mathematics, Moscow, Russia
%webpage: http://spring.inm.ras.ru/osel
%
%For all questions, bugs and suggestions please mail
%ivan.oseledets@gmail.com
%---------------------------

warning('This realisation is old and not optimal. Consider using dmrg_eig');

%Default parameters
y0=[];
nswp=4;
msize=1;
max_l_steps=200;
kickrank=4;
verb=1;
rmax = Inf;
for i=1:2:length(varargin)-1
    switch lower(varargin{i})
        case 'nswp'
            nswp=varargin{i+1};
        case 'kickrank'
            kickrank=lower(varargin{i+1});
        case 'x0'
            y0=varargin{i+1};
        case 'msize'
            msize=varargin{i+1};
        case 'verb'
            verb=varargin{i+1};
        case 'rmax'
            rmax=varargin{i+1};
        otherwise
            error('Unrecognized option: %s\n',varargin{i});

    end
end

n=a.n;
d=a.d;
if ( isempty(y0) )
    kk=max(5,k);
    r=kk*ones(1,d+1);
    r(d+1)=k;
    r(1)=1;
    y0=tt_random(n,d,r);
end
y=round(y0,0);

local_eps = eps/sqrt(d);

%We start from the orthogonalization of the y vector from left-to-right
%(it does not influence the TT-ranks)

psy=y.ps;
ry=y.r;
tta=a.tt;
psa=tta.ps;
ra=tta.r;
cra=tta.core;
cry=y.core;
rm=1;
%Also we will need to compute phi-matrices
phi=cell(d+1,1);
phi{1}=1; %This one is right now unchanged
phi{d+1}=1; %Seems to be unchanged also
pos1=1;
for i=1:d
    cr=cry(psy(i):psy(i+1)-1);
    cr=reshape(cr,[ry(i),n(i)*ry(i+1)]);
    cr=rm*cr;
    ry(i)=size(cr,1);
    cr=reshape(cr,[ry(i)*n(i),ry(i+1)]);
    [u,rm]=qr(cr,0);
    rnew=size(u,2);
    %ry(i+1)=rnew;
    cry(pos1:pos1+ry(i)*n(i)*rnew-1)=u(:);
    pos1=pos1+ry(i)*n(i)*rnew;
    %With this new core compute (Ax,x) to the phi matrix
    %u=reshape(u,[ry(i),n(i),ry(i+1)]);
    crm=cra(psa(i):psa(i+1)-1);
    crm=reshape(crm,[ra(i),n(i),n(i),ra(i+1)]);
    phx=phi{i};
    phx=reshape(phx,[ra(i),ry(i),ry(i)]); phx=permute(phx,[1,3,2]);
    x0=u;
    x0=reshape(x0,[ry(i),n(i)*rnew]);
    phx=reshape(phx,[ra(i)*ry(i),ry(i)]);
    phx=phx*x0; %phx is ra(i)*ry(i)*n(i)*ry(i+1)
    phx=reshape(phx,[ra(i),ry(i),n(i),rnew]);
    crm=permute(crm,[1,3,2,4]); crm=reshape(crm,[ra(i)*n(i),n(i)*ra(i+1)]);
    %Convolve over (ak-1) j with the matrix
    phx=permute(phx,[2,4,1,3]);
    phx=reshape(phx,[ry(i)*rnew,ra(i)*n(i)]);
    phx=phx*crm; %ry(i)*ry(i+1)*n(i)*ra(i+1)
    phx=reshape(phx,[ry(i),rnew,n(i),ra(i+1)]);
    phx=permute(phx,[2,4,1,3]);
    phx=reshape(phx,[rnew*ra(i+1),ry(i)*n(i)]);
    x0=u;
    x0=reshape(x0,[ry(i)*n(i),rnew]);
    phx=phx*x0;
    phx=reshape(phx,[rnew,ra(i+1),rnew]);
    phx=permute(phx,[2,1,3]);
    phi{i+1}=phx;
    %phi{i+1}=permute(phi{i+1},[1,3,2]);
end

ry(d+1)=rnew; %This value should not be modified :(((
%Truncate the core
cry=cry(1:pos1-1);
y.r=ry;
y.core=cry;
y.ps=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
 %Test back transform
%  y0=y;
%  y0.n=[2*ones(d,1);k];
%  y0.r=[y.r;1];
%  y0.d=d;
%  e0=eye(ry(d));
%  cry=[cry,e0(:)'];
%  y0.core=cry;
%  ww1=full(y0); ww1=reshape(ww1,[numel(ww1)/k,k]);
%  bm=ww0'*ww1;
%  norm(ww1-ww0*bm)
%keyboard;
phi{d+1}=1; %Bydlocode, but seems necessary
y.r=ry;
y.core=cry;
y.ps=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
psy=y.ps;
%Now start the main DMRG sweep
swp=1;
not_converged=true;
err_max = 0;
%ry(1)=1;
ry(d+1)=1;
dir='rl';
i=d-1;
cry_left=cry(1:psy(d)-1);
cry_right=cry(psy(d):psy(d+1)-1);
while ( swp <= nswp && not_converged )
       %As usual, compute (local) B-matrix in the TT-format
       cra1=cra(psa(i):psa(i+1)-1);
       cra2=cra(psa(i+1):psa(i+2)-1);
       px1=phi{i}; px1=reshape(px1,[ra(i),ry(i),ry(i)]);
       %px1=permute(px1,[1,2,3]);
       px1=permute(px1,[1,3,2]);
       px1=reshape(px1,[ra(i),ry(i)*ry(i)]);
       px2=phi{i+2};
       px2=reshape(px2,[ra(i+2),ry(i+2),ry(i+2)]);
       %px2=permute(px2,[1,3,2]);
       %Compute the local matrix just by putting px1 into cra1, px2 into
       %cra2
       cra2=reshape(cra2,[ra(i+1)*n(i+1)*n(i+1),ra(i+2)]);
       px2=reshape(px2,[ra(i+2),ry(i+2)*ry(i+2)]);
       b2=cra2*px2; %
       b1=px1.'*reshape(cra1,[ra(i),numel(cra1)/ra(i)]);
       b1=reshape(b1,[ry(i),ry(i),n(i),n(i),ra(i+1)]); b1_save=b1; %Save for phi computations
       b1=permute(b1,[1,3,2,4,5]);
       b1=reshape(b1,[ry(i)*n(i),ry(i)*n(i),ra(i+1)]);
       b2=reshape(b2,[ra(i+1),n(i+1),n(i+1),ry(i+2),ry(i+2)]);
       b2_save=b2; %Save for phi computations
       b2=permute(b2,[2,4,3,5,1]);
       b2=reshape(b2,[n(i+1)*ry(i+2),n(i+1)*ry(i+2),ra(i+1)]);
       mm=cell(2,1); mm{1}=b1; mm{2}=b2;
       cry=[cry_left(:);cry_right(:)];
       y.r=ry;
       y.core=cry;
       y.ps=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
       %mm1=tt_matrix(mm);
       %Now setup the initial guess: i the core
       if ( strcmp(dir,'rl') ) %The block index is in the second core
         pos=numel(cry_left);
         cry1=cry_left(pos-ry(i)*n(i)*ry(i+1)+1:pos);
         cry2=cry_right(1:ry(i+1)*n(i+1)*k*ry(i+2));
         cry1=reshape(cry1,[ry(i)*n(i),ry(i+1)]);
         cry2=reshape(cry2,[ry(i+1),n(i+1)*k*ry(i+2)]);
         w=cry1*cry2; w=reshape(w,[ry(i),n(i),n(i+1),k,ry(i+2)]);
         w=permute(w,[1,2,3,5,4]); w=reshape(w,[numel(w)/k,k]);
       else %The block index is in the first core
           pos=numel(cry_left);
           cry1=cry_left(pos-ry(i)*n(i)*k*ry(i+1)+1:pos);
           cry2=cry_right(1:ry(i+1)*n(i+1)*ry(i+2));
           cry1=reshape(cry1,[ry(i)*n(i)*k,ry(i+1)]);
           cry2=reshape(cry2,[ry(i+1),n(i+1)*ry(i+2)]);
           w=cry1*cry2; w=reshape(w,[ry(i),n(i),k,n(i+1),ry(i+2)]);
           w=permute(w,[1,2,4,5,3]); w=reshape(w,[numel(w)/k,k]);
       end
       %Now run the eigenvalue solver
        bw=bfun(mm,w); ev=bw'*w;
           %er0=norm(bw-w*ev,'fro')/norm(w,'fro');
           er_old=bw-w*ev;
        erc=zeros(1,size(w,2));
        for j=1:size(w,2)
            erc(j)=norm(er_old(:,j));
        end
           er0=norm(er_old,'fro')/norm(w,'fro');
       if ( size(w,1) >= max(5*k,msize) )
           matvec='bfun';
           [wnew,ev,fail_flag]=lobpcg(w,@(x) bfun(mm,x),local_eps,max_l_steps);
       else
          matvec='full';
          fm=full(tt_matrix(mm));
          [v,dg]=eig(fm);
          %[v,dg]=eigs(sparse(fm),k+1);
          %keyboard;
          ev=diag(dg);
          [ev,ind]=sort(ev,'ascend');
          v=v(:,ind);
          wnew=v(:,1:k);
          ev=ev(1:k);
       end
       er1=norm(bfun(mm,wnew)-wnew*diag(ev),'fro')/norm(wnew,'fro');

       % Check the distance between iterations
       dx = norm(wnew*(wnew'*w)-w);

       fv=sum(ev); %The functional we minimize;
       if ( verb>1 )
         fprintf('sweep=%d block=%d fv=%10.15f loc solve=%3.2e old_solve=%3.2e \n',swp,i,fv,er1,er0);
         disp(erc);
       end
       if ( strcmp(dir,'rl') ) %Implant the auxiliary core into the i-th core
           %(a1,i1,a2,a2,i2*g,a3)-> (a1,i1*g,a2,a2,i2,a3)
           %Delete old block from the core_left, add new block to the core
           %right

           %Prepare the truncation block
           rhs=wnew*diag(ev);
            if (strcmp(matvec,'full'))
                res_true = norm(fm*wnew-rhs)/norm(rhs);
            else
                res_true = norm(bfun(mm,wnew)-rhs)/norm(rhs);
            end;

           wnew=reshape(wnew,[ry(i),n(i),n(i+1),ry(i+2),k]);
           wnew=permute(wnew,[1,2,5,3,4]); wnew=reshape(wnew,[ry(i)*n(i)*k,n(i+1)*ry(i+2)]);
           [u,s,v]=svd(wnew,'econ'); s=diag(s);
           %Truncation block
           rnew=my_chop2(s,local_eps*norm(s));

           %%%
           rnew = min(rnew, numel(s));
           rnew = min(rnew, rmax);
           %%%%

           u=u(:,1:rnew); s=s(1:rnew); v=v(:,1:rnew);% v=v';
           u=u*diag(s); %u has to be reshaped

           % Residual truncation
%            r0=1; r1=min(numel(s),rmax);
%            r=1;
%            while ( (r ~= r0 || r ~= r1) && r0 <= r1)
%             r=min(floor((r0+r1)/2),rmax);
%             %er0=norm(s(r+1:numel(s)));
%             u1=u(:,1:r)*diag(s(1:r));
%             %Sonate u1
%             u1=reshape(u1,[ry(i)*n(i),k,r]);
%             u1=permute(u1,[1,3,2]);
%             u1=reshape(u1,[numel(u1)/k,k]);
%             [u2,~,v2]=svd(u1,'econ');
%             u1=u2*v2';
%             u1=reshape(u1,[ry(i)*n(i),r,k]);
%             u1=permute(u1,[1,3,2]);
%             u1=reshape(u1,[numel(u1)/r,r]);
%             sol = u1*(v(:,1:r))';
%
%             sol = reshape(sol,[ry(i),n(i),k,n(i+1),ry(i+2)]);
%             sol=permute(sol,[1,2,4,5,3]); sol=reshape(sol,[numel(sol)/k,k]);
%             %if ( norm(sol'*sol-eye(k))>1e-3 )
%             %  keyboard
%             %end
%             if (strcmp(matvec,'full'))
%                 resid = norm(fm*sol-rhs)/norm(rhs);
%             else
%                 resid = norm(bfun(mm,sol)-rhs)/norm(rhs);
%             end;
%             if ( verb )
%             fprintf('sweep %d, block %d, r0=%d, r1=%d, r=%d, resid=%g, MatVec=%s\n', swp, i, r0, r1, r, resid,matvec);
%             end
%             if ((resid<max(res_true*1.2, eps)) ) %Value of the rank is OK
%               r1=r;
%             else %Is not OK.
%               r0=min(r+1,rmax);
%             end;
%
%            end
%            rnew=r;
%            u=u1; v=v(:,1:rnew);

%            if ( norm(sol'*sol-eye(k)) > 1e-7 )
%              keyboard;
%            end
           %u=u(:,1:rnew); s=s(1:rnew); v=v(:,1:rnew);% v=v';
           %u=u*diag(s); %u has to be reshaped

           %Random restart block
           radd=min(kickrank,size(v,1)-rnew);
           rnew=rnew+radd;
           if ( radd >  0 )
             vr=randn(size(v,1),radd);
             ur=zeros(size(u,1),radd);
             %Orthogonalize vr to v by Golub-Kahan reorth
             mvr=v'*vr; vnew=vr-v*mvr;
             reort_flag=false;
             for j=1:radd
                if ( norm(vnew(:,j)) <= 0.5*norm(vr(:,j)))
                   reort_flag=true;
                end
             end
             if (reort_flag)
                 sv=v'*vnew;
                 %mvr=mvr+v'*vnew;
                 vnew=vnew-v*sv;
             end
             [vnew,~]=qr(vnew,0);

             v=[v,vnew];
             u=[u,ur];
             %norm(u*v'-u1*v1');
             %keyboard;
             %keyboard;
           end
           v=v';

           %Memory stuff
           pos=numel(cry_left);
           cry_left(pos-ry(i)*n(i)*ry(i+1)+1:pos)=[];
           cry_right(1:ry(i+1)*n(i+1)*k*ry(i+2))=[]; %Delete the top core from cry_right
           cry_right=[u(:)', v(:)',cry_right]; %Simply add new block to cry_right
           ry(i+1)=rnew;

           %Recalculate phi block; we need to recalculate phi{i+1} here
           %using phi{i+2}
           %px2(ra(i+2),ry(i+2),ry(i+2))*cra2(ra(i+1),n(i),m(i),ra(i+2)*v(ry(i+1),n(i+1),ry(i+2))*v(ry(i+1),n(i+1),ry(i+2))
           %we already have b2 ---  (b2_save)
           %(ra(i+1),n(i+1),n(i+1),ry(i+2),ry(i+2)), convolve over the
           %n(i+1),ry(i+2)
           phx=reshape(b2_save,[ra(i+1),n(i+1),n(i+1),ry(i+2),ry(i+2)]);
           phx=permute(phx,[2,4,1,3,5]);
           %phx=permute(phx,[3,4,1,2,5]);
           %phx=permute(phx,[3,5,1,2,4]);
           % phx=permute(phx,[2,5,1,3,4]);
           phx=reshape(phx,[n(i+1)*ry(i+2),ra(i+1)*n(i+1)*ry(i+2)]);
           v0=v;
           v0=reshape(v0,[ry(i+1),n(i+1)*ry(i+2)]);
           phx=v0*phx;
           phx=reshape(phx,[ry(i+1),ra(i+1),n(i+1),ry(i+2)]);
           phx=permute(phx,[3,4,1,2]);
           phx=reshape(phx,[n(i+1)*ry(i+2),ry(i+1)*ra(i+1)]);
           phx=v0*phx;
           phx=reshape(phx,[ry(i+1),ry(i+1),ra(i+1)]);
           %phx=permute(phx,[3,2,1]);
           phx=permute(phx,[3,2,1]);
           phi{i+1}=phx;
       else %Implant the auxiliary core from the left block to the right block

           %Prepare the truncation block
           rhs=wnew*diag(ev);
            if (strcmp(matvec,'full'))
                res_true = norm(fm*wnew-rhs)/norm(rhs);
            else
                res_true = norm(bfun(mm,wnew)-rhs)/norm(rhs);
            end;
           wnew=reshape(wnew,[ry(i),n(i),n(i+1),ry(i+2),k]);
           wnew=permute(wnew,[1,2,3,5,4]); wnew=reshape(wnew,[ry(i)*n(i),n(i+1)*k*ry(i+2)]);
           [u,s,v]=svd(wnew,'econ'); s=diag(s);
           rnew=my_chop2(s,local_eps*norm(s));

           %%%%
           rnew = min(rnew, rmax);
           rnew = min(rnew, numel(s));
           %%%%

           u=u(:,1:rnew); s=s(1:rnew); v=v(:,1:rnew);% v=v';
           v=v*diag(s); %u has to be reshaped

           % Residual truncation
%            r0=1; r1=min(size(s,1),rmax);
%            r=1;
%            while ( (r ~= r0 || r ~= r1) && r0 <= r1)
%             r=min(floor((r0+r1)/2),rmax);
%             %er0=norm(s(r+1:numel(s)));
%             v1=v(:,1:r)*diag(s(1:r));
%             %Sonate v1
%             v1=reshape(v1,[n(i+1),k,ry(i+2),r]);
%             v1=permute(v1,[1,3,4,2]);
%             v1=reshape(v1,[numel(v1)/k,k]);
%             [u2,~,v2]=svd(v1,'econ');
%             v1=u2*v2';
%             v1=reshape(v1,[n(i+1),ry(i+2),r,k]);
%             v1=permute(v1,[1,4,2,3]);
%             v1=reshape(v1,[numel(v1)/r,r]);
%             sol=u(:,1:r)*v1';
%             sol = reshape(sol,[ry(i),n(i),n(i+1),k,ry(i+2)]);
%             sol=permute(sol,[1,2,3,5,4]); sol=reshape(sol,[numel(sol)/k,k]);
%             if (strcmp(matvec,'full'))
%                 resid = norm(fm*sol-rhs)/norm(rhs);
%             else
%                 resid = norm(bfun(mm,sol)-rhs)/norm(rhs);
%             end;
%             if ( verb )
%             fprintf('sweep %d, block %d, r0=%d r1=%d r=%d, resid=%g, MatVec=%s\n', swp, i, r0, r1, r, resid,matvec);
%             end
%             if ((resid<max(res_true*1.2, eps)) ) %Value of the rank is OK
%               r1=r;
%             else %Is not OK.
%               r0=min(r+1,rmax);
%             end;
%            end
%            rnew=r;
%            %Truncation block
%            %rnew=my_chop2(s,eps*norm(s));
%            u=u(:,1:rnew); v=v1;

           %Random restart block
           radd=min(kickrank,size(u,1)-rnew);
           rnew=rnew+radd;
           if ( radd >  0 )
             vr=zeros(size(v,1),radd);
             ur=randn(size(u,1),radd);
             %Orthogonalize vr to v by Golub-Kahan reorth
             mur=u'*ur; unew=ur-u*mur;
             reort_flag=false;
             for j=1:radd
                if ( norm(unew(:,j)) <= 0.5*norm(ur(:,j)))
                   reort_flag=true;
                end
             end
             if (reort_flag)
                 sv=u'*unew;
                 %mvr=mvr+v'*vnew;
                 unew=unew-u*sv;
             end
             [unew,~]=qr(unew,0);

             u=[u,unew];
             v=[v,vr];
           end
           v=v';

           cry_right(1:ry(i+1)*n(i+1)*ry(i+2))=[];
           pos=numel(cry_left);
           cry_left(pos-ry(i)*n(i)*k*ry(i+1)+1:pos)=[];
           cry_left=[cry_left,u(:)',v(:)'];

           ry(i+1)=rnew;

           %Recalculate phi block; we need to recalculate phi{i+1} using
           %phi{i}
           phx=reshape(b1_save,[ry(i),ry(i),n(i),n(i),ra(i+1)]);
           u0=u; u0=reshape(u0,[ry(i)*n(i),ry(i+1)]);
           phx=permute(phx,[1,3,5,2,4]);
           %phx=permute(phx,[2,4,5,1,3]);
           %phx=permute(phx,[1,4,5,2,3]);
           %phx=permute(phx,[2,3,5,1,4]);
           phx=reshape(phx,[ry(i)*n(i)*ra(i+1),ry(i)*n(i)]);
           phx=phx*u0;
           phx=reshape(phx,[ry(i),n(i),ra(i+1),ry(i+1)]);
           phx=permute(phx,[3,4,1,2]);
           phx=reshape(phx,[ra(i+1)*ry(i+1),ry(i)*n(i)]);
           phx=phx*u0; phx=reshape(phx,[ra(i+1),ry(i+1),ry(i+1)]);
           phx=permute(phx,[1,2,3]);
           phi{i+1}=phx;
       end

   err_max = max(err_max, dx);
   %Choose the next direction block; now implement the simple case
   if ( strcmp(dir,'rl') )
     if ( i > 1 )
       i=i-1;
     else %Change direction %The last optimization was for (1,2) core
       dir='lr';
       %One block should go from cry_right to cry_left
       cry_left=cry_right(1:ry(1)*n(1)*ry(2)*k); %This seems correct
       cry_right(1:ry(1)*n(1)*ry(2)*k)=[];
       if (verb>0)
        fprintf('--- sweep=%d, err_max=%3.3e \n',swp, err_max);
       end;
       swp=swp+1;
       not_converged = (err_max>eps);
       err_max = 0;
     end
   else
    if ( i < d-1 )
        i=i+1;
     else
       dir='rl';
       pos=numel(cry_left);
       cry_right=cry_left(pos-ry(d)*n(d)*ry(d+1)*k+1:pos);
       cry_left(pos-ry(d)*n(d)*ry(d+1)*k+1:pos)=[];
       if (verb>0)
        fprintf('--- sweep=%d, err_max=%3.3e \n',swp, err_max);
       end;
       swp=swp+1;
       not_converged = (err_max>eps);
       err_max = 0;
       %One block should go from cry_left to cry_right (?) --- seems no :)
     end
   end

end
  %Gather the solution

   ry(d+1)=k; %This is the final
   y.r=ry;
   cry=[cry_left,cry_right];
   y.core=cry;
   y.r=ry;
   y.d=d;
   y.ps=cumsum([1;n.*ry(1:d).*ry(2:d+1)]);
end

    function [x]=bfun(a,x)
    %[Y]=BFUN(A,X,K)
    %a is given as U(i1,j1,s)*V(i2,j2,s)
    %\sum_{j1,j2,s} U(i1,j1,s)*V(i2,j2,s)*X(j1,j2,q)
    re=size(x,2);
    ul=a{1}; vl=a{2};
    n1=size(ul,1);m1=size(ul,2); ral=size(ul,3);
    n2=size(vl,1); m2=size(vl,2);
    ul=permute(ul,[1,3,2]); ul=reshape(ul,[numel(ul)/m1,m1]);
    x=reshape(x,[m1,numel(x)/m1]);
    x=ul*x; %x is i1xsxj2xq s,j2
    x=reshape(x,[n1,ral,m2,re]);
    x=permute(x,[3,2,1,4]);
    x=reshape(x,[m2*ral,n1*re]);
    vl=reshape(vl,[n2,m2*ral]);
    x=vl*x; %is n2*n1*k
    x=reshape(x,[n2,n1,re]);
    x=permute(x,[2,1,3]);
    x=reshape(x,[numel(x)/re,re]);
    return
    end


%% Estimate the quality of QRS complex detections
% Calculate the quality of QRS detections based on the the likelihood of
% measurements from RR interval series with respect to a trained model. See
% for further details. See 'A Pattern-Recognition Approach for
% Lead-Selection in Heartbeat Detection' CINC 2014.
%
% Example
%
%   [ ratio, estimated_labs ] = CalcRRserieRatio(time_serie, ECG_header, start_end, ECG_type)
%
% Arguments:
%      +time_serie: [cell] REQUIRED
%
%           Cell array of size [nsig 1] with the detections
%           performed for each lead.
%
%     +ECG_header: [struct] OPTIONAL.
%
%             Description of the ECG typically available in the
%             ECG_header. Structure with fields:
%
%               -freq: Sampling rate in Hz. (1)
%
%               -nsig: Number of ECG leads. (size(ECG,2))
%
%               -nsamp: Number of ECG samples. (size(ECG,1))
%
%     +start_end: [numeric] OPTIONAL. Vector of size [2 1] with the start
%                           and end samples.
%
%     +ECG_type: [numeric] OPTIONAL. Char indicating if you know something
%                          about the ECG recording type a priori. Possible
%                          values are: 'stress' 'arrhythmia' 'longterm' 'sinus'
%
% Output:
%     + ratio: a measure of quality of the QRS detections
%
%     + estimated_labs: a label for each heartbeat: {TP, FP and FN}
%
% See also ECGtask_QRS_detection
%
% Author: Mariano Llamedo Soria llamedom@electron.frba.utn.edu.ar
% Version: 0.1 beta
% Last update: 14/5/2014
% Birthdate  : 23/4/2013
% Copyright 2008-2015
function [ ratio, estimated_labs ] = CalcRRserieRatio(time_serie, ECG_header, start_end, ECG_type)

    %% constants

    min_pattern_separation = 350;  % ms
    max_pattern_separation = 1500; % ms
    cECGtypes = {'stress' 'arrhythmia' 'longterm' 'sinus'};

    %% start

    if( nargin < 2 || isempty(time_serie) || isempty(ECG_header) )
        ratio = 0;
        return
    end

    if( nargin < 3 || isempty(start_end) )
        start_end = [1 ECG_header.nsamp];
    end

    if( nargin < 4 || ~any(strcmpi(ECG_type, cECGtypes)) )
        ECG_type = 'stress';
    end

    start_sample = start_end(1);
    end_sample = start_end(2);

    pb = progress_bar( 'Calculating ratios', 'Start' );

    % co_ocurrences respect other leads
    co_ocurrence = calc_co_ocurrences(time_serie);

    if( isempty(co_ocurrence) )
        return
    end

    dummy = prdataset([]);
    dummy = prmapping([]);

    % load the trained classifier.
    aux_load = load([ 'QRS_q_' ECG_type '.mat']);

    w_lablist = cellstr(getlabels(aux_load.wTrained_Classifier));
    FN_lab = find(strcmpi(w_lablist, 'FN'));
    FP_lab = find(strcmpi(w_lablist, 'FP'));
    TP_lab = find(strcmpi(w_lablist, 'TP'));

    lreferences = length(time_serie);

    pb.Loops2Do = lreferences;
    pb.checkpoint('Calculating ratios.');

    ratio = nan(lreferences,1);

    estimated_labs = cell(lreferences,1);

    for ii = 1:lreferences

        pb.start_loop();

        this_ts = colvec(time_serie{ii});

        if( length(this_ts) < (ECG_header.nsamp / max_pattern_separation) )
%             warning( [mfilename ':few_anns'], 'Few detections in  %d\n', ii );
            ratio(ii) = 0;
        else

            pb.checkpoint([]);

            k_gaps = calc_k_gaps(this_ts);

            % build the feature matrix [ RR_i-1 RR_i RR_10 RR_60 co_ocurrences]
            % RR_i-1
            this_ts = round(this_ts * 1000 / ECG_header.freq);

            pb.checkpoint([]);

            aux_fm = [ calculate_RR_features(this_ts) colvec(co_ocurrence{ii}) ];

            pb.checkpoint([]);

            ds_result = prdataset(aux_fm) * aux_load.wTrained_Classifier;

            estimated_labs{ii} = renumlab(ds_result * labeld, char(w_lablist) );

            aux_val = sum(estimated_labs{ii} == TP_lab | estimated_labs{ii} == FN_lab);
            if( aux_val == 0 )
                this_se = 0;
            else
                this_se = sum(estimated_labs{ii} == TP_lab) / aux_val;
            end

            aux_val = sum(estimated_labs{ii} == TP_lab | estimated_labs{ii} == FP_lab);
            if( aux_val == 0 )
                this_pp = 0;
            else
                this_pp = sum(estimated_labs{ii} == TP_lab) / aux_val;
            end

            % metric used for the paper experiment
            %this_q = (2*this_se + this_pp)/3;
            % F1 score
            this_q = 2/(1/this_se + 1/(eps+this_pp));
            ratio(ii) = k_gaps * this_q;

        end

        pb.end_loop();
    end

%     clear pb

    function k = calc_k_gaps( time_serie )

        time_serie = time_serie( time_serie >= start_sample & time_serie <= end_sample );

        % add some samples in order to calculate the gaps among heartbeats (FN)
        if( time_serie(1) ~= start_sample )
            time_serie = [start_sample; time_serie];
        end

        if( time_serie(end) ~= end_sample )
            time_serie = [time_serie; end_sample];
        end

        time_serie = round(time_serie * 1000 / ECG_header.freq);

        RRserie = colvec(diff(time_serie));
        RRserie = [0;RRserie];

        gap_idx = find(RRserie > max_pattern_separation);
%         gap_end_idx = gap_idx;
%         gap_start_idx = gap_start_idx - 1;

        % Percent of the time we have gaps.
        k = 1 - (sum(RRserie(gap_idx)) / time_serie(end));

    end

    function fm = calculate_RR_features( time_serie )

        RRserie = diff(time_serie);
        RRserie = [RRserie(1); RRserie];

        % build the feature matrix [ RR_i-1 RR_i RR_10 RR_60 ]
        % RR_i-1
        fm = [ RRserie(1); RRserie(1:end-1) ];
        fm = [fm RRserie CalcFeatureRRx(time_serie, RRserie, 10000) CalcFeatureRRx(time_serie, RRserie, 60000)];

    end

    function start_end_aux = findStartEnd( bAux )

        start_aux = find(  bAux, 1, 'first' );
        end_aux = find(  bAux, 1, 'last' );
        start_end_aux = [start_aux end_aux];

    end

    function aux_mean = CalcFeatureRRx(time_serie, RRserie, win_size)
%         win_size in milliseconds

        aux_seq = 1:length(RRserie);
        aux_idx = arrayfun(@(a)( findStartEnd(  time_serie >= (time_serie(a) - win_size) & ...
                                                time_serie <= time_serie(a) )), ...
                           aux_seq, 'UniformOutput', false);

        aux_mean = colvec(cellfun(@(a)(round(median(RRserie(a(1):a(2))))), aux_idx));

    end

end


classdef PostProcessor

  methods (Static)

    function posts = PostProcess(curs,postprocessparams,scoreNorm)
      if isempty(postprocessparams);
        posts = curs;
        return;
      end;

      if strcmpi(postprocessparams.method,'hysteresis')
        posts = PostProcessor.ApplyHysteresis(curs,postprocessparams,scoreNorm);
      else
        posts = PostProcessor.ApplyFiltering(curs,postprocessparams);
      end

      posts = PostProcessor.RemoveSmallBouts(posts,postprocessparams);
    end

    % ---------------------------------------------------------------------
    function posts = RemoveSmallBouts(posts,postprocessparams)
      if postprocessparams.blen > 1 && numel(posts)>0,
        if numel(posts)<= postprocessparams.blen
          if nnz(posts>0) > numel(posts)/2,
            posts(:) = 1;
          else
            posts(:) = -1;
          end
          return;
        end

        while true,
          tposts = [posts(:)' 1-posts(end)];
          ends = find(tposts(1:end-1)~=tposts(2:end));
          ends = [1 ends+1];
          blens = ends(2:end)-ends(1:end-1);
          [minblen,smallbout] = min(blens);
          if minblen>=postprocessparams.blen, break; end
          posts(ends(smallbout):ends(smallbout+1)-1) = ...
            1 - posts(ends(smallbout):ends(smallbout+1)-1);
        end
      end
    end

    % ---------------------------------------------------------------------
    function posts = ApplyHysteresis(curs,params,scoreNorm)
      % Use imfill to find the regions.
      if isempty(curs), posts = curs; return; end

      % Select pos bouts that have at least one frame about the high
      % threshold.
      hthresh = curs > params.hystopts(1).value*scoreNorm;
      lthresh = curs > 0;
      if( nnz(hthresh)>0)
        pos = imfill(~lthresh,find(hthresh(:))) & lthresh;
        computeNeg = true;
      else
        pos = false(size(curs));
        computeNeg = false;
      end
      % Select neg bouts that have at least one frame below the low
      % threshold.
      hthresh = curs < params.hystopts(2).value*scoreNorm;
      lthresh = curs < params.hystopts(1).value*scoreNorm;
      if nnz(hthresh)>0 && computeNeg,
        neg = imfill(~lthresh,find(hthresh(:))) & lthresh;
      else
        neg = true(size(curs));
      end

      posts = pos | ~neg;

    end

    % ---------------------------------------------------------------------
    function posts = ApplyFiltering(curs,params)
      % Use filt to find the regions.
      if isempty(curs), posts = curs; return; end
      filts = conv(curs,ones(1,params.filtopts(1).value),'same');
      posts = filts>0;
    end

  end

end


function model = run_finetuning(model)
% run generative finetuning
% Input: generatively pretrained model
% Output: generatively finetuned model

rng('shuffle');
kernels;
debug = 0;

data_list = read_data_list(model.data_path, model.classnames, ...
    model.volume_size + 2 * model.pad_size, 'train', debug);

num_layer = length(model.layers);
for l = 2 : num_layer
    model.layers{l}.grdw = zeros(size(model.layers{l}.w), 'single');
    model.layers{l}.grdb = zeros(size(model.layers{l}.b), 'single');
    model.layers{l}.grdc = zeros(size(model.layers{l}.c), 'single');
end

% fine-tuning phase: wake-sleep algorithm
param = [];
param.epochs = 200;
param.lr = 0.00003;
param.momentum = [0.9, 0.9];
param.kCD = 3;
param.persistant = 1;
param.batch_size = 32;
param.sparse_damping = 0;
param.sparse_target = 0;
param.sparse_cost = 0;

[model]= wake_sleep(model, data_list, param);

save('finetuned_model','model');


function m = copymatrix(varargin)

% function m = copymatrix(m,sub,val)
%
% <m> is a matrix.  can be [] when <sub> is a logical, in which case
%   we assume <m> is zeros(size(<sub>)).
% <sub> is
%   (1) some sort of index (e.g. vector of indices, logical mask)
%   (2) a function that accepts <m> and outputs an index
% <val> is something that can be assigned to the <sub> indices
%
% return a copy of <m> that has <val> shoved into <sub>.
% this function is useful for making modifications of a matrix on-the-fly.
%
% example:
% imagesc(copymatrix(randn(10,10),rand(10,10)>.5,0));
% isequal(copymatrix([1 2 3],@(x) x > 1,1),[1 1 1])
%
% OR
%
% function m = copymatrix(m,sub,dim,val)
%
% <m> is a matrix
% <sub> is a vector of indices or a vector of logicals
% <dim> is a dimension of <m>
% <val> is something that can be assigned to the <sub> indices of <m>,
%   assuming ':' for all other dimensions
%
% return a copy of <m> that has <val> shoved into <sub>.
% this function is useful for making modifications of a matrix on-the-fly.
%
% example:
% copymatrix([1 2 3; 4 5 6],2,1,[1 2 3])

if nargin==3
  m = varargin{1};
  sub = varargin{2};
  val = varargin{3};
  if isa(sub,'logical') && isempty(m)
    m = zeros(size(sub));
  end
  if isa(sub,'function_handle')
    m(feval(sub,m)) = val;
  else
    m(sub) = val;
  end
else
  m = varargin{1};
  sub = varargin{2};
  dim = varargin{3};
  val = varargin{4};
  ix = repmat({':'},[1 max(ndims(m),dim)]);
  ix{dim} = sub;
  m(ix{:}) = val;
end


function [intersection_nodes] = ...
                    get_unique_node_xy(parsed_osm, intersection_node_indices)
% get the x,y coordinates of unique nodes at road intersections
%
% 2010.11.20 (c) Ioannis Filippidis, jfilippidis@gmail.com

ids = parsed_osm.node.xy(:, intersection_node_indices);
xys = parsed_osm.node.xy(:, intersection_node_indices);

intersection_nodes.id = ids;
intersection_nodes.xys = xys;


function tapas_sem_display_posterior_summary(results, summary)
%% Display the posterior summary.
%
% Input
%       results     -- Results structure
%       summary     -- Summary structure of the posterior.
% Output
%

% aponteeduardo@gmail.com
% copyright (C) 2019
%

CONG = 0;
INCONG = 1;

data = results.data;

% Offset of the data
if ~isfield(data(1).y, 'offset')
    offset = 0;
else
    offset = data(1).y.offset;
end

% Assume data in milliseconds and a scaling of 100ms
if ~isfield(data(1).y, 'scale')
    scale = 100;
else
    scale = data(1).y.scale;
end

% Collapse all the data
t = arrayfun(@(x) x.y.t, data, 'UniformOutput', false);
t = vertcat(t{:});

t = scale * t + offset;

% (a)ctions
a = arrayfun(@(x) x.y.a, data, 'UniformOutput', false);
a = vertcat(a{:});

% (t)rial (t)ype
tt = arrayfun(@(x) x.u.tt, data, 'UniformOutput', false);
tt = vertcat(tt{:});

% Plot only correct trials
ttype = mod(tt, 2);
correct = a == ttype;

%% Normalized fits

fits = summary.fits;
nf = numel(fits);

% Normalized fits
nfits = cell(nf, 1);

for i = 1:numel(fits)
    nfits{i} = tapas_sem_normalized_fits(data(i), fits{i});
end

%% Display a table with the summaries
tapas_display_table(summary.summaries, 'Posterior summary')

%% Plot all the responses combined
fig = figure('name', 'Group responses and fits');
fig.Color = 'w';

all_y = struct('t', t, 'a', a);
all_u = struct('tt', tt);
edges = tapas_sem_plot_responses(all_y, all_u);
dt = edges(2) - edges(1);

plot_predicted_normalized_fits(nfits, dt, offset, scale);

%% Delta plots
fig = figure('name', 'Group delta plots');
fig.Color = 'w';

hold on
% Plot all together
tapas_sem_empirical_delta_plot(...
    t(correct & (ttype == CONG)), ...
    t(correct & (ttype == INCONG)));

xlabel('time')
ylabel('Delta incong. RT - cong. RT')
% Make delta plots
time = scale * fits{1}(1).t + offset;

nt = numel(time);

cong = zeros(nt, 1);
incong = zeros(nt, 1);

for i = 1:numel(nfits)
    for j = 1:numel(nfits{i})
        cong = cong + nfits{i}(j).pro;
        incong = incong + nfits{i}(j).anti;
    end
end

time = reshape(time, numel(time), 1);

tapas_sem_predicted_delta_plot(time, cong, incong);
legend({'Empirical delta', 'Predicted delta'})
end

function plot_predicted_normalized_fits(nfits, dt, offset, scale)

fits = nfits{1};

for i = 2:numel(nfits)
    for j = 1:numel(fits)
        fits(j).pro = fits(j).pro + nfits{i}(j).pro;
        fits(j).anti = fits(j).anti + nfits{i}(j).anti;
    end
end

for j = 1:numel(fits)
    fits(j).t = fits(j).t * scale + offset;
end

tapas_sem_plot_fits(fits, dt/scale)

end


function nvar = p12_nvar ( option )

%*****************************************************************************80
%
%% P12_NVAR sets the number of variables for problem 12.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    03 September 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer OPTION, the option index.
%
%    Output, integer NVAR, the number of variables.
%
  if ( option == 1 )
    npolys = 2;
    nderiv = 1;
  elseif ( option == 2 )
    npolys = 4;
    nderiv = 1;
  elseif ( option == 3 )
    npolys = 4;
    nderiv = 2;
  elseif ( option == 4 )
    npolys = 6;
    nderiv = 1;
  elseif ( option == 5 )
    npolys = 6;
    nderiv = 2;
  elseif ( option == 6 )
    npolys = 6;
    nderiv = 3;
  end

  nint = 8;
  nvary = nint * npolys;
  nbcz = 1;
  nbco = 1;
  nvarz = nbcz + ( nint - 1 ) * nderiv + nbco;
  nvar = nvary + nvarz + 1;

  return
end


i_fish=8;
% also load fish 8 in GUI
% change alpha_max to 1 in 'DrawCellsOnAnatProj.mat'

CR = getappdata(hfig,'CellResp');

%% plot basic stats of time series
TS = CR(:,1:3000);
TS_max = max(TS,[],2);
TS_avr = mean(TS,2);
figure;
hist(TS_max,0:0.1:5)
figure
hist(TS_avr,0:0.01:1)

%% color-code based on average activity
cIX_plot = 1:size(CR,1);
TS_avr = mean(TS,2);

TSmax = 0.3;
TSmin = min(TS_avr);
TS_avr(TS_avr>TSmax) = TSmin;

score = (TS_avr-TSmin)/(TSmax-TSmin);
gIX_plot = ceil(score*64);
gIX_plot(gIX_plot==0)=1;
wIX = score;%.^2; % set transparency weight, and enhance contrast

numK = max(gIX_plot);
clrmap = parula(numK);

setappdata(hfig,'isRefAnat',0);
I = LoadCurrentFishForAnatPlot(hfig,cIX_plot,gIX_plot,clrmap,wIX);
DrawCellsOnAnat(I);

DrawCustomColorbar(clrmap,[TSmin,TSmax],2,gcf);
%%
savedir = GetOutputDataDir;
filename = fullfile(savedir,'Zstack.tif');
WriteZstack(hfig,filename,cIX_plot,gIX_plot,clrmap);


function varargout=abs(varargin)
%ABS (overloaded)
%
% t = abs(x)
%
% The variable t can only be used in convexity preserving
% operations such as t<=0, minimize t etc.

%% ***************************************************
% This file defines a nonlinear operator for YALMIP
%
% It can take three different inputs
% For double inputs, it returns standard double values
% For sdpvar inputs, it genreates a an internal variable
% When first input is 'model' it generates the epigraph

%% ***************************************************
switch class(varargin{1})

    case 'double'
        error('Overloaded SDPVAR/ABS CALLED WITH DOUBLE. Report error')

    case 'sdpvar' % Overloaded operator for SDPVAR objects. Pass on args and save them.
        if isreal(varargin{1})
            varargout{1} = yalmip('addextendedvariable',mfilename,varargin{1});
        else
            % For complex args, abs(X) is defined [norm(X(i,j),2)] in MATLAB
            y = [];
            x = varargin{1};
            for i = 1:size(x,1)
                temp = [];
                for j = 1:size(x,2)
                    temp = [temp norm(extsubsref(x,i,j))];
                end
                y = [y;temp];
            end
            varargout{1} = y;
        end

    case 'char' % YALMIP send 'graph' when it wants the epigraph or hypograph
        switch varargin{1}
            case 'graph'
                % Description using epigraphs
                t = varargin{2};
                X = varargin{3};
                varargout{1} = (-t <= X <= t);
                varargout{2} = struct('convexity','convex','monotonicity','none','definiteness','positive');
                varargout{3} = X;

            case 'milp'
                % Exact description using binary variables
                t = varargin{2};
                X = varargin{3};
                F = ([]);
                [M,m]=derivebounds(X);
                if m>=0
                    F = F + (t == X);
                elseif M<0
                    F = F + (t == -X);
                else
                    d = binvar(1,1);
                    F = F + (X <= M*d)     + (-2*(M-m)*d     <= t+X <= 2*(M-m)*d);
                    F = F + (X >= m*(1-d)) + (-2*(M-m)*(1-d) <= t-X <= 2*(M-m)*(1-d));
                end
                varargout{1} = F;
                varargout{2} = struct('convexity','milp','monotonicity','milp','definiteness','positive');
                varargout{3} = X;
            otherwise
                error('SDPVAR/ABS called with CHAR argument?');
        end
    otherwise
        error('Strange type on first argument in SDPVAR/ABS');
end


%% Plane
% Concrete subclass of <ZeroVolShape.html |Shape|> representing a plane.

%%% Description
% |Plane| represents the shape of a plane.  It does not have a volume, but it is
% used to force a user-defined primary grid plane in <maxwell_run.html
% |maxwell_run|>.

%%% Construction
%  shape = Plane(normal_axis, intercept)
%  shape = Plane(normal_axis, intercept, dl_max)

% *Input Arguments*
%
% * |normal_axis|: axis normal to the plane.  It should be one of |Axis.x|,
% |Axis.y|, |Axis.z|.
% * |intercept|: location of the plane in the |normal_axis| direction.
% * |dl_max|: maximum grid size allowed in the plane.  It can be either |[dx dy
% dz]| or a single real number |dl| for |dx = dy = dz|.  If unassigned, |dl_max
% = Inf| is used.  In the |normal_axis| direction, |dl_max| is meaningless.

%%% Example
%   % Create an instance of Plane.
%   shape = Plane(Axis.y, 100);
%
%   % Use the constructed shape in maxwell_run().
%   [E, H] = maxwell_run({INITIAL ARGUMENTS}, 'OBJ', {'vacuum', 'none', 1.0}, shape, {REMAINING ARGUMENTS});

%%% See Also
% <Rectangle.html |Rectangle|>, <Line.html |Line|>, <Point.html |Point|>,
% <ZeroVolShape.html |ZeroVolShape|>, <maxwell_run.html |maxwell_run|>

classdef Plane < ZeroVolShape

	properties
		normal_axis
		intercept
	end

	methods
        function this = Plane(normal_axis, intercept, dl_max)
			chkarg(istypesizeof(normal_axis, 'Axis'), '"normal_axis" should be instance of Axis.');
			chkarg(istypesizeof(intercept, 'real'), '"intercept" should be real.');

			function level = lsf(x, y, z)
				chkarg(istypeof(x, 'real'), '"x" should be array with real elements.');
				chkarg(istypeof(y, 'real'), '"y" should be array with real elements.');
				chkarg(istypeof(z, 'real'), '"z" should be array with real elements.');
				chkarg(isequal(size(x), size(y), size(z)), '"x", "y", "z" should have same size.');

				loc = {x, y, z};
				level = -abs(loc{normal_axis} - intercept);
			end

			lprim = cell(1, Axis.count);
			for w = Axis.elems
				if w == normal_axis
					lprim{w} = intercept;
				else
					lprim{w} = [-Inf Inf];
				end
			end

			if nargin < 3  % no dl_max
				super_args = {lprim, @lsf};
			else
				dl_max = expand2row(dl_max, Axis.count);
				dl_max(normal_axis) = Inf;  % dl_max is meaningless in normal direction
				super_args = {lprim, @lsf, dl_max};
			end

			this = this@ZeroVolShape(super_args{:});
			this.normal_axis = normal_axis;
			this.intercept = intercept;
		end
	end
end


function [optimState,t_func] = initdesign_vbmc(optimState,Ns,funwrapper,t_func,options)
%INITDESIGN_VBMC Initial sample design (provided or random box).

x0 = optimState.Cache.X_orig;
[N0,D] = size(x0);

if N0 <= Ns
    Xs = x0;
    ys = optimState.Cache.y_orig;
    if N0 < Ns
        switch lower(options.InitDesign)
            case 'plausible'
                % Uniform random samples in the plausible box (in transformed space)
                Xrnd = bsxfun(@plus,bsxfun(@times,rand(Ns-N0,D),optimState.PUB-optimState.PLB),optimState.PLB);
            case 'narrow'
                xstart = warpvars_vbmc(x0(1,:),'dir',optimState.trinfo);
                Xrnd = bsxfun(@plus,bsxfun(@times,rand(Ns-N0,D)-0.5,0.1*(optimState.PUB-optimState.PLB)),xstart);
                Xrnd = bsxfun(@min,bsxfun(@max,Xrnd,optimState.PLB),optimState.PUB);
            otherwise
                error('Unknown initial design for VBMC.');
        end
        Xrnd = warpvars_vbmc(Xrnd,'inv',optimState.trinfo);  % Convert back to original space
        Xs = [Xs; Xrnd];
        ys = [ys; NaN(Ns-N0,1)];
    end
    idx_remove = true(N0,1);

elseif N0 > Ns
    % Cluster starting points
    kmeans_options = struct('Display','off','Method',2,'Preprocessing','whiten');
    idx = fastkmeans(x0,Ns,kmeans_options);

    % From each cluster, take points with higher density in original space
    Xs = NaN(Ns,D); ys = NaN(Ns,1); idx_remove = false(N0,1);
    for iK = 1:Ns
        idxK = find(idx == iK);
        xx = optimState.Cache.X_orig(idxK,:);
        yy = optimState.Cache.y_orig(idxK);
        [~,idx_y] = max(yy);
        Xs(iK,:) = xx(idx_y,:);
        ys(iK) = yy(idx_y);
        idx_remove(idxK(idx_y)) = true;
    end
end
% Remove points from starting cache
optimState.Cache.X_orig(idx_remove,:) = [];
optimState.Cache.y_orig(idx_remove) = [];

Xs = warpvars_vbmc(Xs,'d',optimState.trinfo);

for is = 1:Ns
    timer_func = tic;
    if isnan(ys(is))    % Function value is not available
        [~,optimState] = funlogger_vbmc(funwrapper,Xs(is,:),optimState,'iter');
    else
        [~,optimState] = funlogger_vbmc(funwrapper,Xs(is,:),optimState,'add',ys(is));
    end
    t_func = t_func + toc(timer_func);
end

end


function [A] = ft2A(ft)
% Convert length from feet to angstroms.
% Chad A. Greene 2012
A = ft*3048000000;


function gen_cropped_MPII_test_images()
% Author: Guanghan Ning, Feb 2017
clear all;
clc;

target_dist = 1.45;
boxsize = 368;

% Get [input images paths] and [output images path]
mpii_img_folder_path = '../dataset/MPI/images/';
img_output_folder = 'dataset_mpii/images_test_cropped/qualitative/';

% Load annotations
load('eval_MPII/mpii_Tompson_test', 'annolist');
load('eval_MPII/mpii_Tompson_test', 'keypointsAll');
load('eval_MPII/mpii_Tompson_test', 'RELEASE_img_index');
load('eval_MPII/mpii_Tompson_test', 'RELEASE_person_index');

annolist_test = annolist;
num_test_imgs = size(annolist_test, 2);

load('eval_MPII/mpii_human_pose_v1_u12_1','RELEASE');
annolist_all = RELEASE.annolist;

%% Start from here if everything already read from mat file.
for img_id = 1:num_test_imgs

    img_index = RELEASE_img_index(img_id);

    % (1) Deal with rect ids

        % Get rect id. Note the difference between rect_ct and rect_id
        rect_id = RELEASE_person_index(img_id);

        % Find scale and mid-points in order to convert prediction
        x_mid = annolist_test(img_id).annorect.objpos.x;
        y_mid = annolist_test(img_id).annorect.objpos.y;
        objpos_org = [x_mid, y_mid];
        scale_provided = annolist_test(img_id).annorect.scale;

        % (2) Deal with images
        img_name = annolist_all(img_index).image.name;
        img_path = strcat(mpii_img_folder_path, img_name);
        if ~exist(img_path, 'file')
            continue
        end

        img = imread(img_path);

        scale_in_cpp = target_dist/scale_provided;
        center_s = objpos_org * scale_in_cpp;
        fprintf('scale: %f\n', scale_in_cpp);

        img = imresize(img, scale_in_cpp);
        disp(size(img));
        [img, pad] = padAround(img, boxsize, center_s);
        disp(size(img));

        [pathstr, name, ext] = fileparts(img_path);
        img_output_path = strcat(img_output_folder, name, '_', num2str(rect_id), ext);
        imwrite(img, img_output_path);

        output_scale(img_id) = scale_in_cpp;
end
save('scale_in_cpp_MPII_test.mat', 'output_scale');

function [img_padded, pad] = padAround(img, boxsize, center)
    center = round(center);
    h = size(img, 1);
    w = size(img, 2);
    pad(1) = boxsize/2 - center(2); % up
    pad(3) = boxsize/2 - (h-center(2)); % down
    pad(2) = boxsize/2 - center(1); % left
    pad(4) = boxsize/2 - (w-center(1)); % right

    pad_up = repmat(img(1,:,:)*0, [pad(1) 1 1])+128;
    img_padded = [pad_up; img];
    pad_left = repmat(img_padded(:,1,:)*0, [1 pad(2) 1])+128;
    img_padded = [pad_left img_padded];
    pad_down = repmat(img_padded(end,:,:)*0, [pad(3) 1 1])+128;
    img_padded = [img_padded; pad_down];
    pad_right = repmat(img_padded(:,end,:)*0, [1 pad(4) 1])+128;
    img_padded = [img_padded pad_right];

    center = center + [max(0,pad(2)) max(0,pad(1))];
    img_padded = img_padded(center(2)-(boxsize/2-1):center(2)+boxsize/2, center(1)-(boxsize/2-1):center(1)+boxsize/2, :); %cropping if needed

function [x,y] = findMaximum(map)
    [~,i] = max(map(:));
    [x,y] = ind2sub(size(map), i);


function [output_stack, IRR, ORR, ARR] = evalfisx(input_stack, fis)
%EVALFISX Evaluation of a fuzzy inference system.
%
%   See evalfis1 for syntax and explanation.
%
%   It is completely based on MATLAB's evalfis1
%   with some modifications, so it's compatible
%   with extendent fuzzy rule structure.
%
%   Compare also code of this function
%   with code of original the evalfis1.
%
%   Example:
%   load carsmall;
%   fis = genfis4([Weight, Displacement], Acceleration, 'mamdani');
%   out = evalfisx([2000 100; 2000 200; 2000 300], fis);

%   Per Konstantin A. Sidelnikov, 2009.

persistent CURRENT_FIS;
persistent FIS_NAME FIS_TYPE IN_N OUT_N IN_MF_N OUT_MF_N RULE_N;
persistent AND_METHOD OR_METHOD IMP_METHOD AGG_METHOD DEFUZZ_METHOD;
persistent BOUND IN_MF_TYPE OUT_MF_TYPE;
persistent IN_RULE_LIST OUT_RULE_LIST AND_OR RULE_WEIGHT;
persistent IN_PARAM OUT_PARAM;
persistent OUT_TEMPLATE_MF OUT_MF QUALIFIED_OUT_MF OVERALL_OUT_MF;

point_n = 101;
mf_para_n = 4;

initialization = 1;
% Check if initialization necessary.
if isequal(fis, CURRENT_FIS)
    initialization = 0;
end

% Unpack data and initialize global variables.
if initialization
    CURRENT_FIS = fis;
    FIS_NAME = fis.name;
    FIS_TYPE = fis.type;
    IN_N = length(fis.input);
    OUT_N = length(fis.output);

    for i = 1 : IN_N
        IN_MF_N(i) = length(fis.input(i).mf);
    end
    for i = 1 : OUT_N
        OUT_MF_N(i) = length(fis.output(i).mf);
    end

    RULE_N = length(fis.rule);

    AND_METHOD = fis.andMethod;
    OR_METHOD =  fis.orMethod;
    IMP_METHOD = fis.impMethod;
    AGG_METHOD = fis.aggMethod;
    DEFUZZ_METHOD = fis.defuzzMethod;

    IN_MF_TYPE = [];
    for i = 1 : IN_N
        IN_MF_TYPE = [IN_MF_TYPE; {fis.input(i).mf.type}'];
    end
    OUT_MF_TYPE = [];
    for i = 1 : OUT_N
        OUT_MF_TYPE = [OUT_MF_TYPE; {fis.output(i).mf.type}'];
    end

    in_bound = reshape([fis.input.range], IN_N, 2);
    out_bound = reshape([fis.output.range], OUT_N, 2);
    BOUND = [in_bound; out_bound];

    RULE_WEIGHT = [fis.rule.weight]';
    AND_OR = [fis.rule.connection]';
    IN_RULE_LIST = {fis.rule.antecedent}';
    OUT_RULE_LIST = reshape([fis.rule.consequent], RULE_N, OUT_N);

    k = 1;
    totalInputMFs = sum(IN_MF_N);
    totalOutputMFs = sum(OUT_MF_N);
    IN_PARAM = zeros(totalInputMFs, 4);
    for i = 1 : IN_N
        for j = 1 : length(fis.input(i).mf)
            tmp = fis.input(i).mf(j).params;
            IN_PARAM(k, 1 : length(tmp)) = tmp;
            k = k + 1;
        end
    end
    k = 1;
    OUT_PARAM = zeros(totalOutputMFs, 4);
    for i = 1 : OUT_N
        for j = 1 : length(fis.output(i).mf)
            tmp = fis.output(i).mf(j).params;
            OUT_PARAM(k, 1 : length(tmp)) = tmp;
            k = k + 1;
        end
    end

    if strcmp(FIS_TYPE, 'sugeno')
        OUT_PARAM = OUT_PARAM(:, 1 : IN_N + 1);
    elseif strcmp(FIS_TYPE, 'mamdani')
        OUT_PARAM = OUT_PARAM(:, 1 : mf_para_n);
    else
        error('Unknown FIS type!');
    end

    if strcmp(FIS_TYPE, 'mamdani')
        % Compute OUT_TEMPLATE_MF
        OUT_TEMPLATE_MF = zeros(sum(OUT_MF_N), point_n);
        cum_mf = cumsum(OUT_MF_N);
        for i = 1 : sum(OUT_MF_N)
            % index for output
            output_index = find((cum_mf - i) >= 0, 1, 'first');
            tmp = linspace(BOUND(IN_N + output_index, 1), ...
                BOUND(IN_N + output_index, 2), point_n);
            OUT_TEMPLATE_MF(i, :) = ...
                evalmf(tmp, OUT_PARAM(i, :), OUT_MF_TYPE{i});
        end

        % Reorder to fill OUT_MF, an (RULE_N X point_n * OUT_N) matrix.
        OUT_MF = zeros(RULE_N, point_n * OUT_N);
        for i = 1 : RULE_N
            for j = 1 : OUT_N
                mf_index = OUT_RULE_LIST(i, j);
                index = sum(OUT_MF_N(1 : j - 1)) + abs(mf_index);
                tmp = (j - 1) * point_n + 1 : j * point_n;
                if mf_index > 0    % regular MF
                    OUT_MF(i, tmp) = OUT_TEMPLATE_MF(index, :);
                elseif mf_index < 0 % Linguistic hedge "NOT"
                    OUT_MF(i, tmp) = 1 - OUT_TEMPLATE_MF(index, :);
                else                % Don't care (MF index == 0)
                    OUT_MF(i, tmp) = ones(1, point_n);
                end
            end
        end

        % Allocate other matrices
        QUALIFIED_OUT_MF = zeros(RULE_N, point_n * OUT_N);
        OVERALL_OUT_MF = zeros(1, point_n * OUT_N);
    end
    %   fprintf('Global variables for %s FIS are initialized\n', FIS_NAME);
end
% End of initialization

% Error checking for input stack
m = size(input_stack, 1);
n = size(input_stack, 2);
if ~((n == IN_N) || ((n == 1) && (m == IN_N)))
    fprintf('The input stack is of size %dx%d,', m, n);
    fprintf('while expected input vector size is %d.\n', IN_N);
    error('Exiting ...');
end
if (n == 1) && (m == IN_N)
    data_n = 1;
    input_stack = input_stack';
else
    data_n = m;
end

% Allocate output stack
output_stack = zeros(data_n, OUT_N);

% Iteration through each row of input stack
for kkk = 1 : data_n
    input = input_stack(kkk, :);

    % Find in_template_mf_value
    in_template_mf_value = zeros(sum(IN_MF_N), 1);
    cum_mf = cumsum(IN_MF_N);
    for i = 1 : sum(IN_MF_N)
        input_index = find((cum_mf - i) >= 0, 1, 'first');
        in_template_mf_value(i) = ...
            evalmf(input(input_index), IN_PARAM(i, :), IN_MF_TYPE{i});
    end

    % Reordering to fill in_mf_value, which is an (RULE_N X 1) cell matrix.
    tmp = cumsum([0, IN_MF_N(1 : IN_N - 1)]);
    in_mf_value = cell(RULE_N, 1);
    for ind = 1 : RULE_N
        index = tmp(IN_RULE_LIST{ind}(1, :)) + abs(IN_RULE_LIST{ind}(2, :));
        in_mf_value{ind} = in_template_mf_value(index)';
        % Take care of linguistic hedge NOT (MF index is negative)
        neg_index = find(IN_RULE_LIST{ind}(2, :) < 0);
        in_mf_value{ind}(neg_index) = 1 - in_mf_value{ind}(neg_index);
    end

    % Find the firing strengths
    % AND_METHOD = 'min' or 'prod'; which is used as function name too
    % OR_METHOD = 'max' or 'probor'; which is used as function name too
    firing_strength = zeros(RULE_N, 1);
    and_index = find(AND_OR == 1);
    or_index = find(AND_OR == 2);
    if IN_N ~= 1
        firing_strength(and_index) = ...
            cellfun(@(x) feval(AND_METHOD, x'), in_mf_value(and_index, :))';
        firing_strength(or_index) = ...
            cellfun(@(x) feval(OR_METHOD, x'), in_mf_value(or_index, :))';
    else
        firing_strength = [in_mf_value{:}]';
    end

    % Recalculate firing strengths scaled by rule weights
    firing_strength = firing_strength .* RULE_WEIGHT;

    % Find output
    if strcmp(FIS_TYPE, 'sugeno')
        template_output = OUT_PARAM * [input(:); 1]; % Output for template

        % Reordering according to the output part of RULE_LIST;
        % Negative MF index will becomes positive
        tmp = cumsum([0, OUT_MF_N(1 : OUT_N - 1)]);
        index = repmat(tmp, RULE_N, 1) + abs(OUT_RULE_LIST);
        index(index == 0) = 1;   % temp. setting for easy indexing
        rule_output = template_output(index);
        rule_output(OUT_RULE_LIST == 0) = 0;  % take care of zero index
        sum_firing_strength = sum(firing_strength);

        if sum_firing_strength == 0
            fprintf('input = [');
            for i = 1 : IN_N
                fprintf('%f ', input(i));
            end
            fprintf(']\n');
            error('Total firing strength is zero!');
        end

        switch DEFUZZ_METHOD
            case 'wtaver'
                output_stack(kkk, :) = firing_strength' * rule_output / ...
                    sum_firing_strength;
            case 'wtsum'
                output_stack(kkk, :) = firing_strength' * rule_output;
            otherwise
                error('Unknown defuzzification method!');
        end
    elseif strcmp(FIS_TYPE, 'mamdani')
        % Transform OUT_MF to QUALIFIED_OUT_MF
        % Duplicate firing_strength.
        tmp = firing_strength(:, ones(1, point_n * OUT_N));

        if strcmp(IMP_METHOD, 'prod')      % IMP_METHOD == 'prod'
            QUALIFIED_OUT_MF = tmp .* OUT_MF;
        elseif strcmp(IMP_METHOD, 'min')   % IMP_METHOD == 'min'
            QUALIFIED_OUT_MF = feval(IMP_METHOD, tmp, OUT_MF);
        else    % IMP_METHOD is user-defined
            tmp1 = feval(IMP_METHOD, [tmp(:)'; OUT_MF(:)']);
            QUALIFIED_OUT_MF = reshape(tmp1, RULE_N, point_n * OUT_N);
        end

        % AGG_METHOD = 'sum' or 'max' or 'probor' or user-defined
        OVERALL_OUT_MF = feval(AGG_METHOD, QUALIFIED_OUT_MF);

        for i = 1 : OUT_N
            tmp = linspace(BOUND(IN_N + i, 1), BOUND(IN_N + i, 2), point_n);
            tmp1 = (i - 1) * point_n + 1 : i * point_n;
            output_stack(kkk, i) = ...
                defuzz(tmp, OVERALL_OUT_MF(1, tmp1), DEFUZZ_METHOD);
        end
    else
        fprintf('fis_type = %d\n', FIS_TYPE);
        error('Unknown FIS type!');
    end
end

if nargout >= 2
    IRR = in_mf_value;
end

if strcmp(FIS_TYPE, 'sugeno')
    if nargout >= 3
        ORR = rule_output;
    end
    if nargout >= 4
        ARR = firing_strength(:, ones(1, OUT_N));
    end
else
    if nargout >= 3
        ORR = [];
        for iii = 1 : OUT_N
            tmp = (iii - 1) * point_n + 1 : iii * point_n;
            ORR = [ORR; QUALIFIED_OUT_MF(:, tmp)];
        end
        ORR = ORR';
    end
    if nargout >= 4
        ARR = reshape(OVERALL_OUT_MF, point_n, OUT_N);
    end
end


function relaxMtFit(dataDir,outDir,voxRange)
%
% relaxMtFit(dataDir,outDir,voxRange)
%
% Calculates the k and f maps using the MT TOF sequence.
%
% See Yarnykh & Yuan (2004). Cross-relaxation imaging reveals
% detailed anatomy of white matter fiber tracts in the human
% brain. Neuroimage, 23(1):409-24. (PMID: 15325389)
%
% KEY DEPENDENCIES:
% lorentzian.m, relaxMtFitFunc.m
%
% SEE ALSO:
% relaxPreprocess.m to get raw DICOM data into the right format for
% this function.
%
% HISTORY:
% 2006.02.19 Nikola Stikov wrote it.
% 2007.01.?? NS recoded it to use lsqnonlin
% 2007.02.27 RFD rewrote the loop structure and data format.
% 2007.03.02 RFD slight performance enhancements. Also renamed
% several functions and added them all to the mrDiffusion
% repository.
%

if(~exist('dataDir','var')|isempty(dataDir))
  dataDir = pwd;
end

if(~exist('outDir','var')|isempty(outDir))
  outDir = dataDir;
end

if(~exist('voxRange','var')|isempty(voxRange))
  voxRange = '';
end

if(~exist(outDir,'dir'))
  mkdir(outDir);
end

disp(['Loading data from ' dataDir '...']);

ni = niftiRead(fullfile(dataDir,'T1.nii.gz'));
T1 = ni.data;
xform = ni.qto_xyz;
clear ni;
nz = T1>0;

if(exist(fullfile(dataDir,'brainMask.nii.gz'),'file'))
  ni = niftiRead(fullfile(dataDir,'brainMask.nii.gz'));
  brainMask = ni.data==1;
  clear ni;
else
  brainMask = nz;
end

%ni = niftiRead(fullfile(dataDir,'PD.nii.gz'));
%PD = ni.data;

ni = niftiRead(fullfile(dataDir,'S0.nii.gz'));
S0 = ni.data;
clear ni;

d = dir(fullfile(dataDir,'MT_*.nii.gz'));
delta = zeros(1,length(d));
for(ii=1:length(d))
  ni = niftiRead(fullfile(dataDir,d(ii).name));
  MT(:,:,:,ii) = ni.data;
  clear ni;
  % TODO: fix this crude hack. Maybe store offset freqs in nifti header?
  delta(ii) = sscanf(d(ii).name,'MT_%dkHz.nii.gz') ;
end
delta = delta'*1e3; % offset frequencies, in Hz

iterMax = 50; %maximum number of iterations allowed

sz = size(brainMask);

% Clip T1 and PD to reasonable values
% Tissue       T1 (s)          T2 (ms)        PD*
% CSF        0.8 - 20        110 - 2000    70 - 230
% White	    0.76 - 1.08       61 - 100 	   70 - 90
% Gray      1.09 - 2.15       61 - 109     85 - 125
% Meninges   0.5 - 2.2        50 - 165      5 - 44
% Muscle    0.95 - 1.82       20 - 67      45 - 90
% Adipose    0.2 - 0.75       53 - 94      50 - 100
% (PD values are based on PD=111 for 12mM aqueous NiCl2)
% (From http://www.cis.rit.edu/htbooks/mri/chap-8/chap-8.htm#8.7 )
% Our PDs seem to be scaled by 50 or so???
T1(T1<0.01) = 0.01;
T1(T1>10) = 10;
%PD(PD<0) = 0;
%PD(PD>12000) = 12000;

R1 = zeros(size(T1)); R1(nz) = 1./T1(nz);
%if(~exist(fullfile(dataDir,'R1.nii.gz'),'file'))
%  dtiWriteNiftiWrapper(single(R1),xform,fullfile(dataDir,'R1.nii.gz'));
%end

% Tidy-up the brain mask a bit
%brainMask(PD<1000|PD>10000|T1<0.2|T1>2|S0<20) = 0;
brainMask(T1<0.2|T1>2|any(MT==0,4)) = 0;

% RFD: empirically, [1 .08] is the median for brain tissue
x0 = [1 .08];
%x0 = [2.4, .10]'; %this is our initial guess, bese [3.4, .15]'

lb = [.1 .03]; % [k f]
ub = [5 .28];

t_m = 8e-3; %bese 8e-3
t_s = 5e-3; %bese 5e-3
t_r = 19e-3; %bese 19e-3
T2_B = 11e-6;
% Where does this come from? Should we estimate it from the data?
w1rms = 2400; % omega-1 RMS

for ii = 1:length(delta)
    W_B(ii) = pi*(w1rms^2)*lorentzian (delta(ii), T2_B);
end;

options = optimset('LevenbergMarquardt','on', 'Display', 'off');

% FOR DEBUGGING:
%showMontage(brainMask);

brainInds = find(brainMask);
numVoxelsPerUpdate = 10000;
nVoxAll = length(brainInds);
for(ii=1:size(MT,4))
  tmpVol = MT(:,:,:,ii);
  tmpMT(ii,:) = tmpVol(brainInds);
end
clear tmpVol;
MT = tmpMT;
clear tmpMT;
R1 = R1(brainInds);
S0 = S0(brainInds);

f = zeros(1,nVoxAll);
k = zeros(1,nVoxAll);
gof = zeros(1,nVoxAll);
totalSecs = 0;
if(~isempty(voxRange))
  if(any(voxRange<0))
	doVox = [floor(nVoxAll*voxRange(1))+1:ceil(nVoxAll*voxRange(2))];
  else
	doVox = [max(1,voxRange(1)):min(nVoxAll,voxRange(2))];
  end
  fprintf('Processing voxels %d - %d...\n', doVox(1), doVox(end));
else
   doVox = [1:nVoxAll];
   fprintf('Processing all %d voxels...\n',nVoxAll);
end

% What we compute here is actually W_F./R1_F. R1_F is computed in
% the fit function, but we precompute the rest out here to save a
% few cpu cycles in the loop below.
W_F = (w1rms./(2*pi*delta)).^2/.055;

warning off;
nVox = doVox(end)-doVox(1);
tmp = zeros(size(brainMask));
tmpName = tempname
tic;
for(ii=doVox)
  if(mod(ii,numVoxelsPerUpdate)==0)
    prevSecs = toc;
    totalSecs = totalSecs+prevSecs;
    secsPerVox = totalSecs./(ii-doVox(1));
    estTime = secsPerVox*(doVox(end)-ii);
    if(estTime>5400) estTime=estTime./3600; estTimeUnits='hours';
    elseif(estTime>90) estTime=estTime./60; estTimeUnits='minutes';
    else estTimeUnits='seconds'; end
    fprintf('Processed %d of %d voxels- %0.1f %s remaining (%0.3f secs per vox)...\n',ii-doVox(1),nVox,estTime,estTimeUnits,secsPerVox);
	tmp(brainInds) = f;
    m = makeMontage(tmp,[1:5:size(tmp,3)]);
	m = uint8(round(m./max(f).*255));
	imwrite(m,['/home/bob/public_html/f.png']);
	save(tmpName,'k','f','gof','voxRange','xform');
    tic;
  end

  % Some voxels produce a "Input to EIG must not contain NaN
  % or Inf" error in lsqnonl in. Tweaking the bounds or
  % starting estimate can fix it sometimes, but they are
  % probably junk voxels anyway, so we'll catch and skip them.
  try
    %[x, resnorm, residual, exitflag, output] = lsqnonlin(@(x) relaxMtFitFunc(x, MT(:,ii), W_B, W_F, T2_B, R1(ii), S0(ii), t_m, t_s, t_r), x0, lb, ub, options); %bese j-12;
    [x, resnorm, exitflag] = fminsearch(@(x) relaxMtFitFuncLs(x, MT(:,ii), W_B, W_F, T2_B, R1(ii), S0(ii), t_m, t_s, t_r), x0, options);
    if(exitflag>0)
      k(ii) = x(1);
      f(ii) = x(2);
      gof(ii) = resnorm;
    else
      gof(ii) = NaN;
    end
  catch
    % Leave the fit values at zero.
  end
end
warning on;

fprintf('Finished processing %d slices (%d voxels) in %0.1f seconds.\n\n',sz(3),nVox,totalSecs);

if(isempty(voxRange))
   outName = '';
else
   outName = sprintf('%0.2f-%0.2f',voxRange(1),voxRange(2));
end
im=zeros(size(brainMask)); im(brainInds) = f; f = im;
im=zeros(size(brainMask)); im(brainInds) = k; k = im;
im=zeros(size(brainMask)); im(brainInds) = gof; gof = im;
try
   dtiWriteNiftiWrapper(single(f),xform,fullfile(outDir,[outName 'f.nii.gz']));
   dtiWriteNiftiWrapper(single(k),xform,fullfile(outDir,[outName 'k.nii.gz']));
   dtiWriteNiftiWrapper(single(gof),xform,fullfile(outDir,[outName 'gof.nii.gz']));
catch
   outName = tempname
   save(outName,'k','f','gof','voxRange','xform');
end
% FOR DEBUGGING:
keyboard;
return;


%% Author: epokh
%% Website: www.epokh.org/drupy
%% This software is under GPL

function Tmat=Tsfer(gamma,beta,r)
%%Transformation in spherical coordinates
%%input: alfa angle along z axis, beta angle along y axis, r trasl along z
%%along z axis
%%output: trasformation matrix
Tmat=RotZ(gamma)*RotY(beta)*Tras(0,0,r);

end


classdef LatitudeTermCondition < AbstractEventTerminationCondition
    %LatitudeTermCondition Summary of this class goes here
    %   Detailed explanation goes here

    properties
        lat(1,1) double = 0; %rad
        bodyInfo KSPTOT_BodyInfo
        direction(1,1) double = 0;
    end

    methods
        function obj = LatitudeTermCondition(lat)
            obj.lat = lat;
        end

        function evtTermCondFcnHndl = getEventTermCondFuncHandle(obj)
            evtTermCondFcnHndl = @(t,y) obj.eventTermCond(t,y);
        end

        function initTermCondition(obj, initialStateLogEntry)
            obj.bodyInfo = initialStateLogEntry.centralBody;
        end

        function name = getName(obj)
            name = sprintf('Latitude (%.3f deg)', rad2deg(obj.lat));
        end

        function tf = shouldBeReinitOnRestart(obj)
            tf = true;
        end

        function params = getTermCondUiStruct(obj)
            params = struct();

            params.paramName = 'Latitude';
            params.paramUnit = 'deg';
            params.useParam = 'on';
            params.useStages = 'off';
            params.useTanks = 'off';
            params.useEngines = 'off';
            params.useStopwatches = 'off';

            params.value = obj.lat;
            params.refStage = LaunchVehicleStage.empty(1,0);
            params.refTank = LaunchVehicleEngine.empty(1,0);
            params.refEngine = LaunchVehicleEngine.empty(1,0);
            params.refStopwatch = LaunchVehicleStopwatch.empty(1,0);
        end

        function optVar = getNewOptVar(obj)
            optVar = LatitudeOptimizationVariable(obj);
        end

        function optVar = getExistingOptVar(obj)
            optVar = obj.optVar;
        end

        function tf = usesStage(obj, stage)
            tf = false;
        end

        function tf = usesEngine(obj, engine)
            tf = false;
        end

        function tf = usesTank(obj, tank)
            tf = false;
        end

        function tf = usesEngineToTankConn(obj, engineToTank)
            tf = false;
        end

        function tf = usesStopwatch(obj, stopwatch)
            tf = false;
        end
    end

    methods(Static)
        function termCond = getTermCondForParams(paramValue, stage, tank, engine, stopwatch)
            termCond = LatitudeTermCondition(paramValue);
        end
    end

    methods(Access=protected)
        function [value,isterminal,direction] = eventTermCond(obj, t,y)
            rVect = y(1:3);
            vVect = y(4:6);
            cartElem = CartesianElementSet(t, rVect(:), vVect(:), obj.bodyInfo.getBodyCenteredInertialFrame());
            geoElem = cartElem.convertToFrame(obj.frame).convertToGeographicElementSet();

            value = geoElem.lat - obj.lat;
            isterminal = 1;
            direction = obj.direction;
        end
    end
end


function a = getBestAction(Q, state, actions)

    % Select the best action a in state s

    n_actions = length(actions);
    status = find(Q(state,:), 1);

    if isempty(status)

        a = randi(n_actions);
    else

        [Q_value, a] = max(Q(state,:));
    end

end


clear variables;
EXPORT_DIR = '/home/mhughes/git/BPARHMM-NEW/figs/Mocap6/';

%jobID = 1121860; taskID = 'best';
%jobID = 2134873; taskID = 1;
jobID = 111; taskID = 1;

% ------------------------------------------------------- Load Ground truth
F{1} = zeros(6, 12 );
Q = loadSamplerInfo( jobID, taskID);
for ii = 1:Q.data.N
   activeFeatIDs =  unique( Q.data.zTrue(ii) );
   F{1}(ii, activeFeatIDs) = 1;
end

% ------------------------------------------------------- Load best BPHMM
%  Note that meanFfrac is aggregated across all post-burn-in samples
MIN_THR = 0.02;
% disregard feature if appeared active in less that 2%
%     of state sequence assignment zs when avg'd across all samples
X = loadSamplerOutput( jobID, taskID );
F{2} =  X.Summary.meanFfrac > 0.02;

if size( F{2}, 2 ) > 12
    if all( sum( F{2}(:,13:end), 1 ) == 0 )
        F{2} = F{2}(:,1:12);
    end
end

doTHR = 0;
MINITER = 10000;

keepiters = find( X.iters.Psi >= MINITER );

avgF = zeros( 6, 15 );
for id = keepiters
    K = size( X.A( id ).Ffrac, 2 );
    Fcur = zeros( size(avgF) );
    if doTHR
        Fcur(:,1:K) = X.A( id ).Ffrac > 0.02;
    else
        Fcur(:, 1:K) = X.A( id ).F;
    end
    avgF = avgF + Fcur;
end
avgF = avgF ./ length(keepiters );

kIDs = union( 1:12, find( sum( avgF, 1) > 0) );
F{2} = avgF(:, kIDs);

% ------------------------------------------------------- Load GMM/HMM
Q = load( '/data/liv/mhughes/data/MoCap6/Results/GMM_25trials_K2-20.mat' );
F{3} = Q.GMM(2,12).alignedF;
Q = load( '/data/liv/mhughes/data/MoCap6/Results/HMM_25trials_K2-20.mat' );
F{4} = Q.HMM(2,12).alignedF;

FigNames = {'GroundTruth','BPHMM','GMM','HMM'};
for ff = 1:length( F )
figure;
imagesc( F{ff} );
set( gcf, 'Name', ['Mocap6_FeatMatrix_' FigNames{ff} ]);
colormap( 'bone' );
set( gca, 'FontSize', 20 );

if ff==2
    rStr = num2str(doTHR);
else
    rStr = '';
end
set( gcf, 'InvertHardcopy','off','Color',[1 1 1]);

  export_fig( fullfile( EXPORT_DIR, ['Mocap6_FeatMatrix_' rStr FigNames{ff} ]), '-eps');
end


%kkhypot 'Output = sqrt[(Input 1)**2 + (Input 2 or Constant)**2]'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros khypot.pane file
%
% Parameters:
% InputFile: i1 'Input 1', required: 'First input data object'
% OutputFile: o 'Output', required: 'Resulting output data object'
% InputFile: i2 'Input 2', optional: 'Second input data object'
%
% Example: o = kkhypot({i1, i2}, {'i1','';'o','';'i2',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% khypot - Output = sqrt[(Input 1)**2 + (Input 2 or Constant)**2]
%
%  DESCRIPTION
% The "Hypotenuse" operator returns the square root of the result
% produced by adding the square of each point in \fBInput 1" and the square
% of either the corresponding point in \fBInput 2" or the \fBConstant\fP
% value, which ever is specified by the user.
%
% Executing "Hypotenuse" runs the program \fIkarith2\fP with the -hypot flag.
%
%  "Data Type - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/value_type_1input
%
%  "Data Type - Two Input Objects" 5
% .cI $DATAMANIP/repos/shared/man/sections/value_type_2input
%
%  "Map Data - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/map_1input
%
%  "Map Data - Two Input Objects" 5
% .cI $DATAMANIP/repos/shared/man/sections/map_2input
%
%  "Validity Mask" 5
% .cI $DATAMANIP/repos/shared/man/sections/mask_2input
%
%  "Input Objects of Different Sizes" 5
% .cI $DATAMANIP/repos/shared/man/sections/resize_2input
% The values used to pad the data when input files are not the same size are
% (0.0, 0.0).
%
%  "Explicit Location and Time Data - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/loc_and_time_1input
%
%  "Explicit Location and Time Data - Two Input Objects" 5
% .cI $DATAMANIP/repos/shared/man/sections/loc_and_time_2input
%
%  "Failure Modes - Single Input"
% .cI $DATAMANIP/repos/shared/man/sections/fail_1input
%
%  "Failure Modes - Two Input Objects"
% .cI $DATAMANIP/repos/shared/man/sections/fail_2input
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
% DATAMANIP::karith2, DATAMANIP::kcmplx2real, DATAMANIP::kmag
%
%  RESTRICTIONS
% Operations on complex data are not supported at this time.  If the
% input object is complex, only the real component of the data is
% processed, and the output object is real.  To calculate the hypotenuse,
% or magnitude of a complex number, use DATAMANIP::kcmplx2real.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kkhypot(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kkhypot(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i1', '__input';'o', '__output';'i2', '__input'};
maxval={0,0,1};
minval={0,0,1};
istoggle=[0,0,1];
was_set=istoggle * 0;
paramtype={'InputFile','OutputFile','InputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'karith2"  -hypot'],Inputs,narglist);


function cond = wilk03_condition ( )

%*****************************************************************************80
%
%% WILK03_CONDITION returns the L1 condition of the WILK03 matrix.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    18 January 2015
%
%  Author:
%
%    John Burkardt
%
%  Reference:
%
%    James Wilkinson,
%    Error Analysis of Direct Methods of Matrix Inversion,
%    Journal of the Association for Computing Machinery,
%    Volume 8, 1961, pages 281-330.
%
%  Parameters:
%
%    Output, real COND, the L1 condition.
%
  cond = 1.8 * ( 13.0 * 1.0E+10 / 9.0 );

  return
end


function newDwImg = dtiRawRohdeApplyIntensityCorrection(mc, phaseDir, origDwImg)
% Applies intensity correction given Rohode eddy/motion params in mc
%
%   newDwImg = dtiRawRohdeApplyIntensityCorrection(mc, phaseDir, origDwImg)
%
% mc is the 14 motion/eddy parameters, with translations in 1-3, rotations
% in 4-6. The remaining 8 elements are the eddy-current warping parameters
% (Rohde et al's 'c' parameters used to estimate their b_eddy).
%
% phaseDir specifies the phase-encoding direction, either a 1x3 with a '1'
% indicating the phase-encode dir, or a scalar value of 1, 2 or 3.
%
% Applies the Rohde intensity correction to origDwImg, which should be the
% ORIGINAL diffusion-weighted image volume for which the Rohde parameters
% in mc were estimated. We undo the motion correction to apply this, since
% it is specified in the motion-corrected image space.
%
%   Rohde, Barnett, Basser, Marenco and Pierpaoli (2004). Comprehensive
%   Approach for Correction of Motion and Distortion in Diffusion-Weighted
%   MRI. MRM 51:103-114.
%
% HISTORY:
%
% 2007.05.03 RFD wrote it.

if(numel(phaseDir)>1) phaseDir = find(phaseDir); end
if(length(phaseDir)>1 || phaseDir<1 || phaseDir>3) error('phaseDir specification error.'); end

sz = size(origDwImg);
% For our purposes here, ndgrid and meshgrid are equivalent- the only
% difference is which dim increases fastest, but this doesn't matter for a
% simple list of coords.
[X,Y,Z] = ndgrid([1:sz(1)],[1:sz(2)],[1:sz(3)]);
x = [X(:), Y(:), Z(:)];

% x and y are coordinate lists (names are same as used in Rohde et. al.
% paper). x is a list of coords in native image space, y is a list of the
% corresponsding coordinates in motion-corrected space.
motionMat = affineBuild(mc(1:3),mc(4:6));
y = mrAnatXformCoords(motionMat,x);

% Intensity correction (ic) is computed in motion-corrected space (y):
c = mc(7:14);
if(phaseDir==1)
    ic = 1 + c(1) + c(4)*y(:,2) + c(5)*y(:,3) + 2*c(7)*y(:,1) - 2*c(8)*y(:,1);
elseif(phaseDir==2)
    ic = 1 + c(2) + c(4)*y(:,1) + c(6)*y(:,3) - 2*(c(7)+c(8))*y(:,2);
else
    ic = 1 + c(3) + c(5)*y(:,1) + c(6)*y(:,2) + 4*c(8)*y(:,3);
end

% We want to apply ic (in motion-corrected space) to the original,
% uncorrected space. This is easier for the downstream processing pipeline,
% since we will likely reslice the original to a standard space and thus
% never actually generate a motion-corrected version. Otherwise, we would
% be forced to resample twice. So, we interpolate ic back to the
% uncorrected space x.
ic = myCinterp3(double(ic),[sz(1) sz(2)], sz(3), x, 1.0);
ic = reshape(ic,sz);
% Now apply it:
newDwImg = double(origDwImg).*ic;

return;

% Rohde et. al. only work out the intensity correction for phaseDir = 2.
% Here we solve for the other two options. Writing their eq. 12 out:
% c1*y1 + c2*y2 + c3*y3 + c4*y1*y2 + c5*y1*y3 + c6*y2*y3 + (c7*y1^2 - c7*y2^2) + (2*c8*y3^2 - c8*y1^2 - c8*y2^2)
%
% For phaseDir = 1:
% c1    + 0     + 0     + c4*y2    + c5*y3    + 0        + (2*c7*y1 - 0      ) + (0         - 0       - 2*c8*y1)
% c1 + c4*y2 + c5*y3 + 2*c7*y1 - 2*c8*y1
%
% For phaseDir = 2:
% NOTE: there is a typo in the Rohde paper where they solve for this
% derivative. In eq. 13, they present: c2 + c4*y1 + c6*y3 + 2*(c7+c8)*y2
% 0     + c2*y2 + 0     + c4*y1    + 0        + c6*y3    + (0       - 2*c7*y2) + (0         - 0       - 2*c8*y2)
% c2 + c4*y1 + c6*y3 - 2*c7*y2 - 2*c8*y2
% c2 + c4*y1 + c6*y3 - 2*(c7+c8)*y2
%
% For phaseDir = 3:
% 0     +       + c3*y3 + 0        + c5*y1    + c6*y2    + (0       - 0      ) + (4*c8*y3   - 0       - 0     )
% c3 + c5*y1 + c6*y2 + 4*c8*y3
%
% Or, simply:
% eq = 'c1*y1 + c2*y2 + c3*y3 + c4*y1*y2 + c5*y1*y3 + c6*y2*y3 + (c7*y1^2 - c7*y2^2) + (2*c8*y3^2 - c8*y1^2 - c8*y2^2)';
% maple(['diff(' eq ',y1)'])
% maple(['diff(' eq ',y2)'])
% maple(['diff(' eq ',y3)'])


function [L_l,R_l] = lindemannInh(L,R,fs,c_inh,dim)
%LINDEMANNINH Signal pre-processing for Lindemann's cross-correlation
%
%   [L_L,R_L] = IOSR.AUDITORY.LINDEMANNINH(L,R,FS) pre-processes left L and
%   right R signals for the cross-correlation function based on Lindemann's
%   precedence model [1,2]. A crucial parameter for Lindemann's model is
%   the "operating point", which controls the amount of inhibition. The
%   parameter is actually a function of the input related to its RMS level.
%   After half-wave rectifying and filtering the input signals L and R
%   (sampled at FS Hz) along the first non-singleton dimension, this
%   function applies a gain related to the inhibition parameter C_INH
%   (default is 0.3). The gain is identical for all rows, columns, etc.
%   Lastly, values outside of the interval [0,1] are not permitted in
%   Lindemann's model and hence these values are clipped.
%
%   [L_L,R_L] = IOSR.AUDITORY.LINDEMANNINH(L,R,FS,C_INH) uses the specified
%   inhibition parameter C_INH. The value must be in the interval [0,1].
%
%   [L_L,R_L] = IOSR.AUDITORY.LINDEMANNINH(L,R,FS,C_INH,DIM) pre-processes
%   L and R along the dimension DIM.
%
%   References
%
%   [1] Lindemann, W. (1986), Extension of a binaural cross-correlation
%       model by contralateral inhibition. I. Simulation of lateralization
%       for stationary signals, The Journal of the Acoustical Society of
%       America 80, 6, 1608-1622.
%
%   [2] Lindemann, W. (1986), Extension of a binaural cross-correlation
%       model by contralateral inhibition. II. The law of the first wave
%       front, The Journal of the Acoustical Society of America 80, 6,
%       1623-1630.
%
%   Further reading
%
%   Hummersone, C., Mason, R., Brookes, T. (2013), A comparison of
%       computational precedence models for source separation in
%       reverberant environments, The Journal of the Audio Engineering
%       Society 61, 7/8, 508-520.
%
%   See also IOSR.AUDITORY.XCORRLINDEMANN.

%   Copyright 2016 University of Surrey.

    %% check input

    assert(isequal(size(L),size(R)), 'iosr:lindemannInh:invalidInputs', 'L and R arrays must be the same size')
    assert(isscalar(fs), 'iosr:lindemannInh:invalidFs', 'FS must be a scalar')

    % default dim
    if nargin<5
        dim = find(size(L)>1,1,'first');
    end

    %% process

    % half-wave rectify
    L_l = hwr(L);
    R_l = hwr(R);

    % filter
    cutofffreq=800;
    [b,a] = butter(1,cutofffreq*2/fs);
    L_l = filter(b,a,L_l,[],dim);
    R_l = filter(b,a,R_l,[],dim);

    % inhibition parameter
    if nargin < 4
        c_inh = .3;
    else
        assert(isscalar(c_inh) & isnumeric(c_inh), 'iosr:lindemannInh:invalidCinh', 'c_inh must be a scalar');
        assert(c_inh>=0 || c_inh<=1, 'iosr:lindemannInh:invalidX', 'c_inh must be in the interval (0,1].')
    end

    % gain
    c_gamma_L = calc_gamma(L,dim);
    c_gamma_R = calc_gamma(R,dim);
    c_gamma = max([c_gamma_L(:); c_gamma_R(:)]);

    % apply parameters
    L_l = apply_gamma(L_l,c_inh,c_gamma);
    R_l = apply_gamma(R_l,c_inh,c_gamma);

    % restrict range
    L_l = clip(L_l);
    R_l = clip(R_l);

end

function y = hwr(x)
%HWR half-wave rectify

    y = max(x,0);

end

function gamma = calc_gamma(x,dim)
%CALC_GAMMA calculate the gamma

    gamma = sqrt(2).*iosr.dsp.rms(x,dim);

end

function y = apply_gamma(x,c_inh,c_gamma)
%APPLY_GAMMA apply gamma to achieve inhibition parameter

    y = (c_inh/c_gamma).*x;

end

function y = clip(x)
%CLIP clip input data to [0,1] interval

    y = x;
    y(y<0) = 0;
    y(y>1) = 1;

end


function [passed_clusters] = plot(this,p_skip,cl,cluster_draw_thresh,only_cluster)

% plotDatabase(this,p_skip,cl,cluster_draw_thresh,only_cluster)
%
% this is pathway database
% 'cl' is vector with clustered path labels

if ~exist('p_skip','var')
    p_skip=1;
end
if ~exist('only_cluster','var')
    only_cluster = 0;
end
if ~exist('cluster_draw_thresh','var')
    cluster_draw_thresh = 0;
end

if exist('cl','var')
    cluster_sizes = hist(cl,[1:max(cl)]);
    npaths = sum(cluster_sizes);
    if (cluster_draw_thresh == 0)
        cluster_draw_thresh = floor(0.05*npaths);
    end
    n = sum(cluster_sizes > cluster_draw_thresh);
    passed_clusters = find(cluster_sizes > cluster_draw_thresh);
else
    passed_clusters = 0;
end

hold off;

if exist('cl','var')
    % Iterate over all clusters
    for c = 1:length(passed_clusters)
        ci = find( cl == passed_clusters(c) );
        % Iterate over all paths in clusters
        for i = 1:length(ci)
            p = ci(i);
            npts = length(this.pathways(p).xpos(1:p_skip:end));
            % Maybe I am supposed to only draw one particular cluster out of
            % the ones that passed
            if ( only_cluster == 0 || cl(p) == only_cluster)
                label = ones(1,npts)*c;
                tubeplot(this.pathways(p).xpos(1:p_skip:end),this.pathways(p).ypos(1:p_skip:end),this.pathways(p).zpos(1:p_skip:end),0.5,label);
                hold on;
            end
        end
    end
    colormap( jet );
    caxis( [1 length(passed_clusters)+1] );
else
    % Setting up rendering parameters
    for p = 1:length(this.pathways)
%         if p > 500
%             p
%         end
        if(rand > 0 && length(this.pathways(p).xpos) >= 10)
            %tubeplot(pd(p).xpos,pd(p).ypos,pd(p).zpos,0.5,pd(p).xpos,10);
            %tubeplot(pd(p).xpos,pd(p).ypos,pd(p).zpos,0.5,ones(length(pd(p
            %).xpos),1));
            tubeplot(this.pathways(p).xpos(1:p_skip:end),this.pathways(p).ypos(1:p_skip:end),this.pathways(p).zpos(1:p_skip:end),0.5);
            hold on;
        end
    end
    colormap(jet);
end

axis([0 73*2 0 98*2 0 66*2])
daspect([1 1 1])

lighting phong;
hidden off;
%shading interp;

%plot3(45,0,50,'ro','LineWidth',10);


%GETNAME High level routine for finding names of datasets and classifiers
%
%   NAME = getname(A,N)
%
% INPUT
%   A    Dataset, mapping or cell array of datasets or mappings
%   N    Number of characters in NAME (default: all)
%
% OUTPUT
%   NAME  Dataset name or cell array of names
%
% DESCRIPTION
% If N given, the return string has exactly N characters. This is done by
% truncation or by padding with blanks. This is useful for display purposes.
% SEE ALSO (PRTools Guide)
% DATASETS, MAPPINGS

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com

function name = getname(c,n)

  if nargin < 2, n = []; end
  if iscell(c)
    name = cell(1,numel(c));
    for j=1:numel(c)
      name{j} = getname(c{j},n);
    end
  elseif isa(c,'prdataset') || ismapping(c)
    % this will go to dataset or mapping getname
    name = getname(c,n);
  else
    error('Illegal data type')
  end

return


function testFliplrMatrix
%testFliplrMatrix Unit test for fliplr with matrix input

in = magic(3);
assertEqual(fliplr(in), in(:, [3 2 1]));


function out=sparseconv(x,f)

% SPARSECONV Speedy convolution of sparse vectors
%
%    OUT = SPARSECONV(X, F) computes the convolution of the
%    (sparse) input vectors X and F.
%
%    Gert Cuypers 4/10/2001
%    Esat Sista KULeuven

[rij,kol]=size(x);
if kol>1
   x=x.';
end
[rij,kol]=size(x);
if kol>1
   error('x moet vector zijn')
end

[rij,kol]=size(f);
if kol>1
   f=f.';
end

[rij,kol]=size(f);
if kol>1
   error('f moet vector zijn');
end
xs=sparse(x);
[xrow, xcol, xval]=find(xs);
nietnulx=length(xrow);
fs=sparse(f);
[frow, fcol, fval]=find(fs);
nietnulf=length(frow);
xpos=zeros(nietnulf*nietnulx,1);
ypos=zeros(nietnulf*nietnulx,1);
elementen=zeros(nietnulf*nietnulx,1);
dummy=(frow)*ones(1,nietnulx);
ypos(:)=dummy;
xpos(:)=dummy+ones(nietnulf,1)*xrow.'-1;
elementen(:)=ones(nietnulf,1)*xval.';
out=full(sparse(xpos,ypos,elementen,length(x)+length(f)-1,length(f))*fs);