classdef SettingsLevelSetCreatorFactory < handle

    methods (Access = public, Static)

        function obj = create(s)
            switch s.type
                case 'circle'
                    obj = SettingsLevelSetSphereNdim(s);
                case 'circleInclusion'
                    obj = SettingsLevelSetSphereNdim(s);
                case 'sphere'
                    obj = SettingsLevelSetSphereNdim(s);
                case 'sphereInclusion'
                    obj = SettingsLevelSetSphereNdim(s);
                case 'cylinder'
                    obj = SettingsLevelSetSphereNdim(s);
                case 'horizontal'
                    obj = SettingsLevelSetHorizontalInclusion(s);
                case 'squareInclusion'
                    obj = SettingsLevelSetSquareInclusion(s);
                case 'smoothSquare'
                    obj = SettingsLevelSetSmoothSquareInclusion(s);
                case {'rectangle', 'rectangleInclusion'}
                    obj = SettingsLevelSetRectangleInclusion(s);
                case 'smoothRectangle'
                    obj = SettingsLevelSetSmoothRectangleInclusion(s);
                case 'feasible'
                    obj = SettingsLevelSetCreator(s);
                case 'rand'
                    obj = SettingsLevelSetCreator(s);
                case 'holes'
                    obj = SettingsLevelSetSeveralHoles(s);
                case 'full'
                    obj = SettingsLevelSetCreator(s);
                case 'horizontalFibers'
                    obj = SettingsLevelSetHorizontalFibers(s);
                case 'given'
                    obj = SettingsLevelSetGiven(s);
                case 'Vigdergauz'
                    obj = SettingsLevelSetVigdergauz(s);
                otherwise
                    error('Invalid initial value of design variable.');
            end

        end
    end

end


addpath(genpath('.'))
load('./Metadata/SUNRGBDMeta.mat')
% load('./Metadata/SUNRGBD2Dseg.mat')
%% change path
imageId = 1;
for i = 1:size(SUNRGBDMeta, 2)
    SUNRGBDMeta(i).depthpath = strrep(SUNRGBDMeta(i).depthpath, '/n/fs/sun3d/data/', '/home/siyuan/Documents/Dataset/SUNRGBD_ALL/');
    SUNRGBDMeta(i).rgbpath = strrep(SUNRGBDMeta(i).rgbpath, '/n/fs/sun3d/data/', '/home/siyuan/Documents/Dataset/SUNRGBD_ALL/');
end
data = SUNRGBDMeta(imageId);
[rgb,points3d,depthInpaint,imsize]=read3dPoints(data);

%% draw
figure,
imshow(data.rgbpath);
hold on;
for kk =1:length(data.groundtruth3DBB)
    rectangle('Position', [data.groundtruth3DBB(kk).gtBb2D(1) data.groundtruth3DBB(kk).gtBb2D(2) data.groundtruth3DBB(kk).gtBb2D(3) data.groundtruth3DBB(kk).gtBb2D(4)],'edgecolor','y');
    text(data.groundtruth3DBB(kk).gtBb2D(1),data.groundtruth3DBB(kk).gtBb2D(2),data.groundtruth3DBB(kk).classname,'BackgroundColor','y')
end
%% draw 3D
figure,
vis_point_cloud(points3d,rgb)
hold on;
for kk =1:length(data.groundtruth3DBB)
   vis_cube(data.groundtruth3DBB(kk),'r')
end

%%
% anno2d = SUNRGBD2Dseg(imageId);
% figure,
% imagesc(anno2d.seglabel);
% % category name in 37 categories list
% load('./Metadata/seg37list.mat');
% objectname37 = unique(anno2d.seglabel(:));
% objectname37 = seg37list(objectname37(objectname37~=0));
%
% figure,
% imagesc(anno2d.seglabelall);
% % category name of all categories
% objectnameall = anno2d.names

%% Example to read single data
% data = readframeSUNRGBD('/n/fs/sun3d/data/SUNRGBD/kv2/kinect2data/000002_2014-05-26_14-23-37_260595134347_rgbf000103-resize/','/n/fs/sun3d/data/');
% [rgb,points3d,depthInpaint,imsize]=read3dPoints(data);
% %% draw
% figure,
% imshow(data.rgbpath);
% hold on;
% for kk =1:length(data.groundtruth3DBB)
%     rectangle('Position', [data.groundtruth3DBB(kk).gtBb2D(1) data.groundtruth3DBB(kk).gtBb2D(2) data.groundtruth3DBB(kk).gtBb2D(3) data.groundtruth3DBB(kk).gtBb2D(4)],'edgecolor','y');
%     text(data.groundtruth3DBB(kk).gtBb2D(1),data.groundtruth3DBB(kk).gtBb2D(2),data.groundtruth3DBB(kk).classname,'BackgroundColor','y')
% end
% %% draw 3D
% figure,
% vis_point_cloud(points3d,rgb)
% hold on;
% for kk =1:length(data.groundtruth3DBB)
%    vis_cube(data.groundtruth3DBB(kk),'r')
% end


%SETLABLIST Set names of classes or targets
%
%    A = SETLABLIST(A,LABLIST)
%
% LABLIST should be a column vector of C elements (integers, characters,
% or strings), in which C is the number of classes or targets of A.
% In case of multiple label lists this resets the current label list.
%
%    A = SETLABLIST(A)
%
% Removes entries in the lablist of A to which no objects are assigned,
% i.e. it remove empty classes. This command also removes duplicates in the
% lablist. An example of the merge of two classes in a 3-class dataset X
% with class names 'A', 'B' and 'C' can be realized by
%   X = setlablist(X,char('A','B','A'));
%   X = setlablist(X);
%
% SEE ALSO (PRTools Guide)
% MULTI_LABELING


%  internal function

%  - Jimmy Shen (jimmy@rotman-baycrest.on.ca)

function hdr = load_untouch_nii_hdr(fileprefix, machine, filetype)

   if filetype == 2
      fn = sprintf('%s.nii',fileprefix);

      if ~exist(fn)
         msg = sprintf('Cannot find file "%s.nii".', fileprefix);
         error(msg);
      end
   else
      fn = sprintf('%s.hdr',fileprefix);

      if ~exist(fn)
         msg = sprintf('Cannot find file "%s.hdr".', fileprefix);
         error(msg);
      end
   end

   fid = fopen(fn,'r',machine);

   if fid < 0,
      msg = sprintf('Cannot open file %s.',fn);
      error(msg);
   else
      fseek(fid,0,'bof');
      hdr = read_header(fid);
      fclose(fid);
   end

   return					% load_nii_hdr

%---------------------------------------------------------------------
function [ dsr ] = read_header(fid)

        %  Original header structures
	%  struct dsr
	%       {
	%       struct header_key hk;            /*   0 +  40       */
	%       struct image_dimension dime;     /*  40 + 108       */
	%       struct data_history hist;        /* 148 + 200       */
	%       };                               /* total= 348 bytes*/

    dsr.hk   = header_key(fid);
    dsr.dime = image_dimension(fid);
    dsr.hist = data_history(fid);

    %  For Analyze data format
    %
    if ~strcmp(dsr.hist.magic, 'n+1') && ~strcmp(dsr.hist.magic, 'ni1')
        dsr.hist.qform_code = 0;
        dsr.hist.sform_code = 0;
    end

    return					% read_header

