function [y]=tt_mvk4(A, x, tol, varargin)

% step_dpow = 0.1; % stepsize for d-power in truncations
% min_dpow = 1; % Minimal d-power for truncation
%
% bot_conv = 0.1; % bottom convergence factor - if better, we can decrease dpow and drank
% top_conv = 0.99; % top convergence factor - if worse, we have to increase dpow and drank

nswp=10;

als_tol_low = 5;
als_iters = 4;

rmax=1000;

verb=1;
kickrank = 10;
 kicktype = 'mr';
% kicktype = 'rand';

% pcatype = 'svd';
pcatype = 'uchol';

y=[];
block_order = [];

for i=1:2:length(varargin)-1
    switch lower(varargin{i})
        case 'nswp'
            nswp=varargin{i+1};
        case 'rmax'
            rmax=varargin{i+1};
        case 'kicktype'
            kicktype=lower(varargin{i+1});
        case 'pcatype'
            pcatype=lower(varargin{i+1});
        case 'y0'
            y=varargin{i+1};
        case 'verb'
            verb=varargin{i+1};
        case 'kickrank'
            kickrank=varargin{i+1};
        case 'step_dpow'
            step_dpow=varargin{i+1};
        case 'min_dpow'
            min_dpow=varargin{i+1};
        case 'bot_conv'
            bot_conv=varargin{i+1};
        case 'top_conv'
            top_conv=varargin{i+1};
        case 'block_order'
            block_order=varargin{i+1};
        case 'als_tol_low'
            als_tol_low=varargin{i+1};
        case 'als_iters'
            als_iters=varargin{i+1};

        otherwise
            error('Unrecognized option: %s\n',varargin{i});
    end
end

rx = x.r;
d = x.d;
tailranks = [false,false];
if (rx(1)~=1)
    e1 = tt_tensor;
    e1.d=1;
    e1.n = [rx(1)];
    e1.r = [1; rx(1)];
    e1.ps = [1; rx(1)^2+1];
    e1.core = reshape(eye(rx(1)), rx(1)^2, 1);
    x = kron(e1, x);

    A = kron(tt_matrix(eye(rx(1))), A);
    tailranks(1)=true;
end;
if (rx(d+1)~=1)
    e1 = tt_tensor;
    e1.d=1;
    e1.n = [rx(d+1)];
    e1.r = [rx(d+1); 1];
    e1.ps = [1; rx(d+1)^2+1];
    e1.core = reshape(eye(rx(d+1)), rx(d+1)^2, 1);
    x = kron(x, e1);

    A = kron(A, tt_matrix(eye(rx(d+1))));
    tailranks(2)=true;
end;

d = x.d;
rx = x.r;
ra = A.r;
n = A.n;
m = A.m;
if (isempty(y))
%     y = tt_rand(n, A.d, kickrank);
    y = tt_ones(n, A.d);
end;

if (isempty(block_order))
    block_order = [+(d), -(d)];
end;

ry = y.r;

cry = core2cell(y);
crA = core2cell(A);
crx = core2cell(x);

phia = cell(d+1,1); phia{1}=1; phia{d+1}=1;

