function [document, scores] = report_difficulty(context, experiment, trackers, sequences, varargin)
% report_difficulty Generate a difficulty report for tags or sequences
%
% Performs A-R ranking analysis and generates a report that shows the difficulty of individual
% tags or sequences.
%
% Input:
% - context (structure): Report context structure.
% - experiment (struct): An experiment structure.
% - trackers (cell): An array of tracker structures.
% - sequences (cell): An array of sequence structures.
% - varargin[UsePractical] (boolean): Use practical difference.
% - varargin[UseTags] (boolean): Rank according to tags (otherwise rank according to sequences).
% - varargin[Alpha] (boolean): Statistical significance parameter.
%
% Output:
% - document (structure): Resulting document structure.
%

usetags = true;
usepractical = true;
alpha = 0.05;
adaptation = get_global_variable('report_ranking_adaptation', 'mean');

scores = [];

for i = 1:2:length(varargin)
    switch lower(varargin{i})
        case 'usepractical'
            usepractical = varargin{i+1};
        case 'usetags'
            usetags = varargin{i+1};
        case 'alpha'
            alpha = varargin{i+1};
        otherwise
            error(['Unknown switch ', varargin{i}, '!']) ;
    end
end

document = create_document(context, 'difficulty', 'title', 'Difficulty');

trackers_hash = md5hash(strjoin((cellfun(@(x) x.identifier, trackers, 'UniformOutput', false)), '-'), 'Char', 'hex');
parameters_hash = md5hash(sprintf('%f-%d-%d-%s', alpha, usetags, usepractical, adaptation));

if ~strcmp(experiment.type, 'supervised')
   error('Difficulty analysis only suitable for supervised experiments!');
end

document.section('Experiment %s', experiment.name);

tags = {};

if usetags && isfield(experiment, 'tags')
    tags = experiment.tags;

    sequences_hash = md5hash(strjoin(tags, '-'), 'Char', 'hex');
else
    sequences_hash = md5hash(strjoin((cellfun(@(x) x.name, sequences, 'UniformOutput', false)), '-'), 'Char', 'hex');
end;

cache_identifier = sprintf('ranking_%s_%s_%s_%s', experiment.name, trackers_hash, sequences_hash, parameters_hash);

result = report_cache(context, cache_identifier, @analyze_ranks, experiment, trackers, ...
    sequences, 'tags', tags, 'usepractical', usepractical, ...
    'alpha', alpha, 'adaptation', adaptation);

selector_tags = result.tags;

median_accuracy = nanmedian(result.accuracy.values, 2);
median_robustness = nanmedian(result.robustness.normalized, 2);

median_diff_accuracy = nanmedian(bsxfun(@minus, result.accuracy.values, mean(result.accuracy.values, 1)), 2);
median_diff_robustness = nanmedian(bsxfun(@minus, result.robustness.normalized, mean(result.robustness.normalized, 1)), 2);

table_data = [median_accuracy, median_robustness * 100, median_diff_accuracy, median_diff_robustness * 100];

table_data = highlight_best_rows(num2cell(table_data), {'ascending', 'descending', 'ascending', 'descending'})';
row_tags = {'Accuracy', 'Robustness', 'Accuracy difference', 'Robustness difference'};

title = sprintf('Difficulty for experiment %s', experiment.name);