%---------------------------------------------------------------------
function [ hk ] = header_key(fid)

    fseek(fid,0,'bof');

	%  Original header structures
	%  struct header_key                     /* header key      */
	%       {                                /* off + size      */
	%       int sizeof_hdr                   /*  0 +  4         */
	%       char data_type[10];              /*  4 + 10         */
	%       char db_name[18];                /* 14 + 18         */
	%       int extents;                     /* 32 +  4         */
	%       short int session_error;         /* 36 +  2         */
	%       char regular;                    /* 38 +  1         */
	%       char dim_info;   % char hkey_un0;        /* 39 +  1 */
	%       };                               /* total=40 bytes  */
	%
	% int sizeof_header   Should be 348.
	% char regular        Must be 'r' to indicate that all images and
	%                     volumes are the same size.

    v6 = version;
    if str2num(v6(1))<6
       directchar = '*char';
    else
       directchar = 'uchar=>char';
    end

    hk.sizeof_hdr    = fread(fid, 1,'int32')';	% should be 348!
    hk.data_type     = deblank(fread(fid,10,directchar)');
    hk.db_name       = deblank(fread(fid,18,directchar)');
    hk.extents       = fread(fid, 1,'int32')';
    hk.session_error = fread(fid, 1,'int16')';
    hk.regular       = fread(fid, 1,directchar)';
    hk.dim_info      = fread(fid, 1,'uchar')';

    return					% header_key

%---------------------------------------------------------------------
function [ dime ] = image_dimension(fid)

	%  Original header structures
	%  struct image_dimension
	%       {                                /* off + size      */
	%       short int dim[8];                /* 0 + 16          */
        %       /*
        %           dim[0]      Number of dimensions in database; usually 4.
        %           dim[1]      Image X dimension;  number of *pixels* in an image row.
        %           dim[2]      Image Y dimension;  number of *pixel rows* in slice.
        %           dim[3]      Volume Z dimension; number of *slices* in a volume.
        %           dim[4]      Time points; number of volumes in database
        %       */
	%       float intent_p1;   % char vox_units[4];   /* 16 + 4       */
	%       float intent_p2;   % char cal_units[8];   /* 20 + 4       */
	%       float intent_p3;   % char cal_units[8];   /* 24 + 4       */
	%       short int intent_code;   % short int unused1;   /* 28 + 2 */
	%       short int datatype;              /* 30 + 2          */
	%       short int bitpix;                /* 32 + 2          */
	%       short int slice_start;   % short int dim_un0;   /* 34 + 2 */
	%       float pixdim[8];                 /* 36 + 32         */
	%	/*
	%		pixdim[] specifies the voxel dimensions:
	%		pixdim[1] - voxel width, mm
	%		pixdim[2] - voxel height, mm
	%		pixdim[3] - slice thickness, mm
	%		pixdim[4] - volume timing, in msec
	%					..etc
	%	*/
	%       float vox_offset;                /* 68 + 4          */
	%       float scl_slope;   % float roi_scale;     /* 72 + 4 */
	%       float scl_inter;   % float funused1;      /* 76 + 4 */
	%       short slice_end;   % float funused2;      /* 80 + 2 */
	%       char slice_code;   % float funused2;      /* 82 + 1 */
	%       char xyzt_units;   % float funused2;      /* 83 + 1 */
	%       float cal_max;                   /* 84 + 4          */
	%       float cal_min;                   /* 88 + 4          */
	%       float slice_duration;   % int compressed; /* 92 + 4 */
	%       float toffset;   % int verified;          /* 96 + 4 */
	%       int glmax;                       /* 100 + 4         */
	%       int glmin;                       /* 104 + 4         */
	%       };                               /* total=108 bytes */

    dime.dim        = fread(fid,8,'int16')';
    dime.intent_p1  = fread(fid,1,'float32')';
    dime.intent_p2  = fread(fid,1,'float32')';
    dime.intent_p3  = fread(fid,1,'float32')';
    dime.intent_code = fread(fid,1,'int16')';
    dime.datatype   = fread(fid,1,'int16')';
    dime.bitpix     = fread(fid,1,'int16')';
    dime.slice_start = fread(fid,1,'int16')';
    dime.pixdim     = fread(fid,8,'float32')';
    dime.vox_offset = fread(fid,1,'float32')';
    dime.scl_slope  = fread(fid,1,'float32')';
    dime.scl_inter  = fread(fid,1,'float32')';
    dime.slice_end  = fread(fid,1,'int16')';
    dime.slice_code = fread(fid,1,'uchar')';
    dime.xyzt_units = fread(fid,1,'uchar')';
    dime.cal_max    = fread(fid,1,'float32')';
    dime.cal_min    = fread(fid,1,'float32')';
    dime.slice_duration = fread(fid,1,'float32')';
    dime.toffset    = fread(fid,1,'float32')';
    dime.glmax      = fread(fid,1,'int32')';
    dime.glmin      = fread(fid,1,'int32')';

    return					% image_dimension

%---------------------------------------------------------------------
function [ hist ] = data_history(fid)

	%  Original header structures
	%  struct data_history
	%       {                                /* off + size      */
	%       char descrip[80];                /* 0 + 80          */
	%       char aux_file[24];               /* 80 + 24         */
	%       short int qform_code;            /* 104 + 2         */
	%       short int sform_code;            /* 106 + 2         */
	%       float quatern_b;                 /* 108 + 4         */
	%       float quatern_c;                 /* 112 + 4         */
	%       float quatern_d;                 /* 116 + 4         */
	%       float qoffset_x;                 /* 120 + 4         */
	%       float qoffset_y;                 /* 124 + 4         */
	%       float qoffset_z;                 /* 128 + 4         */
	%       float srow_x[4];                 /* 132 + 16        */
	%       float srow_y[4];                 /* 148 + 16        */
	%       float srow_z[4];                 /* 164 + 16        */
	%       char intent_name[16];            /* 180 + 16        */
	%       char magic[4];   % int smin;     /* 196 + 4         */
	%       };                               /* total=200 bytes */

    v6 = version;
    if str2num(v6(1))<6
       directchar = '*char';
    else
       directchar = 'uchar=>char';
    end

    hist.descrip     = deblank(fread(fid,80,directchar)');
    hist.aux_file    = deblank(fread(fid,24,directchar)');
    hist.qform_code  = fread(fid,1,'int16')';
    hist.sform_code  = fread(fid,1,'int16')';
    hist.quatern_b   = fread(fid,1,'float32')';
    hist.quatern_c   = fread(fid,1,'float32')';
    hist.quatern_d   = fread(fid,1,'float32')';
    hist.qoffset_x   = fread(fid,1,'float32')';
    hist.qoffset_y   = fread(fid,1,'float32')';
    hist.qoffset_z   = fread(fid,1,'float32')';
    hist.srow_x      = fread(fid,4,'float32')';
    hist.srow_y      = fread(fid,4,'float32')';
    hist.srow_z      = fread(fid,4,'float32')';
    hist.intent_name = deblank(fread(fid,16,directchar)');
    hist.magic       = deblank(fread(fid,4,directchar)');

    return					% data_history


%% initializeWecSim
% Initialize WEC-Sim run, called from wecSim.m
%
%% Following Classes are required to be defined in the WEC-Sim input file:
%%
%% simu = simulationClass();                                               - To Create the Simulation Variable
%%
%% waves = waveClass('<wave type');                                       - To create the Wave Variable and Specify Type
%%
%% body(<body number>) = bodyClass('<hydrodynamics data file name>.h5');   - To initialize bodyClass:
%%
%% constraint(<constraint number>) = constraintClass('<Constraint name>'); - To initialize constraintClass
%%
%% pto(<PTO number>) = ptoClass('<PTO name>');                             - To initialize ptoClass
%%
%% mooring(<mooring number>) = mooringClass('<Mooring name>');             - To initialize mooringClass (only needed when mooring blocks are used)
%%
%%

%% Start WEC-Sim log
% Clear old input, plots, log file and start new log file.
diary off
clear body waves simu output pto constraint ptoSim mooring values names InParam
try delete('*.log'); end
diary('simulation.log')

%% Add 'temp' directory
% Store root directory of this *.m file
projectRootDir = pwd;
if exist('pctDir')
    projectRootDir = [projectRootDir filesep pctDir];
end

% Create 'temp' directory if it doesn't exist and add to 'temp' path
warning('off','MATLAB:MKDIR:DirectoryExists')
if exist('pctDir')
    mkdir ([pctDir filesep 'temp'])
else
    if mkdir('temp') == 0
        mkdir 'temp'
    end
end
addpath(fullfile(projectRootDir,'temp'),'-end');

% Save Simulink-generated helper files to 'temp' directory
Simulink.fileGenControl('set',...
    'CacheFolder',fullfile(projectRootDir,'temp'))

%% Read input file
tic

% Set input parameters based on how the simulation is called
if exist('runWecSimCML','var') && runWecSimCML==1
    % wecSim input from wecSimInputFile.m of case directory in the standard manner
    fprintf('\nWEC-Sim Input From Standard wecSimInputFile.m Of Case Directory... \n');
    bdclose('all');
    run('wecSimInputFile');
else
    % Get global reference frame parameters
    values = get_param([bdroot,'/Global Reference Frame'],'MaskValues');    % Cell array containing all Masked Parameter values
    names = get_param([bdroot,'/Global Reference Frame'],'MaskNames');      % Cell array containing all Masked Parameter names
    j = find(strcmp(names,'InputMethod'));
    if strcmp(values{j},'Input File')
        % wecSim input from input file selected in Simulink block
        fprintf('\nWEC-Sim Input From File Selected In Simulink... \n');
        i = find(strcmp(names,'InputFile'));
        run(values{i});
    elseif strcmp(values{j},'Custom Parameters')
        % wecSim input from custom parameters in Simulink block
        fprintf('\nWEC-Sim Input From Custom Parameters In Simulink... \n');
        inputFile = 'wecSimInputFile_simulinkCustomParameters';
        writeInputFromBlocks(inputFile);
        run(inputFile);
    end
end
clear values names i j;

% Read Inputs for Multiple Conditions Run
try fprintf('wecSimMCR Case %g\n',imcr); end

if exist('mcr','var') == 1
    for n=1:length(mcr.cases(1,:))
        if strcmp(mcr.header{n},'LoadFile')                                 % added this flag for system ID efforts!
            load(mcr.cases{imcr,n});
        elseif iscell(mcr.cases)
            eval([mcr.header{n} '= mcr.cases{imcr,n};']);
        else
            eval([mcr.header{n} '= mcr.cases(imcr,n);']);
        end
    end; clear n combine;
    try
        waves.spectrumFile = ['..' filesep pctDir filesep '..' filesep waves.spectrumFile];
        waves.elevationFile = ['..' filesep pctDir filesep '..' filesep waves.elevationFile];
    end
end

% Waves and Simu: check inputs
waves.checkInputs();
simu.checkInputs();

% Constraints: count & set orientation
if exist('constraint','var') == 1
    simu.numConstraints = length(constraint(1,:));
    for ii = 1:simu.numConstraints
        constraint(ii).setNumber(ii);
        constraint(ii).setOrientation();
    end; clear ii
end

% PTOs: count & set orientation & set pretension
if exist('pto','var') == 1
    simu.numPtos = length(pto(1,:));
    for ii = 1:simu.numPtos
        pto(ii).setNumber(ii);
        pto(ii).setOrientation();
        pto(ii).setPretension();
    end; clear ii
end

% Mooring Configuration: count
if exist('mooring','var') == 1
    simu.numMoorings = length(mooring(1,:));
    for ii = 1:simu.numMoorings
        mooring(ii).setLoc();
        mooring(ii).setNumber(ii);
    end; clear ii
end

% Bodies: count, check inputs, read hdf5 file, and check inputs
numHydroBodies = 0;
numNonHydroBodies = 0;
numDragBodies = 0;
hydroBodLogic = zeros(length(body(1,:)),1);
nonHydroBodLogic = zeros(length(body(1,:)),1);
dragBodLogic = zeros(length(body(1,:)),1);
for ii = 1:length(body(1,:))
    body(ii).setNumber(ii);
    body(ii).checkInputs(simu.explorer);
    if body(ii).nonHydro==0
        if numNonHydroBodies > 0 || numDragBodies > 0
            error('All hydro bodies must be specified before any drag or non-hydro bodies.')
        end
        numHydroBodies = numHydroBodies + 1;
        hydroBodLogic(ii) = 1;
    elseif body(ii).nonHydro==1
        numNonHydroBodies = numNonHydroBodies + 1;
        nonHydroBodLogic(ii) = 1;
    elseif body(ii).nonHydro==2
        numDragBodies = numDragBodies + 1;
        dragBodLogic(ii) = 1;
    end
end
simu.numHydroBodies = numHydroBodies; clear numHydroBodies
simu.numDragBodies = numDragBodies; clear numDragBodies
for ii = 1:simu.numHydroBodies
    body(ii).setDOF(simu.numHydroBodies,simu.b2b);

    % Determine if hydro data needs to be reloaded from h5 file, or if hydroData
    % was stored in memory from a previous run.
    if exist('totalNumOfWorkers','var') ==0 && exist('mcr','var') == 1 && simu.reloadH5Data == 0 && imcr > 1
        body(ii).loadHydroData(hydroData(ii));
    else
        % check for correct h5 file
        h5Info = dir(body(ii).h5File);
        h5Info.bytes;
        if h5Info.bytes < 1000
            error(['This is not the correct *.h5 file. Please install git-lfs to access the correct *.h5 file, or run \hydroData\bemio.m to generate a new *.h5 file'])
        end
        clearvars h5Info
        % Read hydro data from BEMIO and load into the bodyClass
        tmp_hydroData = readBEMIOH5(body(ii).h5File, body(ii).number, body(ii).meanDrift);
        body(ii).loadHydroData(tmp_hydroData);
        clear tmp_hydroData
    end
end; clear ii

% Cable Configuration: count, set Cg/Cb, PTO loc, L0 and initialize bodies
if exist('cable','var')==1
    simu.numCables = length(cable(1,:));
    for ii = 1:simu.numCables
        cable(ii).setNumber(ii);
        cable(ii).setCg();
        cable(ii).setCb();
        cable(ii).setTransPTOLoc();
        cable(ii).setLength();
        cable(ii).dragForcePre(simu.rho);
        cable(ii).setVolume(simu.rho);
        cable(ii).setOrientation();
        cable(ii).linearDampingMatrix();
    end
end

% PTO-Sim: read input, count
if exist('ptoSim','var') == 1
    simu.numPtoSim = length(ptoSim(1,:));
    for ii = 1:simu.numPtoSim
        ptoSim(ii).number = ii;
    end; clear ii
end

toc

%% Pre-processing start
tic
fprintf('\nWEC-Sim Pre-processing ...   \n');
if exist('pctDir')
    cd(pctDir);
end

%% HydroForce Pre-Processing: Wave Setup & HydroForcePre.
% simulation setup
simu.setup();

% wave setup
if any(hydroBodLogic == 1)
    % When hydro bodies (and an .h5) are present, define the wave using those
    % parameters.
    waves.setup(body(1).hydroData.simulation_parameters.w, body(1).hydroData.simulation_parameters.waterDepth, simu.rampTime, simu.dt, simu.maxIt, simu.time, simu.gravity, simu.rho);
    % Check that direction and freq are within range of hydro data
    if  min(waves.direction) <  min(body(1).hydroData.simulation_parameters.direction) || max(waves.direction) >  max(body(1).hydroData.simulation_parameters.direction)
        error('waves.direction outside of range of available hydro data')
    end
    if strcmp(waves.type,'elevationImport')~=1 && strcmp(waves.type,'noWave')~=1 && strcmp(waves.type,'noWaveCIC')~=1
        if  min(waves.omega) <  min(body(1).hydroData.simulation_parameters.w) || max(waves.omega) >  max(body(1).hydroData.simulation_parameters.w)
            error('waves.omega outside of range of available hydro data')
        end
    end
else
    % When no hydro bodies (and no .h5) are present, define the wave using
    % input file parameters
    waves.setup([], [], simu.rampTime, simu.dt, simu.maxIt, simu.time, simu.gravity, simu.rho);
end

% Nonlinear hydro
for kk = 1:length(body(1,:))
    if (body(kk).nonlinearHydro > 0) || (simu.paraview.option == 1)
        body(kk).importBodyGeometry(simu.domainSize)
    end
end; clear kk

% hydroForcePre
idx = find(hydroBodLogic==1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).hydroForcePre(waves.omega,waves.direction,simu.cicTime,waves.bem.count,simu.dt,...
            simu.rho,simu.gravity,waves.type,waves.waveAmpTime,simu.stateSpace,simu.b2b);
    end; clear kk idx
end

% nonHydroPre
idx = find(nonHydroBodLogic==1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).nonHydroForcePre(simu.rho);
    end; clear kk idx
end

% dragBodyPre
idx = find(dragBodLogic == 1);
if ~isempty(idx)
    for kk = 1:length(idx)
        it = idx(kk);
        body(it).dragForcePre(simu.rho);
    end; clear kk idx
end

% Check cicEndTime
if waves.typeNum~=0 && waves.typeNum~=10
    for iBod = 1:simu.numHydroBodies
        if simu.cicEndTime > max(body(iBod).hydroData.hydro_coeffs.radiation_damping.impulse_response_fun.t)
            error('simu.cicEndTime is larger than the length of the IRF');
        end
    end
end

% Check that the hydro data for each body is given for the same frequencies
for ii = 1:simu.numHydroBodies
    if length(body(1).hydroData.simulation_parameters.w) ~= length(body(ii).hydroData.simulation_parameters.w)
        error('BEM simulations for each body must have the same number of frequencies')
    else
        for jj = 1:length(body(1).hydroData.simulation_parameters.w)
            if body(1).hydroData.simulation_parameters.w(jj) ~= body(ii).hydroData.simulation_parameters.w(jj)
                error('BEM simulations must be run with the same frequencies.')
            end; clear jj;
        end
    end
end; clear ii;

% Check for elevationImport with nonlinearHydro
for ii = 1:simu.numHydroBodies
    if strcmp(waves.type,'elevationImport') && body(ii).nonlinearHydro == 1
        error(['Cannot run WEC-Sim with nonlinear hydro (body(ii).nonlinearHydro) and "elevationImport" wave type'])
    end
end

% Check for elevationImport with morisonElement
for ii = 1:simu.numHydroBodies
    if strcmp(waves.type,'elevationImport') && body(ii).morisonElement.option ~= 0
        error(['Cannot run WEC-Sim with Morison Element (body(ii).morisonElement.option>0) and "elevationImport" wave type'])
    end
end

% Check for morisonElement inputs for body(ii).morisonElement.option == 1 || body(ii).morisonElement.option == 2
for ii = 1:length(body(1,:))
    if body(ii).morisonElement.option == 1
        if body(ii).nonHydro ~=1
            [rgME,~] = size(body(ii).morisonElement.rgME);
            for jj = 1:rgME
                if true(isfinite(body(ii).morisonElement.z(jj,:))) == true
                    warning(['"body.morisonElement.z" is not used for "body.morisonElement.option = 1". Check body ',num2str(ii),' element ',num2str(jj)])
                end
                if length(body(ii).morisonElement.cd(jj,:)) ~= 3 || length(body(ii).morisonElement.ca(jj,:)) ~= 3 || length(body(ii).morisonElement.area(jj,:)) ~= 3
                    error(['cd, ca, and area coefficients for each elelement for "body.morisonElement.option = 1" must be of size [1x3] and all columns of data must be real and finite. Check body ',num2str(ii),' element ',num2str(jj),' coefficients'])
                end
            end; clear jj
        else
            if body(ii).morisonElement.option == 1 || body(ii).morisonElement.option == 2
                warning(['Morison elements are not available for non-hydro bodies. Please check body ',num2str(ii),' inputs.'])
            end
        end
    elseif body(ii).morisonElement.option == 2
        if body(ii).nonHydro ~=1
            [rgME,~] = size(body(ii).morisonElement.rgME);
            for jj = 1:rgME
                if body(ii).morisonElement.cd(jj,3) ~= 0 || body(ii).morisonElement.ca(jj,3) ~= 0 || body(ii).morisonElement.area(jj,3) ~= 0
                    warning(['cd, ca, and area coefficients for "body.morisonElement.option == 2" must be of size [1x2], third column of data is not used. Check body ',num2str(ii),' element ',num2str(jj),' coefficients'])
                end
            end; clear jj
        else
            if body(ii).morisonElement.option ==1 || body(ii).morisonElement.option ==2
                warning(['Morison elements are not available for non-hydro bodies. Please check body ',num2str(ii),' inputs.'])
            end
        end
    end; clear ii
end

%% Set variant subsystems options
for ii=1:length(body(1,:))
    if body(ii).nonHydro==0
        % Nonlinear FK Force Variant Subsystem
        eval(['nonLinearHydro_' num2str(ii) ' = body(ii).nonlinearHydro;']);
        eval(['sv_b' num2str(ii) '_linearHydro = Simulink.Variant(''nonLinearHydro_', num2str(ii), '==0'');']);
        eval(['sv_b' num2str(ii) '_nonlinearHydro=Simulink.Variant(''nonLinearHydro_', num2str(ii), '>0'');']);
        % Nonlinear Wave Elevation Variant Subsystem
        eval(['sv_b' num2str(ii) '_meanFS=Simulink.Variant(''nonLinearHydro_', num2str(ii), '<2'');']);
        eval(['sv_b' num2str(ii) '_instFS=Simulink.Variant(''nonLinearHydro_', num2str(ii), '==2'');']);
    end
end; clear ii;
% Morison Element
for ii=1:length(body(1,:))
    if body(ii).nonHydro ~=1
        eval(['morisonElement_' num2str(ii) ' = body(ii).morisonElement.option;'])
        eval(['sv_b' num2str(ii) '_MEOff = Simulink.Variant(''morisonElement_' num2str(ii) '==0'');'])
        eval(['sv_b' num2str(ii) '_MEOn = Simulink.Variant(''morisonElement_' num2str(ii) '==1 || morisonElement_' num2str(ii) '==2'');'])
    end
end; clear ii;
% Radiation Damping
if waves.typeNum==0 || waves.typeNum==10 %'noWave' & 'regular'
    radiation_option = 1;
elseif simu.stateSpace == 1
    radiation_option = 3;
else
    radiation_option = 2;
end
sv_constantCoeff=Simulink.Variant('radiation_option==1');
sv_convolution=Simulink.Variant('radiation_option==2');
sv_stateSpace=Simulink.Variant('radiation_option==3');
% Wave type
typeNum = waves.typeNum;
sv_noWave=Simulink.Variant('typeNum<10');

% Passive Yaw
for ii=1:length(body(1,:))
    eval(['yaw_' num2str(ii) ' = body(ii).yaw.option;'])
    eval(['sv_regularWaves_b' num2str(ii) '= Simulink.Variant(''typeNum>=10 && typeNum<20 && yaw_', num2str(ii), '==0'');'])
    eval(['sv_regularWavesYaw_b' num2str(ii) '= Simulink.Variant(''typeNum>=10 && typeNum<20 && yaw_' num2str(ii) '==1'');'])
    eval(['sv_irregularWaves_b' num2str(ii) '= Simulink.Variant(''typeNum>=20 && typeNum<30 && yaw_' num2str(ii) '==0'');'])
    eval(['sv_irregularWavesYaw_b' num2str(ii) '= Simulink.Variant(''typeNum>=20 && typeNum<30 && yaw_' num2str(ii) '==1'');'])
end; clear ii

sv_udfWaves=Simulink.Variant('typeNum>=30');
% Body2Body
B2B = simu.b2b;
sv_noB2B=Simulink.Variant('B2B==0');
sv_B2B=Simulink.Variant('B2B==1');
numBody=simu.numHydroBodies;
% nonHydro
for ii=1:length(body(1,:))
    eval(['nhbody_' num2str(ii) ' = body(ii).nonHydro;'])
    eval(['sv_b' num2str(ii) '_hydroBody = Simulink.Variant(''nhbody_' num2str(ii) '==0'');'])
    eval(['sv_b' num2str(ii) '_nonHydroBody = Simulink.Variant(''nhbody_' num2str(ii) '==1'');'])
    eval(['sv_b' num2str(ii) '_dragBody = Simulink.Variant(''nhbody_' num2str(ii) '==2'');'])
end; clear ii

%Efficiency model for hydraulic motor PTO-Sim block
try
    for ii=1:length(ptoSim(1,:))
        eval(['EffModel_' num2str(ii) ' = ptoSim(ii).hydraulicMotor.effModel;']);
        eval(['sv_b' num2str(ii) '_AnalyticalEfficiency = Simulink.Variant(''EffModel_', num2str(ii), '==1'');']);
        eval(['sv_b' num2str(ii) '_TabulatedEfficiency = Simulink.Variant(''EffModel_', num2str(ii), '==2'');']);
    end; clear ii;
end

% Visualization Blocks
if ~isempty(waves.marker.location) && typeNum < 30
    visON = 1;
else
    visON = 0;
end
sv_visualizationON  = Simulink.Variant('visON==1');
sv_visualizationOFF = Simulink.Variant('visON==0');

%% End Pre-Processing and Output All the Simulation and Model Setting
toc
simu.listInfo(waves.typeNum);
waves.listInfo
fprintf('\nList of Body: ');
fprintf('Number of Bodies = %u \n',simu.numHydroBodies)
for i = 1:simu.numHydroBodies
    body(i).listInfo
end;  clear i
fprintf('\nList of PTO(s): ');
if (exist('pto','var') == 0)
    fprintf('No PTO in the system\n')
else
    fprintf('Number of PTOs = %G \n',length(pto(1,:)))
    for i=1:simu.numPtos
        pto(i).listInfo
    end; clear i
end
fprintf('\nList of Constraint(s): ');
if (exist('constraint','var') == 0)
    fprintf('No Constraint in the system\n')
else
    fprintf('Number of Constraints = %G \n',length(constraint(1,:)))
    for i=1:simu.numConstraints
        constraint(i).listInfo
    end; clear i
end
fprintf('\n')

%% Load simMechanics file & Run Simulation
tic
fprintf('\nSimulating the WEC device defined in the SimMechanics model %s...   \n',simu.simMechanicsFile)
% Modify some stuff for simulation
for iBod = 1:simu.numHydroBodies
    body(iBod).adjustMassMatrix(simu.adjMassFactor,simu.b2b);
end; clear iBod
warning('off','Simulink:blocks:TDelayTimeTooSmall');
warning('off','Simulink:blocks:BusSelDupBusCreatorSigNames');
warning('off','MATLAB:loadlibrary:FunctionNotFound');
warning('off','MATLAB:loadlibrary:parsewarnings');
warning('off','MATLAB:printf:BadEscapeSequenceInFormat');
warning('off','Simulink:blocks:DivideByZero');
warning('off','sm:sli:setup:compile:SteadyStateStartNotSupported')
set_param(0, 'ErrorIfLoadNewModel', 'off')
% Load parameters to Simulink model
simu.loadSimMechModel(simu.simMechanicsFile);

toc


function ParforProgressStressTest2(N)
% Stress test for 'ParforProgressStarter2'. In case of timeouts, you can
% use this function to determine how many simultaneous connections your
% computer can handle, and adjust the ppm.increment() call accordingly.
%
% Copyright (c) 2010-2012, Andreas Kotowicz
%
%%
    if nargin < 1
        N = 10000;
    end

    do_debug = 1;

    %% initialize ParforProgress monitor

    try % Initialization
        ppm = ParforProgressStarter2('test task - ParforProgressStarter2', N, 0.1, do_debug);
    catch me % make sure "ParforProgressStarter2" didn't get moved to a different directory
        if strcmp(me.message, 'Undefined function or method ''ParforProgressStarter2'' for input arguments of type ''char''.')
            error('ParforProgressStarter2 not in path.');
        else
            % this should NEVER EVER happen.
            msg{1} = 'Unknown error while initializing "ParforProgressStarter2":';
            msg{2} = me.message;
            print_error_red(msg);
            % backup solution so that we can still continue.
            ppm.increment = nan(1, N);
        end
    end

    %% execute dummy loop - replace 'rand(1, 10000)' with your function.

    t0 = tic();

    parfor i = 1 : N
        rand(1, 10000);
        ppm.increment(i); %#ok<PFBNS>
    end

    total_time = toc(t0);

    %% clean up
    try % use try / catch here, since delete(struct) will raise an error.
        delete(ppm);
    catch me %#ok<NASGU>
    end

    %% show runtime

    disp(['ParforProgressStressTest2 running time: ' num2str(total_time) 's.']);

end
%% EOF


function tests = test_spm_dcm_fit
% Unit Tests for spm_dcm_fit
%__________________________________________________________________________
% Copyright (C) 2016 Wellcome Trust Centre for Neuroimaging

% $Id: test_spm_dcm_fit.m 7365 2018-07-03 14:10:43Z peter $

tests = functiontests(localfunctions);

function test_fmri_noparfor(testCase)

data_path = get_data_path();

% Load DCMs
GCM = load(fullfile(data_path,'models','GCM_simulated.mat'));
GCM = GCM.GCM;
GCM = GCM(1:1,1);

% Remove old posteriors
for i = 1:length(GCM)
    GCM{i} = rmfield(GCM{i},'Ep');
    GCM{i} = rmfield(GCM{i},'F');
    GCM{i}.options.nograph = true;
end

% Estimate
GCM = spm_dcm_fit(GCM);

% Check DCM
testCase.assertTrue(length(GCM) == 1);
for i = 1:length(GCM)
    testCase.assertTrue(isfield(GCM{i},'Ep'));
    testCase.assertTrue(isfield(GCM{i},'F'));
end

% -------------------------------------------------------------------------
function test_fmri_parfor(testCase)

data_path = get_data_path();

% Load DCM (one will be enough)
GCM = load(fullfile(data_path,'models','GCM_simulated.mat'));
GCM = GCM.GCM;
GCM = GCM(1:1,1);

% Remove old posteriors
for i = 1:length(GCM)
    GCM{i} = rmfield(GCM{i},'Ep');
    GCM{i} = rmfield(GCM{i},'F');
    GCM{i}.options.nograph = true;
end

% Estimate
GCM = spm_dcm_fit(GCM, true);

% Check DCM
testCase.assertTrue(length(GCM) == 1);
for i = 1:length(GCM)
    testCase.assertTrue(isfield(GCM{i},'Ep'));
    testCase.assertTrue(isfield(GCM{i},'F'));
end

% -------------------------------------------------------------------------
function data_path = get_data_path()

data_path = fullfile( spm('Dir'), 'tests', ...
    'data', 'fMRI', 'simulated_2region');


function [source] = fixinside(source, target)

% FIXINSIDE ensures that the region of interest (which is indicated by the
% field "inside") is consistently defined for source structures and volume
% structures. Furthermore, it solves backward compatibility problems.
%
% Use as
%   [source] = fixinside(source, 'logical');
% or
%   [source] = fixinside(source, 'index');

% Copyright (C) 2006, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

if nargin<2
  target = 'logical';
end

if ~isfield(source, 'inside')
  if isfield(source, 'leadfield')
    % determine the positions outside the brain on basis of the empty cells
    source.inside = ~cellfun(@isempty, source.leadfield);
  elseif isfield(source, 'filter')
    % determine the positions outside the brain on basis of the empty cells
    source.inside = ~cellfun(@isempty, source.filter);
  elseif isfield(source, 'pos')
    % assume that all positions are inside the region of interest
    source.inside  = [1:size(source.pos,1)]';
    source.outside = [];
  elseif isfield(source, 'dim') && isfield(source, 'transform')
    % assume that all positions are inside the region of interest
    source.inside  = [1:prod(source.dim(1:3))]';
    source.outside = [];
  end
end

if ~isfield(source, 'inside')
  % nothing to do
  return;
end

% determine the format
if isa(source.inside, 'logical')
  current = 'logical';
elseif all(source.inside(:)==0 | source.inside(:)==1)
  source.inside = logical(source.inside);
  current = 'logical';
else
  current = 'indexed';
end

if strcmp(current, 'indexed') && strcmp(target, 'indexed')
  % nothing to do
elseif strcmp(current, 'logical') && strcmp(target, 'logical')
  % nothing to do
elseif strcmp(current, 'indexed') && strcmp(target, 'logical')
  % remove outside
  if isfield(source, 'outside')
    source = rmfield(source, 'outside');
  end
  % convert inside to a logical array
  if isfield(source, 'pos')
    tmp = false(size(source.pos,1), 1);
  elseif isfield(source, 'dim') && isfield(source, 'transform')
    tmp = false(source.dim(1:3));
  end
  tmp(source.inside) = true;
  source.inside = tmp;
elseif strcmp(current, 'logical') && strcmp(target, 'index')
  % convert to a vectors with indices
  tmp = source.inside;
  source.inside  = find( tmp(:));
  source.outside = find(~tmp(:));
else
  ft_error('incorrect specification of the insidestyle')
end

if strcmp(target, 'logical')
  if isfield(source, 'pos')
    % reshape it so that it matches the positions
    source.inside = reshape(source.inside, size(source.pos,1), 1);
  elseif isfield(source, 'dim') && isfield(source, 'transform')
    % reshape it so that it matches the volume dimensions
    source.inside = reshape(source.inside, source.dim(1:3));
  end
end


function [handles] = playerturn(ha,handles)
%if it is the start of the turn then...
if handles.ipx==0
%     act only if the player is trying to move his own piece
    if handles.box(handles.x,handles.y)==handles.plr
        handles.ipx=handles.x;
        handles.ipy=handles.y;
    end
%if it is the completion of turn then...
else
    handles.fpx=handles.x;
    handles.fpy=handles.y;
    %first check whether its a valid turn or not.
    [check,handles.box] = checkfp(ha,handles.plr,handles.box,handles.ipx,handles.ipy,handles.fpx,handles.fpy,handles.dblbox,handles.blank);
    if check==1
%         if valid then update box
        handles.box(handles.ipx,handles.ipy)=0;
        handles.box(handles.fpx,handles.fpy)=handles.plr;
%         when someone's piece reaches its end line it is now double
        if handles.plr==1 && handles.fpx==8
            handles.dblbox(handles.fpx,handles.fpy)=1;
        end
        if handles.plr==2 && handles.fpx==1
            handles.dblbox(handles.fpx,handles.fpy)=1;
        end
%     if trying to move already doubled piece then update dblbox with new location
        if handles.dblbox(handles.ipx,handles.ipy)==1
            handles.dblbox(handles.ipx,handles.ipy)=0;
            handles.dblbox(handles.fpx,handles.fpy)=1;
        end
%         and change the display of the buttons accordingly
%         changing the initial button's string
        xy=handles.ipx*10+handles.ipy;
        set(ha(xy),'CData',handles.blank)
%         changing the destination button's string
        xy=handles.fpx*10+handles.fpy;
        if handles.dblbox(handles.fpx,handles.fpy)==1 && handles.plr==1
            set(ha(xy),'CData',handles.plr1dblmark)
        elseif handles.dblbox(handles.fpx,handles.fpy)==1 && handles.plr==2
            set(ha(xy),'CData',handles.plr2dblmark)
        elseif handles.plr==1
            set(ha(xy),'CData',handles.plr1mark)
        elseif handles.plr==2
            set(ha(xy),'CData',handles.plr2mark)
        end
%         and update player
        if handles.plr==1
            handles.plr=2;
            axes(ha(1));
            image(handles.plr2mark);
            axis off;
        else
            handles.plr=1;
            axes(ha(1));
            image(handles.plr1mark);
            axis off;

        end
%       now zero the initial position
        handles.ipx=0;
        handles.ipy=0;
%        loser command is applied after updating plr becoz one will turn
%        and other will lose, so ohter one needs to be checked
        loser=wholoses(handles.plr,handles.box);
        if loser~=0
            if loser==2
                msgbox('BLACK Wins','Winner','custom',handles.plr1mark);
            elseif loser==1
                msgbox('RED Wins','Winner','custom',handles.plr2mark);
            elseif loser==-1
                msgbox('Its a Draw');
            end
            close(handles.figure1);
        end
    else
%       if wrong input then initialize the positions so as to get input again
        msgbox('Wrong input','Error');
        handles.ipx=0;
        handles.ipy=0;
        handles.fpx=0;
        handles.fpy=0;
    end
end
end


function [pyra, model_dp] = gdetect_pos_prepare_c(im, model, valid)
% Prepare a set of foreground examples in the same image for processing
% with gdetect_pos.m.
%   [pyra, model_dp] = gdetect_pos_prepare(im, model, boxes, fg_overlap)
%
% Return values
%   pyra          Feature pyramid for image im
%   model_dp      Model augmented with dynamic programming tables
%
% Arguments
%   im            Foreground image with one or more foreground examples
%   model         Object model

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2009-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

% get feature pyramid
pyra = featpyramid(im, model);
pyra = project_pyramid(model, pyra);

pyra.valid_levels(:) = false;
pyra.valid_levels([valid.l valid.l-model.interval]) = true;

% compute dynamic programming tables (stored in model_dp)
model_dp = gdetect_dp(pyra, model);


% make results movies
function MakeCtraxResultsMovieWrapper(moviefile,trxfile,xvidfile,movietitle,ctraxresultsmovie_params)

%% ctrax movie parameters
defaulttempdatadir = '/groups/branson/bransonlab/projects/olympiad/TempData_FlyBowlMakeCtraxResultsMovie';
if ~isfield(ctraxresultsmovie_params,'tempdatadir'),
  ctraxresultsmovie_params.tempdatadir = defaulttempdatadir;
elseif isunix
  [status1,res] = unix(sprintf('echo %s',ctraxresultsmovie_params.tempdatadir));
  if status1 == 0,
    ctraxresultsmovie_params.tempdatadir = strtrim(res);
  end
end

if ~exist(ctraxresultsmovie_params.tempdatadir,'dir'),
  [success1,msg1] = mkdir(ctraxresultsmovie_params.tempdatadir);
  if ~success1,
    error('Error making directory %s: %s',ctraxresultsmovie_params.tempdatadir,msg1);
  end
end

%% location of data

[~,avifilestr,ext] = fileparts(xvidfile);
avifile = fullfile(ctraxresultsmovie_params.tempdatadir,[avifilestr,'_temp',ext]);

%% which frames

trx = load_tracks(trxfile);
start_frame = min([trx.firstframe]);
end_frame = max([trx.endframe]);
nframes = end_frame-start_frame + 1;
firstframes_off = min(max(0,round(ctraxresultsmovie_params.firstframes*nframes)),nframes-1);
firstframes_off(ctraxresultsmovie_params.firstframes < 0) = nan;
middleframes_off = round(ctraxresultsmovie_params.middleframes*nframes);
middleframes_off(ctraxresultsmovie_params.middleframes < 0) = nan;
endframes_off = round(ctraxresultsmovie_params.endframes*nframes);
endframes_off(ctraxresultsmovie_params.endframes < 0) = nan;
idx = ~isnan(middleframes_off);
firstframes_off(idx) = ...
  min(nframes-1,max(0,middleframes_off(idx) - ceil(ctraxresultsmovie_params.nframes(idx)/2)));
idx = ~isnan(endframes_off);
firstframes_off(idx) = ...
  min(nframes-1,max(0,endframes_off(idx) - ctraxresultsmovie_params.nframes(idx)));
endframes_off = firstframes_off + ctraxresultsmovie_params.nframes - 1;

firstframes = start_frame + firstframes_off;

%% option to not specify nzoomr, nzoomc

if ischar(ctraxresultsmovie_params.nzoomr) || ischar(ctraxresultsmovie_params.nzoomc),

  nflies = zeros(1,nframes);
  for i = 1:numel(trx),
    j0 = trx(i).firstframe-start_frame+1;
    j1 = trx(i).endframe-start_frame+1;
    nflies(j0:j1) = nflies(j0:j1)+1;
  end
  mediannflies = median(nflies);

  if isnumeric(ctraxresultsmovie_params.nzoomr),
    nzoomr = ctraxresultsmovie_params.nzoomr;
    nzoomc = round(mediannflies/nzoomr);
  elseif isnumeric(ctraxresultsmovie_params.nzoomc),
    nzoomc = ctraxresultsmovie_params.nzoomc;
    nzoomr = round(mediannflies/nzoomc);
  else
    nzoomr = ceil(sqrt(mediannflies));
    nzoomc = round(mediannflies/nzoomr);
  end
  ctraxresultsmovie_params.nzoomr = nzoomr;
  ctraxresultsmovie_params.nzoomc = nzoomc;

  if iscell(ctraxresultsmovie_params.figpos),
    [readframe,~,fid] = get_readframe_fcn(moviefile);
    im = readframe(1);
    [nr,nc,~] = size(im);

    rowszoom = floor(nr/nzoomr);
    imsize = [nr,nc+rowszoom*nzoomc];
    figpos = str2double(ctraxresultsmovie_params.figpos);
    if isnan(figpos(3)),
      figpos(3) = figpos(4)*imsize(2)/imsize(1);
    elseif isnan(figpos(4)),
      figpos(4) = figpos(3)*imsize(1)/imsize(2);
    end
    ctraxresultsmovie_params.figpos = figpos;

    if fid > 1,
      fclose(fid);
    end
  end

end

%% create movie

[succeeded,~,~,height,width]= ...
  make_ctrax_result_movie('moviename',moviefile,'trxname',trxfile,'aviname',avifile,...
  'nzoomr',ctraxresultsmovie_params.nzoomr,'nzoomc',ctraxresultsmovie_params.nzoomc,...
  'boxradius',ctraxresultsmovie_params.boxradius,'taillength',ctraxresultsmovie_params.taillength,...
  'fps',ctraxresultsmovie_params.fps,...
  'maxnframes',ctraxresultsmovie_params.nframes,...
  'firstframes',firstframes,...
  'figpos',ctraxresultsmovie_params.figpos,...
  'movietitle',movietitle,...
  'compression','none',...
  'useVideoWriter',false,...
  'titletext',false,...
  'avifileTempDataFile',[avifile,'-temp'],...
  'dynamicflyselection',true,...
  'doshowsex',true);

if ishandle(1),
  close(1);
end

if ~succeeded,
  error('Failed to create raw avi %s',avifile);
end

%% create subtitle file

subtitlefile = [tempname(ctraxresultsmovie_params.tempdatadir),'.srt'];
fid = fopen(subtitlefile,'w');
dt = [0,ctraxresultsmovie_params.nframes];
ts = cumsum(dt);
for i = 1:numel(dt)-1,
  fprintf(fid,'%d\n',i);
  fprintf(fid,'%s --> %s\n',...
    datestr(ts(i)/ctraxresultsmovie_params.fps/(3600*24),'HH:MM:SS,FFF'),...
    datestr((ts(i+1)-1)/ctraxresultsmovie_params.fps/(3600*24),'HH:MM:SS,FFF'));
  fprintf(fid,'%s, fr %d-%d\n\n',movietitle,...
    firstframes_off(i)+1,...
    endframes_off(i)+1);
end
fclose(fid);

%% compress

tmpfile = [xvidfile,'.tmp'];
newheight = 4*ceil(height/4);
newwidth = 4*ceil(width/4);
% subtitles are upside down, so encode with subtitles and flip, then flip
% again
cmd = sprintf('mencoder %s -o %s -ovc xvid -xvidencopts fixed_quant=4 -vf scale=%d:%d,flip -sub %s -subfont-text-scale 2 -msglevel all=2',...
  avifile,tmpfile,newwidth,newheight,subtitlefile);
status = system(cmd);
if status ~= 0,
  fprintf('*****\n');
  warning('Failed to compress avi to %s',xvidfile);
  fprintf('Need to run:\n');
  fprintf('%s\n',cmd);
  cmd2 = sprintf('mencoder %s -o %s -ovc xvid -xvidencopts fixed_quant=4 -vf flip -msglevel all=2',...
    tmpfile,xvidfile);
  fprintf('then\n');
  fprintf('%s\n',cmd2);
  fprintf('then delete %s %s %s\n',tmpfile,avifile,subtitlefile);
  fprintf('*****\n');
else
  cmd = sprintf('mencoder %s -o %s -ovc xvid -xvidencopts fixed_quant=4 -vf flip -msglevel all=2',...
    tmpfile,xvidfile);
  status = system(cmd);
  if status ~= 0,
    fprintf('*****\n');
    warning('Failed to add subtitles to %s',xvidfile);
    fprintf('Need to run:\n');
    fprintf('%s\n',cmd);
    fprintf('then delete %s %s %s\n',tmpfile,avifile,subtitlefile);
    fprintf('*****\n');
  else
    delete(tmpfile);
    delete(avifile);
    delete(subtitlefile);
  end
end


classdef LaunchVehicleViewProfileGroundObjData < matlab.mixin.SetGet
    %LaunchVehicleViewProfileGroundObjData Summary of this class goes here
    %   Detailed explanation goes here

    properties
        groundObj LaunchVehicleGroundObject
        celBodyData CelestialBodyData

        timesArr(1,:) cell = {};
        xInterps(1,:) cell = {};
        yInterps(1,:) cell = {};
        zInterps(1,:) cell = {};
%         xInterpsSc(1,:) cell = {};
%         yInterpsSc(1,:) cell = {};
%         zInterpsSc(1,:) cell = {};

        vehPosVelData LaunchVehicleViewPosVelInterp

        viewFrames(1,:) AbstractReferenceFrame
        showGrdObjLoS(1,:) logical

        markerPlot(1,:) cell = {}
        losMarkerPlot(1,:) cell = {};
    end

    methods
        function obj = LaunchVehicleViewProfileGroundObjData(groundObj, vehPosVelData, celBodyData)
            obj.groundObj = groundObj;
            obj.vehPosVelData = vehPosVelData;
            obj.celBodyData = celBodyData;
        end

        function addData(obj, times, rVectsGrdObj, viewInFrame, showGrdObjLoS)
            obj.timesArr(end+1) = {times};

            obj.xInterps{end+1} = griddedInterpolant(times, rVectsGrdObj(1,:), 'spline', 'linear');
            obj.yInterps{end+1} = griddedInterpolant(times, rVectsGrdObj(2,:), 'spline', 'linear');
            obj.zInterps{end+1} = griddedInterpolant(times, rVectsGrdObj(3,:), 'spline', 'linear');

%             obj.xInterpsSc{end+1} = griddedInterpolant(times, rVectsSc(1,:), 'spline', 'linear');
%             obj.yInterpsSc{end+1} = griddedInterpolant(times, rVectsSc(2,:), 'spline', 'linear');
%             obj.zInterpsSc{end+1} = griddedInterpolant(times, rVectsSc(3,:), 'spline', 'linear');

            obj.viewFrames(end+1) = viewInFrame;
            obj.showGrdObjLoS(end+1) = showGrdObjLoS;

            obj.markerPlot{end+1} = [];
            obj.losMarkerPlot{end+1} = [];
        end

        function plotBodyMarkerAtTime(obj, time, hAx)
            [rVect, ~] = obj.vehPosVelData.getPositionVelocityAtTime(time);
            rVectInd = 1;

            for(i=1:length(obj.timesArr))
                times = obj.timesArr{i};

                if(time >= min(floor(times)) && time <= max(ceil(times)))
                    xInterp = obj.xInterps{i};
                    xGrd = xInterp(time);

                    yInterp = obj.yInterps{i};
                    yGrd = yInterp(time);

                    zInterp = obj.zInterps{i};
                    zGrd = zInterp(time);

                    if(not(isempty(obj.markerPlot{i})) && isvalid(obj.markerPlot{i}) && isa(obj.markerPlot{i}, 'matlab.graphics.primitive.Transform'))
                        M = makehgtform('translate',[xGrd,yGrd,zGrd]);
                        set(obj.markerPlot{i},'Matrix',M);
                        obj.markerPlot{i}.Visible = 'on';

                    else
                        hold(hAx,'on');
                        obj.markerPlot{i} = hgtransform('Parent', hAx);
                        hBM = plot3(hAx, 0,0,0, 'MarkerEdgeColor','k', 'Marker',obj.groundObj.markerShape.shape, 'MarkerFaceColor',obj.groundObj.markerColor.color);
                        set(hBM,'Parent',obj.markerPlot{i});

                        M = makehgtform('translate',[xGrd,yGrd,zGrd]);
                        set(obj.markerPlot{i},'Matrix',M);
                        obj.markerPlot{i}.Visible = 'on';

                        hold(hAx,'off');
                    end

                    if(obj.showGrdObjLoS(i))
%                         xInterp = obj.xInterpsSc{i};
%                         xSc = xInterp(time);
%
%                         yInterp = obj.yInterpsSc{i};
%                         ySc = yInterp(time);
%
%                         zInterp = obj.zInterpsSc{i};
%                         zSc = zInterp(time);

                        xSc = rVect(1, rVectInd);
                        ySc = rVect(2, rVectInd);
                        zSc = rVect(3, rVectInd);
                        rVectInd = rVectInd + 1;

                        if(not(isempty(obj.losMarkerPlot{i})) && isvalid(obj.losMarkerPlot{i})) % && isa(obj.markerPlot{i}, 'matlab.graphics.primitive.Transform')
                            obj.losMarkerPlot{i}.XData = [xGrd xSc];
                            obj.losMarkerPlot{i}.YData = [yGrd ySc];
                            obj.losMarkerPlot{i}.ZData = [zGrd zSc];
                        else
                            hold(hAx,'on');
                            hLoS = plot3(hAx, [xGrd xSc], [yGrd ySc], [zGrd zSc], 'g-');
                            obj.losMarkerPlot{i} = hLoS;

                            hold(hAx,'off');
                        end

                        scFrame = obj.viewFrames(i);
                        bodyInfo = scFrame.getOriginBody;
                        bodyInfoInertialFrame = bodyInfo.getBodyCenteredInertialFrame();

                        scElemSet = CartesianElementSet(time, [xSc; ySc; zSc], [0;0;0], scFrame);
                        scElemSet = scElemSet.convertToFrame(bodyInfoInertialFrame);
                        rVectScConverted = scElemSet.rVect;
                        rVectScConverted = rVectScConverted(:)';

                        stateLogEntry = [time, rVectScConverted];

                        targetBodyInfo = obj.groundObj.centralBodyInfo;

                        allBodyInfo = obj.celBodyData.getAllBodyInfo();
                        hasLoSAll = true;
                        for(j=1:length(allBodyInfo))
                            eclipseBodyInfo = allBodyInfo(j);

                            hasLoS = LoS2Target(stateLogEntry, bodyInfo, eclipseBodyInfo, targetBodyInfo, obj.celBodyData, obj.groundObj);

                            if(hasLoS == 0)
                                hasLoSAll = false;
                                break;
                            end
                        end

                        if(hasLoSAll)
                            obj.losMarkerPlot{i}.Visible = 'on';
                        else
                            obj.losMarkerPlot{i}.Visible = 'off';
                        end
                    else
                        if(not(isempty(obj.losMarkerPlot{i})) && isvalid(obj.losMarkerPlot{i})) %&& isa(obj.losMarkerPlot{i}, 'matlab.graphics.primitive.Transform')
                            obj.losMarkerPlot{i}.Visible = 'off';
                        end
                    end
                else
                    if(not(isempty(obj.markerPlot{i})) && isvalid(obj.markerPlot{i}) && isa(obj.markerPlot{i}, 'matlab.graphics.primitive.Transform'))
                        obj.markerPlot{i}.Visible = 'off';
                    end

                    if(not(isempty(obj.losMarkerPlot{i})) && isvalid(obj.losMarkerPlot{i})) %&& isa(obj.losMarkerPlot{i}, 'matlab.graphics.primitive.Transform')
                        obj.losMarkerPlot{i}.Visible = 'off';
                    end
                end
            end
        end
    end
end


function rel_els = find_rel_subjs(up, group_type)
%FIND_REL_SUBJS identifies the subjects for each group (YHVs, EHVs and
%OHVs)
%
%	Inputs:
%		db_data         demographic data extracted from the Vortal database
%       up              universal parameters structure
%
%	Outputs:
%       rel_els         a structure of subject els for each group
%

if strcmp(group_type, 'group')
    groups = up.paramSet.groups;
    group_names = unique(groups);
elseif strcmp(group_type, 'sub_group')
    groups = up.paramSet.sub_groups;
    group_names = unique(groups);
end

for group_no = 1 : length(group_names)
    eval(['rel_els.' group_names{group_no} ' = find(strcmp(groups, group_names{group_no} ));']);
end

%% Old Code
%
% loaded_info = load(up.paths.db_data);
%
% temp.group = loaded_info.db_data.group(loaded_info.rel_els);
%
% if length(unique(temp.group)) == 2
%     rel_els.yhv = find(temp.group == 1);
%     rel_els.ehv = find(temp.group == 2);
%     rel_els.global = 1:length(temp.group);
% elseif length(unique(temp.group)) == 3
%     rel_els.bw = find(temp.group == 1);
%     rel_els.am = find(temp.group == 2);
%     rel_els.fm = find(temp.group == 3);
%     rel_els.global = 1:length(temp.group);
% else
%     fprint('Unexpected Number of Groups');
% end

end


%% Scene Text Detection using CNN
%
% References:
%
% * Minghui Liao, Baoguang Shi, Xiang Bai, Xinggang Wang, Wenyu Liu.
%   "TextBoxes: A Fast Text Detector with a Single Deep Neural Network",
%   AAAI 2017. <https://arxiv.org/abs/1611.06779>
%
% Sources:
%
% * <https://github.com/opencv/opencv_contrib/blob/3.4.1/modules/text/samples/textbox_demo.cpp>
% * <https://github.com/opencv/opencv_contrib/blob/3.4.1/modules/text/samples/deeptextdetection.py>
%

%%
% load model
[net, blobOpts] = TextBoxes();

%%
% detect text
im = which('handicapSign.jpg');
assert(~isempty(im), 'specify input image');
img = cv.imread(im, 'Color',true);
[bboxes, confs] = detectText(img, net, blobOpts);
fprintf('%d text detections\n', size(bboxes,1));

%%
% post-processing
if true
    % non-maximum suppression
    ind = cv.Net.NMSBoxes(bboxes, confs, 0.3, 0.4);
    ind = ind + 1;  % zero to one-based indices
    bboxes = bboxes(ind,:);
    confs = confs(ind);
else
    % confidence thresholding
    idx = confs > 0.6;
    bboxes = bboxes(idx,:);
    confs = confs(idx);
end
fprintf('%d text detections after filtering\n', size(bboxes,1));

%%
% show results
for i=1:size(bboxes,1)
    img = insertAnnotation(img, bboxes(i,:), sprintf('%.2f', confs(i)), ...
        'Color',[255 0 255], 'TextColor',[255 255 255], 'Thickness',2, 'FontScale',0.3);
    fprintf('text box: [%3d %3d %3d %3d], confidence: %3.0f%%\n', ...
        bboxes(i,:), confs(i)*100);
end
imshow(img), title('TextBox Demo')

%%
% Helper functions

function [rects, confs] = detectText(img, net, blobOpts)
    %DETECTTEXT  Detect text on input image

    % detect text
    net.setInput(cv.Net.blobFromImages(img, blobOpts{:}));
    dets = net.forward();

    % SSD output is 1-by-1-by-ndetections-by-7
    % d = [img_id, class_id, confidence, left, top, right, bottom]
    dets = permute(dets, [3 4 2 1]);

    % filter
    dets = dets(dets(:,2) == 1,:);  % 0: background, 1: text

    % adjust relative coordinates to image size
    sz = [size(img,2) size(img,1)];
    dets(:,4:7) = bsxfun(@times, dets(:,4:7), [sz sz]);

    % output detections (clamp coords)
    rects = cv.Rect.from2points(dets(:,4:5), dets(:,6:7));
    rects = cv.Rect.intersect(rects, [0 0 sz]);
    rects = round(rects);
    confs = dets(:,3);
end

function img = insertAnnotation(img, rect, str, varargin)
    % See also: insertObjectAnnotation, insertShape, insertText
    p = inputParser();
    p.addParameter('Alpha', 0.6);
    p.addParameter('Thickness', 1);
    p.addParameter('Color', [255 255 0]);
    p.addParameter('TextColor', [0 0 0]);
    p.addParameter('FontFace', 'HersheySimplex');
    p.addParameter('FontScale', 0.4);
    p.addParameter('AntiAlias', true);
    p.addParameter('Shape', 'rectangle');
    p.parse(varargin{:});
    opts = p.Results;
    opts.Shape = validatestring(opts.Shape, {'rectangle','circle'});
    thick = 1;

    [sz,b] = cv.getTextSize(str, 'Thickness',thick, ...
        'FontFace',opts.FontFace, 'FontScale',opts.FontScale);
    txt_rect = [rect(1), rect(2)-sz(2)-b, sz(1), sz(2)+b];
    txt_orig = [rect(1), rect(2)-b];

    if opts.AntiAlias
        alias = {'LineType','AA'};
    else
        alias = {'LineType',8};
    end

    overlay = img;
    if strcmp(opts.Shape, 'rectangle')
        overlay = cv.rectangle(overlay, rect, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    else
        c = rect(1:2) + rect(3:4)/2;
        r = max(rect(3:4)/2);
        overlay = cv.circle(overlay, c, r, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    end
    overlay = cv.rectangle(overlay, txt_rect, ...
        'Color',opts.Color, 'Thickness','Filled', alias{:});
    if opts.Thickness > 1
        overlay = cv.rectangle(overlay, txt_rect, ...
            'Color',opts.Color, 'Thickness',opts.Thickness, alias{:});
    end
    overlay = cv.putText(overlay, str, txt_orig, ...
        'FontFace',opts.FontFace, 'FontScale',opts.FontScale, ...
        'Color',opts.TextColor, 'Thickness',thick, alias{:});

    img = cv.addWeighted(img,1-opts.Alpha, overlay,opts.Alpha, 0);
end

%%
% Pretrained models

function dname = get_dnn_dir(dname)
    %GET_DNN_DIR  Path to model files, and show where to get them if missing

    dname = fullfile(mexopencv.root(), 'test', 'dnn', dname);
    b = isdir(dname);
    if ~b
        % display help of calling function
        % (assumed to be a local function in current file)
        st = dbstack(1);
        help([mfilename() filemarker() st(1).name])
    end
    assert(b, 'Missing model: %s', dname);
end

function [net, blobOpts] = TextBoxes()
    %TEXTDETECTOR  Text detector model [Caffe]
    %
    % homepage = https://github.com/MhLiao/TextBoxes
    %
    % ## Model
    %
    % file = test/dnn/TextBoxes/textbox.prototxt
    % url  = https://github.com/opencv/opencv_contrib/raw/3.4.1/modules/text/samples/textbox.prototxt
    % hash = c294416fe6d156b9383342d62b9158ab707170c0
    %
    % ## Weights
    %
    % file = test/dnn/TextBoxes/TextBoxes_icdar13.caffemodel
    % url  = https://www.dropbox.com/s/g8pjzv2de9gty8g/TextBoxes_icdar13.caffemodel?dl=0
    % hash = e4b32aef3db3d66fec0630c96006e10999f785c4
    % size = 90.68 MB
    %

    dname = get_dnn_dir('TextBoxes');
    net = cv.Net('Caffe', ...
        fullfile(dname, 'textbox.prototxt'), ...
        fullfile(dname, 'TextBoxes_icdar13.caffemodel'));
    blobOpts = {'SwapRB',true, 'Crop',false, 'Size',[300 300], 'Mean',[104 117 123]};
end


%SoundSubCodes SoundSubCodes enumeration for the brick
%
% Notes::
% - SoundSubCodes can be found in the EV3 documentation and source code
% (bytecodes.h)

classdef SoundSubCodes < uint8
    enumeration
         Break (0)
         Tone (1)
         Play (2)
         Repeat (3)
         Service (4)
    end
end


% pop_biosig() - import data files into EEGLAB using BIOSIG toolbox
%
% Usage:
%   >> OUTEEG = pop_biosig; % pop up window
%   >> OUTEEG = pop_biosig( filename, channels, type);
%
% Inputs:
%   filename - [string] file name
%
% Optional inputs:
%   'channels'   - [integer array] list of channel indices
%   'blockrange' - [min max] integer range of data blocks to import, in seconds.
%                  Entering [0 3] will import the first three blocks of data.
%                  Default is empty -> import all data blocks.
%  'importevent' - ['on'|'off'] import events. Default if 'on'.
%  'importannot' - ['on'|'off'] import annotations (EDF+ only). Default if 'on'
%  'blockepoch'  - ['on'|'off'] force importing continuous data. Default is 'on'
%  'ref'         - [integer] channel index or index(s) for the reference.
%                  Reference channels are not removed from the data,
%                  allowing easy re-referencing. If more than one
%                  channel, data are referenced to the average of the
%                  indexed channels. WARNING! Biosemi Active II data
%                  are recorded reference-free, but LOSE 40 dB of SNR
%                  if no reference is used!. If you do not know which
%                  channel to use, pick one and then re-reference after
%                  the channel locations are read in. {default: none}.
%                  For more information see http://www.biosemi.com/faq/cms&drl.htm
%  'refoptions'  - [Cell] Option for the pop_reref function. Default is to
%                  remove the reference channel if there is one of them and to
%                  keep it if there are several of them from the graphic
%                  interface. From the command line default option is to
%                  keep the reference channel.
%  'rmeventchan' - ['on'|'off'] remove event channel after event
%                  extraction. Default is 'on'.
%  'memorymapped' - ['on'|'off'] import memory mapped file (useful if
%                  encountering memory errors). Default is 'off'.
%
% Outputs:
%   OUTEEG   - EEGLAB data structure
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, Oct. 29, 2003-
%
% Note: BIOSIG toolbox must be installed. Download BIOSIG at
%       http://biosig.sourceforge.net
%       Contact a.schloegl@ieee.org for troubleshooting using BIOSIG.

% Copyright (C) 2003 Arnaud Delorme, SCCN, INC, UCSD, arno@salk.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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [EEG, command, dat] = pop_biosig(filename, varargin);
EEG = [];
command = '';

if ~plugin_askinstall('Biosig', 'sopen'), return; end;
biosigpathfirst;

if nargin < 1
	% ask user
	[filename, filepath] = uigetfile('*.*', 'Choose a data file -- pop_biosig()'); %%% this is incorrect in original version!!!!!!!!!!!!!!
    drawnow;

	if filename == 0 return; end;
	filename = [filepath filename];

    % look if MEG
    % -----------
    if length(filepath)>4
        if strcmpi(filepath(end-3:end-1), '.ds'), filename = filepath(1:end-1); end;
    end;

    % open file to get infos
    % ----------------------
    disp('Reading data file header...');
    dat = sopen(filename, 'r', [], 'OVERFLOWDETECTION:OFF');
    if ~isfield(dat, 'NRec')
        error('Unsuported data format');
    end;

    % special BIOSEMI
    % ---------------
    eeglab_options;
    if strcmpi(dat.TYPE, 'BDF')
        disp(upper('We highly recommend that you choose a reference channel IF these are Biosemi data'));
        disp(upper('(e.g., a mastoid or other channel). Otherwise the data will lose 40 dB of SNR!'));
        disp('For more information, see http://www.biosemi.com/faq/cms&drl.htm');
    end;
    uilist = { { 'style' 'text' 'String' 'Channel list (defaut all):' } ...
                 { 'style' 'edit' 'string' '' } ...
                 { 'style' 'text' 'String' [ 'Data range (in seconds) to read (default all [0 ' int2str(dat.NRec) '])' ] } ...
                 { 'style' 'edit' 'string' '' } ...
                 { 'style' 'text' 'String' 'Extract event' } ...
                 { 'style' 'checkbox' 'string' '' 'value' 1 'enable' 'on' } {} ...
                 { 'style' 'text' 'String' 'Import anotations (EDF+ only)' } ...
                 { 'style' 'checkbox' 'string' '' 'value' 1 'enable' 'on' } {} ...
                 { 'style' 'text' 'String' 'Force importing continuous data' 'value' 1} ...
                 { 'style' 'checkbox' 'string' '' 'value' 0 } {} ...
                 { 'style' 'text' 'String' 'Reference chan(s) indices - required for BIOSEMI' } ...
                 { 'style' 'edit' 'string' '' } ...
                  { 'style' 'checkbox' 'String' 'Import as memory mapped file (use if out of memory error)' 'value' option_memmapdata } };
    geom = { [3 1] [3 1] [3 0.35 0.5] [3 0.35 0.5] [3 0.35 0.5] [3 1] [1] };

    result = inputgui( geom, uilist, 'pophelp(''pop_biosig'')', ...
                                 'Load data using BIOSIG -- pop_biosig()');
    if length(result) == 0 return; end;

    % decode GUI params
    % -----------------
    options = {};
    if ~isempty(result{1}), options = { options{:} 'channels'   eval( [ '[' result{1} ']' ] ) }; end;
    if ~isempty(result{2}), options = { options{:} 'blockrange' eval( [ '[' result{2} ']' ] ) }; end;
    if length(result) > 2
        if ~result{3},          options = { options{:} 'importevent'  'off'  }; end;
        if ~result{4},          options = { options{:} 'importannot'  'off'  }; end;
        if  result{5},          options = { options{:} 'blockepoch'   'off' }; end;
        if ~isempty(result{6}), options = { options{:} 'ref'        eval( [ '[' result{6} ']' ] ) }; end;
        if  result{7},          options = { options{:} 'memorymapped' 'on' }; end;
    end;
    if length(eval( [ '[' result{6} ']' ] )) > 1
        options = { options{:} 'refoptions' { 'keepref' 'off' } };
    end;
else
    options = varargin;
end;

% decode imput parameters
% -----------------------
g = finputcheck( options, { 'blockrange'   'integer' [0 Inf]    [];
                            'channels'     'integer' [0 Inf]    [];
                            'ref'          'integer' [0 Inf]    [];
                            'refoptions'   'cell'    {}             { 'keepref' 'on' };
                            'rmeventchan'  'string'  { 'on';'off' } 'on';
                            'importevent'  'string'  { 'on';'off' } 'on';
                            'importannot'  'string'  { 'on';'off' } 'on';
                            'memorymapped' 'string'  { 'on';'off' } 'off';
                            'blockepoch'   'string'  { 'on';'off' } 'off' }, 'pop_biosig');
if isstr(g), error(g); end;

% import data
% -----------
EEG = eeg_emptyset;
[dat DAT interval] = readfile(filename, g.channels, g.blockrange, g.memorymapped);

if strcmpi(g.blockepoch, 'off')
    dat.NRec = 1;
end;

EEG = biosig2eeglab(dat, DAT, interval, g.channels, strcmpi(g.importevent, 'on'));

if strcmpi(g.rmeventchan, 'on') & strcmpi(dat.TYPE, 'BDF') & isfield(dat, 'BDF')
    if size(EEG.data,1) >= dat.BDF.Status.Channel,
        disp('Removing event channel...');
        EEG.data(dat.BDF.Status.Channel,:) = [];
        if ~isempty(EEG.chanlocs) && length(EEG.chanlocs) >= dat.BDF.Status.Channel
            EEG.chanlocs(dat.BDF.Status.Channel) = [];
        end;
    end;
    EEG.nbchan = size(EEG.data,1);
end;

% rerefencing
% -----------
if ~isempty(g.ref)
    disp('Re-referencing...');
    EEG = pop_reref(EEG, g.ref, g.refoptions{:});
%     EEG.data = EEG.data - repmat(mean(EEG.data(g.ref,:),1), [size(EEG.data,1) 1]);
%     if length(g.ref) == size(EEG.data,1)
%         EEG.ref  = 'averef';
%     end;
%     if length(g.ref) == 1
%         disp([ 'Warning: channel ' int2str(g.ref) ' is now zeroed (but still present in the data)' ]);
%     else
%         disp([ 'Warning: data matrix rank has decreased through re-referencing' ]);
%     end;
end;

% test if annotation channel is present
% -------------------------------------
if isfield(dat, 'EDFplus') && strcmpi(g.importannot, 'on')
    tmpfields = fieldnames(dat.EDFplus);
    for ind = 1:length(tmpfields)
        tmpdat = getfield(dat.EDFplus, tmpfields{ind});
        if length(tmpdat) == EEG.pnts
            EEG.data(end+1,:) = tmpdat;
            EEG.nbchan        = EEG.nbchan+1;
            if ~isempty(EEG.chanlocs)
                EEG.chanlocs(end+1).labels = tmpfields{ind};
            end;
        end;
    end;
end;

% convert data to single if necessary
% -----------------------------------
EEG = eeg_checkset(EEG,'makeur');   % Make EEG.urevent field
EEG = eeg_checkset(EEG);

% history
% -------
if isempty(options)
    command = sprintf('EEG = pop_biosig(''%s'');', filename);
else
    command = sprintf('EEG = pop_biosig(''%s'', %s);', filename, vararg2str(options));
end;

% Checking if str2double is on top of the path
biosigpathlast;

% ---------
% read data
% ---------
function [dat DAT interval] = readfile(filename, channels, blockrange, memmapdata);

if isempty(channels), channels = 0; end;
dat = sopen(filename, 'r', channels,'OVERFLOWDETECTION:OFF');

if strcmpi(memmapdata, 'off')
    fprintf('Reading data in %s format...\n', dat.TYPE);

    if ~isempty(blockrange)
        newblockrange    = blockrange;
%         newblockrange    = newblockrange*dat.Dur;
        DAT=sread(dat, newblockrange(2)-newblockrange(1), newblockrange(1));
    else
        DAT=sread(dat, Inf);% this isn't transposed in original!!!!!!!!
        newblockrange    = [];
    end
    sclose(dat);
else
    fprintf('Reading data in %s format (file will be mapped to memory so this may take a while)...\n', dat.TYPE);
    inc = ceil(250000/(dat.NS*dat.SPR)); % 1Mb block

    if isempty(blockrange), blockrange = [0 dat.NRec]; end;
    blockrange(2) = min(blockrange(2), dat.NRec);
    allblocks = [blockrange(1):inc:blockrange(end)];
    count = 1;
    for bind = 1:length(allblocks)-1
        TMPDAT=sread(dat, (allblocks(bind+1)-allblocks(bind))*dat.Dur, allblocks(bind)*dat.Dur);
        if bind == 1
            DAT = mmo([], [size(TMPDAT,2) (allblocks(end)-allblocks(1))*dat.SPR]);
        end;
        DAT(:,count:count+length(TMPDAT)-1) = TMPDAT';
        count = count+length(TMPDAT);
    end;
    sclose(dat);
end;

if ~isempty(blockrange)
     interval(1) = blockrange(1) * dat.SampleRate(1) + 1;
     interval(2) = blockrange(2) * dat.SampleRate(1);
else interval = [];
end


function autoChoiceBestModels_VASARI_LGG(pathExperiments,fSetNames,nameOutcomes,metric,maxOrder,pathFig)
% -------------------------------------------------------------------------
% function autoChoiceBestModels_VASARI_LGG(pathExperiments,fSetNames,nameOutcomes,metric,maxOrder,pathFig)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function automatically chooses (hard-coded in the function) the set
% types and models orders providing the best parsimonious models for all
% outcoms analyzed in the LGG study.
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathExperiments: Full path to the directory containing all experiments.
%                     --> Ex: '/myProject/WORKSPACE/VASARI'
% 2. fSetNames: Cell of strings specifying the name of the type of feature
%               set analyzed.
%               --> Ex: {'VASARI'}
% 3. nameOutcomes: Cell of strings specifying the outcome names to analyze.
%                  --> Ex: {'nonIDH1','IDHcodel','progression','lowGrade'}
% 4. metric: String specifying the metric to display.
%            --> 'AUC632'
% 5. maxOrder: Integer specifying the maximal multivariable model order.
%              --> Ex: 10
% 6. pathFig: (optional).  Full path to where figure is saved without
%             displaying it. Put '' for displaying the figure and not
%             saving it to 'pathFig' (default).
%             --> Ex: ''
% -------------------------------------------------------------------------
% OUTPUTS: Final prediction models saved in a folder named
%          '/myProject/WORKSPACE/VASARI/FINAL_MODELS/nameOutcome/fSetName'
% -------------------------------------------------------------------------
% 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;
cd(pathExperiments)
nOutcomes = length(nameOutcomes);
nType = length(fSetNames);

n = 0;
order = [2,4,5,3]; % For 'nonIDH1', IDHcodel, 'progression', 'lowGrade'

cd(pathExperiments), pathExperimentBinary = pathExperiments;
cd(pathExperimentBinary), mkdir('FINAL_MODELS'), cd('FINAL_MODELS'), pathFinalModelsBinary = pwd;
for o = 1:nOutcomes
    cd(pathFinalModelsBinary), mkdir(nameOutcomes{o}), cd(fullfile(pathFinalModelsBinary,nameOutcomes{o})), pathOutcomeBinary = pwd;
    plotPredictionResults_VASARI(fullfile(pathExperimentBinary,'RESULTS'),nameOutcomes{o},fSetNames,metric,maxOrder,pathFig)
    for f = 1:nType
        n = n + 1;
        cd(pathOutcomeBinary), mkdir(fSetNames{f}), cd(fullfile(pathOutcomeBinary,fSetNames{f})), pathFinalBinary = pwd;
        cd(fullfile(pathExperimentBinary,'RESULTS'))
        results = load(['RESULTS_',fSetNames{f},'_',nameOutcomes{o}]); results = struct2cell(results); results = results{1};
        finalModel = results.(['Order',num2str(order(n))]);
        finalModel.Order = order(n);
        finalModel.outcome = nameOutcomes{o};
        cd(pathFinalBinary), save('finalModel','finalModel')
    end
end

cd(startpath)
end


function test_suite=test_moxunit_fieldtrip_util_parse_dependencies
% #   For MOxUnit_fieldtrip's copyright information and license terms,   #
% #   see the COPYING file distributed with MOxUnit_fieldtrip.           #

    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function varargout=randint(mx)
    n=nargout;
    varargout=arrayfun(@(unused)ceil(rand()*mx),1:n,'UniformOutput',false);

function assert_output(input, expected_output)
    output=moxunit_fieldtrip_util_parse_dependency(input);
    if isempty(output)
        assertTrue(isempty(expected_output));
    else
        assertEqual(output,expected_output,sprintf('input: ''%s''',input));
    end

function test_fieldtrip_util_parse_dependencies_basics()
    empty={};
    input_outputs={'foo',{'foo'};...
                  'foo  ',{'foo'};...               % whitespace
                  'foo bar',{'foo','bar'};...
                  'foo  bar  baz ',{'foo','bar','baz'};...
                  '',empty;...
                  };
    for k=1:size(input_outputs)
        input_output=input_outputs(k,:);
        assert_output(input_output{1},input_output{2});
    end

function moxunit_fieldtrip_util_parse_dependency_exceptions
    aet=@(varargin)assertExceptionThrown(@()...
            moxunit_fieldtrip_util_parse_dependency(varargin{:}),'');
    % inputs that are not string throw an exception
    aet(struct)
    aet([1 2])
    aet(1);


function [fet clu spktimes wav]= ConvertKlusta2Matlab(shank,basepath,...
    basename,wvformExtract,saveFiles,numCores,keepMUA)
% USAGE
%
% [fet clu spktimes wav]= ConvertKlusta2Matlab(shank,basepath,basename,wvformExtract,saveFiles,numCores)
%
% INPUTS
%
% shank - the shank number (as a number, not text) equalling the name of
%         the folder under basepath with the data of interst.  Default = 1.
% basepath - directory path to the main recording folder with .dat and .xml
%            as well as shank folders made by makeProbeMapKlusta2.m (default is
%            current directory matlab is pointed to)
% basename - shared file name of .dat and .xml (default is last part of
%            current directory path, ie most immediate folder name)
% wvformExtract - binary (0 or 1) to choose whether to extract raw waveforms
% saveFiles - binary (0 or 1) to choose whether to save clu, res, fet, wav(.spk)
%             files or just return them as outputs
% numCores  - double between 1 and inf that sets the number of cores for a
%             parloop to run (set 2-12 on SSD or RAID0 systems, set to 1
%             for data stored on a single hard drive
% keepMUA   - binary (0 or 1) to choose whether to keep MUA as a separate
%             cluster (#1)
%
% OUTPUTS
%
% fet - features of waveforms
% clu - cluster ID's for waveforms
% spktimes(res) - spike times of waveforms(in sample #'s, not seconds)
% wav- waveform shapes taken from raw .dat file
%
%Converts .kwik/kwx files from Klusta into klusters-compatible
% fet,res,clu,spk files.  Works on a single shank of a recording, assumes a
% 16bit .dat and an .xml file is present in "basepath" (home folder) and
% that they are named basename.dat and basename.xml.  Also assumes that
% subdirectories in that basepath are made for each shank with names
% specified by numbers (ie 1,2,3,4..8).  In each shank folder should be
% .kwik and .kwx files made by klusta with names as follows:
% basename_sh[shankumber].kwik/kwx.
%
%
% Brendon Watson 2016
% edited by David Tingley 1/2017
% edited by Aza and Antonio 7/2017

% p = inputParser;
% addRequired(p,'shank',@isnumeric)
% addRequired(p,'basepath',@isstr)
% addRequired(p,'basename',@isstr)
%
% addParameter(p,'saveFiles',[],@isnumeric)
% addParameter(p,'numCores',[1],@isnumeric)
% p.parse(varargin{:})
%
% shank = p.Results.shank;
% basepath = p.Results.basepath;
% saveFiles = p.Results.saveFiles;
% numCores = p.Results.numCores;
% basename = p.Results.basename;

%
% Handle inputs
if ~exist('shank','var')
    shank = 1;
end
if ~exist('basepath','var');
    basepath = cd;
end
if ~exist('saveFiles','var');
    saveFiles = 0;
end
if ~exist('numCores','var');
    numCores = 1;
end
if ~exist('basename','var');
    [~,basename] = fileparts(pwd);
elseif strcmp(basename, 'lfpfile')
    d = dir('*lfp');
    basename = d.name(1:end-4);
end

if saveFiles % make file names
    cluname = fullfile(basepath,[basename '.clu.' num2str(shank)]);
    resname = fullfile(basepath,[basename '.res.' num2str(shank)]);
    fetname = fullfile(basepath,[basename '.fet.' num2str(shank)]);
    spkname = fullfile(basepath,[basename '.spk.' num2str(shank)]);
end

% Start grabbing data
datpath = fullfile(basepath,[basename '.dat']);
tkwik = fullfile(basepath,num2str(shank),[basename '_sh' num2str(shank) '.kwik']);
tkwx = fullfile(basepath,num2str(shank),[basename '_sh' num2str(shank) '.kwx']);
kwikinfo = h5info(tkwik,['/channel_groups/' num2str(shank) '/clusters/main']);
clu = h5read(tkwik,['/channel_groups/' num2str(shank) '/spikes/clusters/main']);
cluster_names = unique(clu);

try % different klusta versions have different names for this field
    totalch = h5readatt(tkwik,'/application_data/spikedetekt','nchannels');
catch
    totalch = h5readatt(tkwik,'/application_data/spikedetekt','n_channels');
end
try % some files don't have these variables saved in them?
    sbefore = h5readatt(tkwik,'/application_data/spikedetekt','extract_s_before');
    safter = h5readatt(tkwik,'/application_data/spikedetekt','extract_s_after');
catch
    wvform_nsamples = h5readatt(tkwik,'/application_data/spikedetekt','waveforms_nsamples');
    sbefore = wvform_nsamples/2;
    safter = wvform_nsamples/2;
end
channellist = h5readatt(tkwik,['/channel_groups/' num2str(shank)],'channel_order')+1;

%% Getting spiketimes
spktimes = h5read(tkwik,['/channel_groups/' num2str(shank) '/spikes/time_samples']);

%% spike extraction from dat
wav = [];
tsampsperwave = (sbefore+safter);
ngroupchans = length(channellist);
valsperwave = tsampsperwave * ngroupchans;
if wvformExtract & ~exist(spkname)  % only create .spk file if it doesn't already exist
    disp('loading from .dat file')
    dat=memmapfile(datpath,'Format','int16');
    wvforms_all = zeros(length(spktimes),valsperwave,'int16');

    if numCores>1 % if using multiple cores, start parfor loop
        delete(gcp('nocreate')); parpool(numCores)
        parfor j=1:length(spktimes)
            try
                byteList = [];
                for i = 1:length(channellist)
                    ind1 = (double(spktimes(j)).*totalch); % correction for problems with high values
                    ind11 = int64(ind1) - int64(sbefore.*totalch) + 1;
                    ind2 = (double(spktimes(j)).*totalch);
                    ind22 = int64(ind2) + int64(safter.*totalch);
                    ind = ind11:ind22;

                    byteList = [byteList; ind(double(channellist(i)):totalch:end)];
                end
                wvforms = dat.data(byteList);
            catch
                disp(['Error extracting spike at sample ' int2str(double(spktimes(j))) '. Saving as zeros']);
                disp(['Time range of that spike was: ' num2str(double(spktimes(j))-sbefore) ' to ' num2str(double(spktimes(j))+safter) ' samples'])
                wvforms = zeros(valsperwave,1,'int16');
            end
            if rem(j,50000) == 0
                disp([num2str(j) ' out of ' num2str(length(spktimes)) ' done'])
            end
            wvforms_all(j,:)=wvforms(:);
        end
    else % if we are using only one core, use a regular for loop
        for j=1:length(spktimes)
            try
                byteList = [];
                for i = 1:length(channellist)
                    ind1 = (double(spktimes(j)).*totalch); % correction for problems with high values
                    ind11 = int64(ind1) - int64(sbefore.*totalch) + 1;
                    ind2 = (double(spktimes(j)).*totalch);
                    ind22 = int64(ind2) + int64(safter.*totalch);
                    ind = ind11:ind22;

                    byteList = [byteList; ind(double(channellist(i)):totalch:end)];
                end
                wvforms = dat.data(byteList);
            catch
                disp(['Error extracting spike at sample ' int2str(double(spktimes(j))) '. Saving as zeros']);
                disp(['Time range of that spike was: ' num2str(double(spktimes(j))-sbefore) ' to ' num2str(double(spktimes(j))+safter) ' samples'])
                wvforms = zeros(valsperwave,1,'int16');
            end
            if rem(j,50000) == 0
                disp([num2str(j) ' out of ' num2str(length(spktimes)) ' done'])
            end
            wvforms_all(j,:)=wvforms(:);
        end

    end

    clear dat
    wvforms_all = reshape(wvforms_all',size(wvforms_all,1)*size(wvforms_all,2),1);
    wav = reshape(wvforms_all,ngroupchans,tsampsperwave,[]);
    wav = permute(wav,[3 1 2]);
elseif wvformExtract & exist(spkname) % load from .spk if it exists
    disp('loading from .spk file')
    fid = fopen(spkname,'r');
    wav = fread(fid,[1 inf],'int16');
    wav = reshape(wav,ngroupchans,tsampsperwave,[]);
    wav = permute(wav,[3 1 2]);
end
%% Spike features

fets = h5read(tkwx,['/channel_groups/' num2str(shank) '/features_masks']);
fets = double(squeeze(fets(1,:,:)));
fet = fets';
fetMultiplier = double(intmax('int32'))/max(abs(fets(:))); % masked klustakwik has small floats that need to be expanded before rounding below
fet = fet .* fetMultiplier;

%% writing to clu, res, fet, spk
if saveFiles

    %clu
    for i=1:length(kwikinfo.Groups)  % sometimes .kwik has more groups than clu (empty?)..
        group(i) = h5readatt(tkwik,kwikinfo.Groups(i).Name,'cluster_group');
        temp = strsplit(kwikinfo.Groups(i).Name,'/');
        clusterID(i) = str2num(temp{length(temp)}); % fix for if channel ordering is mixed up (we don't know that the first clusters are noise/MUA)
    end

    cluster_names = unique(clu);
    for i=1:length(cluster_names)
        [a b] =ismember(cluster_names(i),double(clusterID));
        if isempty(b)
            warning('could not find cluster name?')
        end
        if keepMUA
            if group(b) == 0 || group(b) == 3
                clu(clu == clusterID(b)) = 0; % noise
            elseif group(b) == 1
                clu(clu == clusterID(b)) = 1; % MUA
            end

        else
            if group(b)~=2
                clu(clu == clusterID(b)) = 0;  % re-name unsorted and noise as MUA/Noise cluster for FMATToolbox
            end
        end
    end

    clu = cat(1,length(unique(clu)),double(clu));
    fid=fopen(cluname,'w');
    fprintf(fid,'%.0f\n',clu);
    fclose(fid);
    clear fid

    fid=fopen(resname,'w');
    fprintf(fid,'%.0f\n',spktimes);
    fclose(fid);
    clear fid

    SaveFetIn(fetname,fet);
    %spk

    if wvformExtract & ~exist(spkname)
        fid=fopen(spkname,'w');
        fwrite(fid,wvforms_all,'int16');
        fclose(fid);
        clear fid
    end
    disp(['Shank ' num2str(shank) ' done'])
end

function SaveFetIn(FileName, Fet, BufSize)
if nargin<3 | isempty(BufSize)
    BufSize = inf;
end

nFeatures = size(Fet, 2);
formatstring = '%d';
for ii=2:nFeatures
    formatstring = [formatstring,'\t%d'];
end
formatstring = [formatstring,'\n'];

outputfile = fopen(FileName,'w');
fprintf(outputfile, '%d\n', nFeatures);

if isinf(BufSize)
    fprintf(outputfile,formatstring,round(Fet'));
else
    nBuf = floor(size(Fet,1)/BufSize)+1;

    for i=1:nBuf
        BufInd = [(i-1)*nBuf+1:min(i*nBuf,size(Fet,1))];
        fprintf(outputfile,formatstring,round(Fet(BufInd,:)'));
    end
end


function [ objects ] = buildObjStruct( list_path, list_img, list_event, list_event2 )
%BUILDOBJSTRUCT Builds the objects structure for storing all the
%   information.
%%%%

    %% Build struct for storing all objects found and their classes
    objects = struct('folder', [], 'imgName', [], 'idEvent', [], 'labelEvent', [], 'objects', struct('ULx', [], 'ULy', [], ...
        'BRx', [], 'BRy', [], 'objScore', [], 'eventAwareScore', [], 'features', [], 'label', 0));
    % label = 0 ---> not analyzed
    % label = 1 ---> no object

    %% Extract object candidates and objectness from each of them
    lenImgs = length(list_img);
    disp(['Starting extraction of objects from ' num2str(lenImgs) ' images.']);
    for i = 1:lenImgs
        % Initialize info for this image
        path = regexp(list_img{i}, '/', 'split');
        objects(i).folder = list_path{i};
        objects(i).imgName = path{length(path)};
        objects(i).idEvent = list_event(i);
        objects(i).labelEvent = list_event2(i);
    end

end


classdef TopOptComputer < handle

    properties (Access = public)
        computation
        settings
    end

    properties (Access = private)
        testName
    end

    methods (Access = public)
        function obj = TopOptComputer(cParams)
            obj.testName = cParams.testName;
        end

        function compute(obj)
            obj.createSettings();
            topOptSolver = TopOpt_Problem(obj.settings);
            topOptSolver.computeVariables();
            obj.computation = topOptSolver;
        end
    end

    methods (Access = private)

        function createSettings(obj)
            obj.createOldSettings();
            obj.translateToNewSettings();
        end

        function createOldSettings(obj)
            fileName = obj.testName;
            s = Settings(fileName);
            s.warningHoleBC = false;
            s.printIncrementalIter = false;
            s.printChangingFilter = false;
            s.printing = false;
            obj.settings = s;
        end

        function translateToNewSettings(obj)
            translator = SettingsTranslator();
            translator.translate(obj.settings);
            fileName = translator.fileName;
            obj.settings  = SettingsTopOptProblem(fileName);
        end

    end
end


%% rotate_vertices
% Below is a demonstration of the features of the |rotate_vertices| function

%%
clear; close all; clc;

%% Syntax
% |[Vr]=rotate_vertices(V,R_mat);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% 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/>.