function sr_img = superresolve_drcn(slidingWindows, magFactor)

lIm       = slidingWindows.referenceFrame;
upscaling = magFactor;

% Important Note: Grayscale or YCbCr input data expected!

currentDir = pwd;
cd('../algorithms/SRAlgorithms/DRCN'); % MAGI-FIX

run('snu_matconvnet/matlab/vl_setupnn.m');

model = ['sf',num2str(upscaling),'/DRCN_sf',num2str(upscaling),'.mat'];

if isempty(model)
    error('no model');
else
    modelPath = ['DRCN model/',model];
    %gpu = 1;
    gpu = 0; % MAGI-ADAPT
end

load(modelPath);

net = dagnn.DagNN.loadobj(net);

if gpu
    net.move('gpu');
end

managableMax = 300000;

if isa(lIm,'uint8'),
    lIm = single(lIm)/255;
else
    lIm = single(lIm);
end

lIm = imresize(lIm, upscaling, 'bicubic');

if size(lIm,3)>1
    imlowy = lIm(:,:,1);
    imlowy = max(16.0/255, min(235.0/255, imlowy));
    imlowcb = lIm(:,:,2);
    imlowcr = lIm(:,:,3);
else
    imlowy = lIm;
end

% Perform actual SR
if size(imlowy,1)*size(imlowy,2) > managableMax
    impred = runPatchDRCN(net, imlowy, gpu, 20);
else
    if gpu,
        imlowy = gpuArray(imlowy);
    end
    impred = runDRCN(net, imlowy, gpu);
end

if size(lIm,3) > 1
    impredColor = cat(3,impred,imlowcb,imlowcr);
else
    impredColor = impred;
end

sr_img = im2double(impredColor); % Going back to double images

cd(currentDir);


function [E, Es, O] = detectEdge(Iin, Din, id, C, model, sc, outFile, cacheFile)
  if(isstr(Iin)), Iin = imread(Iin); end
  if(isstr(Din)), Din = imread(Din); end
  if(isstr(model)), load(model); end

  opts = model.opts;
  rgbd = opts.rgbd;

  model.opts.multiscale=1;          % for top accuracy set multiscale=1
  model.opts.nTreesEval=4;          % for top speed set nTreesEval=1
  model.opts.nThreads=1;            % max number threads for evaluation
  model.opts.sharpen=0;             % for top speed set sharpen=0

  if(rgbd == 3),
    colorModel = model.colorModel.model;
    cues = getAllCues(Iin, Din, C, colorModel, opts.rgbd3opts.vars, cacheFile);
  end

  for i = 1:length(sc),
    I = imresize(Iin, sc(i), 'lanczos3');
    ng = zeros(size(I,1), size(I,2), 0);

    if(rgbd),
      D = imresize(Din, sc(i), 'nearest');
      D=single(D)/1e4;
    end
    if(rgbd==1),
      I=D;
    elseif(rgbd==2),
      I=cat(3,single(I)/255,D);
    elseif(rgbd==3),
      I=cat(3,single(I)/255,D,1e-3./D);
      ng = cat(3, cues{:});
      ng = imresize(ng, sc(i), 'lanczos3');
    end
    model.opts.nms = 0;
    [Es{i}, O{i}] = edgesDetect(I,model,ng);
    E{i} = edgesNmsMex(Es{i}, O{i}, 1, 5, 1.01, model.opts.nThreads);

    % model.opts.nms = 1;
    % [E{i} O{i}] = edgesDetect(I,model,ng);
  end
  if(~isempty(outFile)), save(outFile, 'E', 'Es', 'O'); end
end


function [RFcov, figHandle, all_models, weight, data] = rmPlotCoverage(vw, varargin)
% [RFcov, figHandle, all_models, weight, data] = rmPlotCoverage(vw, varargin)
% rmPlotCoverage - calulate the visual field coverage within an ROI
%
%
% Before you run this script, you have to load 'variance explained', 'eccentricity',
% 'polar-angle' and 'prf size' into 'co', 'map', 'ph' and 'amp' fields, respectively
%
% OUTPUT
%  RFcov
% INPUT
%  prf_size:        0 = plot pRF center; 1 = use pRF size
%  fieldRange:      maximum eccentricity to plot (deg)
%  method:          'sum','max', 'clipped average', 'signed profile'
%  newfig:          make a new figure (1) or not (0). (-1 indicates don't plot
%                       anything, just return the coverage map.)
%  nboot:           the number of bootstrapping (0 means no bootstrapping)
%  normalizeRange:  if true, scale z axis to [0 1]
%  smoothSigma:     median smoothing default: 2 nearest values
%  cothresh:        threshold by variance explained in model
%  eccthresh:       2-vector ecc limits (default = [0 1.5*fieldRange])
%  nsamples:        num samples in square grid (default = 128)
%  weight:          any of {'fixed', 'parameter map', 'variance explained'} (default = fixed)
%  weightBeta:      use beta values from rmModel to weight pRFs (default = false)
%  threshByCoh:     if true, threshold by values in coherence map, not variance explained in model
%                       (note: these are often the same, but don't have to be)  %   addcenters
%  addcenters:      1 = superimpose dots for pRF centers; 0 = do not show centers
%  dualVEthresh:    use both pRF model VE and GLM fit VE as threshold
%

% 08/02 KA wrote
% 08/04 KA added bootstrapping
% 08/04 SD various mods
% 09/02 SD large rearrangments
% 09/08 JW allow superposition of pRF centers; various minor debugs
% 02/10 MB added method 'betasum'
% 09/16 RL edited code to allow for the edge case of ROIs that are 1 or 2 voxels large
if notDefined('vw'),       error('View must be defined.'); end
if notDefined('varargin'), varargin{1} = 'dialog'; end

%% default parameters
vfc.prf_size = true;
vfc.fieldRange = min(30, vw.rm.retinotopyParams.analysis.maxRF);
vfc.method = 'max';
vfc.newfig = true;
vfc.nboot = 50;
vfc.normalizeRange = true;
vfc.smoothSigma = true;
vfc.cothresh = viewGet(vw, 'co thresh');
vfc.eccthresh = [0 1.5*vfc.fieldRange];
vfc.nSamples = 128;
vfc.meanThresh = 0;
vfc.weight = 'fixed';
vfc.weightBeta = 0;
vfc.cmap = 'jet';
vfc.clipn = 'fixed';
vfc.threshByCoh = false;
vfc.addCenters = true;
vfc.verbose = prefsVerboseCheck;
vfc.dualVEthresh = 0;

compVolume = false;

%% parse options
if strcmpi(varargin{1},'dialog')
    % get parameters from a dialog
    vfc = rmPlotCoverageDialog(vfc);
else
    for ii = 1:2:length(varargin)
        switch lower(varargin{ii})
            case 'prf_size',        vfc.prf_size        = varargin{ii+1}; % deg
            case 'fieldrange',      vfc.fieldRange      = varargin{ii+1}; % deg
            case 'method',          vfc.method          = varargin{ii+1}; % string
            case 'newfig',          vfc.newfig          = varargin{ii+1}; % boolean
            case 'nboot',           vfc.nboot           = varargin{ii+1}; % integer
            case 'normalizerange',  vfc.normalizeRange  = varargin{ii+1}; % boolean
            case 'smoothsigma',     vfc.smoothSigma     = varargin{ii+1}; % boolean
            case 'cothresh',        vfc.cothresh        = varargin{ii+1};
            case 'eccthresh',       vfc.eccthresh       = varargin{ii+1};
            case 'nsamples',        vfc.nSamples        = varargin{ii+1};
			case 'minmeanmap',      vfc.meanThresh		= varargin{ii+1};
            case 'weight',          vfc.weight          = varargin{ii+1};
            case 'weightbeta',      vfc.weightBeta      = varargin{ii+1};
			case 'cmap',			vfc.cmap			= varargin{ii+1};
            case 'threshbycoh',     vfc.threshByCoh     = varargin{ii+1};
            case 'addcenters',      vfc.addCenters      = varargin{ii+1}; % boolean
            case 'vfc.verbose',     vfc.verbose         = varargin{ii+1}; % boolean
            case 'dualvethresh',    vfc.dualVEthresh    = varargin{ii+1}; % boolean
        end
    end
end

%% load different pRF parameters
try
    rmModel   = viewGet(vw,'rmSelectedModel');
catch %#ok<CTCH>
    error('Need retModel information. Try using rmSelect. ');
end

% Get coordinates for current ROI
roi.coords   = viewGet(vw, 'roiCoords');
roi.indices  = viewGet(vw, 'roiIndices');
roi.name     = viewGet(vw, 'roiName');
curScan      = viewGet(vw, 'curScan');

% Get co and ph (vectors) for the current scan, within the
% current ROI.
vt      = vw.viewType;
co      = rmCoordsGet(vt, rmModel,'varexp',     roi.indices);
sigma1  = rmCoordsGet(vt, rmModel,'sigmamajor', roi.indices);
sigma2  = rmCoordsGet(vt, rmModel,'sigmaminor', roi.indices);
theta   = rmCoordsGet(vt, rmModel,'sigmatheta', roi.indices);
beta    = rmCoordsGet(vt, rmModel,'beta',       roi.indices);
x0      = rmCoordsGet(vt, rmModel,'x0',         roi.indices);
y0      = rmCoordsGet(vt, rmModel,'y0',         roi.indices);
clear rmModel

%%%%%%%%%%%%%%
% y flip note (r.a.s., 10/2009):
% ------------------------------
% I believe the stimulus generation code has had problems (from around
% 2006-2009) where all stimuli were up/down flipped with respect to the pRF
% sampling grid [X, Y]. As a consequence, all models solved in this time
% seem to have a Y flip. The values saved on disk are off; you can
% manually test this with an ROI like V2d which covers a quarterfield.
%
% I'm now trying to fix this issue. All accessor functions seem to have
% implicitly corrected the flip, but in hard to trace ways. (This involves
% a lot of post-hoc calls to functions like 'flipud' which made things
% confusing.) I'm trying to (1) fix the core problems in the stim code; and
% (2) remove the post-hoc corrections to the accessor code.
%
% But for now, there are a lot of models saved on disk, and it will take a
% long time to fix them. So, I'm keeping the y-flip correction, but making
% it explicit here. When the code is fixed and most models saved on disk
% are correct, we can remove this.
% y0 = -y0;

% ok. I think it is time to remove. I suggest putting in a flag to flip the
% y-dimension if requested, but otherwise not to.
if getpref('VISTA', 'verbose')
    warning('Negative y values plotted as lower visual field. This may be incorrect for old pRF models, as old code used to treat neagitve y as upper field.'); %#ok<*WNTAG>
end
%%%%%%%%%%%%%%

% grabbing both (x0, y0) and (pol, ecc) are redundant, and allow for the
% two specifications to get separated (because of the y-flip issue). So,
% re-express ecc and pol using x0 and y0.
[ph ecc] = cart2pol(x0, y0);

% If 'threshByCoh' is set (i.e., true), thresholding will be set based on the
% view struct's coherence field, instead of from the variance explained in
% the model.
if vfc.threshByCoh, co = vw.co{curScan}(roi.indices); end
if ~any(co), co = []; end

% If 'dualVEthresh' is set, thresholding will be based on both the VE of
% the model AND the VE of the GLM fit
if vfc.dualVEthresh == 1
    fprintf('[%s] Using dual VE thresholding \n',mfilename);
    if isempty(vw.dualVE)
        error('Need VE from GLM fit');
    else
        co = (co + vw.dualVE(roi.indices)) ./ 2;
    end
end

% Remove NaNs from subCo and subAmp that may be there if ROI
% includes volume voxels where there is no data.
NaNs = sum(isnan(co));
if NaNs
    fprintf('[%s]:WARNING:ROI includes voxels that have no data. These voxels are being ignored.',mfilename);
    notNaNs = ~isnan(co);
    co      = co(notNaNs);
    ph      = ph(notNaNs);
    ecc     = ecc(notNaNs);
end

% Find voxels which satisfy cothresh and eccthresh.
coIndices = co>vfc.cothresh & ...
    ecc>=vfc.eccthresh(1) & ecc<=vfc.eccthresh(2);
if ~any(coIndices)
   fprintf(1,'[%s]:No values above threshold.\n',mfilename);
   RFcov = zeros(vfc.nSamples);
   figHandle    = [];
   all_models   = [];
   weight       = [];
   data         = [];
   return
end

% also select by the mean map, if that's selected
if vfc.meanThresh > 0
	meanMapFile = fullfile(dataDir(vw), 'meanMap.mat');
	if ~exist(meanMapFile, 'file')
		warning(['A mean map threshold is specified, but no mean map ' ...
				 'exists. This threshold will be ignored for now.'])
	elseif isempty(map{vw.curScan})
		warning(['A mean map threshold is specified, but no mean map ' ...
				 'is computed for the current scan. ' ...
				 'This threshold will be ignored for now.'])
	else
		load(meanMapFile, 'map');
		meanVals = map{vw.curScan};
		if NaNs
			meanVals = meanVals(notNaNs);
		end
		coIndices = coIndices & (meanVals > vfc.meanThresh);
	end
end

% check
if vfc.verbose
    fprintf(1,'[%s]:co-thresh:%.2f.\n',mfilename,vfc.cothresh);
    fprintf(1,'[%s]:ecc-thresh:[%.2f %.2f].\n',mfilename,vfc.eccthresh(1),vfc.eccthresh(2));
    fprintf(1,'[%s]:Number of voxels above thresh in ROI: %d (total=%d).\n',...
        mfilename,sum(coIndices),numel(coIndices));
end

% Pull out co and ph for desired pixels
subCo    = co(coIndices);
subPh    = ph(coIndices);
subEcc   = ecc(coIndices);
subSize1 = single(sigma1(coIndices));
subSize2 = single(sigma2(coIndices));
subTheta = single(theta(coIndices));
subx0    = x0(coIndices);
suby0    = y0(coIndices);

% smooth sigma
if vfc.smoothSigma

    % Cannot do sigma smoothing if the ROI has less than 3 voxels.
    if size(subx0,2) < 3
        error('Cannot perform sigma smoothing when the ROI has less than 3 voxels. ')
    end

    if vfc.smoothSigma == 1
        vfc.smoothSigma = 3; %default
    end
    n = vfc.smoothSigma;

    %check sigma1==sigma2
    if subSize1 == subSize2
        for ii = 1:length(subSize1)
            %compute nearest coords
            dev = sqrt(abs(subx0(ii) - subx0).^2 + abs(suby0(ii) - suby0).^2);
            [dev, ix] = sort(dev); %#ok<*ASGLU>
            subSize1(ii) = median(subSize1(ix(1:n)));
        end
        subSize2 = subSize1;
    else
        for ii = 1:length(subSize1)
            %compute nearest coords
            dev = sqrt(abs(subx0(ii) - subx0).^2 + abs(suby0(ii) - suby0).^2);
            [dev, ix] = sort(dev);
            subSize1(ii) = median(subSize1(ix(1:n)));
            subSize2(ii) = median(subSize2(ix(1:n)));
        end
    end
end

% polar plot
subX = single(subEcc .* cos(subPh));
subY = single(subEcc .* sin(subPh));

% visual field
x = single( linspace(-vfc.fieldRange, vfc.fieldRange, vfc.nSamples) );
[X,Y] = meshgrid(x,x);

% gather this data to make accessible in the plot
if vfc.newfig  > -1   % -1 is a flag that we shouldn't plot the results
	data.figHandle = gcf;
	data.co        = co;
	data.ph        = ph;
	data.subCo     = subCo;
	data.subPh     = subPh;
	data.subEcc    = subEcc;
	data.subx0     = subx0;
	data.suby0     = suby0;
    data.subSize1  = subSize1;
    data.subSize2  = subSize2;
    data.X         = X;
	data.Y         = Y;

end

% For the pRF center plot, use a small constant pRF size
if vfc.prf_size==0
   subSize1 = ones(size(subSize1)) * 0.1;
   subSize2 = ones(size(subSize2)) * 0.1;
   subTheta = zeros(size(subTheta));
end

switch lower(vfc.weight)
    case 'fixed'
        weight = ones(size(subCo));

    case 'parameter map'
        weight = getCurDataROI(vw,'map',curScan,roi.coords);
        weight = weight(coIndices);

    case {'variance explained', 'varexp', 've'}
        weight = subCo;

    otherwise
        error('Unknown weight parameter: %s',vfc.weight);
end

if vfc.weightBeta==1
    weight = weight .* beta(coIndices);
end
weight = single(weight);

%% special case: for the 'density' coverage option, we don't need to
%% do a lot of memory-hungry steps like making all pRFs. So, I've set those
%% computations aside in their own subroutine. (ras)
if isequal( lower(vfc.method), 'density' )
	RFcov = prfCoverageDensityMap(vw, subx0, suby0, subSize1, X, Y);

	all_models = []; % not created for this option
	if vfc.newfig==-1
		figHandle = [];
	else
		[figHandle, data]  = createCoveragePlot(vw, RFcov, vfc, roi, data);
	end

	return
end

%% make all pRFs:
% make in small steps so we don't go into swap space for large ROIs
n = numel(subX);
s = [(1:ceil(n./1000):n-2) n+1];

% For the line above (which assumes that we have at least 3 voxels,
% probably for median smoothing),  s is an empty vector when n < 3
% -- and an empty rfcov is  returned when we try to plot the
% coverage. So modify s accordingly for these edge cases:
% The definition of s is not very intuitive
if n < 3
    s = [1:n+1];
end

all_models = zeros( numel(X), n, 'single' );
fprintf(1,'[%s]:Making %d pRFs:...', mfilename, n);
drawnow;
for n=1:numel(s)-1,
    % make rfs
    rf   = rfGaussian2d(X(:), Y(:),...
						subSize1(s(n):s(n+1)-1), ...
						subSize2(s(n):s(n+1)-1), ...
						subTheta(s(n):s(n+1)-1), ...
						subX(s(n):s(n+1)-1), ...
						subY(s(n):s(n+1)-1));
    all_models(:,s(n):s(n+1)-1) = rf;
end;
clear n s rf pred;
fprintf(1, 'Done.\n');
drawnow;

% Correct volume
if compVolume
    tmp = ones(size(all_models, 1), 1, 'single');

    vol = sigma1(coIndices).^2;
    vol = vol * (2 * pi);

    all_models = all_models ./ (tmp * vol);
end

% For the pRF center plot, put a constant value (1) within each Gaussian
if vfc.prf_size==0
    all_models(all_models>0.1)=1;
end

% weight all models
if isequal( lower(vfc.weight), 'fixed' )
	% if the weights are even, we avoid the redundant, memory-hungry
	% multiplication step that would otherwise be done.
	all_models_weighted = all_models;
else
	tmp = ones(size(all_models, 1), 1, 'single');
	all_models_weighted = all_models .* (tmp * weight);
	clear tmp
end

%% Different ways of combining them:
% 1) bootstrap (yes, no) 2) which statistic (sum, max, etc),
% bootstrap