% Orthogonalization
for i=d:-1:2
    cr = cry{i};
    cr = reshape(cr, ry(i), n(i)*ry(i+1));
    [cr, rv]=qr(cr.', 0);
    cr2 = cry{i-1};
    cr2 = reshape(cr2, ry(i-1)*n(i-1), ry(i));
    cr2 = cr2*(rv.');
    ry(i) = size(cr, 2);
    cr = reshape(cr.', ry(i), n(i), ry(i+1));
%     psy(i) = psy(i+1)-ry(i)*n(i)*ry(i+1);
%     cry(psy(i):(psy(i+1)-1))=cr(:);
%     psyo(i) = psyo(i-1)+ry(i-1)*n(i-1)*ry(i);
%     cry(psyo(i-1):(psyo(i)-1)) = cr2(:);
    cry{i-1} = reshape(cr2, ry(i-1), n(i-1), ry(i));
    cry{i} = cr;

    phia{i} = compute_next_Phi(phia{i+1}, cr, crA{i}, crx{i}, 'rl');
%     phia{i} = compute_next_Phi(phia{i+1}, cr, reshape(cra(psa(i):(psa(i+1)-1)), ra(i), n(i), n(i), ra(i+1)), reshape(crx(psx(i):(psx(i+1)-1)), rx(i), n(i), rx(i+1)), 'rl');

end;
% cry = cry(psy(1):(psy(d+1)-1));

last_sweep = false;
swp = 1;
i = 1;

% dy_old = ones(d,1);
dy = zeros(d,1);
max_dy = 0;
% For extra-rank addition
% dpows = ones(d,1)*min_dpow;
% dranks = zeros(d,1);

cur_order = block_order;
order_index = 1;
dir = sign(cur_order(order_index));

% DMRG sweeps
while (swp<=nswp)

    % Extract elements - matrix
    Phi1 = phia{i}; Phi2 = phia{i+1};
%     A1 = reshape(cra(psa(i):(psa(i+1)-1)), ra(i), n(i), n(i), ra(i+1));
%     x1 = reshape(crx(psx(i):(psx(i+1)-1)), rx(i), n(i), rx(i+1));
    x1 = crx{i};
    A1 = crA{i};

    newy = reshape(Phi1, ry(i)*ra(i), rx(i));
    newy = newy*reshape(x1, rx(i), m(i)*rx(i+1));
    newy = reshape(newy, ry(i), ra(i)*m(i), rx(i+1));
    newy = permute(newy, [2, 1, 3]);
    newy = reshape(newy, ra(i)*m(i), ry(i)*rx(i+1));
    A1 = permute(A1, [2, 4, 1, 3]);
    A1 = reshape(A1, n(i)*ra(i+1), ra(i)*m(i));
    newy = A1*newy;
    newy = reshape(newy, n(i), ra(i+1), ry(i), rx(i+1));
    newy = permute(newy, [3, 1, 2, 4]);
    newy = reshape(newy, ry(i)*n(i), ra(i+1)*rx(i+1));
    if (dir>0)&&(strcmp(kicktype, 'mr'))
        newy_save = newy;
    end;
    newy = newy*(reshape(Phi2, ry(i+1), ra(i+1)*rx(i+1)).');
    newy = reshape(newy, ry(i)*n(i)*ry(i+1), 1);

    oldy = reshape(cry{i}, ry(i)*n(i)*ry(i+1), 1);

    dy(i) = norm(newy-oldy)/norm(newy);
    max_dy = max(max_dy, dy(i));
%
%     % The new core does not converge - increase rank
%     if (dy(i)/dy_old(i)>top_conv)&&(dy(i)>tol)
%         dranks(i)=dranks(i)+1;
%         dpows(i)=dpows(i)+step_dpow;
%     end;
%     % The new core converges well - try to decrease rank
%     if (dy(i)/dy_old(i)<bot_conv)||(dy(i)<tol)
%         dranks(i)=max(dranks(i)-1, 0);
%         dpows(i)=max(dpows(i)-step_dpow, min_dpow);
%     end;
%
%     if (last_sweep)
%         dpows(i)=0.5;
%         dranks(i)=0;
%     end;

    % Truncation
    if (dir>0) % left-to-right
        newy = reshape(newy, ry(i)*n(i), ry(i+1));
    else
        newy = reshape(newy, ry(i), n(i)*ry(i+1));
    end;

    if (kickrank>=0)
    [u,s,v]=svd(newy, 'econ');
    s = diag(s);
    r = my_chop2(s, tol/sqrt(d)*norm(s));
%     % Artificial rank increasing
%     r = r+dranks(i);
    r = min(r, numel(s));
    r = min(r, rmax);
    else
        if (dir>0)
            [u,v]=qr(newy, 0);
            v=v';
            r = size(u,2);
            s = ones(r,1);
        else
            [v,u]=qr(newy.', 0);
            v=conj(v);
            u=u.';
            r = size(u,2);
            s = ones(r,1);
        end;
    end;

    if (verb>1)
	fprintf('=mvk4=   block %d{%d}, dy: %3.3e, r: %d\n', i, dir, dy(i), r);
%     elseif (verb>0)
%         fprintf('                                                                          \r')
%         fprintf('=mvk4=   block %d{%d}, dy: %3.3e, r: %d\r', i, dir, dy(i), r);
    end;

    if (dir>0)&&(i<d) % left-to-right, kickrank, etc
        u = u(:,1:r);
        v = v(:,1:r)*diag(s(1:r));

        % kick
        radd = 0; rv = 1;
        if (~last_sweep)&&(kickrank>0)
%             newy = reshape(Phi1, ry(i)*ra(i), rx(i));
%             newy = newy*reshape(crx{i}, rx(i), n(i)*rx(i+1));
%             newy = reshape(newy, ry(i), ra(i)*n(i), rx(i+1));
%             newy = permute(newy, [2, 1, 3]);
%             newy = reshape(newy, ra(i)*n(i), ry(i)*rx(i+1));
%             A1 = permute(crA{i}, [2, 4, 1, 3]);
%             A1 = reshape(A1, n(i)*ra(i+1), ra(i)*n(i));
%             newy = A1*newy;
%             newy = reshape(newy, n(i), ra(i+1), ry(i), rx(i+1));
%             newy = permute(newy, [3, 1, 2, 4]);
%             newy = reshape(newy, ry(i)*n(i), ra(i+1)*rx(i+1));
            if (strcmp(kicktype, 'mr'))
                leftresid = [reshape(u*v', ry(i)*n(i), ry(i+1)), -newy_save];
                if (strcmp(pcatype, 'svd'))
                    [uk,sk,vk]=svd(leftresid, 'econ');
                    uk = uk(:,1:min(kickrank, size(uk,2)));
                else
                    uk = uchol(leftresid.', kickrank*2);
                    uk = uk(:,size(uk,2):-1:max(size(uk,2)-kickrank+1,1));
                end;

%                 if (i+1<d)
%                     newy = reshape(phia{i+3}, ry(i+3)*ra(i+3), rx(i+3));
%                     newy = newy*reshape(crx{i+2}, rx(i+2)*n(i+2), rx(i+3)).';
%                     newy = reshape(newy, ry(i+3), ra(i+3), rx(i+2), n(i+2));
%                     newy = permute(newy, [2, 4, 1, 3]);
%                     newy = reshape(newy, ra(i+3)*n(i+2), ry(i+3)*rx(i+2));
%                     A1 = permute(crA{i+2}, [2, 1, 4, 3]);
%                     A1 = reshape(A1, n(i+2)*ra(i+2), ra(i+3)*n(i+2));
%                     newy = A1*newy;
%                     newy = reshape(newy, n(i+2), ra(i+2), ry(i+3), rx(i+2));
%                     newy = permute(newy, [1, 3, 2, 4]);
%                     newy = reshape(newy, n(i+2)*ry(i+3), ra(i+2)*rx(i+2));
%
%                     oldy = reshape(cry{i+2}, ry(i+2), n(i+2)*ry(i+3)).';
%                     rightresid = [oldy, -newy];
%                     vk = uchol(rightresid.', kickrank*2);
%                     vk = vk(:,size(vk,2):-1:max(size(vk,2)-kickrank+1,1));
%
%                     [oldy,rv]=qr([oldy,vk], 0);
%                     newy = reshape(cry{i+1}, ry(i+1)*n(i+1), ry(i+2));
%                     newy = [newy, zeros(ry(i+1)*n(i+1), size(vk,2))];
%                     newy = newy*(rv.');
%                     ry(i+2) = size(oldy,2);
%                     cry{i+1} = reshape(newy, ry(i+1), n(i+1), ry(i+2));
%                     cry{i+2} = permute(reshape(oldy, n(i+2), ry(i+3), ry(i+2)), [3, 1, 2]);
%                     phia{i+2} = compute_next_Phi(phia{i+3}, cry{i+2}, crA{i+2}, crx{i+2}, 'rl');
%                 end;
            else
                uk = rand(ry(i)*n(i), kickrank);
            end;
            [u,rv]=qr([u,uk], 0);
            radd = size(uk,2);
        end;
%         radd = size(u, 2)+size(uk,2)-r;
        v = [v, zeros(ry(i+1), radd)];
        v = rv*(v');
        r = size(u,2);
%         r = r+radd;
        cr2 = cry{i+1};
        cr2 = reshape(cr2, ry(i+1), n(i+1)*ry(i+2));
        v = v*cr2; % size r+radd, n2, r3

%         y{i} = reshape(u, ry(i), n(i), r);
%         y{i+1} = reshape(v, r, n(i+1), ry(i+2));
        ry(i+1) = r;

%         % Add residual component
%         rescomp = als_rank_one(x, 'A', A, 'f', y);
%         % We have to update ALL phis
%         Rl = [1,1];
%         for j=1:i
%             addbas = rescomp{i};
%             addbas = addbas/norm(addbas);
%             newbas = zeros(ry(i)+1, n(i), ry(i+1)+1);
%             newbas(1:ry(i), :, 1:ry(i+1))=y{i};
%             newbas(ry(i)+1, :, ry(i+1)+1)=addbas;
%             newbas = reshape(newbas, ry(i)+1, n(i)*(ry(i+1)+1));
%             newbas = Rl*newbas;
%             ry(i) = size(Rl, 1);
%             newbas = reshape(newbas, ry(i)*n(i), ry(i+1)+1);
%             % We have to orthogonalize it. approximately, if j<i
%             proj = newbas(:,ry(i+1)+1) - newbas(:,1:ry(i+1))*(newbas(:,1:ry(i+1))'*newbas(:,ry(i+1)+1));
%             if (i<j)&&(norm(proj)<tol/(d^dpows(i)))
%                 % Remove the new vector
%                 newbas2 = newbas(:, 1:ry(i+1));
%                 Rl = [eye(ry(i+1)); zeros(1,ry(i+1))];
%             else
%                 % Reort the new vector to the old one
%                 newbas2 = reort(newbas(:,1:ry(i+1)), newbas(:, ry(i+1)+1));
%                 Rl = newbas2'*newbas;
%             end;
%             radd = size(newbas2, 2)-ry(i+1);
%             y{i} = reshape(newbas2, ry(i), n(i), ry(i+1)+radd);
%             if (radd>0) % update phi
%                 addbas = reshape(newbas2(:, ry(i+1)+1), ry(i), n(i), 1);
%                 newphia = zeros(ry(i+1)+1, ra(i+1), rx(i+1));
%                 newphia(1:ry(i+1),:,:)=phia{i+1};
%                 newphia(ry(i+1)+1,:,:) = compute_next_Phi(phia{i}, addbas, A{i}, x{i}, 'lr');
%                 phia{i+1} = newphia;
%             end;
%         end;
%
%         Rr = [1;1];
%         for j=d:-1:i+2
%             addbas = rescomp{i};
%             addbas = addbas/norm(addbas);
%             newbas = zeros(ry(i)+1, n(i), ry(i+1)+1);
%             newbas(1:ry(i), :, 1:ry(i+1))=y{i};
%             newbas(ry(i)+1, :, ry(i+1)+1)=addbas;
%             newbas = reshape(newbas, (ry(i)+1)*n(i), (ry(i+1)+1));
%             newbas = newbas*Rr;
%             ry(i+1) = size(Rr, 2);
%             newbas = reshape(newbas, ry(i)+1, n(i)*ry(i+1));
%             newbas = newbas.';
%             % We have to orthogonalize it. approximately, if j<i
%             proj = newbas(:, ry(i)+1) - newbas(:,1:ry(i))*(newbas(:,1:ry(i))'*newbas(:,ry(i)+1));
%             if (i<j)&&(norm(proj)<tol/(d^dpows(i)))
%                 % Remove the new vector
%                 newbas2 = newbas(:, 1:ry(i));
%                 Rr = [eye(ry(i)), zeros(ry(i), 1)];
%             else
%                 % Reort the new vector to the old one
%                 newbas2 = reort(newbas(:,1:ry(i)), newbas(:, ry(i)+1));
%                 Rr = newbas2'*newbas;
%                 Rr = Rr.';
%             end;
%             radd = size(newbas2, 2)-ry(i);
%             y{i} = reshape(newbas2.', ry(i)+radd, n(i), ry(i+1));
%             if (radd>0) % update phi
%                 addbas = reshape(newbas2(:, ry(i)+1).', 1, n(i), ry(i+1));
%                 newphia = zeros(ry(i)+1, ra(i), rx(i));
%                 newphia(1:ry(i),:,:)=phia{i};
%                 newphia(ry(i)+1,:,:) = compute_next_Phi(phia{i+1}, addbas, A{i}, x{i}, 'lr');
%                 phia{i} = newphia;
%             end;
%         end;

        u = reshape(u, ry(i), n(i), r);
        v = reshape(v, r, n(i+1), ry(i+2));

        % Recompute phi. Left ones, so permute them appropriately
        phia{i+1} = compute_next_Phi(phia{i}, u, crA{i}, crx{i}, 'lr');

        % Stuff back

        cry{i} = u;
        cry{i+1} = v;
    elseif (dir<0)&&(i>1) % right-to-left
        u = u(:,1:r)*diag(s(1:r));
        v = conj(v(:,1:r));
        % kick
        radd = 0; rv = 1;
%          if (~last_sweep)&&(strcmp(kicktype, 'rand'))&&(kickrank>0)
%  %             newy = reshape(Phi2, ry(i+1)*ra(i+1), rx(i+1));
%  %             newy = newy*reshape(crx{i}, rx(i)*n(i), rx(i+1)).';
%  %             newy = reshape(newy, ry(i+1), ra(i+1), rx(i), n(i));
%  %             newy = permute(newy, [2, 4, 1, 3]);
%  %             newy = reshape(newy, ra(i+1)*n(i), ry(i+1)*rx(i));
%  %             A1 = permute(crA{i}, [1, 2, 4, 3]);
%  %             A1 = reshape(A1, ra(i)*n(i), ra(i+1)*n(i));
%  %             newy = A1*newy;
%  %             newy = reshape(newy, ra(i), n(i), ry(i+1), rx(i));
%  %             newy = permute(newy, [1, 4, 2, 3]);
%  %             newy = reshape(newy, ra(i)*rx(i), n(i)*ry(i+1));
%  %             resid = [reshape(u*v', ry(i), n(i)*ry(i+1)); -newy];
%  %             uk = uchol(resid, kickrank+1);
%  %             uk = uk(:,end:-1:max(end-kickrank+1,1));
%              uk = randn(n(i)*ry(i+1), kickrank);
%              [v,rv]=qr([v,uk], 0);
%              radd = size(uk,2);
%  %             v = reort(v, uk);
%          end;

%  %         radd = size(v, 2)+size(uk,2)-r;
        u = [u, zeros(ry(i), radd)];
        u = u*(rv.');
        cr2 = cry{i-1};
        cr2 = reshape(cr2, ry(i-1)*n(i-1), ry(i));
        u = cr2*u;

%         r = r+radd;
        r = size(v,2);

        u = reshape(u, ry(i-1), n(i-1), r);
        v = reshape(v.', r, n(i), ry(i+1));

        % Recompute phi. Here are right phis
        phia{i} = compute_next_Phi(phia{i+1}, v, crA{i}, crx{i}, 'rl');

        % Stuff back
        ry(i) = r;
        cry{i-1} = u;
        cry{i} = v;
    elseif ((dir>0)&&(i==d))||((dir<0)&&(i==1))
        % Just stuff back the last core
        newy = u(:,1:r)*diag(s(1:r))*(v(:,1:r)');
        newy = reshape(newy, ry(i), n(i), ry(i+1));
        cry{i} = newy;
    end;

    i = i+dir;

    % Reversing, residue check, etc
    cur_order(order_index) = cur_order(order_index) - dir;
    % New direction
    if (cur_order(order_index)==0)
        order_index = order_index+1;

        if (verb>0)
%  	    fprintf('                                                   \r')
            fprintf('=mvk4= sweep %d{%d}, max_dy: %3.3e, erank: %g\n', swp, order_index-1, max_dy, sqrt(ry(1:d)'*(n.*ry(2:d+1))/sum(n)));
        end;

        if (last_sweep)
            break;
        end;

        if (kickrank<0)
            kickrank=kickrank-1;
        end;

        if (max_dy<tol*als_tol_low)&&(kickrank>=0)
            kickrank=-1;
        end;

        if (max_dy<tol)&&(kickrank<=-als_iters)
            last_sweep=true;
            kickrank=0;
        end;

        if (order_index>numel(cur_order)) % New global sweep
            cur_order = block_order;
            order_index = 1;
            %residue
            if (last_sweep)
                cur_order = d-1;
            end;

            max_dy = 0;

%             dy_old = dy;
            swp = swp+1;
        end;

        dir = sign(cur_order(order_index));
        i = i+dir;
    end;
end

if (tailranks(1))
    cry{2} = reshape(cry{1}, n(1), ry(2))*reshape(cry{2}, ry(2), n(2)*ry(3));
    cry{2} = reshape(cry{2}, n(1), n(2), ry(3));
end;
if (tailranks(2))
    cry{d-1} = reshape(cry{d-1}, ry(d-1)*n(d-1), ry(d))*reshape(cry{d}, ry(d), n(d));
    cry{d-1} = reshape(cry{d-1}, ry(d-1), n(d-1), n(d));
    cry = cry(1:d-1);
end;
if (tailranks(1))
    cry = cry(2:end);
end;
y = cell2core(y, cry);

end

function [Phi] = compute_next_Phi(Phi_prev, x, A, y, direction)
% Performs the recurrent Phi (or Psi) matrix computation
% Phi = Phi_prev * (x'Ay).
% If direction is 'lr', computes Psi
% if direction is 'rl', computes Phi
% A can be empty, then only x'y is computed.

if (strcmp(direction, 'rl'))
  % Revert ranks to perform the right-to-left recursion
  x = permute(x, [3, 2, 1]);
  y = permute(y, [3, 2, 1]);
  if (~isempty(A))
    A = permute(A, [4, 2, 3, 1]);
  end
end

rx1 = size(x,1); n = size(x,2); rx2 = size(x,3);
ry1 = size(y,1); m = size(y,2); ry2 = size(y,3);
if (~isempty(A))
  ra1 = size(A,1); ra2 = size(A,4);
else
  ra1 = 1; ra2 = 1;
end

Phi = reshape(Phi_prev, [rx1*ra1, ry1]);
y = reshape(y, [ry1, m*ry2]);
Phi = Phi*y;	% complexity §\mcommentfont$\mathcal{O}(n  r_x r_A r_y^2)$§
Phi = reshape(Phi, [rx1, ra1, m, ry2]);
Phi = permute(Phi, [2, 3, 1, 4]);
if (~isempty(A))
  Phi = reshape(Phi, [ra1*m, rx1*ry2]);
  A = permute(A, [4, 2, 1, 3]);
  A = reshape(A, [ra2*n, ra1*m]);
  Phi = A*Phi;	% complexity §\mcommentfont$\mathcal{O}(n^2  r_x r_A^2 r_y)$§
  Phi = reshape(Phi, [ra2, n, rx1, ry2]);
end
Phi = permute(Phi, [3, 2, 1, 4]);
Phi = reshape(Phi, [rx1*n, ra2*ry2]);
x = reshape(x, [rx1*n, rx2]);
Phi = (x')*Phi;	% complexity §\mcommentfont$\mathcal{O}(n  r_x^2 r_A r_y)$§
if (~isempty(A))
  Phi = reshape(Phi, [rx2, ra2, ry2]);
end
end


classdef prtClassRvmSequential < prtClassRvm
    % prtClassRvmSequential  Relevance vector machine classifier using sequential training
    %
    %   CLASSIFIER = prtClassRvmSequential returns a relevance vector
    %   machine classifier based using sequential training.
    %
    %   CLASSIFIER = prtClassRvmSequential(PROPERTY1, VALUE1, ...)
    %   constructs a prtClassRvmSequential object CLASSIFIER with properties as
    %   specified by PROPERTY/VALUE pairs.
    %
    %    A prtClassRvmSequential object inherits all properties from the abstract class
    %    prtClass. In addition is has the following properties:
    %
    %    kernels                - A cell array of prtKernel objects specifying
    %                             the kernels to use
    %    verbosePlot            - Flag indicating whether or not to plot during
    %                             training
    %    verboseText            - Flag indicating whether or not to output
    %                             verbose updates during training
    %    learningMaxIterations  - The maximum number of iterations
    %
    %    A prtClassRvmSequential also has the following read-only properties:
    %
    %    learningConverged  - Flag indicating if the training converged
    %    beta               - The regression weights, estimated during training
    %    sparseBeta         - The sparse regression weights, estimated during
    %                         training
    %    sparseKernels      - The sparse regression kernels, estimated during
    %                         training
    %
    %   For more information on the algorithm and the above properties, see
    %   the following reference:
    %
    %   Tipping, M. E. and A. C. Faul (2003). Fast marginal likelihood
    %   maximisation for sparse Bayesian models. In C. M. Bishop and B. J.
    %   Frey (Eds.), Proceedings of the Ninth International Workshop on
    %   Artificial Intelligence and Statistics, Key West, FL, Jan 3-6.
    %
    %   prtClassRvmSequential is most useful for datasets with a large
    %   number of observations for which the gram matrix can not be held in
    %   memory. The sequential RVM training algorithm is capable of
    %   operating by generating necessary portions of the gram matrix when
    %   needed. The size of the generated portion of the gram matrix is
    %   determined by the property, largestNumberOfGramColumns. Sequential
    %   RVM training will attempt to generate portions of the gram matrix
    %   that are TraingData.nObservations x largesNumberofGramColums in
    %   size. If the entire gram matrix is this size or smaller it need
    %   only be generated once. Therefore if the entire gram matrix can be
    %   stored in memory, training is much faster. For quickest operation,
    %   largestNumberOfGramColumns should be set as large as possible
    %   without exceeding RAM limitations.
    %
    %    Example:
    %
    %    TestDataSet = prtDataGenUnimodal;      % Create some test and
    %    TrainingDataSet = prtDataGenUnimodal;  % training data classifier
    %    classifier = prtClassRvmSequential('verbosePlot',true); % Create a classifier
    %    classifier = classifier.train(TrainingDataSet);    % Train
    %    classified = run(classifier, TestDataSet);         % Test
    %    % Plot
    %    subplot(2,1,1); classifier.plot;
    %    subplot(2,1,2); [pf,pd] = prtScoreRoc(classified,TestDataSet);
    %    h = plot(pf,pd,'linewidth',3);
    %    title('ROC'); xlabel('Pf'); ylabel('Pd');
    %
    %   See also prtClass, prtClassRvm, prtClassRvnFiguerido,
    %   prtRegressRvmSequential

    properties

        learningPoorlyScaledLikelihoodThreshold = 1e4;
        learningLikelihoodIncreaseThreshold = 1e-6;
        largestNumberOfGramColumns = 5000;
        learningCorrelationRemovalThreshold = 0.99;
        learningFactorRemove = true;   % Remove kernels during train?
        learningRepeatedActionLimit = 25;
    end

    properties(Hidden = true)
        learningResults
    end

    properties (Hidden = true)
        Sigma = [];
    end

    methods
        function Obj = prtClassRvmSequential(varargin)
            Obj = prtUtilAssignStringValuePairs(Obj,varargin{:});
        end

        function Obj = set.learningPoorlyScaledLikelihoodThreshold(Obj,val)
            assert(prtUtilIsPositiveScalar(val),'prt:prtClassRvmSequential:learningPoorlyScaledLikelihoodThreshold','learningPoorlyScaledLikelihoodThreshold must be a positive scalar');
            Obj.learningPoorlyScaledLikelihoodThreshold = val;
        end
        function Obj = set.learningLikelihoodIncreaseThreshold(Obj,val)
            assert(prtUtilIsPositiveScalar(val),'prt:prtClassRvmSequential:learningLikelihoodIncreaseThreshold','learningLikelihoodIncreaseThreshold must be a positive scalar');
            Obj.learningLikelihoodIncreaseThreshold = val;
        end
        function Obj = set.largestNumberOfGramColumns(Obj,val)
            assert(prtUtilIsPositiveScalarInteger(val),'prt:prtClassRvmSequential:largestNumberOfGramColumns','largestNumberOfGramColumns must be a positive integer scalar');
            Obj.largestNumberOfGramColumns = val;
        end
        function Obj = set.learningCorrelationRemovalThreshold(Obj,val)
            assert(prtUtilIsPositiveScalar(val),'prt:prtClassRvmSequential:learningCorrelationRemovalThreshold','learningCorrelationRemovalThreshold must be a positive scalar');
            Obj.learningCorrelationRemovalThreshold = val;
        end
        function Obj = set.learningFactorRemove(Obj,val)
            assert(prtUtilIsLogicalScalar(val),'prt:prtClassRvmSequential:learningFactorRemove','learningFactorRemove must be a logical scalar');
            Obj.learningFactorRemove = val;
        end
        function Obj = set.learningRepeatedActionLimit(Obj,val)
            assert(prtUtilIsPositiveScalarInteger(val),'prt:prtClassRvmSequential:learningRepeatedActionLimit','learningRepeatedActionLimit must be a positive integer scalar');
            Obj.learningRepeatedActionLimit = val;
        end
    end

    methods (Access=protected, Hidden = true)
        function Obj = trainAction(Obj,DataSet)
            %Rvm = trainAction(Rvm,DataSet) (Private; see prtClass\train)

            warningState = warning;
            %warning off MATLAB:nearlySingularMatrix

            y = Obj.getMinusOneOneTargets(DataSet);

            localKernels = Obj.kernels.train(DataSet);
            nBasis = localKernels.nDimensions;

            if false && nBasis <= Obj.largestNumberOfGramColumns
                Obj = trainActionSequentialInMemory(Obj, DataSet, y);
                return
            end

            if Obj.verboseText
                fprintf('Sequential RVM training with %d possible vectors.\n', nBasis);
            end

            % The sometimes we want y [-1 1] but mostly not
            ym11 = y;
            y(y ==-1) = 0;

            Obj.beta = zeros(nBasis,1);

            relevantIndices = false(nBasis,1); % Nobody!
            alpha = inf(nBasis,1); % Nobody!
            forbidden = zeros(nBasis,1); % Will hold who is forbidding you from joining

            % Find first kernel
            kernelCorrs = zeros(nBasis,1);
            nBlocks = ceil(nBasis./ Obj.largestNumberOfGramColumns);
            for iBlock = 1:nBlocks
                cInds = ((iBlock-1)*Obj.largestNumberOfGramColumns+1):min([iBlock*Obj.largestNumberOfGramColumns nBasis]);

                trainedKernelDownSelected = localKernels.retainKernelDimensions(cInds);
                blockPhi = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);
                blockPhiNormalized = bsxfun(@rdivide,blockPhi,sqrt(sum(blockPhi.*blockPhi))); % We have to normalize here

                kernelCorrs(cInds) = abs(blockPhiNormalized'*ym11);
            end
            [maxVal, maxInd] = max(kernelCorrs); %#ok<ASGLU>

            % Make this ind relevant
            relevantIndices(maxInd) = true;
            selectedInds = maxInd;

            % Add things to forbidden list
            initLogical = false(nBasis,1);
            initLogical(maxInd) = true;
            firstKernel = localKernels.retainKernelDimensions(initLogical);
            firstPhiNormalized = firstKernel.run_OutputDoubleArray(DataSet);
            firstPhiNormalized = firstPhiNormalized - mean(firstPhiNormalized);
            firstPhiNormalized = firstPhiNormalized./sqrt(sum(firstPhiNormalized.^2));
            phiCorrs = zeros(nBasis,1);
            for iBlock = 1:nBlocks
                cInds = ((iBlock-1)*Obj.largestNumberOfGramColumns+1):min([iBlock*Obj.largestNumberOfGramColumns nBasis]);

                trainedKernelDownSelected = localKernels.retainKernelDimensions(cInds);
                if nBlocks > 1 % If there is only one block we can keep the one from before
                    blockPhi = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);
                end
                blockPhiDemeanedNormalized = bsxfun(@minus,blockPhi,mean(blockPhi));
                blockPhiDemeanedNormalized = bsxfun(@rdivide,blockPhiDemeanedNormalized,sqrt(sum(blockPhiDemeanedNormalized.*blockPhiDemeanedNormalized))); % We have to normalize here

                phiCorrs(cInds) = blockPhiDemeanedNormalized'*firstPhiNormalized;
            end
            forbidden(phiCorrs > Obj.learningCorrelationRemovalThreshold) = maxInd;

            % Start the actual Process
            if Obj.verboseText
                fprintf('\t Iteration 0: Intialized with vector %d.\n', maxInd);

                nVectorsStringLength = ceil(log10(length(nBasis)))+1;
            end

            if nBlocks == 1
                % If we have only 1 block we only need to do this once.
                trainedKernelDownSelected = localKernels.retainKernelDimensions(true(nBasis,1));
                PhiM = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);
            end

            % Get the first relevant kernel matrix
            trainedKernelDownSelected = localKernels.retainKernelDimensions(relevantIndices);
            cPhi = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);

            repeatedActionCounter = 0;
            for iteration = 1:Obj.learningMaxIterations

                % Store old log Alpha
                logAlphaOld = log(alpha);

                if iteration == 1
                    % Initial estimates
                    % Estimate Sigma, mu etc.

                    logOut = (ym11*0.9+1)/2;
                    mu = cPhi \ log(logOut./(1-logOut));

                    alpha(relevantIndices) = 1./mu.^2;

                    % Laplacian approx. IRLS
                    A = diag(alpha(relevantIndices));

                    [mu, SigmaInvChol, obsNoiseVar] = prtUtilPenalizedIrls(y,cPhi,mu,A);

                    SigmaChol = inv(SigmaInvChol);
                    Obj.Sigma = SigmaChol*SigmaChol'; %#ok<MINV>

                    yHat = 1 ./ (1+exp(-cPhi*mu));
                end

                % Eval additions and subtractions
                Sm = zeros(nBasis,1);
                Qm = zeros(nBasis,1);
                cError = y-yHat;

                cPhiProduct = bsxfun(@times,cPhi,obsNoiseVar);
                for iBlock = 1:nBlocks
                    cInds = ((iBlock-1)*Obj.largestNumberOfGramColumns+1):min([iBlock*Obj.largestNumberOfGramColumns nBasis]);

                    if nBlocks > 1 % If there is only one block we can keep the on from before
                        trainedKernelDownSelected = localKernels.retainKernelDimensions(cInds);
                        PhiM = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);
                    end

                    Sm(cInds) = (obsNoiseVar'*(PhiM.^2)).' - sum((PhiM.'*cPhiProduct*SigmaChol).^2,2);
                    Qm(cInds) = PhiM.'*cError;
                end

                % Find little sm and qm (these are different for relevant vectors)
                sm = Sm;
                qm = Qm;

                cDenom = (alpha(relevantIndices)-Sm(relevantIndices));
                sm(relevantIndices) = alpha(relevantIndices) .* Sm(relevantIndices) ./ cDenom;
                qm(relevantIndices) = alpha(relevantIndices) .* Qm(relevantIndices) ./ cDenom;

                theta = qm.^2 - sm;
                cantBeRelevent = theta < 0;

                % Addition
                addLogLikelihoodChanges = 0.5*( theta./Sm + log(Sm ./ Qm.^2) ); % Eq (27)
                addLogLikelihoodChanges(cantBeRelevent) = 0; % Can't add things that are disallowed by theta
                addLogLikelihoodChanges(relevantIndices) = 0; % Can't add things already in
                addLogLikelihoodChanges(forbidden > 0) = 0; % Can't add things that are forbidden

                % Removal
                removeLogLikelihoodChanges = -0.5*( qm.^2./(sm + alpha) - log(1 + sm./alpha) ); % Eq (37) (I think this is wrong in the paper. The one in the paper uses Si and Qi, I got this based on si and qi (or S and Q in their code), from corrected from analyzing code from http://www.vectoranomaly.com/downloads/downloads.htm)
                removeLogLikelihoodChanges(~relevantIndices) = 0; % Can't remove things not in
                removeLogLikelihoodChanges(imag(removeLogLikelihoodChanges) > 0) = inf;

                % Modify
                updatedAlpha = sm.^2 ./ theta;
                updatedAlphaDiff = 1./updatedAlpha - 1./alpha;
                modifyLogLikelihoodChanges = 0.5*( updatedAlphaDiff.*(Qm.^2) ./ (updatedAlphaDiff.*Sm + 1) - log(1 + Sm.*updatedAlphaDiff) );

                modifyLogLikelihoodChanges(~relevantIndices) = 0; % Can't modify things not in
                modifyLogLikelihoodChanges(cantBeRelevent) = 0; % Can't modify things that technically shouldn't be in (they would get dropped)

                [addChange, bestAddInd] = max(addLogLikelihoodChanges);
                [remChange, bestRemInd] = max(removeLogLikelihoodChanges);
                [modChange, bestModInd] = max(modifyLogLikelihoodChanges);

                if iteration == 1
                    % On the first iteration we don't allow removal
                    [maxChangeVal, actionInd] = max([addChange, nan, modChange]);
                else
                    if remChange > 0 && Obj.learningFactorRemove
                        % Removing is top priority.
                        % If removing increases the likelihood, we have two
                        % options, actually remove that sample or modify that
                        % sample if that is better
                        [maxChangeVal, actionInd] = max([nan remChange, modifyLogLikelihoodChanges(bestRemInd)]);
                    else
                        % Not going to remove, so we would be allowed to modify
                        [maxChangeVal, actionInd] = max([addChange, remChange, modChange]);
                    end
                end

                if maxChangeVal > Obj.learningPoorlyScaledLikelihoodThreshold
                    warning('prtClassRvm:BadKernelMatrix','Kernel matrix is poorly conditioned. Consider modifying your kernels. Optimization Exiting...' );
                    break
                end

                if maxChangeVal < Obj.learningLikelihoodIncreaseThreshold
                    % There are no good options right now. Therefore we
                    % should exit with the previous iteration stats.
                    Obj.learningConverged = true;
                    Obj.learningResults.exitReason = 'No Good Actions';
                    Obj.learningResults.exitValue = maxChangeVal;
                    if Obj.verboseText
                        fprintf('Convergence criterion met, no necessary actions remaining, maximal change in log-likelihood %g\n\n',maxChangeVal);
                    end

                    break;
                end

                switch actionInd
                    case 1
                        cBestInd = bestAddInd;
                    case 2
                        cBestInd = bestRemInd;
                    case 3
                        cBestInd = bestModInd;
                end
                if iteration > 1 && lastAction == actionInd && cBestInd == lastInd
                    repeatedActionCounter = repeatedActionCounter + 1;
                else
                    repeatedActionCounter = 0;
                end

                if repeatedActionCounter >= Obj.learningRepeatedActionLimit
                    if Obj.verboseText
                        fprintf('Exiting... repeating action limit has been reached.\n\n');
                    end
                    return
                end

                if Obj.verboseText
                    actionStrings = {sprintf('Addition: Vector %s has been added.  ', sprintf(sprintf('%%%dd',nVectorsStringLength),bestAddInd));
                                     sprintf('Removal:  Vector %s has been removed.', sprintf(sprintf('%%%dd',nVectorsStringLength), bestRemInd));
                                     sprintf('Update:   Vector %s has been updated.', sprintf(sprintf('%%%dd',nVectorsStringLength), bestModInd));};
                    fprintf('\t Iteration %d: %s Change in log-likelihood %g.\n',iteration, actionStrings{actionInd}, maxChangeVal);
                end

                lastAction = actionInd;
                switch actionInd
                    case 1 % Add

                        relevantIndices(bestAddInd) = true;
                        selectedInds = cat(1,selectedInds,bestAddInd);

                        alpha(bestAddInd) = updatedAlpha(bestAddInd);
                        % Modify Mu
                        % (Penalized IRLS will fix it soon but we need good initialization)

                        trainedKernelDownSelected = localKernels.retainKernelDimensions(bestAddInd);
                        newPhi = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);

                        cFactor = (Obj.Sigma*(cPhi)'*(newPhi.*obsNoiseVar));
                        Sigmaii = 1./(updatedAlpha(bestAddInd) + Sm(bestAddInd));
                        newMu = Sigmaii*Qm(bestAddInd);

                        updatedOldMu = mu - newMu*cFactor;
                        sortedSelected = sort(selectedInds);
                        newMuLocation = find(sortedSelected==bestAddInd);

                        mu = zeros(length(mu)+1,1);
                        mu(setdiff(1:length(mu),newMuLocation)) = updatedOldMu;
                        mu(newMuLocation) = newMu;

                        % Add things to forbidden list
                        newPhiDemeanedNormalized = newPhi - mean(newPhi);
                        newPhiDemeanedNormalized = newPhiDemeanedNormalized./sqrt(sum(newPhiDemeanedNormalized.^2));
                        phiCorrs = zeros(nBasis,1);
                        for iBlock = 1:nBlocks
                            cInds = ((iBlock-1)*Obj.largestNumberOfGramColumns+1):min([iBlock*Obj.largestNumberOfGramColumns nBasis]);

                            if nBlocks > 1
                                trainedKernelDownSelected = localKernels.retainKernelDimensions(cInds);
                                blockPhiDemeanedNormalized = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);

                                blockPhiDemeanedNormalized = bsxfun(@minus,blockPhiDemeanedNormalized,mean(blockPhiDemeanedNormalized));
                                blockPhiDemeanedNormalized = bsxfun(@rdivide,blockPhiDemeanedNormalized,sqrt(sum(blockPhiDemeanedNormalized.*blockPhiDemeanedNormalized))); % We have to normalize here

                            %else we have this from before
                            end

                            phiCorrs(cInds) = blockPhiDemeanedNormalized'*newPhiDemeanedNormalized;
                        end
                        forbidden(phiCorrs > Obj.learningCorrelationRemovalThreshold) = bestAddInd;

                        lastInd = bestAddInd;
                    case 2 % Remove

                        removingInd = sort(selectedInds)==bestRemInd;

                        mu = mu + mu(removingInd) .* Obj.Sigma(:,removingInd) ./ Obj.Sigma(removingInd,removingInd);
                        mu(removingInd) = [];

                        relevantIndices(bestRemInd) = false;
                        selectedInds(selectedInds==bestRemInd) = [];
                        alpha(bestRemInd) = inf;

                        % Anything this guy said is forbidden is now
                        % allowed.
                        forbidden(forbidden == bestRemInd) = 0;

                        lastInd = bestRemInd;

                    case 3 % Modify
                        modifyInd = sort(selectedInds)==bestModInd;

                        alphaChangeInv = 1/(updatedAlpha(bestModInd) - alpha(bestModInd));
                        kappa = 1/(Obj.Sigma(modifyInd,modifyInd) + alphaChangeInv);
                        mu = mu - mu(modifyInd)*kappa*Obj.Sigma(:,modifyInd);

                        alpha(bestModInd) = updatedAlpha(bestModInd);

                        lastInd = bestModInd;
                end

                % At this point relevantIndices and alpha have changes.
                % Now we re-estimate Sigma, mu, and sigma2
                if nBlocks > 1
                    trainedKernelDownSelected = localKernels.retainKernelDimensions(relevantIndices);
                    cPhi = trainedKernelDownSelected.run_OutputDoubleArray(DataSet);
                else
                    cPhi = PhiM(:,relevantIndices);
                end

                % Laplacian approx. IRLS
                A = diag(alpha(relevantIndices));

                if isempty(cPhi)
                    yHat = 0.5*ones(size(y));
                else
                    [mu, SigmaInvChol, obsNoiseVar] = prtUtilPenalizedIrls(y,cPhi,mu,A);

                    SigmaChol = inv(SigmaInvChol);
                    Obj.Sigma = SigmaChol*SigmaChol'; %#ok<MINV>

                    yHat = 1 ./ (1+exp(-cPhi*mu)); % We use a logistic here.
                end

                % Store beta
                Obj.beta = zeros(nBasis,1);
                Obj.beta(relevantIndices) = mu;

                if ~mod(iteration,Obj.verbosePlot)
                    if DataSet.nFeatures == 2
                        Obj.verboseIterationPlot(DataSet,relevantIndices);
                    elseif iteration == 1
                        warning('prt:prtClassRvmSequential','Learning iteration plot can only be produced for training Datasets with 2 features');
                    end
                end

                % Check tolerance
                changeVal = abs(log(alpha)-logAlphaOld);
                changeVal(isnan(changeVal)) = 0; % inf-inf = nan
                if all(changeVal < Obj.learningConvergedTolerance) && iteration > 1
                    Obj.learningConverged = true;
                    Obj.learningResults.exitReason = 'Alpha Not Changing';
                    Obj.learningResults.exitValue = TOL;
                    if Obj.verboseText
                        fprintf('Exiting...Precisions no longer changing appreciably.\n\n');
                    end
                    break;
                end
            end

            if Obj.verboseText && iteration == Obj.learningMaxIterations
                fprintf('Exiting...Convergence not reached before the maximum allowed iterations was reached.\n\n');
            end

            % Make sparse represenation
            Obj.sparseBeta = Obj.beta(relevantIndices,1);
            Obj.sparseKernels = localKernels.retainKernelDimensions(relevantIndices);

            % Very bad training
            if isempty(Obj.sparseBeta)
                warning('prt:prtClassRvm:NoRelevantFeatures','No relevant features were found during training.');
            end

            % Reset warning
            warning(warningState);

        end
    end
end


function [ E ] = getSketchTokenEdgemap( img )
%GETSKETCHTOKENEDGEMAP Summary of this function goes here
%   Detailed explanation goes here
load('./Toolbox/SketchTokens-master/models/forest/modelSmall.mat');
st = stDetect( img, model );
E = stToEdges( st, 1 );

end


% VL_TWISTER Random number generator
%    VL_TWISTER() is essentially equivalent to MATLAB native RAND()
%    when using the Twister random number generator. VL_TWISTER(),
%    VL_TWISTER(M,N,P,...)  and VL_TWISTER([M N P ...]) are equivalent
%    to RAND(), RAND(M,N,P,...)  and RAND([M N P ...]) respectively.
%
%    The state of the random generator can be seeded by
%    VL_TWISTER('STATE', X), where X is a DOUBLE scalar (this is
%    equivalent to RAND('TWISTER', X)). The state can be read by
%    VL_TWISTER('STATE') (equivalent to RAND('TWISTER')) and set by
%    VL_TWISTER('STATE', STATE) (equivalent to RAND('TWISTER',
%    STATE)). Here STATE is a vector of 625 elements of class
%    UINT32. Finally VL_TWISTER('STATE',KEY) seeds the generator by a
%    vector of DOUBLE of length not greater than 624.
%
%    VL_TWISTER() is slightly faster than RAND(). Moreover it can be
%    used to control the state of the random number generator used by
%    all VLFEAT functions.
%
%    See also: VL_HELP().

% Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).


function vanity
% VANITY    Display your File Exchange downloads and rank statistics,
%           add them to a history file (fex.mat in work directory, by
%           default), and play Handel's Hallelujah chorus if rank has
%           improved. Modify  code by entering URL of own author page
%           and location of history file, if desired.
% EXAMPLE : vanity (Oh, the FEX code metrics..)
% AUTHOR  : Dimitri Shvorob, dimitri.shvorob@vanderbilt.edu, 7/15/07
file = [matlabroot '\work\fex.mat'];
page = urlread('http://www.mathworks.com/matlabcentral/fileexchange/loadAuthor.do?objectType=author&objectId=1095920');
d = findby('Downloads:\s(\d*)');
r = findby('Rank:\s(\d*)');
if ~exist(file,'file')
   h = [now d r];      %#ok
   save(file,'h')
   disp('History file created')
   fprintf('Current downloads/rank: %d/%d \n',d,r)
else
   load(file)
   fprintf('Former  downloads/rank: %d/%d \nCurrent downloads/rank: %d/%d \n',h(end,2),h(end,3),d,r)    %#ok
   if r < h(end,3)
      s = load('handel');
      sound(s.y,s.Fs)
   end
   h = [h; [now d r]]; %#ok
   save(file,'h')
end

function[n] = findby(pattern)
s = regexp(page,pattern,'tokens');
n = str2double(cell2mat(s{1}));
end

end


% Digital Video Stabilization and Rolling Shutter Correction using Gyroscopes
% Copyright (C) 2011 Alexandre Karpenko
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

function X = track_video_markers(video_file)

% read corresponding movie
xyloObj = mmreader(['data/' video_file '.mov']);
display(xyloObj);

num_frames = xyloObj.NumberOfFrames;
vid_height = xyloObj.Height;
vid_width = xyloObj.Width;

% initialize markers for first frame
figure(1); imshow(read(xyloObj,1));

x=[];
while isempty(x)
    [x, y] = ginput;
    display([x y]);
end

rect_size = 160;

X = cell(numel(x), 1);

% Read one frame at a time.
for f = 1:num_frames
    frame = read(xyloObj, f);

    for i = 1:numel(x)
        min_x = max(round(x(i) - rect_size/2), 1);
        max_x = min(round(x(i) + rect_size/2), vid_width);

        min_y = max(round(y(i) - rect_size/2), 1);
        max_y = min(round(y(i) + rect_size/2), vid_height);

        patch = double(rgb2gray(frame(min_y:max_y, min_x:max_x, :)));
        min_patch = min(patch(:));
        patch = (patch - min_patch) / (max(patch(:)) - min_patch);

        figure(2); subplot(numel(x), 1, i);
        imshow(patch < 0.2);

        [py, px] = find(patch < 0.2);
        x(i) = min_x + mean(px);
        y(i) = min_y + mean(py);
        X{i} = [X{i}; x(i) y(i)];
    end

    figure(1);
    imshow(frame); hold on;
    colors = {'rx', 'gx', 'bx', 'cx'};
    for i = 1:numel(x)
        plot(x(i), y(i), colors{mod(i-1,4)+1});
    end
    hold off;
end


function varargout = size(T,dim)
% overloads size

s = size(T.M);
s = s(T.rank+1:end);

if nargin == 1
  if length(s) <= 1
    s = [s,ones(1,2-length(s))];
  end
elseif dim > numel(s)
  s = 1;
else
  s = s(dim);
end

if nargout > 1
  varargout = num2cell(s);
else
  varargout{1} = s;
end


function  gpmf = gpmf_squared(varargin)
%GPMF_SQUARED  Create a squared mean function
%
%  Description
%    GPMF = GPMF_SQUARED('PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    creates squared mean function structure in which the named
%    parameters have the specified values. Any unspecified
%    parameters are set to default values.
%
%    GPMF = GPMF_SQUARED(GPMF,'PARAM1',VALUE1,'PARAM2,VALUE2,...)
%    modify a mean function structure with the named parameters
%    altered with the specified values.
%
%    Parameters for squared mean function [default]
%      interactions      - twoway interactions (default off)
%      prior_mean        - prior mean (scalar or vector) for base
%                          functions' weight prior (default 0)
%      prior_cov         - prior covariances (scalar or vector)
%                          for base functions' prior corresponding
%                          each selected input dimension (default 100)
%      selectedVariables - vector defining which inputs are active
%
%  See also
%    GP_SET, GPMF_CONSTANT, GPMF_LINEAR
%
% Copyright (c) 2010 Tuomas Nikoskinen
% Copyright (c) 2011 Jarno Vanhatalo

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  ip=inputParser;
  ip.FunctionName = 'GPMF_SQUARED';
  ip.addOptional('gpmf', [], @isstruct);
  ip.addParamValue('selectedVariables',[], @(x) isvector(x) && all(x>0));
  ip.addParamValue('interactions', 'off', @(x) ismember(x,{'on' 'off'}))
  ip.addParamValue('prior_mean',0, @(x) isvector(x));
  ip.addParamValue('prior_cov',100, @(x) isvector(x));
  ip.addParamValue('mean_prior', [], @isstruct);
  ip.addParamValue('cov_prior', [], @isstruct);
  ip.parse(varargin{:});
  gpmf=ip.Results.gpmf;

  if isempty(gpmf)
    % Initialize a mean function
    init=true;
    gpmf.type = 'gpmf_squared';
  else
    % Modify a mean function
    if ~isfield(gpmf,'type') && isequal(gpmf.type,'gpmf_squared')
      error('First argument does not seem to be a squared mean function')
    end
    init=false;
  end
  % Initialize parameters
  if init || ~ismember('interactions',ip.UsingDefaults)
    gpmf.interactions=ip.Results.interactions;
  end
  if init || ~ismember('prior_mean',ip.UsingDefaults)
    gpmf.b=ip.Results.prior_mean(:)';
  end
  if init || ~ismember('prior_cov',ip.UsingDefaults)
    gpmf.B=ip.Results.prior_cov(:)';
  end
  if ~ismember('selectedVariables',ip.UsingDefaults)
    gpmf.selectedVariables=ip.Results.selectedVariables;
  end
  if init || ~ismember('mean_prior',ip.UsingDefaults)
    gpmf.p.b=ip.Results.cov_prior;
  end
  if init || ~ismember('cov_prior',ip.UsingDefaults)
    gpmf.p.B=ip.Results.mean_prior;
  end
  if init
    % Set the function handles to the nested functions
    gpmf.fh.geth = @gpmf_geth;
    gpmf.fh.pak = @gpmf_pak;
    gpmf.fh.unpak = @gpmf_unpak;
    gpmf.fh.lp = @gpmf_lp;
    gpmf.fh.lpg = @gpmf_lpg;
    gpmf.fh.recappend = @gpmf_recappend;
  end

end

function h = gpmf_geth(gpmf, x)
%GPMF_GETH  Calculate the base function values for given input.
%
%  Description
%    H = GPMF_GETH(GPMF,X) takes in a mean function structure
%    GPMF and inputs X. The function returns the squared base
%    function values H in the given input points. If
%    selectedVariables is used the function returns only the
%    values corresponding active inputs. The base function values
%    are returned as a matrix in which each row corresponds to
%    one dimension and the first row is for the smallest
%    dimension.

  if isfield(gpmf,'selectedVariables')
    x=x(:,gpmf.selectedVariables);
  end
  h = x'.^2;
  if isequal(gpmf.interactions,'on')
    m=size(x,2);
    for xi1=1:m
      for xi2=xi1+1:m
        h = [h; x(:,xi1)'.*x(:,xi2)'];
      end
    end
  end

end

function [w, s, h] = gpmf_pak(gpmf, w)
%GPMF_PAK  Combine GP mean function parameters into one vector
%
%  Description
%    W = GPMF_PAK(GPMF) takes a mean function
%    structure GPMF and combines the mean function
%    parameters and their hyperparameters into a single row
%    vector W.
%
%       w = [ log(gpmf.b)
%             (hyperparameters of gpmf.b)
%             log(gpmf.B)
%             (hyperparameters of gpmf.B)]'
%
%  See also
%    GPMF_UNPAK

  w = []; s = {}; h=[];
  if ~isempty(gpmf.p.b)
    w = gpmf.b;
    if numel(gpmf.b)>1
      s = [s; sprintf('gpmf_squared.b x %d',numel(gpmf.b))];
    else
      s = [s; 'gpmf_squared.b'];
    end
    h = [h -1.*ones(1,numel(gpmf.b))];
    % Hyperparameters of b
    [wh, sh, hh] = gpmf.p.b.fh.pak(gpmf.p.b);
    w = [w wh];
    s = [s; sh];
    h = [h -1-hh];
  end

  if ~isempty(gpmf.p.B)
    w = [w log(gpmf.B)];
    if numel(gpmf.B)>1
      s = [s; sprintf('log(gpmf_squared.B x %d)',numel(gpmf.B))];
    else
      s = [s; 'log(gpmf_squared.B)'];
    end
    h = [h -1.*ones(1,numel(gpmf.B))];
    % Hyperparameters of b
    [wh, sh, hh] = gpmf.p.B.fh.pak(gpmf.p.B);
    w = [w wh];
    s = [s; sh];
    h = [h -1-hh];
  end

end

function [gpmf, w] = gpmf_unpak(gpmf, w)
%GPMF_UNPAK  Sets the mean function parameters into the structure
%
%  Description
%    [GPMF, W] = GPMF_UNPAK(GPMF, W) takes a covariance
%    function structure GPMF and a hyper-parameter vector W, and
%    returns a mean function structure identical to the
%    input, except that the covariance hyper-parameters have been
%    set to the values in W. Deletes the values set to GPMF from
%    W and returns the modified W.
%
%    Assignment is inverse of
%       w = [ log(gpmf.b)
%             (hyperparameters of gpmf.b)
%             log(gpmf.B)
%             (hyperparameters of gpmf.B)]'
%
%  See also
%   GPMF_PAK

  gpp=gpmf.p;

  if ~isempty(gpp.b)
    i2=length(gpmf.b);
    i1=1;
    gpmf.b = w(i1:i2);
    w = w(i2+1:end);

    % Hyperparameters of b
    [p, w] = gpmf.p.b.fh.unpak(gpmf.p.b, w);
    gpmf.p.b = p;
  end

  if ~isempty(gpp.B)
    i2=length(gpmf.B);
    i1=1;
    gpmf.B = exp(w(i1:i2));
    w = w(i2+1:end);

    % Hyperparameters of B
    [p, w] = gpmf.p.B.fh.unpak(gpmf.p.B, w);
    gpmf.p.B = p;
  end

end

function lp = gpmf_lp(gpmf)
%GPMF_SEXP_LP  Evaluate the log prior of mean function parameters
%
%  Description
%
%  See also

% Evaluate the prior contribution to the error. The parameters that
% are sampled are transformed, e.g., W = log(w) where w is all
% the "real" samples. On the other hand errors are evaluated in
% the W-space so we need take into account also the Jacobian of
% transformation, e.g., W -> w = exp(W). See Gelman et al. (2013),
% Bayesian Data Analysis, third edition, p. 21.
  lp = 0;
  gpp=gpmf.p;

  if ~isempty(gpmf.p.b)
    lp = lp + gpp.b.fh.lp(gpmf.b, ...
                   gpp.b);
  end

  if ~isempty(gpp.B)
    lp = lp + gpp.B.fh.lp(gpmf.B, ...
                   gpp.B) +sum(log(gpmf.B));
  end
end

function [lpg_b, lpg_B] = gpmf_lpg(gpmf)
%GPMF_SEXP_LPG  Evaluate gradient of the log prior with respect
%               to the parameters.
%
%  Description
%    LPG = GPMF_SEXP_LPG(GPMF) takes a mean function
%    structure GPMF and returns LPG = d log (p(th))/dth, where th
%    is the vector of parameters.
%
%  See also
%    GPMF_SEXP_PAK, GPMF_SEXP_UNPAK, GPMF_SEXP_LP, GP_G

  lpg_b=[];, lpg_B=[];
  gpp=gpmf.p;

  if ~isempty(gpmf.p.b)
    lll = length(gpmf.b);
    lpgs = gpp.b.fh.lpg(gpmf.b, gpp.b);
    lpg_b = [lpgs(1:lll) lpgs(lll+1:end)]; %
  end

  if ~isempty(gpmf.p.B)
    lll = length(gpmf.B);
    lpgs = gpp.B.fh.lpg(gpmf.B, gpp.B);
    lpg_B = [lpgs(1:lll).*gpmf.B+1 lpgs(lll+1:end)];
  end
end

function recmf = gpmf_recappend(recmf, ri, gpmf)
%RECAPPEND  Record append
%
%  Description
%
%  See also
%    GP_MC and GP_MC -> RECAPPEND

% Initialize record
  if nargin == 2
    recmf.type = 'gpmf_squared';

    % Initialize parameters
    recmf.b= [];
    recmf.B = [];

    % Set the function handles
    recmf.fh.geth = @gpmf_geth;
    recmf.fh.pak = @gpmf_pak;
    recmf.fh.unpak = @gpmf_unpak;
    recmf.fh.lp = @gpmf_lp;
    recmf.fh.lpg = @gpmf_lpg;
    recmf.fh.recappend = @gpmf_recappend;

    recmf.p=[];
    recmf.p.b=[];
    recmf.p.B=[];
    if isfield(ri.p,'b') && ~isempty(ri.p.b)
      recmf.p.b = ri.p.b;
    end
    if ~isempty(ri.p.B)
      recmf.p.B = ri.p.B;
    end
    return
  end

  gpp = gpmf.p;

  % record magnSigma2
  if ~isempty(gpmf.b)
    recmf.b(ri,:)=gpmf.b;
    if ~isempty(recmf.p.b)
      recmf.p.b = gpp.b.fh.recappend(recmf.p.b, ri, gpmf.p.b);
    end
  elseif ri==1
    recmf.b=[];
  end

  if ~isempty(gpmf.B)
    recmf.B(ri,:)=gpmf.B;
    if ~isempty(recmf.p.B)
      recmf.p.B = gpp.B.fh.recappend(recmf.p.B, ri, gpmf.p.B);
    end
  elseif ri==1
    recmf.B=[];
  end

end


function [net, info] = cnn_imagenet(varargin)
%CNN_IMAGENET   Demonstrates training a CNN on ImageNet
%  This demo demonstrates training the AlexNet, VGG-F, VGG-S, VGG-M,
%  VGG-VD-16, and VGG-VD-19 architectures on ImageNet data.

run(fullfile(fileparts(mfilename('fullpath')), ...
  '..', '..', 'matlab', 'vl_setupnn.m')) ;

opts.dataDir = fullfile(vl_rootnn, 'data','ILSVRC2012') ;
opts.modelType = 'alexnet' ;
opts.network = [] ;
opts.networkType = 'simplenn' ;
opts.batchNormalization = true ;
opts.weightInitMethod = 'gaussian' ;
[opts, varargin] = vl_argparse(opts, varargin) ;

sfx = opts.modelType ;
if opts.batchNormalization, sfx = [sfx '-bnorm'] ; end
sfx = [sfx '-' opts.networkType] ;
opts.expDir = fullfile(vl_rootnn, 'data', ['imagenet12-' sfx]) ;
[opts, varargin] = vl_argparse(opts, varargin) ;

opts.numFetchThreads = 12 ;
opts.lite = false ;
opts.imdbPath = fullfile(opts.expDir, 'imdb.mat');
opts.train = struct() ;
opts = vl_argparse(opts, varargin) ;
if ~isfield(opts.train, 'gpus'), opts.train.gpus = []; end;

% -------------------------------------------------------------------------
%                                                              Prepare data
% -------------------------------------------------------------------------

if exist(opts.imdbPath)
  imdb = load(opts.imdbPath) ;
  imdb.imageDir = fullfile(opts.dataDir, 'images');
else
  imdb = cnn_imagenet_setup_data('dataDir', opts.dataDir, 'lite', opts.lite) ;
  mkdir(opts.expDir) ;
  save(opts.imdbPath, '-struct', 'imdb') ;
end

% Compute image statistics (mean, RGB covariances, etc.)
imageStatsPath = fullfile(opts.expDir, 'imageStats.mat') ;
if exist(imageStatsPath)
  load(imageStatsPath, 'averageImage', 'rgbMean', 'rgbCovariance') ;
else
  train = find(imdb.images.set == 1) ;
  images = fullfile(imdb.imageDir, imdb.images.name(train(1:100:end))) ;
  [averageImage, rgbMean, rgbCovariance] = getImageStats(images, ...
                                                    'imageSize', [256 256], ...
                                                    'numThreads', opts.numFetchThreads, ...
                                                    'gpus', opts.train.gpus) ;
  save(imageStatsPath, 'averageImage', 'rgbMean', 'rgbCovariance') ;
end
[v,d] = eig(rgbCovariance) ;
rgbDeviation = v*sqrt(d) ;
clear v d ;

% -------------------------------------------------------------------------
%                                                             Prepare model
% -------------------------------------------------------------------------

if isempty(opts.network)
  switch opts.modelType
    case 'resnet-50'
      net = cnn_imagenet_init_resnet('averageImage', rgbMean, ...
                                     'colorDeviation', rgbDeviation, ...
                                     'classNames', imdb.classes.name, ...
                                     'classDescriptions', imdb.classes.description) ;
      opts.networkType = 'dagnn' ;

    otherwise
      net = cnn_imagenet_init('model', opts.modelType, ...
                              'batchNormalization', opts.batchNormalization, ...
                              'weightInitMethod', opts.weightInitMethod, ...
                              'networkType', opts.networkType, ...
                              'averageImage', rgbMean, ...
                              'colorDeviation', rgbDeviation, ...
                              'classNames', imdb.classes.name, ...
                              'classDescriptions', imdb.classes.description) ;
  end
else
  net = opts.network ;
  opts.network = [] ;
end

% -------------------------------------------------------------------------
%                                                                     Learn
% -------------------------------------------------------------------------

switch opts.networkType
  case 'simplenn', trainFn = @cnn_train ;
  case 'dagnn', trainFn = @cnn_train_dag ;
end

[net, info] = trainFn(net, imdb, getBatchFn(opts, net.meta), ...
                      'expDir', opts.expDir, ...
                      net.meta.trainOpts, ...
                      opts.train) ;

% -------------------------------------------------------------------------
%                                                                    Deploy
% -------------------------------------------------------------------------

net = cnn_imagenet_deploy(net) ;
modelPath = fullfile(opts.expDir, 'net-deployed.mat')

switch opts.networkType
  case 'simplenn'
    save(modelPath, '-struct', 'net') ;
  case 'dagnn'
    net_ = net.saveobj() ;
    save(modelPath, '-struct', 'net_') ;
    clear net_ ;
end

% -------------------------------------------------------------------------
function fn = getBatchFn(opts, meta)
% -------------------------------------------------------------------------

if numel(meta.normalization.averageImage) == 3
  mu = double(meta.normalization.averageImage(:)) ;
else
  mu = imresize(single(meta.normalization.averageImage), ...
                meta.normalization.imageSize(1:2)) ;
end

useGpu = numel(opts.train.gpus) > 0 ;

bopts.test = struct(...
  'useGpu', useGpu, ...
  'numThreads', opts.numFetchThreads, ...
  'imageSize',  meta.normalization.imageSize(1:2), ...
  'cropSize', meta.normalization.cropSize, ...
  'subtractAverage', mu) ;

% Copy the parameters for data augmentation
bopts.train = bopts.test ;
for f = fieldnames(meta.augmentation)'
  f = char(f) ;
  bopts.train.(f) = meta.augmentation.(f) ;
end

fn = @(x,y) getBatch(bopts,useGpu,lower(opts.networkType),x,y) ;

% -------------------------------------------------------------------------
function varargout = getBatch(opts, useGpu, networkType, imdb, batch)
% -------------------------------------------------------------------------
images = strcat([imdb.imageDir filesep], imdb.images.name(batch)) ;
if ~isempty(batch) && imdb.images.set(batch(1)) == 1
  phase = 'train' ;
else
  phase = 'test' ;
end
data = getImageBatch(images, opts.(phase), 'prefetch', nargout == 0) ;
if nargout > 0
  labels = imdb.images.label(batch) ;
  switch networkType
    case 'simplenn'
      varargout = {data, labels} ;
    case 'dagnn'
      varargout{1} = {'input', data, 'label', labels} ;
  end
end


function [newTree, pdeSign] = splitTreePDE(treeIn)
%SPLITTREEPDE   Split a syntax tree, specific to PDE problems.
%   [NEWTREE, PDESIGN] = SPLITTREPDE(TREEIN) goes through the syntax tree
%   TREEIN, and isolates the part where the PDE variable, e.g. u_t, appears.
%   splits the syntax tree TREEIN into two trees, NEWTREE and LAMBDATREE.
%   LAMBDATREE contains the syntax tree that the eigenvalue parameter LAMBDA
%   appears in, NEWTREE contains the other part of TREEIN. The value of
%   LAMBDASIGN corresponds to the sign in front of LAMBDA in the original syntax
%   tree TREEIN.
%
% See also: STRINGPARSER/SPLITTREE.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

% Begin by replacing the subtree which contains the pde_variable with a 0
[newTree, ignored, pdeSign] = findPDE(treeIn, 1);

% Do the basic splitting (converting = into -) in newTree
newTree = stringParser.splitTree(newTree);

end

function [newTree, pdeTree, pdeSign] = findPDE(treeIn, pdeSign)
%FINDPDE    Find where the PDE variable (e.g. u_t) appears in TREEIN.

% Initialization.
newTree = treeIn;
% Create some empty trees.
leftEmpty = 1;
rightEmpty = 1;
pdeTree = [];
pdeTreeLeft = [];
pdeTreeRight = [];
treeCenter = treeIn.center;

% Go recursively through the tree on the left.
if ( isfield(treeIn, 'left') )
    [newLeft, pdeTreeLeft, pdeSign] = findPDE(treeIn.left, pdeSign);
    newTree.left = newLeft;
    leftEmpty = 0;
end

% Go recursively through the tree on the right.
if ( isfield(treeIn, 'right') )
    [newRight, pdeTreeRight, pdeSign] = findPDE(treeIn.right, pdeSign);
    newTree.right = newRight;
    rightEmpty = 0;
end

% Return a new pdeTree. If the operator in the center of treeIn is a *,
% we want to return the whole treeIn (e.g. when we see 1*u_t). If not,
% we return the latest pdeTree (e.g. when we see u_t+1).
%
% Start by looking through the left tree.
if ( ~isempty(pdeTreeLeft) )
    if ( strcmp(treeCenter{2}, 'OP*') )
        pdeTree = treeIn;
    else
        pdeTree = pdeTreeLeft;
    end
end

% Now look through the right tree.
if ( ~isempty(pdeTreeRight) )
    if ( strcmp(treeCenter, 'OP*') )
        pdeTree = treeIn;
    % If we have a =, and u_t is on the right, we need to switch signs on
    % the pdeTree.
    elseif ( strcmp(treeCenter{2}, 'OP=') )
        disp('PDE on right')
        pdeSign = -1*pdeSign;
        pdeTree = pdeTreeRight;
    % If we have a -, and we have u_t on the right we need to switch signs
    % on the pdeTree.
    elseif ( strcmp(treeCenter{2}, 'OP-') || strcmp(treeCenter{2}, 'UN-') )
        pdeSign = -1*pdeSign;
        pdeTree = pdeTreeRight;
    else
        pdeTree = pdeTreeRight;
    end
end

% Both left and right trees are empty. We must be at a leaf! Replace the PDEVAR
% with a 0.
if ( leftEmpty && rightEmpty )
    % We encounter a PDE variable. Replace it by a zero.
    if ( strcmp(treeCenter{2}, 'PDEVAR') )
        pdeTree = newTree;
        newTree = struct('center', {{'0', 'NUM'}});
    end
end

end


% POP_TOPOCHANSEL - pop up a topographic interface to select channels
%
% Pops up a topographic interface to select multiple channels with the
% mouse. Click a polygon around the electrodes you wish to select. Right
% click to finish.
%
% Usage:
%   >> [chanlist] = pop_topochansel(chanlocs,selection);
%
% Inputs:
%   chanstruct     - channel structure. See READLOCS
%                    (optional if EEG structure with chanlocs in caller or
%                    base workspace)
%   selection      - currently selected electrodes.
%                    (optional)
%
%
% Output:
%   chanlist      - indices of selected channels
%   cellchannames - names of selected channel names in a cell array
%   strchannames  - names of selected channel names in a concatenated string
%                   (channel names are separated by space characters)
%

function [chanlist, cellchannames, strchannames] = pop_topochansel(chanlocs,select,varargin)
if nargin == 0 || isempty(chanlocs)
    try
        chanlocs = evalin('caller','chanlocs');
    catch
        try
            tmp = evalin('caller','EEG');
            chanlocs = tmp.chanlocs;
        catch
            error('tell me where the electrodes are'),
        end
    end
end
if nargin >= 2
    if ischar(select)|| iscellstr(select)
        select = chnb(select,{chanlocs.labels});
    end
end
g = finputcheck( varargin, ...
                 { 'labels'    'string'    {'on' 'off'}   'off'
                  'cellstrout' 'string'    {'on' 'off'}   'off'
                    },'ignore');

h0 = figure(23537);clf;
set(gcf,'numbertitle','off','name','Channel selection');
topoplot([],chanlocs,'electrodes','on', 'style','blank');

chi = get(gca,'children');
% chi = chi(1:numel(chanlocs));
todel = [];
for i = 1:numel(chi)
    try
        if numel(get(chi(i),'XData')) == sum(~emptycells({chanlocs.X}))
            continue % we found electrodes
        else
            todel(end+1) = i;
        end
    catch
        todel(end+1) = i;
    end
end
chi(todel) = [];
hold on
X = get(chi,'XData');
Y = get(chi,'YData');
delete(chi)
if strcmp(g.labels,'on')
    for i = 1:numel(X)
        text(X(i),Y(i),chanlocs(i).labels);
    end
end

if exist('select','var')
    plot(X(select),Y(select),'r.','markersize',20);
else
    select = [];
end
[dum dum chanlist] = lasso(X,Y);
if isempty(chanlist)
    disp('No new elecs selected. Keeping old selection')
    chanlist = select;
end
strchannames = '';
for i = 1:numel(chanlist)
    strchannames = [strchannames chanlocs(chanlist(i)).labels];
    if i ~= numel(chanlist)
        strchannames = [strchannames ' '];
    end
    cellchannames{i} = chanlocs(chanlist(i)).labels;
end
if strcmp(g.cellstrout,'on')
    chanlist = cellchannames;
end
if nargout == 0
    disp(strchannames)
    disp(chanlist)
    clear
end
% close(h0);

function [selx,sely,indexnr]=lasso(x,y)

% lasso -  enables the selection/encircling of (clusters of) events in a scatter plot by hand
%          using the mouse
%
% Input:    x,y                 - a set of points in 2 column vectors.
% Output:   selx,sely,indexnr   - a set of selected points in 3 column vectors
%
% Note:   After the scatter plot is given, selection by mouse is started after any key press.
%         This is done to be able to ZOOM or CHANGE AXES etc. in the representation before selection
%         by mouse.
%         Encircling is done by pressing subsequently the LEFT button mouse at the requested positions
%         in a scatter plot.
%         Closing the loop is done by a RIGHT button press.
%
% T.Rutten V2.0/9/2003
% downloaded and adapted from mathworks fileexchange by M. Chaumon 2011

plot(x,y,'ob')

las_x=[];
las_y=[];

c=1;

key=0;

while c==1
    try
        [a,b,c]=ginput(1);
        las_x=[las_x;a];las_y=[las_y;b];
        line(las_x,las_y)
    catch
        selx = [];sely = []; indexnr = [];
        return
    end
end

las_x(length(las_x)+1)=las_x(1);
las_y(length(las_y)+1)=las_y(1);

line(las_x,las_y)
pause(.2)

in=inpolygon(x,y,las_x,las_y);

ev_in=find(in>0);

selx=x(ev_in);
sely=y(ev_in);
plot(selx,sely,'r.','markersize',20);
drawnow

indexnr=ev_in;

function nb = chnb(channame, varargin)

% CHNB - return channel number corresponding to channel names in an EEG
%           structure
%
% Usage:
%   >> [nb] = chnb(channame);
%   >> [nb] = chnb(channame, labels);
%
% Input:
%   channame      - name of channels to search. Either a string with space
%                   separated channel names, or a cell array of strings.
%                   Note that regular expressions can be used to match
%                   several channels. See regexp.
%   labels        - channel labels as found in EEG.chanlocs.labels.
%
% Output:
%   nb            - channel numbers in the EEG structure found in the
%                   caller workspace (i.e. where the function is called
%                   from) or in the base workspace, if no EEG structure
%                   exists in the caller workspace.
%
error(nargchk(1,2,nargin));
if nargin == 2
    labels = varargin{1};
else

    try
        EEG = evalin('caller','EEG');
    catch
        try
            EEG = evalin('base','EEG');
        catch
            error('Could not find EEG structure');
        end
    end
    if not(isfield(EEG,'chanlocs'))
        error('No channel list found');
    end
    labels = {EEG.chanlocs.labels};
end
if ischar(channame)
    tmp = regexp(channame,'(\S*) ?','tokens');
    channame = {};
    for i = 1:numel(tmp)
        channame{i} = tmp{i}{1};
    end
    if isempty(channame)
        nb = [];
        return
    end
end

nb = regexpcell(labels,channame,'exactignorecase');

function idx = regexpcell(c,pat, cmds)

% idx = regexpcell(c,pat, cmds)
%
% Return indices idx of cells in c that match pattern(s) pat (regular expression).
% Pattern pat can be char or cellstr. In the later case regexpcell returns
% indexes of cells that match any pattern in pat.
%
% cmds is a string that can contain one or several of these commands:
% 'inv' return indexes that do not match the pattern.
% 'ignorecase' will use regexpi instead of regexp
% 'exact' performs an exact match (regular expression should match the whole strings in c).
% 'all' (default) returns all indices, including repeats (if several pat match a single cell in c).
% 'unique' will return unique sorted indices.
% 'intersect' will return only indices in c that match ALL the patterns in pat.
%
% v1 Maximilien Chaumon 01/05/09
% v1.1 Maximilien Chaumon 24/05/09 - added ignorecase
% v2 Maximilien Chaumon 02/03/2010 changed input method.
%       inv,ignorecase,exact,combine are replaced by cmds

error(nargchk(2,3,nargin))
if not(iscellstr(c))
    error('input c must be a cell array of strings');
end
if nargin == 2
    cmds = '';
end
if not(isempty(regexpi(cmds,'inv', 'once' )))
    inv = true;
else
    inv = false;
end
if not(isempty(regexpi(cmds,'ignorecase', 'once' )))
    ignorecase = true;
else
    ignorecase = false;
end
if not(isempty(regexpi(cmds,'exact', 'once' )))
    exact = true;
else
    exact = false;
end
if not(isempty(regexpi(cmds,'unique', 'once' )))
    combine = 2;
elseif not(isempty(regexpi(cmds,'intersect', 'once' )))
    combine = 3;
else
    combine = 1;
end

if ischar(pat)
    pat = cellstr(pat);
end

if exact
    for i_pat = 1:numel(pat)
        pat{i_pat} = ['^' pat{i_pat} '$'];
    end
end

for i_pat = 1:length(pat)
    if ignorecase
        trouv = regexpi(c,pat{i_pat}); % apply regexp on each pattern
    else
        trouv = regexp(c,pat{i_pat}); % apply regexp on each pattern
    end
    idx{i_pat} = [];
    for i = 1:numel(trouv)
        if not(isempty(trouv{i}))% if there is a match, store index
            idx{i_pat}(end+1) = i;
        end
    end
end
switch combine
    case 1
        idx = [idx{:}];
    case 2
        idx = unique([idx{:}]);
    case 3
        for i_pat = 2:length(pat)
            idx{1} = intersect(idx{1},idx{i_pat});
        end
        idx = idx{1};
end
if inv % if we want to invert result, then do so.
    others = 1:numel(trouv);
    others(idx) = [];
    idx = others;
end


function rbfinfwhiteKernDisplay(kern, spacing)

% RBFINFWHITEKERNDISPLAY Display parameters of the RBF-WHITE kernel (with
% integration limits between minus infinity and infinity).
% FORMAT
% DESC displays the parameters of the RBF-WHITE kernel and the kernel type
% to the console.
% ARG kern : the kernel to display.
%
% FORMAT does the same as above, but indents the display according
% to the amount specified.
% ARG kern : the kernel to display.
% ARG spacing : how many spaces to indent the display of the kernel by.
%
% SEEALSO : rbfinfwhiteKernParamInit, modelDisplay, kernDisplay
%
% COPYRIGHT : David Luengo, 2009

% KERN

if nargin > 1
  spacing = repmat(32, 1, spacing);
else
  spacing = [];
end
spacing = char(spacing);
fprintf(spacing);
fprintf('RBF-INF-WHITE inverse width: %2.4f (length scale %2.4f)\n', ...
        kern.inverseWidth, 1/sqrt(kern.inverseWidth));
fprintf(spacing);
fprintf('RBF-INF-WHITE variance: %2.4f\n', kern.variance)


function h=hmatrix3d(p,xx,yy,zz,dd,hh,varargin)

%   Copyright (C) 2004-2006 Per-Olof Persson. See COPYRIGHT.TXT for details.

h=interpn(xx,yy,zz,hh,p(:,1),p(:,2),p(:,3),'*linear');


function [C]=skinColor()
C=[255 218*0.9 180*0.9]./255;
end


function output = rand_same_class(imdb,label)
    index = find(imdb.images.label2==label);
    output = index(randi(numel(index)));
end


function sz = tsize(a,idx)
%TSIZE Tensor size of sptenmat.
%
%   D = TSIZE(X) returns the size of the tensor being stored as a
%   matrix.
%
%   M = TSIZE(X,DIM) returns the length of the dimension(s) specified
%   by DIM.  For example, SIZE(X,1) returns the size of the first
%   dimension of the tensor.
%
%   See also SPTENMAT, SPTENMAT/SIZE.
%
%MATLAB Tensor Toolbox.
%Copyright 2015, Sandia Corporation.

% This is the MATLAB Tensor Toolbox by T. Kolda, B. Bader, and others.
% http://www.sandia.gov/~tgkolda/TensorToolbox.
% Copyright (2015) Sandia Corporation. Under the terms of Contract
% DE-AC04-94AL85000, there is a non-exclusive license for use of this
% work by or on behalf of the U.S. Government. Export of this data may
% require a license from the United States Government.
% The full license terms can be found in the file LICENSE.txt

if isempty(a.tsize)
    sz = [];
    return;
end

if exist('idx', 'var')
    sz = a.tsize(idx);
else
    sz = a.tsize;
end

return;


function vl_bench_bnorm(gpu)
  if nargin < 1
    gpu = false ;
  end

  T = 100 ;
  x = randn(64,64,32,32,'single') ;
  g = randn(32,1,'single') ;
  b = randn(32,1,'single') ;

  if gpu
    x = gpuArray(x) ;
    g = gpuArray(g) ;
    b = gpuArray(b) ;
  end

  tic
  for t=1:T
    y = vl_nnbnorm(x,g,b) ;
  end
  if gpu, wait(gpuDevice) ; end
  fprintf('new: %f\n',toc);

  tic
  for t=1:T
    y_ = vl_nnbnorm_old(x,g,b) ;
  end
  if gpu, wait(gpuDevice) ; end
  fprintf('old: %f\n',toc);

  dzdy = randn(size(y),'single') ;
  if gpu
    dzdy = gpuArray(dzdy) ;
  end

  tic
  for t=1:T
    [a,b,c] = vl_nnbnorm(x,g,b,dzdy) ;
  end
  if gpu, wait(gpuDevice) ; end
  fprintf('new deriv: %f\n',toc);

  tic
  for t=1:T
    [a_,b_,c_] = vl_nnbnorm_old(x,g,b,dzdy) ;
  end
  if gpu, wait(gpuDevice) ; end
  fprintf('old deriv: %f\n',toc);

  vl_testsim(y,y_);
  vl_testsim(a,a_);
  vl_testsim(b,b_);
  vl_testsim(c,c_);
end


function sc =  dbscale(q)

%tstoolbox/@unit/dbscale
%   returns scaling value when calculating decibel values from data of
%   this unit. dpscale returns either 10 (for power or energy units (e.g.
%   Watt)) or 20 (for all other units (e.g. Volt).
%
% Copyright 1997-2001 DPI Goettingen, License http://www.physik3.gwdg.de/tstool/gpl.txt

sc = q.dBScale;


function showFeatures(I, pts,  ptSyle, ptWidth)
%PLOTFEATURES Plot features using MATLAB
%
% INPUT:
%   - I(image): image corresponding to the points
%   - pts(1, N): array of structure conatining keypoints
%   - ptSyle: defines color and type of scatter plot; eg: '+r'
%   - ptWidth: defines thickness of scatter points

assert (size(pts, 2) > size(pts, 1), 'Input expected to array of shape (1, N)')

% get locations of points
loc_x = zeros(1, size(pts, 2));
loc_y = zeros(1, size(pts, 2));
class_pt = zeros(1, size(pts, 2));

index = 1;
for point = pts
    index = index + 1;
    loc_x(index) = point.location(1);
    loc_y(index) = point.location(2);
    class_pt(index) = point.class;
end

color = ['r', 'b', 'g', 'y'];

% plot image
imshow(I);
hold on;
for i = 1:4
    scatter(loc_y(class_pt == i), loc_x(class_pt == i), ptSyle, color(i), 'LineWidth', ptWidth);
end
legend('Blob mimimum', 'Blob maximum', 'Corner minimum', 'Corner maximum')

end


function plotroute(city, route, current_distance, temperature)
% PLOTROUTE
% PLOTROUTE(city, route, current_distance, temperature) plots the route and
% display current temperautre and distance.

global h;
cycle = route([1:end, 1]);
% update route
set(h,'Xdata',[city(cycle).long],'Ydata',[city(cycle).lat]);

% display current temperature and total distance
xlabel(sprintf('T = %6.1f        Total Distance = %6.1f', ...
                    temperature,                  current_distance));
drawnow


function LDLplot(X1, Y1, S1, C1)
%CREATEFIGURE(X1,Y1,S1,C1)
%  X1:  scatter x
%  Y1:  scatter y
%  S1:  scatter s
%  C1:  scatter c

%  Auto-generated by MATLAB on 21-Sep-2010 11:16:44
%  Copyright 2010 - 2011 MathWorks, Inc.

% Create figure
figure1 = figure;

% Create axes
axes1 = axes('Parent',figure1);
% Uncomment the following line to preserve the X-limits of the axes
% xlim(axes1,[3.6 5]);
% Uncomment the following line to preserve the Y-limits of the axes
% ylim(axes1,[1 5]);
box(axes1,'on');
hold(axes1,'all');

% Create scatter
scatter1 = scatter(X1,Y1,S1,C1,'MarkerEdgeColor',[0 0.498039215803146 0],...
    'Marker','.',...
    'Parent',axes1,...
    'DisplayName','data 1');

% Create xlabel
xlabel('LDL C at BL');

% Create ylabel
ylabel('LDL C at 12 weeks');

% Get xdata from plot
xdata1 = get(scatter1, 'xdata');
% Get ydata from plot
ydata1 = get(scatter1, 'ydata');
% Make sure data are column vectors
xdata1 = xdata1(:);
ydata1 = ydata1(:);

% Get axes ylim
axYLim1 = get(axes1, 'ylim');
% Get axes xlim
axXLim1 = get(axes1, 'xlim');

% Find the mean
xmean1 = mean(xdata1);
% Get coordinates for the mean line
meanValue1 = [xmean1 xmean1];
% Plot the mean
statLine1 = plot(meanValue1,axYLim1,'DisplayName','   x mean',...
    'Parent',axes1,...
    'Tag','mean x',...
    'LineWidth',3,...
    'LineStyle','--',...
    'Color',[1 0 0]);

% Set new line in proper position
setLineOrder(axes1, statLine1, scatter1);

% Find the mean
ymean1 = mean(ydata1);
% Get coordinates for the mean line
meanValue2 = [ymean1 ymean1];
% Plot the mean
statLine2 = plot(axXLim1,meanValue2,'DisplayName','   y mean',...
    'Parent',axes1,...
    'Tag','mean y',...
    'LineWidth',3,...
    'LineStyle','-.',...
    'Color',[1 0 0]);

% Set new line in proper position
setLineOrder(axes1, statLine2, scatter1);

% Create legend
legend(axes1,'show');

%-------------------------------------------------------------------------%
function setLineOrder(axesh1, newLine1, associatedLine1)
%SETLINEORDER(AXESH1,NEWLINE1,ASSOCIATEDLINE1)
%  Set line order
%  AXESH1:  axes
%  NEWLINE1:  new line
%  ASSOCIATEDLINE1:  associated line

% Get the axes children
hChildren = get(axesh1,'Children');
% Remove the new line
hChildren(hChildren==newLine1) = [];
% Get the index to the associatedLine
lineIndex = find(hChildren==associatedLine1);
% Reorder lines so the new line appears with associated data
hNewChildren = [hChildren(1:lineIndex-1);newLine1;hChildren(lineIndex:end)];
% Set the children:
set(axesh1,'Children',hNewChildren);


function [units, data_description] = segtype2units(unitCode)
   %segtype2units   retrieves unit and description based on the segtype code
   %'segtype' in antelope datasets indicate the natural units of the detector
   persistent codeKey
   if isempty(codeKey)
      codeKey = createCodeKey();
   end
   if codeKey.isKey(unitCode)
      details = codeKey(unitCode);
      units = details{1};
      data_description = details{2};
   else
      [units, data_description] = deal('null');
   end

   function SU = createCodeKey()
      %createSegUnits   creates a map  where SU(char) = {units, data_description}
      SU = containers.Map;
      SU('A') = {'nm / sec / sec','acceleration'};
      SU('B') = {'25 mw / m / m','UV (sunburn) index(NOAA)'};
      SU('D') = {'nm', 'displacement'};
      SU('H') = {'Pa','hydroacoustic'};
      SU('I') = {'Pa','infrasound'};
      SU('J') = {'watts','power (Joulses/sec) (UCSD)'};
      SU('K') = {'kPa','generic pressure (UCSB)'};
      SU('M') = {'mm','Wood-Anderson drum recorder'};
      SU('P') = {'mb','barometric pressure'};
      SU('R') = {'mm','rain fall (UCSD)'};
      SU('S') = {'nm / m','strain'};
      SU('T') = {'sec','time'};
      SU('V') = {'nm / sec','velocity'};
      SU('W') = {'watts / m / m', 'insolation'};
      SU('a') = {'deg', 'azimuth'};
      SU('b') = {'bits/ sec', 'bit rate'};
      SU('c') = {'counts', 'dimensionless integer'};
      SU('d') = {'m', 'depth or height (e.g., water)'};
      SU('f') = {'micromoles / sec / m /m', 'photoactive radiation flux'};
      SU('h') = {'pH','hydrogen ion concentration'};
      SU('i') = {'amp','electric curent'};
      SU('m') = {'bitmap','dimensionless bitmap'};
      SU('n') = {'nanoradians','angle (tilt)'};
      SU('o') = {'mg/l','diliution of oxygen (Mark VanScoy)'};
      SU('p') = {'percent','percentage'};
      SU('r') = {'in','rainfall (UCSD)'};
      SU('s') = {'m / sec', 'speed (e.g., wind)'};
      SU('t') = {'C','temperature'};
      SU('u') = {'microsiemens/cm','conductivity'};
      SU('v') = {'volts','electric potential'};
      SU('w') = {'rad / sec', 'rotation rate'};
      SU('-') = {'null','null'};
   end
end


%------------------------------------------------------------------
%--------- Show Conf Mat ------------------------------------------
%------------------------------------------------------------------

function ShowConfMat(ConfMatOpt)
disp('Stimulus\Response | Anger | Happiness | Neutral | Sadness | Surprise');
disp('--------------------------------------------------------------------');
disp(sprintf('Anger                  %4.2f     %4.2f      %4.2f     %4.2f      %4.2f ',    ConfMatOpt(1,1), ConfMatOpt(1,2), ConfMatOpt(1,3), ConfMatOpt(1,4),  ConfMatOpt(1,5)));
disp(sprintf('Happiness              %4.2f     %4.2f      %4.2f     %4.2f      %4.2f ', ConfMatOpt(2,1), ConfMatOpt(2,2), ConfMatOpt(2,3), ConfMatOpt(2,4),  ConfMatOpt(2,5)));
disp(sprintf('Neutral                %4.2f     %4.2f      %4.2f     %4.2f      %4.2f ',    ConfMatOpt(3,1), ConfMatOpt(3,2), ConfMatOpt(3,3), ConfMatOpt(3,4),  ConfMatOpt(3,5)));
disp(sprintf('Sadness                %4.2f     %4.2f      %4.2f     %4.2f      %4.2f ',  ConfMatOpt(4,1), ConfMatOpt(4,2), ConfMatOpt(4,3), ConfMatOpt(4,4),  ConfMatOpt(4,5)));
disp(sprintf('Surprise               %4.2f     %4.2f      %4.2f     %4.2f      %4.2f ',    ConfMatOpt(5,1), ConfMatOpt(5,2), ConfMatOpt(5,3), ConfMatOpt(5,4),  ConfMatOpt(5,5)));
disp('------------------------------------------------------------------------------');
return


function [PB] = EB2PB(EB)
% Convert computery things from exabytes to petabytes.
% Chad A. Greene 2012
PB = EB*1024 ;


function y = getBatch(imdb, images, varargin)
% GET_BATCH  Load, preprocess, and pack images for CNN evaluation

opts.imageSize = [512, 512] - 128 ;
opts.numAugments = 1 ;
opts.transformation = 'none' ;
opts.rgbMean = [] ;
opts.rgbVariance = zeros(0,3,'single') ;
opts.labelStride = 1 ;
opts.labelOffset = 0 ;
opts.classWeights = ones(1,21,'single') ;
opts.interpolation = 'bilinear' ;
opts.numThreads = 1 ;
opts.prefetch = false ;
opts.useGpu = false ;
opts = vl_argparse(opts, varargin);

if opts.prefetch
  % to be implemented
  ims = [] ;
  labels = [] ;
  return ;
end

if ~isempty(opts.rgbVariance) && isempty(opts.rgbMean)
  opts.rgbMean = single([128;128;128]) ;
end
if ~isempty(opts.rgbMean)
  opts.rgbMean = reshape(opts.rgbMean, [1 1 3]) ;
end

% space for images
ims = zeros(opts.imageSize(1), opts.imageSize(2), 3, ...
  numel(images)*opts.numAugments, 'single') ;

% space for labels
lx = opts.labelOffset : opts.labelStride : opts.imageSize(2) ;
ly = opts.labelOffset : opts.labelStride : opts.imageSize(1) ;
labels = zeros(numel(ly), numel(lx), 1, numel(images)*opts.numAugments, 'single') ;
classWeights = [0 opts.classWeights(:)'] ;

im = cell(1,numel(images)) ;

si = 1 ;

for i=1:numel(images)

  % acquire image
  if isempty(im{i})
    rgbPath = sprintf(imdb.paths.image, imdb.images.name{images(i)}) ;
    labelsPath = sprintf(imdb.paths.classSegmentation, imdb.images.name{images(i)}) ;
    rgb = vl_imreadjpeg({rgbPath}) ;
    rgb = rgb{1} ;
    anno = imread(labelsPath) ;
  else
    rgb = im{i} ;
  end
  if size(rgb,3) == 1
    rgb = cat(3, rgb, rgb, rgb) ;
  end

  % crop & flip
  h = size(rgb,1) ;
  w = size(rgb,2) ;
  for ai = 1:opts.numAugments
    sz = opts.imageSize(1:2) ;
    scale = max(h/sz(1), w/sz(2)) ;
    scale = scale .* (1 + (rand(1)-.5)/5) ;

    sy = round(scale * ((1:sz(1)) - sz(1)/2) + h/2) ;
    sx = round(scale * ((1:sz(2)) - sz(2)/2) + w/2) ;
    if rand > 0.5, sx = fliplr(sx) ; end

    okx = find(1 <= sx & sx <= w) ;
    oky = find(1 <= sy & sy <= h) ;
    if ~isempty(opts.rgbMean)
      ims(oky,okx,:,si) = bsxfun(@minus, rgb(sy(oky),sx(okx),:), opts.rgbMean) ;
    else
      ims(oky,okx,:,si) = rgb(sy(oky),sx(okx),:) ;
    end

    tlabels = zeros(sz(1), sz(2), 'uint8') + 255 ;
    tlabels(oky,okx) = anno(sy(oky),sx(okx)) ;
    tlabels = single(tlabels(ly,lx)) ;
    tlabels = mod(tlabels + 1, 256) ; % 0 = ignore, 1 = bkg
    labels(:,:,1,si) = tlabels ;
    si = si + 1 ;
  end
end
if opts.useGpu
  ims = gpuArray(ims) ;
end
y = {'input', ims, 'label', labels} ;