document.table(table_data, 'columnLabels', selector_tags, 'rowLabels', row_tags', 'title', title);

document.raw('
\n');

hf = figure('Visible', 'off');
hold on;
for i = 1:numel(selector_tags)
    if median_diff_accuracy(i) > 0
        rectangle('Position',[i-0.3, 0.5, 0.6, median_diff_accuracy(i)], 'FaceColor', 'green');
    elseif median_diff_accuracy(i) < 0
        rectangle('Position',[i-0.3, 0.5+median_diff_accuracy(i), 0.6, -median_diff_accuracy(i)], 'FaceColor', 'red');
    end;

    plot(ones(size(result.accuracy.values(i, :))) * i, result.accuracy.values(i, :), 'bo');
end
hold off;
set(gca, 'YLim', [0, 1], 'XLim', [0.5, numel(selector_tags) + 0.5], 'XTick', 1:numel(selector_tags), 'XTickLabel', selector_tags);

document.figure(hf, sprintf('difficulty_%s_accuracy_scatter', experiment.name), ...
    sprintf('Accuracy scatter'));

hf = figure('Visible', 'off');
hold on;
origin = mean(median_robustness);
for i = 1:numel(selector_tags)
    if median_diff_robustness(i) > 0
        rectangle('Position',[i-0.3, origin, 0.6, median_diff_robustness(i)], 'FaceColor', 'red');
    elseif median_diff_robustness(i) < 0
        rectangle('Position',[i-0.3, origin+median_diff_robustness(i), 0.6, -median_diff_robustness(i)], 'FaceColor', 'green');
    end;

    plot(ones(size(result.robustness.normalized(i, :))) * i, result.robustness.normalized(i, :), 'bo');
end
hold off;
set(gca, 'YLim', [0, max(result.robustness.normalized(:))], ...
    'XLim', [0.5, numel(selector_tags) + 0.5], 'XTick', 1:numel(selector_tags), 'XTickLabel', selector_tags);

document.figure(hf, sprintf('difficulty_%s_robustness_scatter', experiment.name), ...
    sprintf('Robustness scatter'));

document.raw('
\n');

document.write();

end


function computeAllPrediction_batchVASARI_LGG(pathExperiments,maxOrder,nBoot,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% function computeAllPrediction_batchVASARI_LGG(pathExperiments,maxOrder,nBoot,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes prediction performance estimation for a given
% feature set type, and for all model orders of all experiments with
% different degrees of freedom. See ref. [1] for more details.
% -------------------------------------------------------------------------
% REFERENCE:
% [1] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathExperiments: Full path to where all experiments need to be
%                     performed.
%                     --> Ex: /myProject/WORKSPACE/VASARI
% 2. maxOrder: Integer specifying the maximal model order to construct.
%              --> Ex: 10
% 3. nBoot: Number of bootstrap samples to use.
%           --> Ex: 100
% 4. imbalance: String specifying the type of imbalance-adjustement strategy
%               employed. Either 'IABR' for imbalance-adjusted bootstrap
%               resampling (see ref.[1]), or 'IALR' for imbalance-adjusted
%               logistic regression (formal reference to come).
%               --> Ex: 'IALR'
% 5. nBatch: Number of parallel batch.
%            --> Ex: 8
% 6. matlabPATH: Full path to the MATLAB executable on the system.
%                --> 'matlab' if a symbolic link to the matlab executable
%                     was previously created.
% 7. seed: Random generator seed for reproducibility of experiments.
% -------------------------------------------------------------------------
% OUTPUTS: Prediction performance results are saved in a folder named 'RESULTS' in the
% corresponding folder of 'pathExperiments'.
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: January 2017
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015-2017  Martin Vallieres
%
%    This package 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.
%
%    This package is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;

% INITIALIZATON
time = 60; % Number of seconds to wait before checking if parallel computations are done
cd(pathExperiments), load('outcomes')
pathModels = fullfile(pwd,'MODELS'); mkdir('RESULTS'), cd('RESULTS'), pathResults = pwd;
mkdir('batchLog_Results'), cd('batchLog_Results'), pathBatch = pwd;
setNames = {'VASARI'};
[param] = batchExperiments(setNames,outcomes,nBatch); nBatch = length(param);

% PRODUCE BATCH COMPUTATIONS
save('workspace','pathModels','pathResults','outcomes','param','maxOrder','nBoot','imbalance','seed'), pause(5);
for i = 1:nBatch
    nameScript = ['batch',num2str(i),'_script.m'];
    fid = fopen(nameScript,'w');
    fprintf(fid,'load(''workspace'')\n');
    for j = 1:numel(param{i})
        fprintf(fid,['computeAllPrediction_VASARI_LGG(pathModels,pathResults,outcomes,param{',num2str(i),'}{',num2str(j),'},maxOrder,nBoot,imbalance,seed)\n']);
    end
    fprintf(fid,['system(''touch batch',num2str(i),'_end'');\n']);
    fprintf(fid,'clear all');
    fclose(fid);
    system([matlabPATH,' -nojvm -nodisplay -nodesktop -nosplash < ',nameScript,' >& ',nameScript(1:end-1),'log &']);
end

% WAITING LOOP
waitBatch(pathBatch,time,nBatch)
delete('workspace.mat')

cd(startpath)
end


%kCTRecon 'ML/EM reconstruction of ct-slice '
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros CTRecon.pane file
%
% Parameters:
% InputFile: i 'Sinogram', required: 'First Input data object'
% InputFile: t 'BeamTable', optional: 'BeamTable file in raw format'
% Integer: SPos 'Start Projection', default: 0: 'ProjectionNr to start with'
% Integer: PSize 'Projection Size', default: 768: 'integer'
% Integer: PNumber 'Projection Number', default: 1056: 'integer'
% Integer: n 'Iterations', default: 80: 'integer'
% Toggle: q 'No Quarter Shift', default: 0: 'flag'
% Integer: IX 'Size X', default: 128: 'integer'
% Integer: IY 'Size Y', default: 128: 'integer'
% Toggle: divc 'Use Div Cmp', default: 0: 'compute correction by division'
% Toggle: nn 'Without Norms', default: 0: 'flag'
% Toggle: nfn 'No first norm', default: 0: 'do not use norm in forwardprojection'
% Toggle: rn 'Norms from file', default: 0: 'read the norm from the two files'
% OutputFile: o 'Reconstructed', required: 'Resulting output data object'
% OutputFile: c 'Last Correction', required: 'Correction'
% InputFile: Over 'OverralaxTable', optional: 'optional input file'
% InputFile: s 'Start Image', optional: 'optional input file'
% OutputFile: f 'Forward Projection', optional: 'output file'
%
% Example: [o, c, f] = kCTRecon({i, t, Over, s}, {'i','';'t','';'SPos',0;'PSize',768;'PNumber',1056;'n',80;'q',0;'IX',128;'IY',128;'divc',0;'nn',0;'nfn',0;'rn',0;'o','';'c','';'Over','';'s','';'f',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% CTRecon - ML/EM reconstruction of ct-slice
%
%  DESCRIPTION
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
%
%  RESTRICTIONS
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1996-2003, Rainer Heintzmann,  All rights reserved.
%

function varargout = kCTRecon(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kCTRecon(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'t', '__input';'SPos', 0;'PSize', 768;'PNumber', 1056;'n', 80;'q', 0;'IX', 128;'IY', 128;'divc', 0;'nn', 0;'nfn', 0;'rn', 0;'o', '__output';'c', '__output';'Over', '__input';'s', '__input';'f', '__output'};
maxval={0,1,1,2,2,2,0,2,2,0,0,0,0,0,0,1,1,1};
minval={0,1,1,2,2,2,0,2,2,0,0,0,0,0,0,1,1,1};
istoggle=[0,1,0,0,0,0,1,0,0,1,1,1,1,0,0,1,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','InputFile','Integer','Integer','Integer','Integer','Toggle','Integer','Integer','Toggle','Toggle','Toggle','Toggle','OutputFile','OutputFile','InputFile','InputFile','OutputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=2; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

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


function [dvh,qi] = matRad_indicatorWrapper(cst,pln,resultGUI,refGy,refVol)
% matRad indictor wrapper
%
% call
%   [dvh,qi] = matRad_indicatorWrapper(cst,pln,resultGUI)
%   [dvh,qi] = matRad_indicatorWrapper(cst,pln,resultGUI,refGy,refVol)
%
% input
%   cst:                  matRad cst struct
%   pln:                  matRad pln struct
%   resultGUI:            matRad resultGUI struct
%   refGy: (optional)     array of dose values used for V_XGy calculation
%                         default is [40 50 60]
%   refVol:(optional)     array of volumes (0-100) used for D_X calculation
%                         default is [2 5 95 98]
%                         NOTE: Call either both or none!
%
% output
%   dvh: matRad dvh result struct
%   qi:  matRad quality indicator result struct
%   graphical display of all results
%
% References
%   -
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2017 the matRad development team.
%
% This file is part of the matRad project. It is subject to the license
% terms in the LICENSE file found in the top-level directory of this
% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part
% of the matRad project, including this file, may be copied, modified,
% propagated, or distributed except according to the terms contained in the
% LICENSE file.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if isfield(resultGUI,'RBExDose')
    doseCube = resultGUI.RBExDose;
else
    doseCube = resultGUI.physicalDose;
end

if ~exist('refVol', 'var')
    refVol = [];
end

if ~exist('refGy', 'var')
    refGy = [];
end

dvh = matRad_calcDVH(cst,doseCube,'cum');
qi  = matRad_calcQualityIndicators(cst,pln,doseCube,refGy,refVol);

figure,set(gcf,'Color',[1 1 1]);
subplot(2,1,1)
matRad_showDVH(dvh,cst,pln);
subplot(2,1,2)
ixVoi = cellfun(@(c) c.Visible == 1,cst(:,5));
qi = qi(ixVoi);
matRad_showQualityIndicators(qi);


function model = read_svm_detection_weights( filepath )
load(filepath, 'weights', 'bias');
model = {weights; bias};
end


function val = v_dtiRawPreprocessGE()
% Validate dti raw preprocessing of GE data
%
%    val = v_dtiRawPreprocessGE()
%
% This function checks that the average FA and B0 values resulting from
% dtiRawPreprocess are consistent with the expected value on different
% platforms (LINUX, WINDOWS, etc.).
%
% Requires vistadata and vistasoft on your path. The directory
% vistadata/diffusion/dtiRawPreprocess/GE contains all the necessary data
% files.
%
% This function does the following:
%
% (1) run dtiRawPreprocess on the files in the raw folder. This will align to the T1 and
% create a B0, BVECS and FA values (it creates more stuff but we only focus on these for the moment).
%
% (2) Show a montage of Alignment i.e., the T1 and DTI overalyed. Check for LR flips and correct alignment.
%
% (3) Compute FA, Mean diffusivity, radial diffusivity nd Axial diffusivity
%     across the brain and check the value obtained on different
%     platforms. THis will be done using: [fa,md,rd,ad] = dtiComputeFA(eigVal)
%
% (5) Load the stored FA, MD, RD, AD for the whole brain in:
%     GE/storedMeanDiffusionVals.mat
%
% (6) Recompute them from the data
%
% (7) Compute the difference between the stored and the recomputed ones.
%
% Example:
%  This function is meant to be called by mrvValidate, which will
% compute the difference between the stored values and the re-computed
% values.
%
%   mrvValidate([],[], 'v_dtiRawPreprocessGE');
%
% See also: mrvValidateAll.m
%
% FP and MP 7/6/2011
% Copyright Stanford team, mrVista, 2011

% This function takes too long to run. We want validation functions to be
% fast (say, less than 10 s). this one takes minutes to hours to run.
%
val = [];
return;

%% Get the data pathdata path
% Changing the last parameter and the function name changes
% scanner type (e.g., Siemens)
dataDir = fullfile(mrvDataRootPath,'diffusion','dtiRawPreprocess','GE');

%% Retain original directory, change to data directory
curDir = pwd;
cd(dataDir);

%% Run Preprocess
% setting clobber to 'always', so that output files will be silently replaced.
dtiRawPreprocess('raw/dti_g87_b1000.nii.gz', 't1.nii.gz',[],[],'always');

%% Show alignment to t1
% this was automatically computed by dtiRawPreprocess.m
imshow(imread('dti40trilin/t1pdd.png'));

%% Compute mean FA, RD, MD, AD values and check them with the stored one.

% load the stored values
% by convention mrvValidate.m loads up a storedVals data file
% with the same name of the validate function thta uses it
thisfunction = mfilename;
load(fullfile(mrvDataRootPath,'validate',[thisfunction(3:end),'.mat']));

% load the dti file.
dt = dtiLoadDt6('dti40trilin/dt6.mat');

% extract the eigen values.
eigVal = dt.dt6;

% compute the fractional, mean, radial and axial diffusivity
[fa,md,rd,ad] = dtiComputeFA(eigVal);

% compute the mean across the whole brain
val.fa = nanmean(fa(:));
val.md = nanmean(md(:));
val.rd = nanmean(rd(:));
val.ad = nanmean(ad(:));

% mrvValidate will use the output of this function to compare the
% recomputed values to the stored values.
% fr example doing something like this:
%
% check the computed with the stored values
% val.faErr = diff([mean_fa,meanVals.fa]);
% val.mdErr = diff([mean_md,meanVals.md]);
% val.rdErr = diff([mean_rd,meanVals.rd]);
% val.adErr = diff([mean_ad,meanVals.ad]);
%
% show results on matlab output
% errFields = fields(val);
% meanFields = fields(meanVals);
% for i = 1:length(fields(val))
%  fprintf('[%s] Error in ''%s'': %2.8f\n',mfilename, meanFields{i}, val.(errFields{i}));
% end

%% go back to the original directory, done!
cd(curDir)

return


%--------------------------------------------------------------------------------------------------------
% The system is created based on the principles described in the following paper
% Jimmy SJ. Ren, Li Xu, Qiong Yan, Wenxiu Sun, "Shepard Convolutional Neural Networks",
% Advances in Neural Information Processing Systems (NIPS 2015)
% email: jimmy.sj.ren@gmail.com
%--------------------------------------------------------------------------------------------------------
addpath applications/Shepard_CNN/Shepard_super_res/
addpath utils/
addpath cuda/
addpath mem/
addpath layers/
addpath layers_adapters/
addpath optimization/
addpath pipeline/
addpath data/

clearvars -global config;
clearvars -global mem;
clear gen_mask_patch_cat_idx_for_super_res;
clear;
global config mem;
shepard_sr_x4_configure();
init(0);

load('data/Shepard_CNN/Shepard_super_res/x4/val_1ch/val_1');

images_t = reshape(images_t, size(images_t,1), size(images_t,2), 1, size(images_t,3));
labels_t = reshape(labels_t, size(labels_t,1), size(labels_t,2), 1, size(labels_t,3));

perm = randperm(size(images_t, 4));
images_t = images_t(:,:,:,perm);
labels_t = labels_t(:,:,:,perm);
test_samples = config.NEW_MEM(images_t(:,:,:,1:1000));
test_labels = config.NEW_MEM(labels_t(:,:,:,1:1000));

mask = config.NEW_MEM([1 0 0 0;0 0 0 0;0 0 0 0;0 0 0 0]);
mask = repmat(mask, config.input_size(1)/4, config.input_size(2)/4, config.chs);
%mask = config.NEW_MEM([1 0; 0 0]);
%mask = repmat(mask, config.input_size(1)/2, config.input_size(2)/2, config.chs);
mask = repmat(mask, 1,1,1,config.batch_size);

count = 0;
cost_avg = 0;
epoc = 0;
points_seen = 0;
display_points = 5000;
save_points = 50000;
max_grad = 1;
fprintf('%s\n', datestr(now, 'dd-mm-yyyy HH:MM:SS FFF'));
for pass = 1:10
    for p = 1:50
        load(strcat('data/Shepard_CNN/Shepard_super_res/x4/train_1ch/patches_', num2str(p), '.mat'));

        images = reshape(images, size(images,1), size(images,2), 1, size(images,3));
        labels = reshape(labels, size(labels,1), size(labels,2), 1, size(labels,3));

        perm = randperm(20000);
        images = images(:,:,:,perm);
        labels = labels(:,:,:,perm);
        train_imgs = config.NEW_MEM(images);
        train_labels = config.NEW_MEM(labels);

        for i = 1:size(train_labels, 4) / config.batch_size
            points_seen = points_seen + config.batch_size;
            in = train_imgs(:,:,:,(i-1)*config.batch_size+1:i*config.batch_size);
            %in = train_labels(:,:,:,(i-1)*config.batch_size+1:i*config.batch_size);
            out = train_labels(:,:,:,(i-1)*config.batch_size+1:i*config.batch_size);
            out = out((size(in, 1) - config.output_size(1)) / 2 + 1:(size(in, 1) - config.output_size(1)) / 2 + config.output_size(1), ...
                      (size(in, 2) - config.output_size(2)) / 2 + 1:(size(in, 2) - config.output_size(2)) / 2 + config.output_size(2), :, :);

            % make the mask list
            mask_li = {};
            mask_li{1} = mask;
            % operate the training pipeline
            op_train_pipe_with_mask(in.*mask, mask_li, out);
            % update the weights
            config.UPDATE_WEIGHTS();

            if(cost_avg == 0)
                cost_avg = config.cost;
            else
                cost_avg = (cost_avg + config.cost) / 2;
            end

            % display point
            if(mod(points_seen, display_points) == 0)
                count = count + 1;
                fprintf('%d ', count);
            end
            % save point
            if(mod(points_seen, save_points) == 0)
                fprintf('\n%s', datestr(now, 'dd-mm-yyyy HH:MM:SS FFF'));
                epoc = epoc + 1;
                test_cost = 0;
                for t = 1:size(test_samples, 4) / config.batch_size
                    t_label = test_labels(:,:,:,(t-1)*config.batch_size+1:t*config.batch_size);
                    t_label = config.NEW_MEM(t_label((size(in, 1) - config.output_size(1)) / 2 + 1:(size(in, 1) - config.output_size(1)) / 2 + config.output_size(1), ...
                                            (size(in, 2) - config.output_size(2)) / 2 + 1:(size(in, 2) - config.output_size(2)) / 2 + config.output_size(2), :));

                    op_test_pipe_with_mask(test_samples(:,:,:,(t-1)*config.batch_size+1:t*config.batch_size).*mask, mask_li, t_label);

                    test_out = gather(mem.output);
                    test_cost = test_cost + config.cost;
                end
                test_cost = test_cost / (size(test_samples, 4) / config.batch_size);
                fprintf('\nepoc %d, training avg cost: %f, test avg cost: %f\n', epoc, cost_avg, test_cost);

                save_weights(strcat('applications/Shepard_CNN/Shepard_super_res/results/shepard_layer_x4/epoc', num2str(epoc), '.mat'));

                cost_avg = 0;
            end
        end
    end
end


function success=testJAABAWindowdata()

% This tests to make sure that loading cached windowdata and computing
% windowdata again learn the same classifier.

% AL20150602. This test doesn't work when I run it on the current codebase;
% I think it's because window feature computation is no longer
% deterministic, even with the setting of RNG state below.
% ComputeWindowFeatures() is called within a parfor and has a call to
% randsample.

%%
success = false;
jabFileName='/groups/branson/home/kabram/bransonlab/projects/JAABA/test_data/test_windowdata.jab';
gtMode = false;
data=JLabelData('setstatusfn',@(str)(fprintf('%s\n',str)), ...
                'clearstatusfn',@()(nop()));
data.openJabFile(jabFileName,gtMode);

if matlabpool('size')>0, matlabpool('close'); end
oldcl = data.classifier;

s = RandStream('mt19937ar','Seed',1);
RandStream.setGlobalStream(s);

data.Train;
newcl1 = data.classifier;

data.setWindowFeaturesParams(data.windowfeaturesparams);

s = RandStream('mt19937ar','Seed',1);
RandStream.setGlobalStream(s);

data.Train;
newcl2 = data.classifier;

if ~isequal(oldcl,newcl1) || ~isequal(newcl1,newcl2),
  error('Classifiers trained are not same');

end
success = true;


function [fid, sens, label, sens_label] = read_zebris(Fname_zeb,skip)

% Reads Zebris files:
%   fiducials locations, and
%   either sensor file or headshape file or both
%
% FORMAT [fid, sens, label] = read_zebris(Fname_zeb,skip)
% Input:
% Fname_zeb  - Zebris ASCII file containing sensor locations (mm)
%             (headshape can also be considered here instead of sensors)
% skip       - first channels to skip
%
% Output:
% fid        - fiducial         locations (mm) in rows
% sens       - sensor/headshape locations (mm) in rows
% label      - labels of the fiducials
% sens_label - labels of the surface points, electrodes + headshape
%
% IMPORTANT: Note that Zebris data files should be -ASCII files with
% extension .sfp
% It is assumed that the .sfp file contains the location (mm) of fiducials
% (possibly twice), possibly followed by some additional named points for
% the electrodes, and then so more named location starting with 'sfl' for
% headshape locations.
% In some instances the first few channel locations may pertain to
% reference channels; the skip variable allows these to be skipped if
% necessary.
% The fiducial locations are flaged with the strings 'fidt9','fidnz' and
% 'fidt10'; indicating the leaft ear, nasion, and right ear, respectively.
% _________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Christophe Phillips
% $Id$

% checks and assigments
%--------------------------------------------------------------------------
try, skip;    catch, skip    = 0; end

[pth,nam,ext] = fileparts(Fname_zeb);
if ~strcmp(ext,'.sfp')
  warndlg(sprintf('Wrong input file format\n'));
  return
end

% --- READ Zebris Sensor + fiducial locations ---
%==========================================================================
try
  file = textread(Fname_zeb,'%s');
catch
  file = textread(fullfile(pwd,[nam ext]),'%s');
end
% remove zeros at the end
bool = 0;
while bool == 0
  if strcmp(file{end},'0')
    file = file(1:end-1);
  else
    bool = 1;
  end
end

% read fiducials
%--------------------------------------------------------------------------
NZ   = [];
LE   = [];
RE   = [];
temp = 0;
nl   = 1;

while temp == 0
  if strcmp(file{nl},'fidnz')
    NZ = [NZ ; str2num(file{nl+1}) str2num(file{nl+2}) str2num(file{nl+3}) ];
    nl = nl + 4;
  elseif strcmp(file{nl},'fidt9')
    LE = [LE ; str2num(file{nl+1}) str2num(file{nl+2}) str2num(file{nl+3}) ];
    nl = nl + 4;
  elseif strcmp(file{nl},'fidt10')
    RE = [RE ; str2num(file{nl+1}) str2num(file{nl+2}) str2num(file{nl+3}) ];
    nl = nl + 4;
  else
    temp = 1;
  end
end

% convert from cm to mm
%--------------------------------------------------------------------------
NZ    = mean(NZ,1); LE = mean(LE,1); RE = mean(RE,1);
fid   = [NZ; LE; RE];

label = [{'nas', 'lpa', 'rpa'}];

% read sensor locations or headshape locations
%--------------------------------------------------------------------------
sens  = [];
sens_label = [];

start = nl + skip*3;
for ii = start:4:length(file)
  sens_label = [sens_label, file(ii)];
  sens = [sens; ...
    str2num(file{ii+1}) str2num(file{ii+2}) str2num(file{ii+3})];
end


% Copyright 2011 Zdenek Kalal
%
% This file is part of TLD.
%
% TLD 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.
%
% TLD 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 TLD.  If not, see <http://www.gnu.org/licenses/>.

function [pX,pEx,bbP0] = tldGeneratePositiveData(tld,overlap,im0,p_par)

pX   = [];
pEx  = [];%zeros(prod(tld.patchsize),numWarps);

% Get closest bbox
[~,idxP] = max(overlap);
bbP0 =  tld.grid(1:4,idxP);

% Get overlapping bboxes
idxP = find(overlap > 0.6);
if length(idxP) > p_par.num_closest
    [~,sIdx] = sort(overlap(idxP),'descend');
    idxP = idxP(sIdx(1:p_par.num_closest));
end
bbP  = tld.grid(:,idxP);
if isempty(bbP), return; end

% Get hull
bbH  = bb_hull(bbP);
cols = bbH(1):bbH(3);
rows = bbH(2):bbH(4);

im1 = im0;
pEx = tldGetPattern(im1,bbP0,tld.model.patchsize);
if tld.model.fliplr
pEx = [pEx tldGetPattern(im1,bbP0,tld.model.patchsize,1)];
end
for i = 1:p_par.num_warps
    if i > 1
        randomize = rand; % Sets the internal randomizer to the same state
        %patch_input = img_patch(im0.input,bbH,randomize,p_par);
        patch_blur = img_patch(im0.blur,bbH,randomize,p_par);
        im1.blur(rows,cols) = patch_blur;
        %im1.input(rows,cols) = patch_input;
    end

    % Measures on blured image
    pX  = [pX fern(5,im1,idxP,0)];

    % Measures on input image
    %pEx(:,i) = tldGetPattern(im1,bbP0,tld.model.patchsize);
    %pEx = [pEx tldGetPattern(im1,tld.grid(1:4,idxP),tld.model.patchsize)];

end


function F = in_fread_brainamp(sFile, sfid, SamplesBounds)
% IN_FREAD_BRAINAMP:  Read a block of recordings from BrainVision BrainAmp .eeg file
%
% USAGE:  F = in_fread_brainamp(sFile, sfid, SamplesBounds=[])

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2012-2013

% Parse inputs
if (nargin < 3) || isempty(SamplesBounds)
    SamplesBounds = round(sFile.prop.times .* sFile.prop.sfreq);
end

% BINARY files
if strcmpi(sFile.header.DataFormat, 'BINARY')
    nChan = sFile.header.NumberOfChannels;
    nSamplesToRead = SamplesBounds(2) - SamplesBounds(1) + 1;
    % MULTIPLEXED files
    if strcmpi(sFile.header.DataOrientation, 'MULTIPLEXED')
        % Get start and length of block to read
        offsetData = SamplesBounds(1) * nChan * sFile.header.bytesize;
        % Position file at the beginning of the data block
        fseek(sfid, offsetData, 'bof');
        % Read all values at once
        F = fread(sfid, [nChan, nSamplesToRead], sFile.header.byteformat);
    % VECTORIZED
    elseif strcmpi(sFile.header.DataOrientation, 'VECTORIZED')
        % Get blocks of samples to skip at the beginning and end of each channel
        offsetStart = SamplesBounds(1) * sFile.header.bytesize;
        offsetEnd = (round(sFile.prop.times(2) .* sFile.prop.sfreq) - SamplesBounds(2)) * sFile.header.bytesize;
        % Blocks of samples to skip between two blocks to read
        offsetSkip = offsetStart + offsetEnd;
        % Position file at the beginning of the trial
        fseek(sfid, offsetStart, 'bof');
        % Read the requested samples for all the channels
        % => WARNING: CALL TO FREAD WITH SKIP=0 DOES NOT WORK PROPERLY
        if (offsetSkip == 0)
            F = fread(sfid, [nSamplesToRead, nChan], sFile.header.byteformat)';
        else
            precision = sprintf('%d*%s', nSamplesToRead, sFile.header.byteformat);
            F = fread(sfid, [nSamplesToRead, nChan], precision, offsetSkip)';
        end
    end
% ASCII and VECTORIZED files
elseif (strcmpi(sFile.header.DataFormat, 'ASCII') && strcmpi(sFile.header.DataOrientation, 'VECTORIZED'))
    % Open file
    fid = fopen(sFile.filename, 'r');
    % Initialize data matrix
    F = zeros(sFile.header.NumberOfChannels, sFile.header.DataPoints);
    iChannel = 1;
    % Read the entire file line by line
    while(1)
        % Display message
        % disp(sprintf('BRAINAMP> Reading channel #%d...', iChannel));
        % Reached the end of the file: exit the loop
        if feof(fid)
            break;
        end
        % Read one line
        strChan = strtrim(fgetl(fid));
        if isempty(strChan)
            continue;
        end
        % Find the first separator
        iSep = min([find(strChan == ' ',1), find(strChan == sprintf('\t'),1)]);
        % Replace "," with "." for the numbers
        strChan(strChan == ',') = '.';
        % Read the values
        F(iChannel,:) = sscanf(strChan(iSep+1:end), '%f')';
        iChannel = iChannel+1;
    end
    % Close file
    fclose(fid);
    % Select only the requested time points
    iTime = (SamplesBounds(1):SamplesBounds(2)) - round(sFile.prop.times(1) .* sFile.prop.sfreq) + 1;
    F = F(:,iTime);
end

% Apply gains, if available
if isfield(sFile.header, 'chgain') && (length(sFile.header.chgain) == size(F,1))
    F = bst_bsxfun(@times, F, sFile.header.chgain(:));
% Else: Convert from microVolts to Volts by default
else
    F = F * 1e-6;
end


function out = spm_run_tissue_volumes(cmd, job)
% SPM job execution function for Tissue Volumes
%
% See also: spm_cfg_tissue_volumes, spm_summarise
%__________________________________________________________________________
% Copyright (C) 2013-2018 Wellcome Trust Centre for Neuroimaging

% Ged Ridgway
% $Id: spm_run_tissue_volumes.m 7460 2018-10-29 15:55:12Z john $

switch lower(cmd)
    %----------------------------------------------------------------------
    case 'exec'

        %%
        mat = job.matfiles;
        T   = job.tmax;
        msk = char(job.mask);
        outf = job.outf;
        if isempty(msk)
            msk = 'all';
        end

        %%
        N = numel(mat);
        vol = nan(N, T);
        for n = 1:N
            res = load(mat{n});

            % check for previously computed volumes (to save time if there)
            if isfield(res, 'volumes') && strcmp(res.volumes.mask, msk) ...
                    && numel(res.volumes.litres) >= T
                vol(n, :) = res.volumes.litres(1:T);
                continue
            end

            % determine number of tissue classes Kb
            if isfield(res, 'mg'),
                Kb  = max(res.lkp);
            else
                Kb  = size(res.intensity(1).lik, 2);
            end
            tc  = false(Kb, 4);

            % look for existing mwc files
            mwc = cell(T, 1);
            for t = 1:T
                mwc{t} = spm_file(res.image(1).fname, 'prefix', ['mwc' num2str(t)],'ext','nii');
                if ~exist(mwc{t}, 'file')
                    tc(t, 4) = true; % i.e. need to produce this mwc
                end
            end

            % produce mwc files if required
            if any(tc(:, 4))
                for f = 1:numel(res.image)
                    if ~exist(res.image(f).fname, 'file')
                        error('Original image no longer found at:\n%s\n', res.image(f).fname)
                    end
                end
                spm_preproc_write8(res, tc);
            end

            % compute tissue volumes
            vol(n, :) = spm_summarise(mwc, msk, 'litres');

            % add to mat file for future reuse
            volumes.litres  = vol(n, :);
            volumes.mask    = msk;
            save(mat{n}, 'volumes', '-append')

            % if mwc newly created above, delete now
            for t = 1:T
                if tc(t, 4), spm_unlink(mwc{t}), end
            end
        end

        %% Put into output structure for use with dependencies
        for t = 1:T
            out.(sprintf('vol%d', t)) = vol(:, t);
        end
        out.vol_sum = sum(vol, 2); % (total intracranial volume if T=1:3)

        %% Optionally save in CSV format
        if ~isempty(outf)
            [pth, nam, ext] = spm_fileparts(outf);
            if isempty(ext), ext = '.csv'; end
            fnm = fullfile(pth, [nam ext]);
            fid = fopen(fnm, 'wt');
            if fid < 0, error('Failed to open %s\n', fnm); end
            delim = ',';
            fprintf(fid, 'File');
            fprintf(fid, [delim 'Volume%d'], 1:T);
            for n = 1:N
                fprintf(fid, '\n''%s''', mat{n});
                fprintf(fid, [delim '%d'], vol(n, :));
            end
            fprintf(fid, '\n');
            fclose(fid);
        end

        %% Display in command window
        fprintf('\nSegmentation files:\n');
        fprintf('\t%s\n', mat{:});
        fprintf('\nVolumes (litres):\n');
        disp(vol);
        %------------------------------------------------------------------
    case 'vout'
        try
            T = job.tmax;
        catch
            T = 3;
        end
        out(T+1) = cfg_dep;
        for t = 1:T
            out(t).sname        = num2str(t);
            out(t).src_output   = substruct('.', sprintf('vol%d', t));
            out(t).tgt_spec     = cfg_findspec({
                {'strtype','e', 'strtype','r'}
                });
        end
        out(T+1).sname      = 'Sum';
        out(T+1).src_output = substruct('.', 'vol_sum');
        out(T+1).tgt_spec   = cfg_findspec({
            {'strtype','e', 'strtype','r'}
            });
end


function output = exportAsOpenQuakeCatalogueCsv(catalog, filename)
    % export a Catalog to a csv file readable as a Catalogue
    if isa(catalog, 'ZmapCatalog')
        catalog = catalog.table();
    end
    if ~istable(catalog)
        error('expected a ZmapCatalog')
    end
    ymdHMS = datevec(catalog.Date);
    output = table;
    output.year = ymdHMS(:,1);
    output.month = ymdHMS(:,2);
    output.day = ymdHMS(:,3);
    output.hour = ymdHMS(:,4);
    output.minute = ymdHMS(:,5);
    output.second = ymdHMS(:,6);
    output.latitude = catalog.Latitude;
    output.longitude = catalog.Longitude;
    output.depth = catalog.Depth;
    output.magnitude = catalog.Magnitude;
    % output.moment = 10.^(1.5*magnitude + 9.05);
    output.eventID = "id"+string(1:height(output))';
    output.magnitudeType = catalog.MagnitudeType;
    if exist('filename','var') && (~exist(filename,'file') ||...
            questdlg(['File ',filename,' exists, overwrite?'],'export catalog')=="Yes")
        writetable(output,filename);
    end
end


%% runstats.m
% This script collects statistics about positions of each component,
% creates matrix S 4xM [xmin_0, ..., xmin_{M-1}; ymin_0, ..., ymin_{M-1};
% xmin_0, ..., xmax_{M-1}; ymax_0, ..., ymax_{M-1}] each column defines
% bounding box for one component.
%
% Parameters in structure options are now set to cover approx. 97.5% of all
% tested images (about 12200 images, original database was pruned - file
% ./MAT/eye_on_lfw_pruned.mat - so now it does not contain images with
% multiple faces annotated or detected. Also images with incomplete
% annotations were discarded.)
%
% 15-07-10 Michal Uricar
% 26-02-11 Michal Uricar

% clc;
clearvars; close all;

%% Timestamp

fprintf(1,'Started on %s\n\n', datestr(now));

%% Load data (images with detected frames)

addpath('./Functions');
addpath('../../matlab_toolbox/mex/');

load('./MAT/paths.mat');
load('./MAT/options.mat');

% load('./MAT/eyefd_on_lfw_pruned.mat');
annotation_struct = read_lfw_annotation_file(['../' DB_ANN]);

%% Print parameters

fprintf('Parameters: \nbw = [%d; %d]\n', options.bw);
fprintf('bw_margin = [%d; %d]\n', options.bw_margin);
fprintf('components= \n'); disp(options.components);
fprintf('M = %d\n', options.M);
fprintf('image_path = ''%s''\n', options.image_path);
fprintf('....................................\n');
fprintf('\nStarting test...\n');

%% run test

tic
[cnt, S, bad_idx] = paramStats(annotation_struct, options);
% [cnt, S, bad_idx] = paramStats(annotation_struct, options, 1);
toc

percent = cnt/annotation_struct.N * 100;
fprintf('Passed %d images. That is %f%%.\n', cnt, percent);

%% create new database - only images that passed through test

idx = 1:annotation_struct.N;
idx(bad_idx) = [];
% image = image(idx);
annotation_struct.bbox = annotation_struct.bbox(idx, :);
annotation_struct.names = annotation_struct.names(idx);
annotation_struct.eye_r = annotation_struct.eye_r(idx, :);
annotation_struct.eye_l = annotation_struct.eye_l(idx, :);
annotation_struct.canthus_rr = annotation_struct.canthus_rr(idx, :);
annotation_struct.canthus_rl = annotation_struct.canthus_rl(idx, :);
annotation_struct.canthus_lr = annotation_struct.canthus_lr(idx, :);
annotation_struct.canthus_ll = annotation_struct.canthus_ll(idx, :);
annotation_struct.mouth = annotation_struct.mouth(idx, :);
annotation_struct.mouth_corner_r = annotation_struct.mouth_corner_r(idx, :);
annotation_struct.mouth_corner_l = annotation_struct.mouth_corner_l(idx, :);
annotation_struct.nose = annotation_struct.nose(idx, :);
annotation_struct.N = numel(annotation_struct.names);

fprintf('Creating new database (db_good.mat)...\n');
save('./MAT/db_good.mat', 'annotation_struct');

%% Save data

fprintf('Saving statistics...\n');
save('./results/statistics_orig.mat', 'cnt', 'S', 'percent', 'options');

 %% shrink S (S boundary must represent positions of components center)

load('./results/statistics_orig.mat', 'S');

for i = 1 : options.M
    S(1, i) = S(1, i) + options.components(1, i)/2;
    S(2, i) = S(2, i) + options.components(2, i)/2;
    S(3, i) = S(3, i) - options.components(1, i)/2;
    S(4, i) = S(4, i) - options.components(2, i)/2;
end;

save('./results/statistics_orig.mat', 'cnt', 'S', 'percent', 'options');

%% Show computed S

[Iframe, Annotation] = getImageFrame(options, 1, annotation_struct);
Points = Annotation.P;
Points = prepareS0gt(Annotation.P, options);    % transform nose to the center of face
Points = Points(:, options.comselect);          % extract relevant points only (name list in options.compnames)
Points(:, options.M) = Annotation.P(:, 10);             % copy back original nose position

colors = colormap(hsv(options.M)); close gcf;
% names = fieldnames(Annotation.face);
names = options.compnames;

scrsz = get(0,'ScreenSize');

figure;
imshow(Iframe, [], 'Border', 'tight'); hold on;
set(gcf, 'OuterPosition', [scrsz(1) scrsz(2) scrsz(3) scrsz(4)]);
% set(gcf, 'Position', [100, 100, 800, 600]);
for k = 1 : options.M
%     plot(Points(1, k), Points(2, k), 'r.', 'LineWidth', 2, 'MarkerSize', 10);
    plot(Points(1, k), Points(2, k), '.', 'color', colors(k, :), 'LineWidth', 2, 'MarkerSize', 10);
    bb = makeAABB(S(:, k));
    line([bb(1,:) bb(1,1)], [bb(2,:) bb(2,1)], 'color', colors(k, :));
    bb = makeAABB([Points(1, k) - options.components(1, k)/2 Points(2, k) - options.components(2, k)/2 ...
                   Points(1, k) + options.components(1, k)/2 Points(2, k) + options.components(2, k)/2 ]);
%     text(Points(1, k)-1, Points(2, k)+1, names(k+3), 'color', colors(k, :));
    text(Points(1, k)-1, Points(2, k)+1, names(k), 'color', colors(k, :));
    line([bb(1,:) bb(1,1)], [bb(2,:) bb(2,1)], 'color', 'y');
end;
hold off;

if (~exist('./img/', 'dir'))
    mkdir('./img');
end;

saveas(gcf, './img/S.png');

%% Show S

S = ceil(S);

colors = colormap(hsv(options.M)); close gcf;
% names = fieldnames(Annotation.face);
names = options.compnames;
i = 1;

figure;
subplot(1, 2, 1);
imshow(Iframe, []); hold on;
for j = 1 : options.M;
    bb = makeAABB(S(:, j));
    line([bb(1,:) bb(1,1)], [bb(2,:) bb(2,1)], 'color', colors(j, :));
    text(Points(1, j), Points(2, j), names(j), 'color', colors(j, :));
end;
line([20 20], [0 41], 'color', 'm', 'LineWidth', 2);
hold off;
saveas(gcf, './img/S.png');

%% make each frame of component in S symetric (this works only on lfw database with 4 components!)

S_new = S;

% canthus - inner
S_new(2, 2) = min(S(2, 2), S(2, 3)); S_new(2, 3) = min(S(2, 2), S(2, 3));   % ymin
S_new(4, 2) = max(S(4, 2), S(4, 3)); S_new(4, 3) = max(S(4, 2), S(4, 3));   % ymax
S_new(3, 2) = options.bw(1)/2 + max(S(3, 2) - options.bw(1)/2, options.bw(1)/2 - S(1, 3));     % x_max
S_new(1, 3) = options.bw(1)/2 - max(S(3, 2) - options.bw(1)/2, options.bw(1)/2 - S(1, 3));
S_new(1, 2) = min(S(1, 2), options.bw(1) - S(3, 3));
S_new(3, 3) = options.bw(1) - min(S(1, 2), options.bw(1) - S(3, 3));
% canthus - outer
S_new(2, 6) = min(S(2, 6), S(2, 7)); S_new(2, 7) = min(S(2, 6), S(2, 7));   % ymin
S_new(4, 6) = max(S(4, 6), S(4, 7)); S_new(4, 7) = max(S(4, 6), S(4, 7));   % ymax
S_new(3, 6) = options.bw(1)/2 + max(S(3, 6) - options.bw(1)/2, options.bw(1)/2 - S(1, 7));     % x_max
S_new(1, 7) = options.bw(1)/2 - max(S(3, 6) - options.bw(1)/2, options.bw(1)/2 - S(1, 7));
S_new(1, 6) = min(S(1, 6), options.bw(1) - S(3, 7));
S_new(3, 7) = options.bw(1) - min(S(1, 6), options.bw(1) - S(3, 7));
% mouth corners
S_new(2, 4) = min(S(2, 4), S(2, 5)); S_new(2, 5) = min(S(2, 4), S(2, 5));   % ymin
S_new(4, 4) = max(S(4, 4), S(4, 5)); S_new(4, 5) = max(S(4, 4), S(4, 5));   % ymax
S_new(3, 4) = options.bw(1)/2 + max(S(3, 4) - options.bw(1)/2, options.bw(1)/2 - S(1, 5));     % x_max
S_new(1, 5) = options.bw(1)/2 - max(S(3, 4) - options.bw(1)/2, options.bw(1)/2 - S(1, 5));
S_new(1, 4) = min(S(1, 4), options.bw(1) - S(3, 5));
S_new(3, 5) = options.bw(1) - min(S(1, 4), options.bw(1) - S(3, 5));
% center of face
S_new(3, 1) = options.bw(1)/2 + max(S(3, 1) - options.bw(1)/2, options.bw(1)/2 - S(1, 1));     % x_max
S_new(1, 1) = options.bw(1)/2 - max(S(3, 1) - options.bw(1)/2, options.bw(1)/2 - S(1, 1));
% nose
S_new(3, 8) = options.bw(1)/2 + max(S(3, 8) - options.bw(1)/2, options.bw(1)/2 - S(1, 8));     % x_max
S_new(1, 8) = options.bw(1)/2 - max(S(3, 8) - options.bw(1)/2, options.bw(1)/2 - S(1, 8));

subplot(1, 2, 2);
imshow(Iframe); hold on;
for j = 1 : options.M;
    bb = makeAABB(S_new(:, j));
    line([bb(1,:) bb(1,1)], [bb(2,:) bb(2,1)], 'color', colors(j, :));
    text(Points(1, j), Points(2, j), names(j), 'color', colors(j, :));
end;
line([20 20], [0 41], 'color', 'm', 'LineWidth', 2);
hold off;
saveas(gcf, './img/S_symetric.png');

%% Save symetric S for 4 components
S2 = S_new;
% save('./results/symetric_S_orig.mat', 'S');

% set new options.S
options.S = S2;

fname = './results/options.mat';
fprintf('Saving new options struct to file %s...', fname);
save(fname, 'options');
fprintf(' done.\n');

%% Check for TRN, VAL and TST annotation files

% if (exist('./MAT/LFW_TRN_annotation.txt', 'file') && exist('./MAT/LFW_VAL_annotation.txt', 'file') && exist('./MAT/LFW_TST_annotation.txt', 'file'))
%     annotation_struct = read_lfw_annotation_file('./MAT/LFW_TRN_annotation.txt');
%     save('./MAT/TRN.mat', 'annotation_struct');
%     annotation_struct = read_lfw_annotation_file('./MAT/LFW_VAL_annotation.txt');
%     save('./MAT/VAL.mat', 'annotation_struct');
%     annotation_struct = read_lfw_annotation_file('./MAT/LFW_TST_annotation.txt');
%     save('./MAT/TST.mat', 'annotation_struct');
% end;
if (~isempty(TRN_EXAMPLES)  && ~isempty(VAL_EXAMPLES) && ~isempty(TST_EXAMPLES))
    if (exist(['../' TRN_EXAMPLES], 'file') && exist(['../' VAL_EXAMPLES], 'file') && exist(['../' TST_EXAMPLES], 'file'))
        annotation_struct = read_lfw_annotation_file(['../' TRN_EXAMPLES]);
        save('./MAT/TRN.mat', 'annotation_struct');
        annotation_struct = read_lfw_annotation_file(['../' VAL_EXAMPLES]);
        save('./MAT/VAL.mat', 'annotation_struct');
        annotation_struct = read_lfw_annotation_file(['../' TST_EXAMPLES]);
        save('./MAT/TST.mat', 'annotation_struct');
    end;
end;

%% Timestamp

fprintf(1,'\nFinished on %s\n\n', datestr(now));


function D = spm_eeg_inv_forward(varargin)
% Compute M/EEG leadfield
% FORMAT D = spm_eeg_inv_forward(D,val)
%
% D                - input struct
% (optional) fields of S:
% D                - filename of EEG/MEG mat-file
%
% Output:
% D                - EEG/MEG struct with filenames of Gain matrices)
%__________________________________________________________________________
% Copyright (C) 2008-2018 Wellcome Trust Centre for Neuroimaging

% Jeremie Mattout & Christophe Phillips
% $Id: spm_eeg_inv_forward.m 7702 2019-11-22 11:32:26Z guillaume $

SVNrev = '$Rev: 7702 $';

%-Startup
%--------------------------------------------------------------------------
spm('FnBanner', mfilename, SVNrev);

%-Initialisation
%--------------------------------------------------------------------------
[D, val] = spm_eeg_inv_check(varargin{:});

if numel(D.inv{val}.datareg) ~= numel(D.inv{val}.forward)
    error('Separate coregistration is required for every modality.');
end

Fgraph = spm_figure('FindWin','Graphics');
spm_figure('Clear',Fgraph);
spm('Pointer', 'Watch');
if isempty(Fgraph) || spm('CmdLine'), graph = 'no'; else, graph = 'yes'; end

for i = 1:numel(D.inv{val}.forward)
    M    = D.inv{val}.datareg(i).fromMNI*D.inv{val}.mesh.Affine;

    M    = diag([1e-3 1e-3 1e-3 1])*M; % convert to m

    mesh = spm_eeg_inv_transform_mesh(M, D.inv{val}.mesh);

    mesh_correction = [];

    sens = D.inv{val}.datareg(i).sensors;

    if isequal(D.inv{val}.datareg(i).modality, 'MEG')
        sens = ft_datatype_sens(sens, 'amplitude', 'T', 'distance', 'm');
    else
        sens = ft_datatype_sens(sens, 'amplitude', 'V', 'distance', 'm');
    end

    switch D.inv{val}.forward(i).voltype
        case 'EEG interpolated'
            vol = D.inv{val}.forward(i).vol;
            modality = 'EEG';
        case '3-Shell Sphere'
            cfg              = [];
            cfg.feedback     = graph;
            cfg.siunits      = 'yes';
            cfg.showcallinfo = 'no';

            headshape(1) = export(gifti(mesh.tess_scalp),  'ft');
            headshape(2) = export(gifti(mesh.tess_oskull), 'ft');
            headshape(3) = export(gifti(mesh.tess_iskull), 'ft');

            % determine the convex hull of the brain, to determine the support points
            pnt  = mesh.tess_ctx.vert;
            tric = convhulln(pnt);
            sel  = unique(tric(:));

            % create a triangulation for only the support points
            headshape(4).pnt = pnt(sel, :);
            headshape(4).tri = convhulln(pnt(sel, :));

            cfg.method = 'concentricspheres';

            vol  = ft_prepare_headmodel(cfg, headshape);

            cfg = [];
            cfg.headmodel = vol;
            cfg.grid.pos  = mesh.tess_ctx.vert;
            cfg.spherify  = 'yes';
            gridsphere    = ft_prepare_sourcemodel(cfg);

            mesh_correction    = rmfield(cfg, {'headmodel', 'grid'});

            mesh.tess_ctx.vert = gridsphere.pos;
            modality = 'EEG';

        case 'EEG BEM'
            volfile = spm_file(mesh.sMRI, 'suffix','_EEG_BEM', 'ext','mat');
            vol = [];

            if exist(volfile, 'file')
                vol = ft_read_headmodel(volfile);
                if ~isfield(vol, 'unit') || ~isequal(vol.unit, 'm')
                    vol = [];
                end
            end

            if isempty(vol)

                vol.cond   = [0.3300 0.0041 0.3300];
                vol.source = 1; % index of source compartment
                vol.skin   = 3; % index of skin surface
                % brain
                vol.bnd(1) = export(gifti(mesh.tess_iskull), 'ft');
                % skull
                vol.bnd(2) = export(gifti(mesh.tess_oskull), 'ft');
                % skin
                vol.bnd(3) = export(gifti(mesh.tess_scalp),  'ft');

                % create the BEM system matrix
                cfg        = [];
                cfg.method = 'bemcp';
                cfg.showcallinfo = 'no';
                cfg.siunits      = 'yes';
                vol = ft_prepare_headmodel(cfg, vol);

                spm_progress_bar('Set', 1);

                save(volfile, 'vol', spm_get_defaults('mat.format'));

                spm_progress_bar('Clear');
                spm('Pointer', 'Arrow');
            end

            cfg = [];
            cfg.headmodel = vol;
            cfg.grid.pos = mesh.tess_ctx.vert;
            cfg.moveinward = 6e-3; %move to empirically determined BEM safe zone
            gridcorrect = ft_prepare_sourcemodel(cfg);

            mesh_correction    = rmfield(cfg, {'headmodel', 'grid'});

            mesh.tess_ctx.vert = gridcorrect.pos;

            vol = volfile;
            modality = 'EEG';

        case 'OpenMEEG BEM'
            vol        = [];
            vol.cond   = [0.3300 0.0041 0.3300];
            vol.source = 1; % index of source compartment
            vol.skin   = 3; % index of skin surface
            % brain
            vol.bnd(1) = export(gifti(mesh.tess_iskull), 'ft');
            % skull
            vol.bnd(2) = export(gifti(mesh.tess_oskull), 'ft');
            % skin
            vol.bnd(3) = export(gifti(mesh.tess_scalp),  'ft');

            cfg                         = [];
            cfg.method                 = 'openmeeg';
            cfg.siunits                = 'yes';
            cfg.showcallinfo           = 'no';
            vol                        = ft_prepare_headmodel(cfg, vol);

            cfg                        = [];
            cfg.vol                    = vol;
            cfg.grid.pos               = mesh.tess_ctx.vert;
            cfg.moveinward             = 6e-3; % smaller shift might suffice for OpenMEEG
            gridcorrect                = ft_prepare_sourcemodel(cfg);

            mesh_correction            = rmfield(cfg, {'vol', 'grid'});

            mesh.tess_ctx.vert         = gridcorrect.pos;

            modality = 'EEG';
        case 'Single Sphere'
            cfg                        = [];
            cfg.feedback               = 'yes';
            cfg.showcallinfo           = 'no';
            cfg.grad                   = D.inv{val}.datareg(i).sensors;
            cfg.method                 = 'singlesphere';
            cfg.siunits                = 'yes';

            headshape                  = export(gifti(mesh.tess_scalp), 'ft');

            vol                        = ft_prepare_headmodel(cfg, headshape);
            modality                   = 'MEG';
        case 'MEG Local Spheres'
            cfg                        = [];
            cfg.feedback               = 'yes';
            cfg.showcallinfo           = 'no';
            cfg.grad                   = sens;
            cfg.method                 = 'localspheres';
            cfg.siunits                = 'yes';

            headshape                  = export(gifti(mesh.tess_scalp), 'ft');
            vol                        = ft_prepare_headmodel(cfg, headshape);
            modality                   = 'MEG';
        case  'Single Shell'
            cfg                        = [];
            cfg.feedback               = 'yes';
            cfg.showcallinfo           = 'no';
            cfg.grad                   = sens;
            cfg.method                 = 'singleshell';
            cfg.siunits                = 'yes';

            headshape                  = export(gifti(mesh.tess_iskull), 'ft');

            vol                        = ft_prepare_headmodel(cfg, headshape);
            modality                   = 'MEG';

        otherwise
            error('Unsupported volume model type.');
    end

    D.inv{val}.forward(i).vol             = vol;
    D.inv{val}.forward(i).mesh            = mesh.tess_ctx;
    D.inv{val}.forward(i).mesh_correction = mesh_correction;
    D.inv{val}.forward(i).modality        = modality;
    D.inv{val}.forward(i).siunits         = 1;

    D.inv{val}.forward(i).sensors  = sens;

    D.inv{val}.forward(i).toMNI    = D.inv{val}.datareg(i).toMNI*diag([1e3 1e3 1e3 1]);
    D.inv{val}.forward(i).fromMNI  = diag([1e-3 1e-3 1e-3 1])*D.inv{val}.datareg(i).fromMNI;

    spm_figure('Clear',Fgraph);
end

% This is to force recomputing the lead fields
try, D.inv{val} = rmfield(D.inv{val}, 'gainmat'); end

fprintf('%-40s: %30s\n','Completed',spm('time'));                       %-#
spm('Pointer', 'Arrow');


classdef generalization_DS < handle

%  This datasource object (DS) allows one to train a classifier on a specific
%  set of labels, and then test the classifier on a different set of
%  labels - which enables one to evaluate how similar neural representations
%  are across different but related conditions (i.e., does training on one set of
%  conditions generalization to a different but related set of conditions?). This datasource
%  is a subclass of the handle class (i.e., it has a persistent state) and
%  contains a basic_DS where it gets most of its functionality.
%
%  The constructor for this datasource has the same arguments
%  as basic_DS, plus two additional arguments 'the_training_label_names'
%  and 'the_test_label_names' i.e., the constructor has the form:
%
%     ds = generalization_DS(binned_data_name, specific_binned_label_name, num_cv_splits, the_training_label_names, the_test_label_names, load_data_as_spike_counts)
%
%      the_training_label_names and the_test_label_names are cell arrays that
%        specify which labels should belong to which class, with the first element
%        of these cells arrays specifying the training/test labels for the first class
%        the second element of the cell array specifies which labels belong to
%        the second class, etc..  For example, suppose one was interested in testing
%        position invariance, and had done an experiment in which data was recorded
%        while 7 different objects were shown at three different locations.  If the
%        labels for the 7 objects at the first location had labels 'obj1_loc1', 'obj2_loc1', ..., 'obj7_loc1',
%        at the second location were 'obj1_loc2', 'obj2_loc2', ..., 'obj7_loc2',
%        and at the third location were 'obj1_loc3', 'obj2_loc3', ..., 'obj7_loc3',
%        then one could do a test of position invariance by setting the_training_label_names{1} = {'obj1_loc1},
%        setting the_training_label_names{2} = {'obj2_loc1'}, ...,  the_training_label_names{7} = {'obj7_loc1},
%        and setting the the_test_label_names{1} = {'obj1_loc2', 'obj1_loc3'},
%        the_test_label_names{2} = {'obj2_loc2', 'obj2_loc3'}, ..., the_test_label_names{7} = {'obj7_loc2', 'obj7_loc3'}.
%        The object is able to test such generalization from training on one set of labels and
%        testing on a different set of labels by remapping the training label numbers to the
%        index number in the_training_label_names cell array, and remapping the
%        test label numbers with the the index number into the the_test_label_names cell array.
%
%  This object has all of the same properites as the basic_DS object (except that label_names_to_use which
%    has been replaced by the the_training_label_names, and the_test_label_names properties).  There
%    is also an additional property that can be set for this object which is:
%
%       use_unique_data_in_each_CV_split  (default value is 0).
%
%       When this argument is set to 0, the get_data method returns the normal leave one split
%        out training and test data sets (i.e., the training set consists of
%        (num_cv_splits - 1) splits of the data and the test set consists of 1 split of the data).
%        The data in the training still comes from different splits as the data in the
%        test set, thus one can have some of the same labels in the both
%        the_training_label_names and in the_test_label_names (in fact, if ones sets
%        the_test_label_names = the_training_label_names, then the get_data
%        method will be the same as the basic_DS get_data method).  However,
%        if use_unique_data_in_each_CV_split = 1, then each training
%        and test set will consist data from only split, and thus each cross-validation
%        run is essentially like running an independent decoding experiment.
%        In this case the_training_label_names and the_test_label_names must not contain
%        any of the same labels (otherwise, they would be copies of the same data
%        which would violate the fact that the training and the test set must not have any of the same data).

%==========================================================================

%     This code is part of the Neural Decoding Toolbox.
%     Copyright (C) 2011 by Ethan Meyers (emeyers@mit.edu)
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.

%==========================================================================

properties

    the_basic_DS = [];      % the basic_DS that will be used to give this object most of its functionality

    the_training_label_names = [];   % a cell array specifying which label names (or numbers) should be used for training the classifier
                                       %   i.e., the_training_label_names{1} = {class_1_training_names}; the_training_label_names{2} = {class_2_training_names}; etc.
    the_test_label_names = [];       % a cell array specifying which label names (or numbers) should be used for testing the classifier
                                       %   i.e., the_test_label_names{1} = {class_1_test_names}; the_test_label_names{2} = {class_2_test_names}; etc.

    use_unique_data_in_each_CV_split = 0;  % if this is set to 1 then each CV splits has unique data, i.e.,
                                                         %  each CV split has the amount of data that is typically only in the test set,
                                                         %  and every CV training set does not consist of (num_cv_splits -1) * num_labels data points
                                                         %  but instead consists of length(cell2mat(the_test_label_names)) training points.

    % some properties of basic_DS that will also be available in generalization_DS by setting the basic_DS properties

    num_times_to_repeat_each_label_per_cv_split = 1;     %  how many of each unique label should be in each CV block

    sample_sites_with_replacement = 0;  %  specify whether to sample neurons with replacement - if they are sampled with replacement, then some features will be repeated within a single point data vector
    num_resample_sites = -1;            %  how many sites should be used for each resample iteration - must be less than length(the_data)

    create_simultaneously_recorded_populations = 0;   % to use pseudo-populations or simultaneous populations (2 => that the training set is pseudo and test is simultaneous)

    sites_to_use = -1;                   %  a list of indices of which sites (neurons) to use in the the_data cell array
    sites_to_exclude = [];               %  a list of features that should explicitly be excluded
    time_periods_to_get_data_from = [];  %  a cell array containing vectors that specify which time bins to use from the_data

    % randomly shuffles the labels prior to the get_data method being called - which is useful for creating one point in a null distribution to check if decoding results are above what is expected by change.
    randomly_shuffle_labels_before_running = 0;

end

properties (GetAccess = 'public', SetAccess = 'private')

      initialized = 0;

      the_training_label_numbers = [];   % numbers that the_training_label_names were mapping on to
      the_test_label_numbers = [];       % numbers that the_test_label_names were mapping on to
end

methods

    function ds = generalization_DS(binned_data_name, specific_binned_label_name, num_cv_splits, the_training_label_names, the_test_label_names, load_data_as_spike_counts)

       if nargin < 6
           load_data_as_spike_counts = 0;
       end

       ds.the_basic_DS = basic_DS(binned_data_name, specific_binned_label_name, num_cv_splits, load_data_as_spike_counts);   % set properties using parent constructor

       ds.the_training_label_names = the_training_label_names;   % set properties that are unique to this object
       ds.the_test_label_names = the_test_label_names;

       % get all the unique labels
       if length(the_training_label_names) ~= length(the_test_label_names)
            error('The cell arrays the_training_label_names and the_test_label_names must have the same number of cells (with the names/numbers in cell i containing which labels belong to class i)');
       end

    end

    function the_properties = get_DS_properties(ds)

        the_properties = ds.the_basic_DS.get_DS_properties;

        the_properties.the_training_label_names = ds.the_training_label_names;
        the_properties.the_test_label_names  = ds.the_test_label_names;

        the_properties.use_unique_data_in_each_CV_split = ds.use_unique_data_in_each_CV_split;

    end

    function  [XTr_all_time_cv YTr_all XTe_all_time_cv YTe_all] = get_data(ds)
    %function  [XTr_all_time_cv YTr_all XTe_all_time_cv YTe_all ADDITIONAL_DATASOURCE_INFO] = get_data(ds)

        % initialize things here....
        if ds.initialized == 0

            % creating separate variables for this since calling a field of an object in Matlab is slow
            the_training_label_names = ds.the_training_label_names;
            the_test_label_names = ds.the_test_label_names;

            ds.the_basic_DS.num_times_to_repeat_each_label_per_cv_split = ds.num_times_to_repeat_each_label_per_cv_split;

            ds.the_basic_DS.sample_sites_with_replacement = ds.sample_sites_with_replacement ;
            ds.the_basic_DS.num_resample_sites = ds.num_resample_sites;

            ds.the_basic_DS.create_simultaneously_recorded_populations = ds.create_simultaneously_recorded_populations;

            ds.the_basic_DS.sites_to_use = ds.sites_to_use;
            ds.the_basic_DS.sites_to_exclude = ds.sites_to_exclude;
            ds.the_basic_DS.time_periods_to_get_data_from = ds.time_periods_to_get_data_from;

            ds.the_basic_DS.randomly_shuffle_labels_before_running = ds.randomly_shuffle_labels_before_running;

            % if the_labels are strings, convert these names into label numbers...
            if iscell(ds.the_basic_DS.the_labels{1})

                % put all the training and test names into one cell array called all_training_and_test_names
                cTrainingNames = 0;
                cTestNames = 0;
                for iClass = 1:length(the_training_label_names)
                    for iTrain = 1:size(the_training_label_names{iClass}, 2)
                        cTrainingNames = cTrainingNames + 1;
                        all_training_names{cTrainingNames} = the_training_label_names{iClass}{iTrain};
                    end

                    for iTest = 1:size(the_test_label_names{iClass}, 2)
                        cTestNames = cTestNames + 1;
                        all_test_names{cTestNames} = the_test_label_names{iClass}{iTest};
                    end
                end

               all_training_and_test_names = union(all_training_names, all_test_names);

               % sanity checka that the same label is not in multiple training classes (and the the same label is not in multiple test classes)
               if length(unique(all_training_names)) ~= length(all_training_names)
                    warning('some of the same strings in the_training_label_names are in multiple classes');
               end
               if length(unique(all_test_names)) ~= length(all_test_names)  % same sanity check for the_test_label_names
                    warning('some of the same strings in the_test_label_names are in multiple classes');
               end

               % convert the_labels into numebrs
               ignore_case_of_strings = 0;  % for now, always respect the case of the strings used in the labels

                % by passing all_training_and_test_names as the 3rd argument convert_label_strings_into_numbers this keeps the mapping from the_test_label_names to the_test_label_numbers correct
                % an erorr in convert_label_strings_into_numbers will be thrown if some of the training or test label names are strings that are not in binned labels the_labels
                the_labels_as_numbers = convert_label_strings_into_numbers(ds.the_basic_DS.the_labels, ignore_case_of_strings, all_training_and_test_names);

                ds.the_basic_DS.the_labels = the_labels_as_numbers;

                % remap the_training_label_names and the_training_label_names into numbers
                cTrainingNames = 0;
                cTestNames = 0;
                for iClass = 1:length(the_training_label_names)
                    for iTrain = 1:size(the_training_label_names{iClass}, 2)
                        cTrainingNames = cTrainingNames + 1;
                        the_training_label_numbers{iClass}(iTrain) = find(ismember(all_training_and_test_names, the_training_label_names{iClass}{iTrain}));
                    end

                    for iTest = 1:size(the_test_label_names{iClass}, 2)
                        cTestNames = cTestNames + 1;
                        the_test_label_numbers{iClass}(iTest) = find(ismember(all_training_and_test_names, the_test_label_names{iClass}{iTest}));
                    end
                end

               %  sanity check to make sure that one is not training with label l in class k and then has test label l in class j
               some_of_the_same_labels_are_in_multiple_classes = 0;
               for iClass = 1:length(the_training_label_names)
                    temp_test_label_numbers = the_test_label_numbers;
                    temp_test_label_numbers{iClass} = [];
                    temp_test_label_numbers = cell2mat(temp_test_label_numbers);
                    if ~isempty(intersect(temp_test_label_numbers, the_test_label_numbers{iClass}))
                        some_of_the_same_labels_are_in_multiple_classes = 1;
                    end
               end

               if  some_of_the_same_labels_are_in_multiple_classes == 1
                    warning('some labels that are in training class k, are in a different test class j')
               end

               ds.the_training_label_numbers = the_training_label_numbers;
               ds.the_test_label_numbers = the_test_label_numbers;

            else  % if numbers for labels have been specified instead of names, just use those numbers

               ds.the_training_label_numbers = ds.the_training_label_names;
               ds.the_test_label_numbers = ds.the_test_label_names;

            end

            % only use labels that are listed in the_training_label_numbers and the_test_label_numbers
            the_training_nums = cell2mat(ds.the_training_label_numbers);
            the_test_nums = cell2mat(ds.the_test_label_numbers);
            label_numbers_to_use = unique([the_training_nums(:); the_test_nums(:)]);
            ds.the_basic_DS.label_names_to_use = label_numbers_to_use;

            ds.initialized = 1;
        end

         % creating separate variables for this since calling a field of an object in Matlab is slow
         the_training_label_numbers = ds.the_training_label_numbers;
         the_test_label_numbers = ds.the_test_label_numbers;

        %[XTr_all_time_cv YTr_all XTe_all_time_cv YTe_all] = get_data@basic_DS(ds);  % old version where this object was a subclass of basic_DS
         [XTr_all_time_cv YTr_all XTe_all_time_cv YTe_all] = ds.the_basic_DS.get_data;  % this might be much slower than inheriting from basic_DS :(

        if ds.use_unique_data_in_each_CV_split == 1

            % if running each training and test set separately, there can not be any overlap between the labels listed in the training and test sets
            %(otherwise there could be some of the same data in the training and test sets which is completely forbidden!!!!!
            all_unique_training_labels = unique(cell2mat(the_training_label_numbers));
            all_unique_test_labels = unique(cell2mat(the_test_label_numbers));

            if length(intersect(all_unique_training_labels,  all_unique_test_labels)) > 0
                error('if running each split separately (i.e., use_unique_data_in_each_CV_split == 1), then none of the same labels can be in the training and test sets, otherwise there will be some of the same data in the training and test sets')
            end

            % remap labels (only using old 'test' data because I want each CV split to be independent)
            remapped_YTr_all = NaN .* ones(size(YTr_all));  % only getting labels (and data) from test set b/c want each CV split to contain unique data
            remapped_YTe_all = NaN .* ones(size(YTe_al));

            for iGroup = 1:length(the_training_label_numbers)
                remapped_YTr_all(ismember(YTr_all_cv, the_training_label_numbers{iGroup})) = iGroup;
                remapped_YTe_all(ismember(YTe_all, the_test_label_numbers{iGroup})) = iGroup;
            end

           % remove data from trials in which the labels are not appropriate for the training/test sets
           train_inds  = ~isnan(remapped_YTr_all);
           test_inds  = ~isnan(remapped_YTe_all);

           YTr_all = remapped_YTr_all(train_inds);   % remove NaNs from remapped labels
           YTe_all = remapped_YTe_all(test_inds);

            for iCV = 1:length(XTr_all_time_cv{1})
               for iTime = 1:length(XTr_all_time_cv)
                   XTr_all_time_cv{iTime}{iCV} = XTr_all_time_cv{iTime}{iCV}(:, train_inds);   % only getting data from test set b/c want each CV split to be independent
                   XTe_all_time_cv{iTime}{iCV} = XTe_all_time_cv{iTime}{iCV}(:, test_inds);
               end
            end

        else

            % remap labels
            remapped_YTr_all = NaN .* ones(size(YTr_all));
            remapped_YTe_all = NaN .* ones(size(YTe_all));

            for iGroup = 1:length(the_training_label_numbers)
                remapped_YTr_all(ismember(YTr_all, the_training_label_numbers{iGroup})) = iGroup;
                remapped_YTe_all(ismember(YTe_all, the_test_label_numbers{iGroup})) = iGroup;
            end

           % remove data from trials in which the labels are not appropriate for the training/test sets
           training_data_inds_to_remove = isnan(remapped_YTr_all);
           test_data_inds_to_remove = isnan(remapped_YTe_all);

           for iTime = 1:length(XTr_all_time_cv)
               for iCV = 1:length(XTr_all_time_cv{1})
                    XTr_all_time_cv{iTime}{iCV}(:, training_data_inds_to_remove) = [];
                    XTe_all_time_cv{iTime}{iCV}(:, test_data_inds_to_remove) = [];
               end
           end

           remapped_YTr_all(training_data_inds_to_remove) = [];
           remapped_YTe_all(test_data_inds_to_remove) = [];

           YTr_all = remapped_YTr_all;
           YTe_all = remapped_YTe_all;

        end

    end  % end get_data method

end   % end methods

end % end class


classdef NomadOptimizer < AbstractOptimizer
    %NomadOptimizer Summary of this class goes here
    %   Detailed explanation goes here

    properties
        options(1,1) NomadOptions = NomadOptions();
    end

    methods
        function obj = NomadOptimizer()
            obj.options = NomadOptions();
        end

        function [exitflag, message] = optimize(obj, lvdOpt, writeOutput, callOutputFcn, hLvdMainGUI)
            [x0All, actVars, varNameStrs] = lvdOpt.vars.getTotalScaledXVector();
            [lbAll, ubAll, lbUsAll, ubUsAll] = lvdOpt.vars.getTotalScaledBndsVector();
            typicalX = lvdOpt.vars.getTypicalScaledXVector();
            lvdData = lvdOpt.lvdData;

            if(isempty(x0All) && isempty(actVars))
                exitflag = 0;
                message = 'No variables enabled on script.  Aborting optimization.';

                return;
            end

            evtNumToStartScriptExecAt = obj.getEvtNumToStartScriptExecAt(lvdOpt, actVars);
            evtToStartScriptExecAt = lvdOpt.lvdData.script.getEventForInd(evtNumToStartScriptExecAt);

            objFuncWrapper = @(x) lvdOpt.objFcn.evalObjFcn(x, evtToStartScriptExecAt);
            nonlcon = @(x, tfRunScript, stateLog) lvdOpt.constraints.evalConstraints(x, tfRunScript, evtToStartScriptExecAt, true, stateLog);

            opts = obj.options.getOptionsForOptimizer();
            constrTypeStr = obj.options.getConstrTypeStr();
            useParallel = obj.options.usesParallel();

            [c, ceq] = lvdOpt.constraints.evalConstraints(x0All, true, evtToStartScriptExecAt, false, []);
            numConstr = length(c) + 2*length(ceq);
            bboutput = horzcat({'OBJ'}, repmat({constrTypeStr},1,numConstr));
            numElemPerOutput = length(bboutput);
            bboutput = strjoin(bboutput,' ');
            nomadNonlconWrapper = @(x) NomadOptimizer.nomadConstrWrapper(x, nonlcon);

            pp = gcp('nocreate');
            if(isempty(pp) || useParallel == false) %
                numWorkers = 0;
                useParallel = false;
            else
                numWorkers = pp.NumWorkers;
                useParallel = true;
            end

            numVars = length(x0All);
            f = @(x) NomadOptimizer.nomadObjConstrWrapper(x, objFuncWrapper, nomadNonlconWrapper, numVars, numElemPerOutput, numWorkers, lvdData);
            opts = nomadset(opts, 'bb_output_type',bboutput);

            problem.objective = f; %f
            problem.x0 = x0All;
            problem.lb = lbAll;
            problem.ub = ubAll;
            problem.options = opts;

            problem.solver = 'nomad';

            problem.lvdData = lvdOpt.lvdData; %need to get lvdData in somehow

            %%% Run optimizer
            celBodyData = lvdOpt.lvdData.celBodyData;
            recorder = ma_OptimRecorder();

            if(callOutputFcn)
                propNames = lvdOpt.lvdData.launchVehicle.tankTypes.getFirstThreeTypesCellArr();
%                 handlesObsOptimGui = ma_ObserveOptimGUI(celBodyData, problem, true, writeOutput, [], varNameStrs, lbUsAll, ubUsAll);

                out = AppDesignerGUIOutput();
                ma_ObserveOptimGUI_App(out);
                handlesObsOptimGui = out.output{1};

%                 outputFnc = @(x, optimValues, state) ma_OptimOutputFunc(x, optimValues, state, handlesObsOptimGui, problem.objective, problem.lb, problem.ub, celBodyData, recorder, propNames, writeOutput, varNameStrs, lbUsAll, ubUsAll);
                hOptimStatusLabel = handlesObsOptimGui.optimStatusLabel;
                hFinalStateOptimLabel = handlesObsOptimGui.finalStateOptimLabel;
                hDispAxes = handlesObsOptimGui.dispAxesPanel;
                hCancelButton = handlesObsOptimGui.cancelButton;
                optimStartTic = tic();

                outputFnc = @(x, optimValues, state) NomadOptimizer.getOutputFunction(x, optimValues, state, hOptimStatusLabel, hFinalStateOptimLabel, hDispAxes, hCancelButton, ...
                                                                                problem.objective, problem.lb, problem.ub, celBodyData, recorder, propNames, writeOutput, varNameStrs, lbUsAll, ubUsAll, optimStartTic);

                nomadOutput1 = @(iter, fval, x, state) NomadOptimizer.nomadIterFunWrapper(iter, fval, x, outputFnc, state, numVars);
                nomadOutput2 = @(iter, fval, x) nomadOutput1(iter, fval, x, 'iter');
                problem.options = nomadset(problem.options, 'iterfun',nomadOutput2);
                nomadOutput1(0,NaN,x0All,'init');
            end

            problem.UseParallel = useParallel;
            [exitflag, message] = lvd_executeOptimProblem(celBodyData, writeOutput, problem, recorder, callOutputFcn);

            if(callOutputFcn)
                close(handlesObsOptimGui.ma_ObserveOptimGUI);
            end
        end

        function options = getOptions(obj)
            options = obj.options;
        end

        function tf = usesParallel(obj)
            tf = obj.getOptions().usesParallel();
        end

        function numWorkers = getNumParaWorkers(obj)
            numWorkers = obj.options.getNumParaWorkers();
        end

        function openOptionsDialog(obj)
%             lvd_editNomadOptionsGUI(obj);

            output = AppDesignerGUIOutput({false});
            lvd_editNomadOptionsGUI_App(obj, output);
        end
    end

    methods(Static, Access=private)
        function [f, stateLog] = nomadObjFuncWrapper(x, objFuncWrapper)
            global nomadCachedX nomadCachedStateLog

            [f, stateLog] = objFuncWrapper(x);

            nomadCachedX = x;
            nomadCachedStateLog = stateLog;
        end

        function c = nomadConstrWrapper(x, nonlcon)
            global nomadCachedX nomadCachedStateLog

            if(numel(x) ~= numel(nomadCachedX))
                nomadCachedX = NaN(size(x));
            end

            if(all(x(:) == nomadCachedX(:)))
                [cM, ceqM] = nonlcon(x, false, nomadCachedStateLog);
            else
                [cM, ceqM] = nonlcon(x, true, []);
            end

            c = [cM(:); ceqM(:); -1*ceqM(:)]';
            c = c(:);
        end

        function [fc, stateLogs] = nomadObjConstrWrapper(x, objFun, nonlcon, numVars, numElementsInEachOutput, M, lvdData)
            try
                if(numel(x) == numVars)
                    numEvals = 1;
                else
                    numEvals = size(x,1);
                end

                x = reshape(x, numEvals, numel(x)/numEvals);

                fc = NaN(numEvals,numElementsInEachOutput);

                for(i=1:numEvals) %#ok<NO4LP>
                    stateLogs(i) = LaunchVehicleStateLog(lvdData); %#ok<AGROW>
                end

                if(M > 0 && numEvals > 1)
                    pp = gcp();
                    opts = parforOptions(pp,'RangePartitionMethod','fixed','SubrangeSize',ceil(pp.NumWorkers/numEvals));
                    parfor(i=1:numEvals,opts)
                        xI = x(i,:);
                        [fcRow, stateLog] = NomadOptimizer.loopInternal(xI, objFun, nonlcon);

                        fc(i,:) = fcRow;
                        stateLogs(i) = stateLog;
                    end
                else
                    for(i=1:numEvals)
                        xI = x(i,:);
                        [fcRow, stateLog] = NomadOptimizer.loopInternal(xI, objFun, nonlcon);

                        fc(i,:) = fcRow;
                        stateLogs(i) = stateLog;
                    end
                end
            catch ME
                disp(ME.message);
            end
        end

        function [fcRow, stateLog] = loopInternal(xI, objFun, nonlcon)
            [f, stateLog] = objFun(xI);
            [c] = nonlcon(xI);

            fcRow = [f; c(:);]';
        end

        function stop = nomadIterFunWrapper(iter, fval, x, outputFnc, state, numVars)
            if(numel(x) == numVars)
                numEvals = 1;
            else
                numEvals = size(x,1);
            end

            x = reshape(x, numEvals, numel(x)/numEvals);

            f = fval(:,1);
            c = fval(:,2:end);

            if(isempty(c))
                [fval,I] = min(f);
                cViol = 0;
                xx = x(I,:);
            else
                c(c <= 0) = 0;
                cViol = sqrt(sum(c.^2,2));

                [minViolation,I] = min(cViol);

                if(sum(cViol == minViolation) > 1)
                    boolC = cViol == minViolation;
                    fBool = f(boolC);
                    [minFBool,~] = min(fBool);

                    boolF = f == minFBool;
                    II = find(boolF & boolC,1,'first');

                    fval = f(II);
                    cViol = minViolation;
                    xx = x(II,:);
                else
                    fval = f(I);
                    cViol = minViolation;
                    xx = x(I,:);
                end
            end

            optimValues.constrviolation = max(cViol, 0);
            optimValues.funccount = iter;
            optimValues.fval = fval;
            optimValues.iteration = iter;
            optimValues.stepsize = 0;
            optimValues.firstorderopt = 0;

            stop = outputFnc(xx, optimValues, state);
            stop = logical(stop);
        end

        function stop = getOutputFunction(x, optimValues, state, hOptimStatusLabel, hFinalStateOptimLabel, hDispAxes, hCancelButton, ...
                                                               objFcn, lb, ub, celBodyData, recorder, propNames, writeOutput, varLabels, lbUsAll, ubUsAll, optimStartTic)
            switch state
                case 'iter'
                    stop = get(hCancelButton,'Value');

                    recorder.iterNums(end+1) = optimValues.iteration;
                    recorder.xVals(end+1) = {x};
                    recorder.fVals(end+1) = optimValues.fval;
                    recorder.maxCVal(end+1) = optimValues.constrviolation;
                case {'init','interrupt','done'}
                    stop = get(hCancelButton,'Value');
            end

            if(stop == true)
                return;
            end

            [~, stateLog] = objFcn(x);

%             finalStateLogEntry = stateLog.getFinalStateLogEntry();
%             finalStateLogEntryMA = finalStateLogEntry.getMAFormattedStateLogMatrix(true);

            stateLogMA = stateLog.getMAFormattedStateLogMatrix(true);

            if(strcmpi(state,'init') || strcmpi(state,'iter'))
                NomadOptimizer.writeOptimStatus(hOptimStatusLabel, optimValues, state, writeOutput, optimStartTic);
                ma_UpdateStateReadout(hFinalStateOptimLabel, 'final', propNames, stateLogMA, celBodyData);
                NomadOptimizer.generatePlots(x, optimValues, state, hDispAxes, lb, ub, varLabels, lbUsAll, ubUsAll);
                drawnow;
            end
        end

        function writeOptimStatus(hOptimStatusLabel, optimValues, state, writeOutput, timer)
            elapTime = toc(timer);

            outStr = {};
            outStr{end+1} = ['State                = ', state];
            outStr{end+1} = '                        ';
            outStr{end+1} = ['Iterations           = ', num2str(optimValues.iteration)];
            outStr{end+1} = ['Function Evals       = ', num2str(optimValues.funccount)];
            outStr{end+1} = ['Objective Value      = ', num2str(optimValues.fval)];
            outStr{end+1} = ['Constraint Violation = ', num2str(optimValues.constrviolation)];
            outStr{end+1} = '                       ';
            outStr{end+1} = ['Elapsed Time         = ', num2str(elapTime), ' sec'];

            set(hOptimStatusLabel, 'String', outStr);

            switch state
                case 'iter'
                    formatstr = ' %- 12.1i %- 12.0i %- 12.6g %- 12.3g';

                    iter = optimValues.iteration;
                    fcnt = optimValues.funccount;
                    val  = optimValues.fval;
                    feas = optimValues.constrviolation;

                    hRow = sprintf(formatstr,iter,fcnt,val,feas);
                    writeOutput(hRow,'append');
                case 'init'
                    hdrStr = sprintf('%- 13s%- 13s%- 13s%- 13s', 'Iteration','Fcn-Count','f(x)-Value', 'Feasibility');
                    writeOutput(hdrStr,'append');
            end
        end

        function generatePlots(x, optimValues, state, hDispAxes, lb, ub, varLabels, lbUsAll, ubUsAll)
            persistent fValPlotIsLog tLayout hPlot1 hPlot2 hPlot3

            if(isempty(fValPlotIsLog))
                fValPlotIsLog = true;
            end

            switch state
                case 'init'
                    if(isvalid(hDispAxes))
%                         set(hDispAxes,'Visible','on');
%                         subplot(hDispAxes);
%                         axes(hDispAxes);
                        tLayout = tiledlayout(hDispAxes, 3,1);
                    end
                    fValPlotIsLog = true;
            end

            hPlot1 = nexttile(tLayout, 1);
            if(strcmpi(state,'init'))

                hPlot1.XTickLabel= [];
                hPlot1.YTickLabel= [];
                hPlot1.ZTickLabel= [];
%                 axes(hPlot1);
            else
%                 axes(hPlot1);
            end
            optimplotxKsptot(x, optimValues, state, lb, ub, varLabels, lbUsAll, ubUsAll);

            hPlot2 = nexttile(tLayout, 2);
            if(strcmpi(state,'init'))

                hPlot2.XTickLabel= [];
                hPlot2.YTickLabel= [];
                hPlot2.ZTickLabel= [];
                h = hPlot2;
            else
                h = hPlot2;
%                 axes(hPlot2);
            end
            if(optimValues.fval<=0)
                fValPlotIsLog = false;
                set(h,'yscale','linear');
            end
            optimplotfvalKsptot(x, optimValues, state);
            if(fValPlotIsLog)
                set(h,'yscale','log');
            else
                set(h,'yscale','linear');
            end
            grid on;
            grid minor;

            hPlot3 = nexttile(tLayout, 3);
            if(strcmpi(state,'init'))
                hPlot3.XTickLabel= [];
                hPlot3.YTickLabel= [];
                hPlot3.ZTickLabel= [];
                h = hPlot3;
            else
                h = hPlot3;
%                 axes(hPlot3);
            end
            optimplotconstrviolationKsptot(x, optimValues, state);

            if(not(isempty(h.Children)))
                hLine = h.Children(1);
                if(isa(hLine,'matlab.graphics.chart.primitive.Line'))
                    yDataLine = hLine.YData;
                    if(abs(max(yDataLine) / min(yDataLine)) >= 10 && all(yDataLine > 0))
                        set(h,'yscale','log');
                    else
                        set(h,'yscale','linear');
                    end
                else
                    set(h,'yscale','linear');
                end
            end

            grid on;
            grid minor;
        end
    end
end