% If we are only working with 1 voxel, bootstrapping does not do anything.
% We turn it off because the bootstp function does not handle this case well.
if size(subX,2) == 1
    vfc.nboot = 0;
end

if vfc.nboot>0
    if isempty(which('bootstrp'))
        warndlg('Bootstrap requires statistics toolbox');
        RFcov = [];
        return;
    end
    all_models(isnan(all_models))=0;

    switch lower(vfc.method)
        case {'sum','add','avg','average everything', 'average'}
            m = bootstrp(vfc.nboot, @mean, all_models');

        case {'max','profile','maximum profile' 'maximum'}
            m = bootstrp(vfc.nboot, @max, all_models');

        otherwise
            error('Unknown method %s',vfc.method)
    end
    RFcov=median(m,1)';

% no bootstrap
else
    switch lower(vfc.method)

        % coverage = sum(pRF(i)*w(i)) / (sum(pRF(i))
        case {'beta-sum','betasum','weight average'}
            RFcov = sum(all_models_weighted, 2) ./ sum(all_models,2);

        % coverage = sum(pRF(i)*w(i)) / (sum(pRF(i)) + clipping
        case {'clipped beta-sum','clippedbeta','clipped weight average'}
            % set all pRF beyond 2 sigmas to zero
            clipval = exp( -.5 *((2./1).^2));
            all_models(all_models<clipval) = 0;
            n = all_models > 0;

            % recompute all_models_weighted
			tmp = ones( size(all_models,1), 1, 'single' );
            all_models_weighted = all_models .* (tmp*weight);

            % compute weighted clipped sum/average
            sumn = sum(n,2);
            mask = sumn==0;
            sumn(mask) = 1; % prevent dividing by 0
            RFcov = sum(all_models_weighted,2) ./ sum(all_models,2);
            RFcov(mask) = 0;

            %clip to zero if n<clipn
            if isnumeric(vfc.clipn)
                RFcov(sumn<=vfc.clipn) = 0;
            end

        % coverage = sum(pRF(i)*w(i)) / (sum(w(i))
        case {'sum','add','avg','average','prf average'}
            RFcov = sum(all_models_weighted, 2) ./ sum(weight);

        % coverage = sum(pRF(i)*w(i)) / (sum(w(i)) + clipping
        case {'clipped average','clipped','clipped prf average'}
            % set all pRF beyond 2 sigmas to zero
            clipval = exp( -.5 *((2./1).^2));
            all_models(all_models<clipval) = 0;
            n = all_models > 0;

            % recompute all_models_weighted
			tmp = ones( size(all_models,1), 1, 'single' );
            all_models_weighted = all_models .* (tmp*weight);

            % compute weighted clipped mean
            sumn = sum(weight.*n);
            mask = sumn==0;
            sumn(mask) = 1; % prevent dividing by 0
            RFcov = sum(all_models_weighted,2) ./ sumn;
            RFcov(mask) = 0;

            %clip to zero if n<clipn
            if isnumeric(vfc.clipn)
                RFcov(sumn<=vfc.clipn) = 0;
            end

        % coverage = max(pRF(i))
        case {'maximum profile', 'max', 'maximum'}
            RFcov = max(all_models_weighted,[],2);

        case {'signed profile'}
            RFcov  = max(all_models_weighted,[],2);
            covmin = min(all_models_weighted,[],2);
            ii = RFcov<abs(covmin);
            RFcov(ii)=covmin(ii);

        case {'p','probability','weighted statistic corrected for upsampling'}
            RFcov = zeros(vfc.nSamples);

			% I guess this upsample factor assumes your functional data are
			% 2.5 x 2.5 x 3 mm?
            upsamplefactor = 2.5*2.5*3; % sigh.....
            for ii = 1:size(all_models,1)
                s = wstat(all_models(ii,:),weight,upsamplefactor);
                if isfinite(s.tval)
                    RFcov(ii) = 1 - t2p(s.tval,1,s.df);
                end
            end

        otherwise
            error('Unknown method %s',vfc.method)
    end
end

% convert 1D to 2D
RFcov = reshape( RFcov, [1 1] .* sqrt(numel(RFcov)) );

% When no voxels exceed threshold, return nan matrix rather than empty
% matrix
if sum(size(RFcov))==0
    RFcov=nan(nSamples,nSamples);
end

% if the newfig flag is set to -1, just return the image
if vfc.newfig==-1,
    figHandle = [];
else
	[figHandle, data] = createCoveragePlot(vw, RFcov, vfc, roi, data);
end

return
% /--------------------------------------------------------------------/ %

% /--------------------------------------------------------------------/ %
function [figHandle, data] = createCoveragePlot(vw, RFcov, vfc, roi, data)
% plotting subroutine for rmPlotCoverage. Broken off by ras 10/2009.
if vfc.newfig
    figHandle = figure('Color', 'w');
else
	figHandle = selectGraphWin;
end

headerStr = sprintf('Visual field coverage, ROI %s, scan %i', ...
					roi.name, vw.curScan);
set(gcf, 'Name', headerStr);

% normalize the color plots to 1
if vfc.normalizeRange,
	rfMax = max(RFcov(:));
else
	rfMax = 1;
end

img = RFcov ./ rfMax;
mask = makecircle(length(img));
img = img .* mask;
imagesc(data.X(1,:), data.Y(:,1), img);
set(gca, 'YDir', 'normal');
grid on

data.img = img;

colormap(vfc.cmap);
colorbar;

% start plotting
hold on;

% t = 0:.01:2*pi;
%
% % rings every 5 deg
% for n=(1:3)/3*vfc.fieldRange
%     polar(t,ones(size(t))*n,'w');
% end
% plot([0 0],[-vfc.fieldRange vfc.fieldRange],'w')
% plot([-sqrt(vfc.fieldRange^2/2) sqrt(vfc.fieldRange^2/2)],[-sqrt(vfc.fieldRange^2/2) sqrt(vfc.fieldRange^2/2)],'w')
% plot([-vfc.fieldRange vfc.fieldRange],[0 0],'w')
% plot([-sqrt(vfc.fieldRange^2/2) sqrt(vfc.fieldRange^2/2)],[sqrt(vfc.fieldRange^2/2) -sqrt(vfc.fieldRange^2/2)],'w')

% add polar grid on top
p.ringTicks = (1:3)/3*vfc.fieldRange;
p.color = 'w';
polarPlot([], p);

% add pRF centers if requested
if vfc.addCenters,
    inds = data.subEcc < vfc.fieldRange;
    plot(data.subx0(inds), data.suby0(inds), '.', ...
		'Color', [.5 .5 .5], 'MarkerSize', 4);
end

% scale z-axis
if vfc.normalizeRange
	if isequal( lower(vfc.method), 'maximum profile' )
		caxis([.5 1]);
	else
	    caxis([0 1]);
	end
else
    if min(RFcov(:))>=0
        caxis([0 ceil(max(RFcov(:)))]);
    else
        caxis([-1 1] * ceil(max(abs(RFcov(:)))));
    end
end
axis image;   % axis square;
xlim([-vfc.fieldRange vfc.fieldRange])
ylim([-vfc.fieldRange vfc.fieldRange])

title(roi.name, 'FontSize', 24, 'Interpreter', 'none');

% Save the data in gca('UserData')
set(gca, 'UserData', data);

return;
% /------------------------------------------------------------------/ %

% /------------------------------------------------------------------/ %
function RFcov = prfCoverageDensityMap(vw, x0, y0, sigma, X, Y) %#ok<INUSL>
% for each point (x, y) in visual space, this returns
% the proportion of voxels in the ROI for which (x, y) is
% within one standard deviation of the pRF center.
mask = NaN( size(X, 1), size(X, 2), length(x0) );

for v = 1:length(x0)
	% make a binary mask within one sigma of the center
	R = sqrt( (X - x0(v)) .^ 2 + (Y - y0(v)) .^ 2 );
	mask(:,:,v) = ( R < 2*sigma(v) );
end

% average (sum?) across all masks
RFcov = nansum(mask, 3);

return
% /------------------------------------------------------------------/ %

% /------------------------------------------------------------------/ %
function vfc = rmPlotCoverageDialog(vfc)
%% dialog to get parameters for rmPlotCoverage.
dlg(1).fieldName = 'method';
dlg(end).style = 'popup';
dlg(end).list = {'maximum profile' 'average' 'clipped average' ...
				 'density' 'signed profile' 'probability'};
dlg(end).string = 'Method for combining pRFs?';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'weight';
dlg(end).style = 'popup';
dlg(end).list = {'fixed' 'variance explained' 'parameter map'};
dlg(end).string = 'Method for weighting pRFs?';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'fieldRange';
dlg(end).style = 'number';
dlg(end).string = 'Visual Field Range (deg)?';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'nboot';
dlg(end).style = 'number';
dlg(end).string = 'Number of bootstrapping steps?';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'cmap';
dlg(end).style = 'popup';
dlg(end).list = mrvColorMaps;
dlg(end).string = 'If plotting, color map for coverage?';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'normalizeRange';
dlg(end).style = 'checkbox';
dlg(end).list = {};
dlg(end).string = 'Normalize data range to [0 1]';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'smoothSigma';
dlg(end).style = 'checkbox';
dlg(end).list = {};
dlg(end).string = 'Smooth sigma (medianfilter)';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'prf_size';
dlg(end).style = 'checkbox';
dlg(end).list = {};
dlg(end).string = 'Use pRF sizes from model';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'newfig';
dlg(end).style = 'checkbox';
dlg(end).string = 'Show results in new figure';
dlg(end).value = vfc.(dlg(end).fieldName);

dlg(end+1).fieldName = 'addCenters';
dlg(end).style = 'checkbox';
dlg(end).string = 'Add dots to show pRF centers';
dlg(end).value = vfc.(dlg(end).fieldName);

[resp ok] = generalDialog(dlg, mfilename);
if ~ok
	error('User Aborted.')
end
drawnow;

vfc = mergeStructures(vfc, resp);

return


function MapFig = createMapFig(Rob,Sen,Lmk, Trj, Frm, Fac,SimRob,SimSen,SimLmk,FigOpt)

% CREATEMAPFIG  Create 3D map figure and handles.
%   MAPFIG = CREATEMAPFIG(Rob,Sen,Lmk,SimRob,SimSen,SimLmk,MapFigure)
%   creates the map figure in figure 1, containing the following graphics
%   objects:
%       - a grid representing the ground
%       - simulated robots and sensors - in green
%       - estimated robots and sensors - in blue
%       - simulated landmarks (virtual world) - in red
%       - estimated landmarks, containing mean and uncertainty ellipsoid - in
%       colors depending on the landmark type.
%
%   The output MAPFIG is a structure of handles to all these graphics
%   objects. See the Matlab documentation for information about graphics
%   handles and the way to efficiently manipulate graphics. MAPFIG has the
%   following fields:
%       .fig    handle to the figure
%       .axes   handle to the axes
%       .simRob array of handles to the simulated robots 'patch' objects
%       .simSen array of handles to the simulated sensors 'patch' objects
%       .simLmk handle to the simulated landmarks 'line' object
%       .Rob array of handles to the estimated robots 'patch' objects
%       .Sen array of handles to the estimated sensors 'patch' objects
%       .Lmk array of structures with handles to the estimated landmarks, with
%           .mean       handle to the ellipsoid's center 'line' object
%           .ellipse    handle to the ellipsoid's contour 'line' object
%
%   The figure is updated using drawMapFig.
%
%   See also DRAWMAPFIG, CREATESENFIG, MAPOBSERVER, LINE, PATCH, SURFACE,
%   SET, GET.

%   Copyright 2008-2009 Joan Sola @ LAAS-CNRS.

global Map

% Figure
if FigOpt.createVideo
    MapFig.fig = figure(99);
    set(MapFig.fig, 'WindowStyle', 'normal');
    figPos     = get(MapFig.fig,'position');
    figSize    = FigOpt.map.size;
    newFigPos  = [0 figPos(2)  figSize];
    set(MapFig.fig,'position',newFigPos);
else
    if ishandle(99)
        MapFig.fig = figure(99);
    else
        MapFig.fig = figure(99);
        if ~strcmp( get(MapFig.fig, 'WindowStyle'), 'docked')
            figPos     = get(MapFig.fig,'position');
            figSize    = FigOpt.map.size;
            newFigPos  = [0 figPos(2)  figSize];
            set(MapFig.fig,'position',newFigPos);
        end
    end
end
clf
moreindatatip

set(MapFig.fig,...
    'numbertitle',   'off',...
    'name',          '3D Map',...
    'doublebuffer',  'off',...
    'renderer',      FigOpt.renderer,...
    'toolbar',       'none',...
    'color',         FigOpt.map.colors.bckgnd);
cameratoolbar('show');
cameratoolbar('setmode','orbit');

% World dimensions
world.lims         = FigOpt.map.lims;
world.dims.l       = FigOpt.map.lims.xMax - FigOpt.map.lims.xMin;
world.dims.w       = FigOpt.map.lims.yMax - FigOpt.map.lims.yMin;
world.dims.h       = FigOpt.map.lims.zMax - FigOpt.map.lims.zMin;
world.center.xMean = (FigOpt.map.lims.xMax + FigOpt.map.lims.xMin)/2;
world.center.yMean = (FigOpt.map.lims.yMax + FigOpt.map.lims.yMin)/2;
world.center.zMean = (FigOpt.map.lims.zMax + FigOpt.map.lims.zMin)/2;

% Map viewpoint
viewPnt = mapObserver(world,FigOpt.map.view);

% Axes
axis equal
MapFig.axes = gca;
set(MapFig.axes,...
    'parent',              MapFig.fig,...
    'nextplot',            'replacechildren',...
    'position',            [ 0 0 1 1],...
    'SortMethod',          'childorder',... % {'depth'  'childorder'}
    'projection',          FigOpt.map.proj,...
    'CameraPosition',      viewPnt.X,...
    'CameraPositionMode',  'manual',...
    'CameraUpVector',      viewPnt.upvec,...
    'CameraUpVectorMode',  'manual',...
    'CameraViewAngle',     viewPnt.fov,...
    'CameraViewAngleMode', 'manual',...
    'CameraTarget',        viewPnt.tgt,...
    'CameraTargetMode',    'manual',...
    'xlim',                [FigOpt.map.lims.xMin FigOpt.map.lims.xMax],...
    'ylim',                [FigOpt.map.lims.yMin FigOpt.map.lims.yMax],...
    'zlim',                [FigOpt.map.lims.zMin FigOpt.map.lims.zMax],...
    'alimmode',            'manual',...
    'climmode',            'manual',...
    'vis',                 'off');%,...

% OBJECTS COMMON TO SIMULATION AND ESTIMATION
% Ground
MapFig.ground = createGround(FigOpt.map,MapFig.axes,FigOpt.map.colors.ground);

% ESTIMATED OBJECTS
% robots
for rob = 1:numel(Rob)

    % create and draw robot - with ellipse
    MapFig.Rob(rob).patch = createObjPatch(Rob(rob),FigOpt.map.colors.est,MapFig.axes);
    MapFig.Rob(rob).ellipse = line(...
        'parent', MapFig.axes,...
        'xdata',  [],    ...
        'ydata',  [],    ...
        'zdata',  [],    ...
        'color',  'r',   ...
        'marker', 'none');

    % sensors
    for sen = Rob(rob).sensors

        % create and draw sensor
        MapFig.Sen(sen) = createObjPatch(Sen(sen),FigOpt.map.colors.est,MapFig.axes);

        % redraw sensor in robot frame
        F = composeFrames(Rob(rob).frame,Sen(sen).frame);
        drawObject(MapFig.Sen(sen),Sen(sen),F);

    end

    % trajectory
    n = size(Frm,2);
    Z = zeros(2,n);
    MapFig.Rob(rob).trj = line(...
        Z,    ...
        Z,    ...
        Z,    ...
        'parent', MapFig.axes,...
        'visible','off', ...
        'color',  FigOpt.map.colors.graph.motion,   ...
        'marker', 'o');

    % measurement factors
    n = numel(Fac);
    Z = zeros(2,n);
    MapFig.Rob(rob).factors = line(...
        Z,    ...
        Z,    ...
        Z,    ...
        'parent', MapFig.axes,...
        'visible','off', ...
        'color',  FigOpt.map.colors.graph.meas,   ...
        'marker', 'none');

end

% landmarks
for lmk = 1:numel(Lmk)

    MapFig.Lmk(lmk) = createLmkGraphics(Lmk(lmk),FigOpt.map.colors.label,MapFig.axes);

end

% SIMULATED OBJECTS
if ~isempty(SimRob) && ~isempty(SimSen) && ~isempty(SimLmk)

    % Landmarks - do not loop, draw all at once
    MapFig.simLmk = createSimLmkGraphics(SimLmk,FigOpt.map.colors.simLmk,MapFig.axes,FigOpt.map.showSimLmk);

    % Robots
    for rob = 1:numel(SimRob)

        % create and draw robot
        MapFig.simRob(rob) = createObjPatch(SimRob(rob),FigOpt.map.colors.simu,MapFig.axes);

        % Sensors
        for sen = SimRob(rob).sensors

            % create and draw sensor
            MapFig.simSen(sen) = createObjPatch(SimSen(sen),FigOpt.map.colors.simu,MapFig.axes);

            % redraw sensor in robot frame
            F = composeFrames(SimRob(rob).frame,SimSen(sen).frame);
            drawObject(MapFig.simSen(sen),SimSen(sen),F);
        end
    end
end

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB 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
%   (at your option) any later version.
%
%   SLAMTB 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 SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function [s1 s2 distances] = findTwoNearest(Input,nodes) %#codegen

NumOfNodes = size(nodes,2);

distances = zeros(1,NumOfNodes);
for i=1:NumOfNodes
    distances(i) = norm(Input - nodes(:,i));
end

[sdistances indices] = sort(distances);

s1 = indices(1);
s2 = indices(2);

% MEX Code Generation:

% mexcfg = coder.config('mex');
% mexcfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';
% codegen -config mexcfg findTwoNearest -args {coder.typeof(In(:,n),[Inf 1]),coder.typeof(nodes,[Inf Inf])}


function [t,amps,data,aux] = read_intan_data(filename)

% [t,amps,data,aux] = read_intan_data
%
% Opens file selection GUI to select and then read data from an Intan
% amplifier data file (*.int).
%
% t = time vector (in seconds)
% amps = vector listing active amplifier channels
% data = matrix of electrode-referred amplifier signals (in microvolts)
% aux = matrix of six auxiliary TTL input signals
%
% Example usage:
%  >> [t,amps,data,aux] = read_intan_data;
%  >> plot(t,data(:,1));
%
% Version 1.1, June 26, 2010
% (c) 2010, Intan Technologies, LLC
% For more information, see http://www.intantech.com
% For updates and latest version, see http://www.intantech.com/software.html
%
% 06-22-10 Added GUI file selection and optimized: Craig Patten, Plexon, Inc.

% use MATLAB predefined gui uigetfile to select the file(s) to analyze
if ~exist('filename','var')
    [file, path, filterindex] = uigetfile('*.int','Select a .int file','MultiSelect', 'off');
    filename = [path,file];
end

fid = fopen(filename, 'r');

% Read first three header bytes encoding file version
for i=1:3
    header(i) = fread(fid, 1, 'uint8');
end

if (header(1) ~= 128)
    error('Improper data file format.');
end

if (header(2) ~= 1 || header(3) ~= 1)
    warning('Data file version may not be compatible with this m-file.');
end

% Now see which amplifier channels are saved in this file.
for i=1:64
    amp_on(i) = fread(fid, 1, 'uint8');
end

num_amps = sum(amp_on);

% Create a list of amplifier channels in this file.
amps = zeros(1,num_amps);
index = 1;
for i=1:64
    if (amp_on(i) == 1)
        amps(index) = i;
        index = index + 1;
    end
end

% Now search for the end of the file to find out the length of the data.
% t_count = 0;
% while (~feof(fid))
%    fread(fid, 1+4*num_amps, 'uint8');
%    t_count = t_count + 1;
% end
% t_count = t_count - 1;
% t_max = t_count/25000;

%-----------------------------------
% replace above code with a more efficient method CDP 06-24-10
s = dir(filename);
filesize = s.bytes;
t_count = (filesize - 67)/(num_amps*4 + 1);
t_max = t_count/25000;
%-----------------------------------

% print channel (singular) when there is only one channel! CDP 06-24-10
if num_amps == 1;
    fprintf(1, '\nData file contains %0.2f seconds of data from %d amplifier channel.\n', t_max, num_amps);
    fprintf(1, 'Channel: ');
else
    fprintf(1, '\nData file contains %0.2f seconds of data from %d amplifier channels.\n', t_max, num_amps);
    fprintf(1, 'Channels: ');
end

for i=1:num_amps
    fprintf(1, '%d ', amps(i));
end
fprintf(1, '\n\n');

% Pre-allocate large data matrices.
aux = zeros(t_count,6,'uint8');
t = (0:1:(t_count-1))/25000;
t = t';

%--------------------------------------
% Replace code code below with much faster code CDP 06-24-10
% Go back to the beginning of the file...
frewind(fid);

% ...skip the header this time...
fread(fid, 3+64, 'uint8');

% allocate space to read the entire file
data2 = zeros((filesize-67),1,'uint8');
% read the entire file
data2 = fread(fid,(filesize-67),'uint8=>uint8');

% extract the digital data
aux_data = data2((num_amps*4)+1:num_amps*4+1:filesize-67);

% extract individual bits
aux = [bitget(aux_data,6),bitget(aux_data,5),bitget(aux_data,4),bitget(aux_data,3),bitget(aux_data,2),bitget(aux_data,1)];
clear aux_data;

% delete the digital data
data2((num_amps*4)+1:num_amps*4+1:filesize-67) = [];

% convert the remaining data from bytes to single
data2 = typecast(data2,'single');

data = zeros(t_count,num_amps);
% de-mux the channels
for ind = 1:num_amps
    data(:,ind) = data2(ind:num_amps:length(data2));
end
%---------------------------------------

% % Go back to the beginning of the file...
% frewind(fid);
%
% % ...skip the header this time...
% fread(fid, 3+64, 'uint8');
%
% % ...and read all the data.
% fprintf(1, 'Reading data...  (This may take a while.)\n\n');
% for i=1:t_count
%     for j=1:num_amps
%         data(i,j) = double(fread(fid, 1, 'float32'));
%     end
%
%     aux_byte = fread(fid, 1, 'uint8');
%
%     % Decode auxiliary TTL inputs
%     if aux_byte >= 32
%         aux(i,6) = 1;
%         aux_byte = aux_byte - 32;
%     end
%     if aux_byte >= 16
%         aux(i,5) = 1;
%         aux_byte = aux_byte - 16;
%     end
%     if aux_byte >= 8
%         aux(i,4) = 1;
%         aux_byte = aux_byte - 8;
%     end
%     if aux_byte >= 4
%         aux(i,3) = 1;
%         aux_byte = aux_byte - 4;
%     end
%     if aux_byte >= 2
%         aux(i,2) = 1;
%         aux_byte = aux_byte - 2;
%     end
%     if aux_byte >= 1
%         aux(i,1) = 1;
%         aux_byte = aux_byte - 1;
%     end
% end

% Close file, and we're done.
fclose(fid);


% This script is a demonstration of how to train a toy acoustic model for
% English using filterbank features for the TIMIT task. Note that the demo
% does not contain the TIMIT corpus. You need to provide the TIMIT corpus
% root directory.
%
% Warning: this demo assumes that your system has at least 8GB of free
% memory for Matlab.
%
% Author: Xiong Xiao, NTU, Singapore
% Date Created: 10 Oct 2013
% Last Modified: 16 Jun 2015
%

clear
para.IO.nStream = 2;        % Two input streams, one is the MFCC features, and the other is the frame-level phone label.
para.NET.sequential = 0;    % Use frame-based training, not sentence based training
para.NET.batchSize = 256;   % minibatch size
para.NET.learning_rate = 3e-2;      % global learning rate
para.NET.momentum = [0.5];          % momentun
para.NET.L2weight = 3e-4;           % L2 regularization weight
para.useGPU = 0;                    % whether to use GPU
para.displayInterval = 100;         % display training progress after N minibathces
para.checkGradient = 0;             % whether to perform gradient checking before training
para.stopImprovement = 0.1;         % when to stop the training, Most of time I use Ctr+C :)
para.reduceLearnRate = 0.5;         % when to start reducing learning rate
para.reduceLearnRateSpeed = 0.7;    % how fast do we decay the learning rate in each iteration
para.maxItr = 50;                   % maximum number of iterations allowed
para.minItr = 20;                   % maximum number of iterations allowed

para.local.V = 39;                  % put task specific configurations into para.local. Here we specify that we use only 39 phones.
para.local.fs = 16000;              % sampling rate, we can use 16000Hz or 8000Hz.
para.local.doCMN = 1;               % whether to perform cepstral mean normalization for each utterance

[Data_tr, Data_cv, para] = LoadData_TIMIT(para, 'train');

% generate the preprocessing of streams. We need to generate dynamic
% featuers for stream 1, i.e. the fbank stream.
para.preprocessing{1}{1}.name = 'delta';
para.preprocessing{1}{end}.delta_order = 2;   % use upto second derivative of MFCCs
para.preprocessing{1}{end}.inputDim = 40;
para.preprocessing{1}{end}.outputDim = 120;

feat_tmp = cell2mat(Data_tr(1).data(1:10:end));
feat_tmp_delta = FeaturePipe(feat_tmp, para.preprocessing{1});  % generate the MFCCs with dynamic features
tmpProcessing = genDNNPreprocessing_splice_norm(feat_tmp_delta, para.IO.context(1));    % generate the splicing setting, and global mean and variance normalization processing
para.preprocessing{1} = [para.preprocessing{1} tmpProcessing];
feat_tmp2 = FeaturePipe(feat_tmp, para.preprocessing{1});
plot(mean(feat_tmp2')); hold on         % verify that we now have normal distributed input features
plot(std(feat_tmp2')); hold off;

para.preprocessing{2} = {};     % we don't need any preprocessing for the second stream, i.e. the label

inputDim = para.preprocessing{1}{end}.outputDim;
hiddenLayerSize = [1024 1024 1024];        % you can generate deeper network by using something like: hiddenLayerSize = [512 512 200 512]. Then it will generate 4 hidden layers.
outputDim = length(unique(cell2mat(Data_tr(2).data)));
cost_function = 'cross_entropy';
layer = genNetworkFeedForward_v2(inputDim, hiddenLayerSize, outputDim, cost_function);
para.cost_func.layer_idx = length(layer);
para.cost_func.layer_weight = [1];

para.output = sprintf('nnet/DNN_phone_TIMIT');
para.output = sprintf('%s.U%d.%d', para.output, length(Data_tr(1).data), inputDim);
for i=1:length(hiddenLayerSize)
    para.output = sprintf('%s-%d', para.output, hiddenLayerSize(i));
end
para.output = sprintf('%s-%d.L2_%s.LR_%s/nnet', para.output, outputDim, FormatFloat4Name(para.NET.L2weight),FormatFloat4Name(para.NET.learning_rate));
LOG = [];

trainGraph_SGD(layer, Data_tr, Data_cv, para, LOG);


function [out] = VBA_spm_eeg_displayECD(Pos,Orient,Var,Names,options)
% Plot dipole positions onto the SPM canonical mesh
% FORMAT [out] = spm_eeg_displayECD(Pos,Orient,Var,Names,options)
%
% IN (admissible choices):
%   - Pos: a 3xndip matrix containing the positions of the dipoles in
%   the canonical frame of reference
%   - Orient: the same with dipole orientations
%   - Var: the same with position variance
%   - Names: the same with dipole names
%   - options: an optional structure containing
%       .hfig: the handle of the display figure
%       .tag: the tag to be associated with the created UI objects
%       .add: binary variable ({0}, 1: just add dipole in the figure .hfig)
%
% OUT:
%   - out: a structure containing the handles of the object in the figure
%   (including the mesh, the dipoles, the transparency slider, etc...)
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Jean Daunizeau
% $Id: spm_eeg_displayECD.m 5737 2013-11-10 20:23:49Z karl $

% checks and defaults
%--------------------------------------------------------------------------
hfig       = [];
ParentAxes = [];
query      = [];
handles    = [];
tag        = '';
try, options; catch, options = [];      end
try, hfig        = options.hfig;        end
try, tag         = options.tag;         end
try, ParentAxes  = options.ParentAxes;  end
try, query       = options.query;       end
try, handles     = options.handles;     end
try
    figure(hfig);
catch
    hfig  = VBA_spm_figure('GetWin','Graphics');
    VBA_spm_figure('Clear',hfig);
    ParentAxes = axes('parent',hfig);
end
try
    markersize = options.markersize;
catch
    markersize = 20;
end
try
    meshsurf = options.meshsurf;
catch
    meshsurf = fullfile(spm('Dir'),'canonical','cortex_5124.surf.gii');
end

if isscalar(Var), Var = Pos*0 + Var^2;   end
try, Pos{1};    catch, Pos = {Pos};      end
try, Orient{1}; catch, Orient = {Orient};end
try, Var{1};    catch, Var = {Var};      end

ndip = size(Pos{1},2);
if ~exist('Names','var') || isempty(Names)
    for i=1:ndip
        Names{i} = num2str(i);
    end
end

col = ['b','g','r','c','m','y','k','w'];
tmp = ceil(ndip./numel(col));
col = repmat(col,1,tmp);
pa  = get(ParentAxes,'position');

if ndip > 0

    if isempty(query)
        opt.hfig = hfig;
        opt.ParentAxes = ParentAxes;
        opt.visible = 'off';
        pos2 = [pa(1),pa(2)+0.25*pa(4),0.03,0.5*pa(4)];
        out  = VBA_spm_eeg_render(meshsurf,opt);
        handles.mesh = out.handles.p;
        handles.BUTTONS.transp = out.handles.transp;
        handles.hfig = out.handles.fi;
        handles.ParentAxes  = out.handles.ParentAxes;
        set(handles.mesh,...
            'facealpha',0.1,...
            'visible','on')
        set(handles.BUTTONS.transp,...
            'value',0.1,...
            'position',pos2,...
            'visible','on')
    end

    set(ParentAxes,'nextplot','add')
    for j=1:length(Pos)
        for i =1:ndip
            try
                set(handles.hp(j,i),...
                    'xdata',Pos{j}(1,i),...
                    'ydata',Pos{j}(2,i),...
                    'zdata',Pos{j}(3,i));
            catch
                handles.hp(j,i) = plot3(handles.ParentAxes,...
                    Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                    [col(i),'.'],...
                    'markerSize',markersize,...
                    'visible','off');
            end
            try
                no = sqrt(sum(Orient{j}(:,i).^2));
                if no > 0
                    Oi = 1e1.*Orient{j}(:,i)./no;
                else
                    Oi = 1e-5*ones(3,1);
                end
                try
                    set(handles.hq(j,i),...
                        'xdata',Pos{j}(1,i),...
                        'ydata',Pos{j}(2,i),...
                        'zdata',Pos{j}(3,i),...
                        'udata',Oi(1),...
                        'vdata',Oi(2),...
                        'wdata',Oi(3))
                catch
                    handles.hq(j,i) = quiver3(handles.ParentAxes,...
                        Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                        Oi(1),Oi(2),Oi(3),col(i),...
                        'lineWidth',2,'visible','off');
                end
                if isequal(query,'add')
                    set(handles.hq(j,i),...
                        'LineStyle','--',...
                        'lineWidth',1)
                end
            end
            [x,y,z]= ellipsoid(Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                1.*sqrt(Var{j}(1,i)),1.*sqrt(Var{j}(2,i)),1.*sqrt(Var{j}(1,i)),20);
            try
                set(handles.hs(j,i),...
                    'xdata',x,...
                    'ydata',y,...
                    'zdata',z);
            catch
                handles.hs(j,i) = surf(handles.ParentAxes,...
                    x,y,z,...
                    'edgecolor','none',...
                    'facecolor',col(i),...
                    'facealpha',0.2,...
                    'visible','off');
            end
            try
                set(handles.ht(j,i),...
                    'position',Pos{j}(:,i));
            catch
                handles.ht(j,i) = text(...
                    Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                    Names{i},...
                    'Parent',handles.ParentAxes,...
                    'visible','off');
            end
        end
    end

    if length(Pos) > 1

        try, set(handles.hp(end,:),'visible','on'); end
        try, set(handles.hq(end,:),'visible','on'); end
        try, set(handles.hs(end,:),'visible','on'); end
        try, set(handles.ht(end,:),'visible','on'); end

        handles.uic(1) = uicontrol(handles.fi,...
            'units','normalized',...
            'position',[0.45,0.5,0.2,0.03],...
            'style','radio','string','Show priors',...
            'callback',@doChange1,...
            'BackgroundColor',[1 1 1],...
            'tooltipstring','Display prior locations',...
            'userdata',handles,'value',0,...
            'BusyAction','cancel',...
            'Interruptible','off',...
            'tag','plotEEG');

        handles.uic(2) = uicontrol(handles.fi,...
            'units','normalized',...
            'position',[0.45,0.53,0.2,0.03],...
            'style','radio','string','Show posteriors',...
            'callback',@doChange2,...
            'BackgroundColor',[1 1 1],...
            'tooltipstring','Display posterior locations',...
            'userdata',handles,'value',1,...
            'BusyAction','cancel',...
            'Interruptible','off',...
            'tag','plotEEG');

    else

        try, set(handles.hp(1,:),'visible','on'); end
        try, set(handles.hq(1,:),'visible','on'); end
        try, set(handles.hs(1,:),'visible','on'); end
        try, set(handles.ht(1,:),'visible','on'); end

    end

end

try
    clear out
    out.handles = handles;
catch
    out = [];
end

%==========================================================================
function doChange1(i1,i2)
val = get(i1,'value');
handles = get(i1,'userdata');
if ~val
    try, set(handles.hp(1,:),'visible','off'); end
    try, set(handles.hq(1,:),'visible','off'); end
    try, set(handles.hs(1,:),'visible','off'); end
    try, set(handles.ht(1,:),'visible','off'); end
else
    try, set(handles.hp(1,:),'visible','on');  end
    try, set(handles.hq(1,:),'visible','on');  end
    try, set(handles.hs(1,:),'visible','on');  end
    try, set(handles.ht(1,:),'visible','on');  end
end

%==========================================================================
function doChange2(i1,i2)
val = get(i1,'value');
handles = get(i1,'userdata');
if ~val
    try, set(handles.hp(2,:),'visible','off'); end
    try, set(handles.hq(2,:),'visible','off'); end
    try, set(handles.hs(2,:),'visible','off'); end
    try, set(handles.ht(2,:),'visible','off'); end
else
    try, set(handles.hp(2,:),'visible','on');  end
    try, set(handles.hq(2,:),'visible','on');  end
    try, set(handles.hs(2,:),'visible','on');  end
    try, set(handles.ht(2,:),'visible','on');  end
end


function [batches, batch_padding] = rcnn_extract_regions(im, boxes, rcnn_model)
% [batches, batch_padding] = rcnn_extract_regions(im, boxes, rcnn_model)
%   Extract image regions and preprocess them for use in Caffe.
%   Output is a cell array of batches.
%   Each batch is a 4-D tensor formatted for input into Caffe:
%     - BGR channel order
%     - single precision
%     - mean subtracted
%     - dimensions from fastest to slowest: width, height, channel, batch_index
%
%   im is an image in RGB order as returned by imread
%   boxes are in [x1 y1 x2 y2] format with one box per row

% AUTORIGHTS
% ---------------------------------------------------------
% Copyright (c) 2014, Ross Girshick
%
% This file is part of the R-CNN code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

% convert image to BGR and single
im = single(im(:,:,[3 2 1]));
num_boxes = size(boxes, 1);
batch_size = rcnn_model.cnn.batch_size;
num_batches = ceil(num_boxes / batch_size);
batch_padding = batch_size - mod(num_boxes, batch_size);
if batch_padding == batch_size
  batch_padding = 0;
end

crop_mode = rcnn_model.detectors.crop_mode;
image_mean = rcnn_model.cnn.image_mean;
crop_size = size(image_mean,1);
crop_padding = rcnn_model.detectors.crop_padding;

batches = cell(num_batches, 1);
%for batch = 1:num_batches
parfor batch = 1:num_batches
  batch_start = (batch-1)*batch_size+1;
  batch_end = min(num_boxes, batch_start+batch_size-1);

  ims = zeros(crop_size, crop_size, 3, batch_size, 'single');
  for j = batch_start:batch_end
    bbox = boxes(j,:);
    crop = rcnn_im_crop(im, bbox, crop_mode, crop_size, ...
        crop_padding, image_mean);
    % swap dims 1 and 2 to make width the fastest dimension (for caffe)
    ims(:,:,:,j-batch_start+1) = permute(crop, [2 1 3]);
  end

  batches{batch} = ims;
end


function vf_out = get_vector_field(deformS,toPlanC,toScanNum,fromPlanC,fromScanNum)
% function vf = get_vector_field(deformS,toPlanC,toScanNum,fromPlanC,fromScanNum)
%
% APA, 09/24/2012

% Obtain base and moving scan UIDs
baseScanUID = deformS.baseScanUID;
movScanUID  = deformS.movScanUID;

% Figure out whether an inverse Vector field is required
indexToS = toPlanC{end};
indexFromS = fromPlanC{end};

toScanUID = toPlanC{indexToS.scan}(toScanNum).scanUID;
fromScanUID = fromPlanC{indexFromS.scan}(fromScanNum).scanUID;
if isequal(baseScanUID, fromScanUID)
    calc_inv_vf_flag = 0;
else
    calc_inv_vf_flag = 1;
end

% Create b-spline coefficients file
bspFileName = fullfile(getCERRPath,'ImageRegistration','tmpFiles',['bsp_coeffs_',baseScanUID,'_',movScanUID,'.txt']);
% bspFileName = fullfile(tempdir,'tmpFiles',['bsp_coeffs_',baseScanUID,'_',movScanUID,'.txt']);
success     = write_bspline_coeff_file(bspFileName,deformS.algorithmParamsS);

% Obtain Vf from b-splice coefficients
vfFileName = fullfile(getCERRPath,'ImageRegistration','tmpFiles',['vf_',baseScanUID,'_',movScanUID,'.mha']);
%vfFileName = fullfile(tempdir,'tmpFiles',['vf_',baseScanUID,'_',movScanUID,'.mha']);
system(['plastimatch xf-convert --input ',escapeSlashes(bspFileName), ' --output ', escapeSlashes(vfFileName), ' --output-type vf'])
%system(['plastimatch convert --xf ',escapeSlashes(bspFileName), ' --output-vf=', escapeSlashes(vfFileName)])
delete(bspFileName)

if calc_inv_vf_flag
    % Get dims, origin, spacing for toScan
    [uniformCT, uniformScanInfoS] = getUniformizedCTScan(0,toScanNum,toPlanC);
    uniformCT = permute(uniformCT, [2 1 3]);
    uniformCT = flipdim(uniformCT,3);
    % Change data type to int16 to allow (-)ve values
    uniformCT = int16(uniformCT) - int16(toPlanC{indexToS.scan}(toScanNum).scanInfo(1).CTOffset);
    % [dx, dy, dz]
    resolution = [uniformScanInfoS.grid2Units, uniformScanInfoS.grid1Units, uniformScanInfoS.sliceThickness] * 10;
    [xVals, yVals, zVals] = getUniformScanXYZVals(toPlanC{indexToS.scan}(toScanNum));
    offset = [xVals(1) -yVals(1) -zVals(end)] * 10;
    img_size = size(uniformCT);
    system(['vf_invert --input ', escapeSlashes(vfFileName), ' --output ', escapeSlashes(vfFileName), ' --dims="',num2str(img_size),'" --origin="',num2str(offset),'" --spacing="',num2str(resolution),'"'])
end

% infoS  = mha_read_header(vfFileName);
% vf = mha_read_volume(infoS);
[vf,infoS] = readmha(vfFileName);
%vf = flipdim(permute(vf,[2,1,3]),3);
delete(vfFileName)

vf_out(:,:,:,1) = flipdim(permute(vf(:,:,:,1),[2,1,3]),3)/10;
vf_out(:,:,:,2) = flipdim(permute(-vf(:,:,:,2),[2,1,3]),3)/10;
vf_out(:,:,:,3) = flipdim(permute(-vf(:,:,:,3),[2,1,3]),3)/10;


function [flatView, allDist] = measureCorticalDistanceLineROI(flatView, coords,plotFlag)
%
% USAGE: measureCorticalDistanceLineROI(flatView, coords)
%
% AUTHOR:  Dougherty
% DATE:    2002.04.10
% PURPOSE:
%   Compute the shortest cortical manifold distance between
%   points. The coords is passed in are assume to be a line ROI
%   where the first coord is at one end of the line and the last
%   coord is at the other end.
%
% HISTORY
%
% 7/16/02 djh, replaced mrSESSION.vAnatomyPath with global vANATOMYPATH

sampleDist = 5;

global vANATOMYPATH;
mmPerPix = readVolAnatHeader(vANATOMYPATH);

if (~exist('plotFlag','var'))
    plotFlag=1;
end

% Get a gray structure because we need the gray nodes.
grayView = getSelectedGray;
if isempty(grayView)
    grayView = initHiddenGray;
end

% the third coordinate is the 'slice', which, for flat views, means left or right hemisphere.
slice = coords(3,1);
if (slice==1)
    nodes = grayView.allLeftNodes;
    edges = grayView.allLeftEdges;
else
    nodes = grayView.allRightNodes;
    edges = grayView.allRightEdges;
end

nCoords = size(coords,2);
disp(['Finding nodes for ',num2str(nCoords),' coords...']);
allNodeIndices = zeros(1,nCoords);
% Find the nearest gray node for each coordinate.
%
for(ii=1:nCoords)
    % get nearest flat coordinate (not all points on the flat correspond to flat coordinates)
    flatDistances = (flatView.coords{slice}(1,:) - coords(1,ii)).^2 + ...
        (flatView.coords{slice}(2,:) - coords(2,ii)).^2;
    % There is a one-to-many mapping of flatCoords to grayCoords, but we ignore that
    % here by using 'min', which will always reuturn one value, even if there are several
    % identical minima.
    % FIX THIS- we should always grab layer 1, or something more consistent
    % than relying on min's arbitrary sort.
    [val,coordIndex] = min(flatDistances);

    grayNode = find(nodes(2,:) == flatView.grayCoords{slice}(1,coordIndex) & ...
            nodes(1,:) == flatView.grayCoords{slice}(2,coordIndex) & ...
            nodes(3,:) == flatView.grayCoords{slice}(3,coordIndex));
    % This should produce exactly one index.

    % Catch errors.
    if(isempty(grayNode))
        myErrorDlg('No gray nodes were found!');
    end
    if(length(grayNode)>1)
        disp([mfilename,': WARNING- coord ',num2str(ii),'- more than one grayNode found!']);
        grayNode = grayNode(1);
    end
    allNodeIndices(ii) = grayNode;
end

sampleNodes = allNodeIndices(1);
nodeIndices = allNodeIndices(2:end);
done = 0;
while(~done)
    % Now, compute the manifold distance to all other points from the given 'start' point.
    allDist = mrManDist(nodes, edges, sampleNodes(end), mmPerPix, -1, 0);
    drop = intersect(find(allDist<=sampleDist), nodeIndices);
    if(~isempty(drop))
        for(ii=1:length(drop))
            nodeIndices = nodeIndices(nodeIndices~=drop(ii));
        end
    end
    if(~isempty(nodeIndices))
        % *** WE SHOULD GET THE NEXT NEAREST
        nearest = find(allDist(nodeIndices)==min(allDist(nodeIndices)));
        nearest = nearest(1);
        sampleNodes = [sampleNodes, nodeIndices(nearest)];
        nodeIndices = nodeIndices(nodeIndices~=nodeIndices(nearest));
    end
    if(isempty(nodeIndices))
        done = 1;
    end
end

% This would be much simpler if we kept track of which coords we were sampling in the loop above.
for(ii=1:length(sampleNodes))
    thisOne = find(allNodeIndices==sampleNodes(ii));
    sampleCoords(:,ii) = coords(:, thisOne(1));
end

% We loop for the number of line segements, which is the number of coords - 1.
for(ii=1:size(sampleCoords,2)-1)
    % Draw a line for each measured segment
    h(ii) = line(sampleCoords(2,ii:ii+1), sampleCoords(1,ii:ii+1), 'Color', 'r', 'LineWidth', 2);

    % Now, compute the manifold distance between these points.
    % mrManDist returns the distance to all other points from the given 'start' point.
    allDist = mrManDist(nodes, edges, sampleNodes(ii), mmPerPix, -1, 0);
    % We just want the distance from the start point to the end point, so we
    % pull that out by providing the index of the end point.
    dist(ii) = allDist(sampleNodes(ii+1));
    disp(['Cortical distance of segment ',num2str(ii),': ',num2str(dist(ii)),' mm.']);
end
disp(['Total cortical distance: ',num2str(sum(dist)),' mm.']);
if (plotFlag)
uiwait(msgbox(['Total cortical distance: ',num2str(sum(dist)),' mm.'], ...
        'Cortical Distance', 'modal'));
end

    for ii=1:length(h)
    delete(h(ii))
end
return


function res = spm_eeg_reduce_cva(S)
% Plugin for data reduction using PCA
% FORMAT res = spm_eeg_reduce_pca(S)
%
% S                     - input structure
% fields of S:
%    S.ncomp            - number of PCA components
%
% Output:
%  res -
%   If no input is provided the plugin returns a cfg branch for itself
%
%   If input is provided:
%      montage struct implementing projection to PCA subspace
%______________________________________________________________________________________
% Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_eeg_reduce_cva.m 5675 2013-10-09 14:27:17Z vladimir $

if nargin == 0

    cvachan = cfg_branch;
    cvachan.tag = 'cvachan';
    cvachan.name = 'Channels to reduce';
    cvachan.val = {spm_cfg_eeg_channel_selector};

    refchan = cfg_branch;
    refchan.tag = 'refchan';
    refchan.name = 'Reference channels';
    refchan.val = {spm_cfg_eeg_channel_selector};

    ncomp = cfg_entry;
    ncomp.tag = 'ncomp';
    ncomp.name = 'Number of components';
    ncomp.strtype = 'n';
    ncomp.num = [1 1];
    ncomp.val = {1};
    ncomp.help = {'Number of components to retain'};

    outlabel = cfg_entry;
    outlabel.tag = 'outlabel';
    outlabel.name = 'Output channel label';
    outlabel.strtype = 's';
    outlabel.num = [1 Inf];
    outlabel.help = {'Label for the output channel(s).',...
        'Numbers are added for multiple channels.'};

    foi = cfg_entry;
    foi.tag = 'foi';
    foi.name = 'Frequency band of interest';
    foi.strtype = 'r';
    foi.num = [1 2];
    foi.val = {[0 Inf]};
    foi.help = {'Frequency window to optimize for'};

    tshiftwin = cfg_entry;
    tshiftwin.tag = 'tshiftwin';
    tshiftwin.name = 'Time shift window';
    tshiftwin.strtype = 'r';
    tshiftwin.num = [1 2];
    tshiftwin.val = {[0 0]};
    tshiftwin.help = {'Time shift window (ms). [0 0] - instantaneous'};

    tshiftres = cfg_entry;
    tshiftres.tag = 'tshiftres';
    tshiftres.name = 'Time shift resolution';
    tshiftres.strtype = 'r';
    tshiftres.num = [1 1];
    tshiftres.val = {5};
    tshiftres.help = {'Time shift resolution (ms)'};

    chanset = cfg_branch;
    chanset.tag = 'chanset';
    chanset.name = 'Set';
    chanset.val = {cvachan, refchan, ncomp, outlabel, foi, tshiftwin, tshiftres};

    chansets = cfg_repeat;
    chansets.tag = 'chansets';
    chansets.name = 'Channel sets';
    chansets.values = {chanset};
    chansets.num = [1 Inf];
    chansets.val = {chanset};

    cva = cfg_branch;
    cva.tag = 'cva';
    cva.name = 'CVA';
    cva.val = {chansets};

    res = cva;

    return
end

D = S.D;

nsets = numel(S.chanset);
badind = D.badchannels;

% Assuming projecting to columns
montage = [];
montage.labelorg    = D.chanlabels;
montage.labelnew    = {};
montage.chantypenew = {};
montage.tra         = zeros(0, D.nchannels);

spm('Pointer', 'Watch');drawnow;
spm_progress_bar('Init', nsets, 'Channel sets processed'); drawnow;
if nsets > 100, Ibar = floor(linspace(1, nsets,100));
else Ibar = 1:nsets; end

for i = 1:nsets

    spm_progress_bar('Set','ylabel','preparing data...');

    cvaind = setdiff(D.selectchannels(spm_cfg_eeg_channel_selector(S.chanset(i).cvachan.channels)), badind);
    refind = setdiff(D.selectchannels(spm_cfg_eeg_channel_selector(S.chanset(i).refchan.channels)), badind);

    if any(S.chanset(i).tshiftwin)
        tshiftind = S.chanset(i).tshiftwin(1):S.chanset(i).tshiftres:S.chanset(i).tshiftwin(2);
        tshiftind = [0 round(1e-3*D.fsample*tshiftind)];
        tshiftind = repmat(1:D.nsamples, length(tshiftind), 1)+repmat(tshiftind(:), 1, D.nsamples);
        tshiftind = tshiftind(:, all(tshiftind>0 & tshiftind<=D.nsamples));
    else
        tshiftind = repmat(1:D.nsamples, 2, 1);
    end

    nshift = size(tshiftind, 1)-1;
    nt     = size(tshiftind, 2);

    Y  = zeros(length(cvaind), nt, D.ntrials);
    Yr = zeros(length(refind)*nshift, nt, D.ntrials);

    for j = 1:D.ntrials
        cY  = D(cvaind, :, j);
        cYr = D(refind, :, j);

        if S.chanset(i).foi(1) > 0
            cY  = ft_preproc_highpassfilter(cY, D.fsample, S.chanset(i).foi(1),...
                5, 'but', 'twopass', 'reduce');
            cYr = ft_preproc_highpassfilter(cYr, D.fsample, S.chanset(i).foi(1),...
                5, 'but', 'twopass', 'reduce');
        end

        if isfinite(S.chanset(i).foi(2))
            cY  = ft_preproc_lowpassfilter(cY, D.fsample, S.chanset(i).foi(2),...
                5, 'but', 'twopass', 'reduce');
            cYr = ft_preproc_lowpassfilter(cYr, D.fsample, S.chanset(i).foi(2),...
                5, 'but', 'twopass', 'reduce');
        end

        Y(:, :, j) = cY(:, tshiftind(1, :));

        for k = 1:size(cYr, 1)
            Yr(((k-1)*nshift+1):k*nshift, :, j) = reshape(cYr(k, tshiftind(2:end, :)'),[], nshift)';
        end
    end

    Y  = reshape(Y,  size(Y, 1), []);
    Yr = reshape(Yr, size(Yr, 1), []);

    if D.fsample>2.5*S.chanset(i).foi(2)
        dec = floor(D.fsample/(2.5*S.chanset(i).foi(2)));
        Y   =  Y(:, 1:dec:end);
        Yr  = Yr(:, 1:dec:end);
    end

    spm_progress_bar('Set','ylabel','running CVA...');

    CVA = spm_cva(Y', Yr');

    ncomp = min(S.chanset(i).ncomp, size(CVA.V, 2));
    for j = 1:ncomp
        if ncomp == 1
            montage.labelnew{end+1, 1} = S.chanset(i).outlabel;
        else
            montage.labelnew{end+1, 1} = [S.chanset(i).outlabel num2str(j)];
        end

        montage.tra(end+1, end)  = 0;
        montage.tra(end, cvaind) = CVA.V(:, j)';

        montage.chantypenew{end+1}='LFP';
    end

    if ismember(i, Ibar)
        spm_progress_bar('Set', i); drawnow;
    end
end

spm_progress_bar('Clear');

if ~isempty(S.chanind)
    montage.labelnew = [montage.labelnew; D.chanlabels(S.chanind)'];
    I = eye(D.nchannels);
    montage.tra = [montage.tra; I(S.chanind, :)];
    montage.chantypenew = [montage.chantypenew, D.chantype(S.chanind)];
end

res = montage;


function X = plus(X,Y)
%PLUS Merges two LMI objects to one LMI

if isa(X,'constraint')
    X = lmi(X);
elseif isa(X,'sdpvar')
    X = lmi(X);
end

if isa(Y,'constraint')
    Y = lmi(Y);
elseif isa(Y,'sdpvar')
    Y = lmi(Y);
end

% Support set+[]
if isempty(X)
    X = Y;
    return
elseif isempty(Y)
    return
end

if ~((isa(X,'lmi')) & (isa(Y,'lmi')))
    error('Both arguments must be constraints')
end

nX = length(X.LMIid);
nY = length(Y.LMIid);
if nX==0
    X = Y;
    return
end
if nY == 0
    return;
end

xBlock = isa(X.clauses{1},'cell');
yBlock = isa(Y.clauses{1},'cell');
if yBlock && xBlock && length(X.clauses{1})>1  && length(Y.clauses{1})>1
    % Both objects are long blocks of constraints. Join these on high level
    for i = 1:length(Y.clauses)
        X.clauses{end+1} = Y.clauses{i};
    end
elseif ~xBlock && ~yBlock
    % Both have been flattened

    % Maybe we should build cells of cells
    if length(X.clauses)> 99
        temp = X.clauses;
        X.clauses = [];
        X.clauses{1} = temp;
        X.clauses{2} = {};
        jx = length(X.clauses);
        for i = 1:length(Y.clauses)
            X.clauses{jx}{end+1} = Y.clauses{i};
        end
    else
        for i = 1:length(Y.clauses)
            X.clauses{end+1} = Y.clauses{i};
        end
    end
else
    % This is the standard case. X has been populated with a bunch of
    % constraints (growing list) while Y is yet another constraint to be
    % added to that list.
    if ~xBlock
        % Lift to a single block
        temp = X.clauses;
        X.clauses = [];
        X.clauses{1} = temp;
    end
    % New block in X? This is where performance comes from. Handling cells
    % of cells is way faster in MATLAB, than a long cell (quadratic running
    % time, appears to be nasty copying going on when adding new elements)
    if length(X.clauses{end}) >= 100
        X.clauses{end+1} = {};
    end
    % Special case for performance
    if isa(Y.clauses{1},'cell') && length(Y.clauses)==1
        j = length(X.clauses);
        for i = 1:length(Y.clauses{1})
            X.clauses{j}{end+1} = Y.clauses{1}{i};
        end
    else
        Y = flatten(Y);
        j = length(X.clauses);
        for i = 1:length(Y.clauses)
            X.clauses{j}{end+1} = Y.clauses{i};
        end
    end
end
aux = X.LMIid;
X.LMIid = [X.LMIid Y.LMIid];

% VERY FAST UNIQUE BECAUSE THIS IS CALLED A LOT OF TIMES....
if ~(max(aux) < min(Y.LMIid))
    i = sort(X.LMIid);
    i = i(diff([i NaN])~=0);
    if length(i)<nX+nY
        % Flatten first. This typically doesn't happen, so we accept this.
        X.clauses = [X.clauses{:}];
        [i,j] = unique(X.LMIid);
        X = subsref(X,struct('type','()','subs',{{j}}));
    end
end


% DEMYALESVARGPLVM4 Run the Shared Var. GP-LVM on a subset of the Yale
% faces.
% DESC Run the Shared Var. GP-LVM on a subset of the Yale faces. The code
% for creating this subset out of raw images exists in comments. Unlike
% demYaleSvargplvm1, this demo is not a wrapper, it can be used as a
% standalone demo.
%
% VARGPLVM

%---
% ca;clear; trainModel=true; indPoints = 30;  experimentNo=2; % demSitranArtificialLight1 % BAD
% ca;clear; trainModel=true; indPoints = 60;  experimentNo=3; demSitranArtificialLight1
% ca;clear; trainModel=true; indPoints = 40;  experimentNo=4; demSitranArtificialLight1 % GOOD % Liv. Machines
% ca;clear; trainModel=true; indPoints = 100; experimentNo=5; demSitranArtificialLight1 % so and so
% ca;clear; trainModel=true; indPoints = 35;  experimentNo=6;
        % initVardistIters = 1200; itNo=[500 8000 5000];demSitranArtificialLight1 % GOOD!!

%--

% Fix seeds
randn('seed', 1e4);
rand('seed', 1e4);

% Create the dataset out of the images.
baseDir=[localDatasetsDirectorySmall 'sharedVargplvm' filesep 'sitranFaces2'];
selDirs = {'arif','ricardo','zhenwen','andreas','fariba','max'};

for d=1:length(selDirs)
    dirFrom=[baseDir filesep selDirs{d}];
    a=dir(dirFrom);
    counter = 0;
    for i=1:length(a)
        if length(a(i).name)>4 & strcmp(a(i).name(1:7),'cropped')
            im = imread([dirFrom filesep a(i).name]);
            im = im(:,:,1);
            %imagesc(im), colormap('gray'); title(a(i).name), pause
            counter = counter+1;
            Yall{d}(counter,:)=im(:)';
        end
    end
    Yall{d} = double(Yall{d});
end
height = size(im,1);
width = size(im,2);
for d=1:length(Yall)
    Yall{d} = util_artificialShadow(reshape(Yall{d},height,width),9,30,[],3000);
end

%{
for i=1:size(Yall{1},1)
    for d=1:length(Yall)
        subplot(2,3,d)
        imagesc(reshape(Yall{d}(i,:), height, width)); colormap('gray');
    end
    if i==1
        pause
    else
        pause(0.1)
    end
end
%}

fpm = length(Yall)/2;
Y = Yall(1:fpm);
identities{1} = [];
for i=1:fpm
    Y{end+1}=[];
    identities{1} = [identities{1}; i*ones(size(Y{1},1),1)];
    idTmp{i+fpm}=[];
end
for i=1:size(Yall{1},1)
    perm = randperm(fpm)+fpm;%perm=[1 2 3 4]+4;
    for j=1:fpm
        Y{j+fpm} = [Y{j+fpm}; Yall{perm(j)}(i,:)]; idTmp{j+fpm}=[idTmp{j+fpm}; perm(j)];
    end
end
clear 'Yall'
Yall{1}=[]; Yall{2}=[]; identities{2}=[];
for i=1:fpm
    Yall{1} = [Yall{1}; Y{i}];
    Yall{2} = [Yall{2}; Y{i+fpm}];
    identities{2} = [identities{2}; idTmp{i+fpm}];
end

%{
Y{1} = Yall{1};
Y{2} = Yall{2};
Y{3} = Yall{3};
Y{4} = Yall{4};
Y{5} = [];
Y{6} = [];
Y{7} = [];
Y{8} = [];
identities{1}=[ones(size(Y{1},1),1); 2*ones(size(Y{2},1),1); 3*ones(size(Y{3},1),1); 4*ones(size(Y{4},1),1)];
idTmp{5}=[];idTmp{6}=[];idTmp{7}=[];idTmp{8}=[];
for i=1:size(Yall{4},1)
    perm = randperm(4)+4;%perm=[1 2 3 4]+4;
    Y{5} = [Y{5}; Yall{perm(1)}(i,:)]; idTmp{5}=[idTmp{5}; perm(1)];
    Y{6} = [Y{6}; Yall{perm(2)}(i,:)]; idTmp{6}=[idTmp{6}; perm(2)];
    Y{7} = [Y{7}; Yall{perm(3)}(i,:)]; idTmp{7}=[idTmp{7}; perm(3)];
    Y{8} = [Y{8}; Yall{perm(4)}(i,:)]; idTmp{8}=[idTmp{8}; perm(4)];
end
clear 'Yall'
Yall{1} = [Y{1}; Y{2}; Y{3}; Y{4}];
Yall{2} = [Y{5}; Y{6}; Y{7}; Y{8}];
identities{2} = [idTmp{5};idTmp{6};idTmp{7};idTmp{8};];
%}

clear 'Y' 'idTmp' 'd'

numberOfDatasets = length(Yall);

 %{
for i=1:size(Yall{1},1)
    for d=1:length(Yall)
        subplot(1,2,d)
        imagesc(reshape(Yall{d}(i,:), height, width)); colormap('gray'); title(num2str(identities{d}(i)))
    end
    if i==1
        pause
    else
        pause(0.05)
    end
end
clear 'd'
 %}
%%

if ~exist('trainModel','var'), trainModel = false; end
if ~exist('itNo')         ,  itNo = [500 1500 1500];              end     % Default: 2000
if ~exist('indPoints')    ,  indPoints = 55;          end     % Default: 49
if ~exist('initVardistIters'), initVardistIters = 900;      end
if ~exist('mappingKern')   ,  mappingKern = 'rbfardjit'; end

% Set to empty value {} to work with toy data
if ~exist('dataSetNames')    ,    dataSetNames = {};    end
if ~exist('invWidthMult'),       invWidthMult = 5;                     end
if ~exist('dataType'), dataType = 'artificialLight'; end
if ~exist('latentDimPerModel'), latentDimPerModel = 7; end
if ~exist('experimentNo'), experimentNo = 1; end
if ~exist('doPredictions'), doPredictions = false; end
% If this is true, then the model is in "D > N" mode.
if ~exist('DgtN'), DgtN = true; end
% Create initial X by doing e.g. ppca in the concatenated model.m's or by
% doing ppca in the model.m's separately and concatenate afterwards?
if ~exist('initial_X'), initial_X = 'separately'; end % Other options: 'concatenated'
% Which indices to use for training, rest for test
if ~exist('indTr'), indTr = -1; end

enableParallelism = 0;

if exist('pyramid','var') % extract pyramid representation of the images
    if pyramid
        for e=1:size(Y,2)
            Y{e} = im2pyramid(Y{e}, lbls(1), lbls(2), 4);
        end
    end
end

%-- Load datasets
for i=1:numberOfDatasets
    Y = Yall{i};
    dims{i} = size(Y,2);
    N{i} = size(Y,1);
    if indTr == -1
        indTr = 1:N{i};
    end
    indTs = setdiff(1:size(Y,1), indTr);
    Ytr{i} = Y(indTr,:);
    Yts{i} = Y(indTs,:);
    t{i} = linspace(0, 2*pi, size(Y, 1)+1)'; t{i} = t{i}(1:end-1, 1);
    timeStampsTraining{i} = t{i}(indTr,1); %timeStampsTest = t(indTs,1);
    d{i} = size(Ytr{i}, 2);
end

for i=2:numberOfDatasets
    if N{i} ~= N{i-1}
        error('The number of observations in each dataset must be the same!');
    end
end

%%
if trainModel

    %-- Options for the models
    for i=1:numberOfDatasets
        % Set up models
        options{i} = vargplvmOptions('dtcvar');
        options{i}.kern = mappingKern; %{'rbfard2', 'bias', 'white'};
        %indPoints = 80; %%%%%
        options{i}.numActive = indPoints;
        options{i}.optimiser = 'scg2';
        if ~DgtN
            options{i}.enableDgtN = false;
        end
        % !!!!! Be careful to use the same type of scaling and bias for all
        % models!!!

        % scale = std(Ytr);
        % scale(find(scale==0)) = 1;
        %options.scaleVal = mean(std(Ytr));
        options{i}.scaleVal = sqrt(var(Ytr{i}(:)));
    end

    %-------------- INIT LATENT SPACE ---%
    for i=1:length(Ytr)
        % Compute m, the normalised version of Ytr (to be used for
        % initialisation of X)
        bias = mean(Ytr{i});
        scale = ones(1, d{i});

        if(isfield(options{i},'scale2var1'))
            if(options{i}.scale2var1)
                scale = std(Ytr{i});
                scale(find(scale==0)) = 1;
                if(isfield(options{i}, 'scaleVal'))
                    warning('Both scale2var1 and scaleVal set for GP');
                end
            end
        end
        if(isfield(options{i}, 'scaleVal'))
            scale = repmat(options{i}.scaleVal, 1, d{i});
        end

        % Remove bias and apply scale.
        m{i} = Ytr{i};
        for j = 1:d{i}
            m{i}(:, j) = m{i}(:, j) - bias(j);
            if scale(j)
                m{i}(:, j) = m{i}(:, j)/scale(j);
            end
        end

        %    mAll = [mAll m{i}]; % Concatenation (doesn't work if different sizes)
    end
    if strcmp(initial_X, 'separately')
        fprintf('# Initialising X by performing ppca in each observed (scaled) dataset separately and then concatenating...\n');
        X_init{1} = ppcaEmbed(m{1},latentDimPerModel);
        X_init{2} = ppcaEmbed(m{2},latentDimPerModel);
        X_init = [X_init{1} X_init{2}];
    else
        fprintf('# Initialising X by performing ppca in concatenated observed (scaled) data...\n');
        X_init = ppcaEmbed([m{1} m{2}], latentDimPerModel*2);
    end
    %-----------------

    latentDim = size(X_init,2);

    % Free up some memory
    clear('Y')

    %-- Create the sub-models: Assume that for each dataset we have one model.
    % This can be changed later, as long as we find a reasonable way to
    % initialise the latent spaces.
    for i=1:numberOfDatasets
        %---- Here put some code to assign X to the global common X which must
        % be created by doing pca in the concatenation of Y's...After this
        % point, model{i}.X will be the same for all i's. TODO...
        fprintf(1,'# Creating the model...\n');
        options{i}.initX = X_init;
        model{i} = vargplvmCreate(latentDim, d{i}, Ytr{i}, options{i});

        model{i}.X = X_init; %%%%%%%
        model{i} = vargplvmParamInit(model{i}, m{i}, model{i}.X);
        model{i}.X = X_init; %%%%%%%

        inpScales = invWidthMult./(((max(model{i}.X)-min(model{i}.X))).^2); % Default 5
        %inpScales(:) = max(inpScales); % Optional!!!!!
        model{i}.kern.comp{1}.inputScales = inpScales;

        if strcmp(model{i}.kern.type, 'rbfardjit')
            model{i}.kern.inputScales = model{i}.kern.comp{1}.inputScales;
        end
        params = vargplvmExtractParam(model{i});
        model{i} = vargplvmExpandParam(model{i}, params);
        model{i}.vardist.covars = 0.5*ones(size(model{i}.vardist.covars)) + 0.001*randn(size(model{i}.vardist.covars));

        model{i}.beta=1/(0.01*var(m{i}(:)));
        prunedModelInit{i} = vargplvmPruneModel(model{i});
        %disp(model{i}.vardist.covars)
    end

    %modelInit = model;%%%TEMP

    %--  Unify models into a structure
    svargplvm_init
    model = svargplvmModelCreate(model);
    model.dataSetNames = dataSetNames;
    model.experimentNo = experimentNo;
    model.dataType = dataType;
    %%---
    capName = dataType;
    capName(1) = upper(capName(1));
    modelType = model.type;
    modelType(1) = upper(modelType(1));
    fileToSave = ['dem' capName modelType num2str(experimentNo) '.mat'];
    %%---

    %-- Define what level of parallelism to use (w.r.t submodels or/and w.r.t
    % datapoints).
    %{
fprintf('# Parallel computations w.r.t the submodels!\n');
model.parallel = 1;
model = svargplvmPropagateField(model,'parallel', 1);
%
fprintf('# Parallel computations w.r.t the datapoints!\n');
model.vardist.parallel = 1;
for i=1:model.numModels
    model.comp{i}.vardist.parallel = 1;
end
    %}

    % Force kernel computations
    params = svargplvmExtractParam(model);
    model = svargplvmExpandParam(model, params);

    %%

    display = 1;
    %%%% Optimisation
    % do not learn beta and sigma_f for few iterations for intitialization
    if initVardistIters ~=0
        model.initVardist = 1; model.learnSigmaf = 0;
        model = svargplvmPropagateField(model,'initVardist', model.initVardist);
        model = svargplvmPropagateField(model,'learnSigmaf', model.learnSigmaf);
        fprintf(1,'# Intitiliazing the variational distribution for %s iters...\n',num2str(initVardistIters));
        model = svargplvmOptimise(model, display, initVardistIters); % Default: 20
        %fprintf(1,'1/b = %.4d\n',1/model.beta);

        modelInitVardist = model;
        model.initVardistIters=initVardistIters;
    end

    model.initVardist = 0; model.learnSigmaf=1;
    model = svargplvmPropagateField(model,'initVardist', model.initVardist);
    model = svargplvmPropagateField(model,'learnSigmaf', model.learnSigmaf);

    % Optimise the model.
    model.iters = 0;
    for i=1:length(itNo)
        iters = itNo(i); % default: 2000
        fprintf(1,'\n# Optimising the model for %d iterations (session %d)...\n',iters,i);
        model = svargplvmOptimise(model, display, iters);
        model.iters = model.iters + iters;
        % Save model
        prunedModel = svargplvmPruneModel(model);
        fprintf(1,'# Saving %s\n',fileToSave);
        save(fileToSave, 'prunedModel', 'prunedModelInit');
    end
else
    capName = dataType;
    capName(1) = upper(capName(1));
    modelType = 'svargplvm';
    modelType(1) = upper(modelType(1));
    fileToSave = ['dem' capName modelType num2str(experimentNo) '.mat'];
    load(fileToSave);
    model = svargplvmRestorePrunedModel(prunedModel, Ytr);clear('prunedModel');clear('prunedModelInit');
end

%%

if ~exist('resultsDynamic')  resultsDynamic = 0; end

for i=1:length(model.comp)
    if model.comp{i}.DgtN
        model.comp{i}.m = model.comp{i}.mOrig;
    end
end
for i=1:length(model.comp)
    model.comp{i}.vis.startDim = {1,2};
    model.comp{i}.vis.startPos = model.vardist.means(1,:);
end

%%
% v = 1;
% modelVis = model.comp{v};
% if resultsDynamic
%     %bar(model.comp{v}.kern.comp{1}.inputScales);
%     %figure
%     % The following causes OUTOFMEMORY exception except from when we prune the
%     % video dimensions: (No2te: also, lvmVisualise was changed a bit so that no
%     % dynamic slides is presented, because otherwise a strange error occurs)..
%     modelVis.y = Ytr{v};
%     reduction = 1; % 4
%     opt.showVariance = 1;    opt.showInducing =1 ;
%     %opt.showVariance=0; opt.showInducing=0;
%     [modelP, newHeight, newWidth] = vargplvmReduceVidModel(modelVis, height, width, reduction, reduction);
%     lvmVisualiseGeneral(modelP, [], 'imageMRDVisualise', 'imageMRDModify', opt, [newHeight newWidth],0,0,1);
%     clear modelP
%     figure;svargplvmShowScales(model);
% end

%%
if resultsDynamic
    if exist('modelOrig','var'), model = modelOrig; end
    modelOrig = model;
    clear global visualiseInfo
    for v=1:model.numModels
        model.comp{v}.y = Ytr{v};
        reduction = 2; % 4
        opt.showVariance = 1;    opt.showInducing =1 ;
        %opt.showVariance=0; opt.showInducing=0;
        [model.comp{v}, newHeight, newWidth] = vargplvmReduceVidModel(model.comp{v}, height, width, reduction, reduction);
    end
    lvmVisualiseMRD(model,[],{'imageMRDVisualise','imageMRDVisualise'},{'imageMRDModify','imageMRDModify'},opt,{{[newHeight newWidth],0,0,1},{[newHeight newWidth],0,0,1}});
    clear modelP
    figure;svargplvmShowScales(model);
    set(gca, 'FontSize', 18);
end

%% ---
%%{
axFs = 18;
titleFs = 0;
legFs = 20;

figure
sc = svargplvmShowScales(model,0);
maxScales1 = max(sc{1});
maxScales2 = max(sc{2});
sc{1} = sc{1}./maxScales1;
sc{2} = sc{2}./maxScales2;
sc{1}=sigmoid(sc{1}*5)-0.5;
sc{2}=sigmoid(sc{2}*5)-0.5;

x=1:size(sc{1},2);
K=0.75;
bar1=bar(x, sc{1}, 'FaceColor', 'b', 'EdgeColor', 'b');
set(gca, 'YtickLabel',[])
set(bar1,'BarWidth',K);
hold on;
bar2=bar(x, sc{2}, 'FaceColor', 'r', 'EdgeColor', 'r');
set(gca, 'YtickLabel',[])
set(bar2,'BarWidth',K/2.5);
hold off;
%lg=legend(['scales1'],['scales2']);
%set(lg, 'FontSize',legFs)
set(gca, 'FontSize', axFs);
%%}

%% Eigenfaces
%{
m=1;
Kuu=model.comp{m}.K_uu;
beta = model.comp{m}.beta;
Psi2 = model.comp{m}.Psi2;
Psi1 = model.comp{m}.Psi1;
h=newHeight;
w=newWidth;
mu = zeros(size(Kuu,1),h*w);
for i=1:size(Y,2)
    mu(:,i) = Kuu*pdinv(1/beta*Kuu+Psi2)*Psi1'*model.comp{m}.m(:,i);
end
x = mu';
x=bsxfun(@minus, x', mean(x'))';

% calculate covariance
s = cov(x');
% obtain eigenvalue & eigenvector
[V,D] = eig(s);
eigval = diag(D);
% sort eigenvalues in descending order
eigval = eigval(end:-1:1);
V = fliplr(V);
% show 0th through 15th principal eigenvectors
eig0 = reshape(mean(x,2), [h,w]);
figure,subplot(4,4,1)
imagesc(eig0)
colormap gray
for i = 1:15
    subplot(4,4,i+1)
    imagesc(reshape(V(:,i),h,w))
end

%
figure;imagesc(reshape(V(:,3), h,w)); axis off; colormap('gray'); axis('image')
figure;imagesc(reshape(V(:,5), h,w)); axis off; colormap('gray'); axis('image')
figure;imagesc(reshape(V(:,6), h,w)); axis off; colormap('gray'); axis('image')
figure;imagesc(reshape(V(:,3), h,w)); axis off; colormap('gray'); axis('image');colormap(flipud(colormap));
figure;imagesc(reshape(V(:,5), h,w)); axis off; colormap('gray'); axis('image');colormap(flipud(colormap));
figure;imagesc(reshape(V(:,6), h,w)); axis off; colormap('gray'); axis('image');colormap(flipud(colormap));
%}

%%

%---------------------------- PREDICTIONS ---------------
if ~doPredictions
    return
end

obsMod = 1; % one of the involved sub-models (the one for which we have the data)
infMod = setdiff(1:2, obsMod);

% Find the dimensions that are shared for obsMod and infMod
if ~exist('sharedDims')
    s1 = model.comp{obsMod}.kern.comp{1}.inputScales;
    s2 = model.comp{infMod}.kern.comp{1}.inputScales;
    % Normalise values between 0 and 1
    s1 = s1 / max(s1);
    s2 = s2 / max(s2);

    %  thresh = max(model.comp{obsMod}.kern.comp{1}.inputScales) * 0.001;
    thresh = 0.005;

    retainedScales{obsMod} = find(s1 > thresh);
    %thresh = max(model.comp{infMod}.kern.comp{1}.inputScales) * 0.001;
    retainedScales{infMod} = find(s2  > thresh);
    sharedDims = intersect(retainedScales{obsMod}, retainedScales{infMod});
end

% Find X_* only for the shared dimensions (Xs*):
if ~exist('privateDims')
    privateDims = setdiff(1:model.comp{obsMod}.q, sharedDims);
end

if ~exist('testOnTraining')
    testOnTraining=1;
end

numberTestPoints = 10;
if testOnTraining
    perm = randperm(model.N);
    testInd = perm(1:numberTestPoints);
else
    perm = randperm(size(Yts{obsMod},1));
    testInd = perm(1:numberTestPoints);
end

scrsz = get(0,'ScreenSize');

for i=1:length(testInd)
    curInd = testInd(i);
    fprintf('# Testing indice number %d ', curInd);
    if testOnTraining
        fprintf('taken from the training set\n');
        y_star = model.comp{obsMod}.y(curInd,:);
        x_star = model.comp{obsMod}.vardist.means(curInd,:);
        varx_star = model.comp{obsMod}.vardist.covars(curInd,:);
    else
        fprintf('taken from the test set\n');
        y_star = Yts{obsMod}(curInd,:);
        z_star = Yts{infMod}(curInd,:);
        dst = dist2(y_star, model.comp{obsMod}.y);
        [mind, mini] = min(dst);

        Init(i,:) = model.vardist.means(mini,:);
        vardistx = vardistCreate(model.comp{obsMod}.vardist.means(mini,:), model.q, 'gaussian');
        vardistx.covars = model.comp{obsMod}.vardist.covars(mini,:);
        model.comp{obsMod}.vardistx = vardistx;
        display=1;
        iters = 250;
        % Find p(X_* | Y_*) which is approximated by q(X_*)
        [x_star, varx_star, modelUpdated] = vargplvmOptimisePoint(model.comp{obsMod}, vardistx, y_star, display, iters);%%%
    end
    numberOfNN = 9;
    % Now we selected a datapoint X_* by taking into account only the
    % private dimensions for Y. Now, based on the shared dimensions of
    % that, we select the closest (in a NN manner) X from the training data.
    fprintf('# Finding the %d NN of X_* with the training X based only on the shared dims.\n', numberOfNN);
    [ind, distInd] = nn_class(model.X(:,sharedDims), x_star(:,sharedDims), numberOfNN, 'euclidean');

    ZpredMu = zeros(length(ind), size(model.comp{infMod}.y,2));
    ZpredSigma = zeros(length(ind), size(model.comp{infMod}.y,2));

    % Find p(y_*|x_*) for every x_* found from the NN
    fprintf('# Predicting images from the NN of X_* ');
    for k=1:numberOfNN
        fprintf('.');
        x_cur = model.X(ind(k),:);
        %x_cur(sharedDims) = x_star(sharedDims); %%% OPTIONAL!!!
        %[ZpredMu(k,:), ZpredSigma(k,:)] = vargplvmPosteriorMeanVar(model.comp{infMod}, model.X(ind(k),:));
        ZpredMu(k,:) = vargplvmPosteriorMeanVar(model.comp{infMod}, x_cur);
    end
    fprintf('\n\n');

    %-- Plots
    % Open a big figure (first 2 args control the position, last 2 control
    % the size)
    figure('Position',[scrsz(3)/100.86 scrsz(4)/6.666 scrsz(3)/1.0457 scrsz(4)/1.0682],...
        'Name',['Fig: ' num2str(i) ' (Exp: ' num2str(experimentNo) ')'],'NumberTitle','off')
    numRows = 2;

    if testOnTraining
        numCols = ceil((numberOfNN+1)/numRows);
        plotCounter = 1;
    else
        % For the real test image!
        numCols = ceil((numberOfNN+2)/numRows);
        plotCounter = 2;
    end
    subplot(numRows, numCols, 1)
    imagesc(reshape(y_star,height,width)), title(['Original y (image #' num2str(curInd) ')']), colormap('gray')

    if ~testOnTraining
        subplot(numRows, numCols, 2)
        imagesc(reshape(z_star,height,width)), title(['Corresponding z (image #' num2str(curInd) ')']), colormap('gray')
    end

    for k=1:numberOfNN
        subplot(numRows, numCols, k+plotCounter)
        imagesc(reshape(ZpredMu(k,:), height, width)), title(['NN #' num2str(k)]), colormap('gray');
    end

    %{
    indYnn = [];
    % Do a NN on the DATA space, for every predicted output.
    for j=1:size(ZpredMu,1)
        [indYnn(j), distInd] = nn_class(model.comp{infMod}.y, ZpredMu(j,:),1,'euclidean');
    end
    for j=1:length(indYnn)
        figure, imagesc(reshape(model.comp{infMod}.y(indYnn(j),:),height, width)), title([num2str(j)]), colormap('gray')
    end
    %}
end

if ~testOnTraining
    errsumFull = sum((ZpredMu - Yts).^2);
    errorFull = mean(errsumFull);
end


function [v_res, solution] = MTA_MIQP(OptimizationModel, KOrxn, varargin)
% Returns the OptimizationModel solution of a particular MTA problem and
% an specific model
%
% USAGE:
%
%    [v_res, success, unsuccess] = MTA_MIQP (OptimizationModel, KOrxn, numWorkers, timeLimit, printLevel)
%
% INPUT:
%    OptimizationModel:    Cplex Model struct
%    KOrxn:                perturbation in the model (reactions)
%    numWorkers:           number of threads used by Cplex.
%    FORCE_CPLEX:          1 to force CPLEX solver, 0 (default) for COBRA
%                          solver.
%    printLevel:           1 if the process is wanted to be shown on the
%                          screen, 0 otherwise. Default: 1.
%
% OUTPUTS:
%    Vout:                 Solution flux of MIQP formulation for each case
%    solution:             Cplex solution struct
%
% .. Authors:
%       - Luis V. Valcarcel, 03/06/2015, University of Navarra, CIMA & TECNUN School of Engineering.
%       - Luis V. Valcarcel, 26/10/2018, University of Navarra, CIMA & TECNUN School of Engineering.
%       - Luis V. Valcarcel, 09/03/2021, University of Navarra, CIMA & TECNUN School of Engineering.

p = inputParser; % check the input information
% check requiered arguments
addRequired(p, 'OptimizationModel');
addRequired(p, 'KOrxn');
% Check optional arguments
addParameter(p, 'numWorkers', 0,@(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'timeLimit', inf,@(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'printLevel', 1,@(x)isnumeric(x)&&isscalar(x));
addParameter(p, 'FORCE_CPLEX', 0,@(x)isnumeric(x)&&isscalar(x));
% extract variables from parser
parse(p, OptimizationModel, KOrxn, varargin{:});
numWorkers = p.Results.numWorkers;
timeLimit = p.Results.timeLimit;
printLevel = max(p.Results.printLevel, 0);
FORCE_CPLEX = p.Results.FORCE_CPLEX;

%Indexation of variables
v = OptimizationModel.idx_variables.v;
y_plus_F = OptimizationModel.idx_variables.y_plus_F;
y_minus_F = OptimizationModel.idx_variables.y_minus_F;
y_plus_B = OptimizationModel.idx_variables.y_plus_B;
y_minus_B = OptimizationModel.idx_variables.y_minus_B;
OptimizationModel = rmfield(OptimizationModel,'idx_variables');

% Temporal way: use ibm_cplex if installed until MIQP API for COBRA is
% implemented
global SOLVERS;
global CBT_MIQP_SOLVER
if FORCE_CPLEX || (SOLVERS.ibm_cplex.installed && strcmp(CBT_MIQP_SOLVER,'ibm_cplex'))
    % Generate CPLEX model
    cplex = Cplex('MIQP');
    CplexModel = OptimizationModel;

    b_L(CplexModel.csense == 'E') = CplexModel.b(CplexModel.csense == 'E');
    b_U(CplexModel.csense == 'E') = CplexModel.b(CplexModel.csense == 'E');
    b_L(CplexModel.csense == 'G') = CplexModel.b(CplexModel.csense == 'G');
    b_U(CplexModel.csense == 'G') = inf;
    b_L(CplexModel.csense == 'L') = -inf;
    b_U(CplexModel.csense == 'L') = CplexModel.b(CplexModel.csense == 'L');
    CplexModel.rhs = b_U;
    CplexModel.lhs = b_L;
    CplexModel.Q = CplexModel.F;
    CplexModel.obj = CplexModel.c;
    CplexModel.ctype = CplexModel.vartype;
    CplexModel.sense = 'minimize';

    cplex.Model = CplexModel;
    % include the knock-out reactions
    cplex.Model.lb(KOrxn) = 0;
    cplex.Model.ub(KOrxn) = 0;

    % Cplex Parameter
    if numWorkers>0
        cplex.Param.threads.Cur = numWorkers;
    end
    if printLevel <=1
        cplex.Param.output.clonelog.Cur = 0;
        cplex.DisplayFunc = [];
    elseif printLevel <=2
        cplex.Param.output.clonelog.Cur = 0;
    end
    if timeLimit < 1e75
        cplex.Param.timelimit.Cur = timeLimit;
    end
    %reduce the tolerance
    cplex.Param.mip.tolerances.mipgap.Cur = 1e-5;
    % cplex.Param.mip.tolerances.absmipgap.Cur = 1e-8;
    % cplex.Param.threads.Cur = 16;

    % SOLVE the CPLEX problem if not singular
    try
        cplex.solve();
    catch
        v_res = zeros(length(v),1);
        return
    end

    if cplex.Solution.status ~= 103
        v_res = cplex.Solution.x(v);
        solution = cplex.Solution;
    else
        v_res = zeros(length(v),1);
        solution = nan;
    end

    % clear the cplex object
    delete(cplex)
    clear cplex
else
    % Generate OptimizationModel for this iteration
    MIQPproblem = OptimizationModel;
    % include the knock-out reactions
    MIQPproblem.lb(KOrxn) = 0;
    MIQPproblem.ub(KOrxn) = 0;

    % Solver Parameter
    if timeLimit > 1e75
        timeLimit = 1e75;
    end

    % SOLVE the MIQP problem
    solution = solveCobraMIQP(MIQPproblem, ...
        'timeLimit',timeLimit, 'relMipGapTol',  1e-5, ...
        'printLevel', max(printLevel-1,0), 'logFile', 0,...
        'threads',numWorkers);

    if isnumeric(solution.stat) && solution.stat == 1
        v_res = solution.full(v);
    elseif ischar(solution.stat) && strcmp(solution.stat, 'OPTIMAL')
        v_res = solution.full(v);
    else
        % Use of try for different outputs of COBRA MIQP solver
        try
            v_res = solution.full(v);
        catch
            v_res = zeros(length(v),1);
        end
    end

end