%% v2struct
% v2struct Pack/Unpack Variables to/from a scalar structure.
function varargout = v2struct(varargin)

%% Description
%    v2struct has dual functionality in packing & unpacking variables into structures and
%    vice versa, according to the syntax and inputs.
%
%    Function features:
%      * Pack variables to structure with enhanced field naming
%      * Pack and update variables in existing structure
%      * Unpack variables from structure with enhanced variable naming
%      * Unpack only specific fields in a structure to variables
%      * Unpack without over writing existing variables in work space
%
%    In addition to the obvious usage, this function could by highly useful for example in
%    working with a function with multiple inputs. Packing variables before the call to
%    the function, and unpacking it in the beginning of the function will make the function
%    call shorter, more readable, and you would not have to worry about arguments order any
%    more. Moreover you could leave the function as it is and you could pass same inputs to
%    multiple functions, each of which will use its designated arguments placed in the
%    structure.
%
%% Syntax
%    Pack
%      S = v2struct
%      S = v2struct(x,y,z,...)
%      S = v2struct(fieldNames)
%      S = v2struct(A,B,C,..., fieldNames)
%      S = v2struct(x,..., nameOfStruct2Update, fieldNames)
%      v2struct
%      v2struct(x,y,z,...)
%      v2struct(fieldNames)
%      v2struct(A,B,C,..., fieldNames)
%      v2struct(x,..., nameOfStruct2Update, fieldNames)
%
%    Unpack
%      v2struct(S)
%      [a,b,c,...] = v2struct(S)
%      v2struct(S,fieldNames)
%      [a,b,c,...] = v2struct(S,fieldNames)
%
%% Inputs & Outputs
%    Pack - inputs
%      x,y,z,...           - any variable to pack. can be replaced by fieldNames below.
%      nameOfStruct2Update - optional, name of structure to update if desired.
%      fieldNames          - optional, cell array of strings, which must include a cell
%                            with the string 'fieldNames' and must be the last input.
%    Pack - outputs
%      S - the packed structure. If there is no output argument then a structure named
%          Sv2struct would be created in the caller workspace.
%
%    Unpack - inputs
%      S          - name of structure to be unpacked.
%      fieldNames - optional, cell array of strings, which must include a cell with the
%                   string 'fieldNames' and must be the last input.
%    Unpack - outputs
%      a,b,c,... - variables upacked from the structure.
%                  if there are no output arguments then variables would be created in
%                  the caller workspace with naming according to name of inputs.
%
%% Examples
%  % see 'Usage example' section below for convenient presentation of these examples.
%
%    % NOTE: whenever using filedNames cell array please note the following
%    %  1. fieldNames cell array must include a cell with the string 'fieldNames'
%    %  2. fieldNames cell array input must be the last input.
%
%  % Pack
%      x = zeros(3); x2 = ones(3); y = 'Testing123'; z = cell(2,3);
%      fieldNames1 = {'fieldNames', 'x', 'y', 'z'};
%      fieldNames2 = {'fieldNames', 'a', 'b', 'c'};
%      fieldNames3 = {'fieldNames', 'x'};
%      nameOfStruct2Update = 'S';
%
%       % The four examples below return structure S with same values however the
%       % structure's field names are defined differently in every syntax.
%      % Example 1.
%      % structure field names defined by variables names.
%       S = v2struct(x,y,z)
%      % Example 2.
%      % structure field names defined according to the cell array fieldNames.
%       % NOTE: variables with the names in fieldNames1 must exist in the caller workspace.
%       S = v2struct(fieldNames1)
%      % Example 3.
%      % same as #1. but arguments are passed explicitly
%       S = v2struct(zeros(3), 'Testing123', cell(2,3), fieldNames1)
%      % Example 4.
%      % field names defined by content of fieldNames2 while
%      % the values are set according to the passed arguments. In this case the structure
%      % S returned would be: S.a=x, S.b=y, S.c=z
%       S = v2struct(x,y,z, fieldNames2)
%
%      % Example 5.
%      % update structure S. The fields that would be updated are according to content
%      % of fieldNames3. Note that you must pass a variable with the name
%      % 'nameOfStruct2Update' placed before 'fieldNames3'. This variable should contain
%      % the name of the structure you want to update as a string. Also note that if you
%      % set an output structure name which is different than the one stated in
%      % nameOfStruct2Update a new structure would be created and the structure that was
%      % meant to be updated would not get updated.
%       S.oldField = 'field to be saved for future use'
%       S = v2struct(x2, nameOfStruct2Update, fieldNames3)
%
%      % Example 6.
%      % pack all variables in caller workspace. Call without input arguments.
%        S = v2struct
%
%      % The following examples return the same results as the examples above but the
%      % structure would be returned with the default name 'Sv2struct'. Be cautious as
%      % this might lead to overriding of arguments.
%      % Example 7.
%       v2struct(x,y,z)
%      % Example 8.
%       v2struct(fieldNames1)
%      % Example 9.
%       v2struct(zeros(3), 'Testing123', cell(2,3), fieldNames1)
%      % Example 10.
%       v2struct(x,y,z, fieldNames2)
%      % Example 11.
%       S.oldField = 'field to be saved for future use'
%       v2struct(x2, nameOfStruct2Update, fieldNames3)
%      % Example 12.
%       v2struct
%
%  % Unpack
%      clear S x x2 y z fieldNames1 fieldNames2 fieldNames3 nameOfStruct2Update
%      S.x = zeros(3); S.y = 'Testing123'; S.z = cell(2,3);
%      fieldNames3 = {'fieldNames','x','z'};
%
%      % Example 1.
%      % This example creates or overwrites variables x, y, z in the caller with the
%      % contents of the corresponding named fields.
%       v2struct(S)
%
%      % Example 2.
%      % This example assigns the contents of the fields of the scalar structure
%      % S to the variables a,b,c rather than overwriting variables in the caller. If
%      % there are fewer output variables than there are fields in S, the remaining fields
%      % are not extracted.
%       [a,b,c] = v2struct(S)
%
%      % Example 3.
%      % This example creates or overwrites variables x and z in the caller with the
%      % contents of the corresponding named fields.
%       v2struct(S, fieldNames3)
%
%      % Example 4.
%      % This example assigns the contents of the fields 'x' and 'z' defined by
%      % fieldNames3 of the scalar structure S to the variables a and b rather than
%      % overwriting variables in the caller. If there are fewer output variables than
%      % there are fields in S, the remaining fields are not extracted.
%       [a,b] = v2struct(S, fieldNames3)
%
%       % This example unpacks variables 'y' and 'z' only without overwriting variable 'x'.
%       % NOTE the addition of the field named 'avoidOverWrite' to the structure to be
%       % unpacked. This is mandatory in order to make this functionality work. The
%       % contents of this field can be anything, it does not matter.
%      S.avoidOverWrite = '';
%      x = 'do not overwrite me';
%      v2struct(S)
%
%% Usage example (includes sub-functions)
%    1. run attached v2structDemo1.m file for on screen presentation of examples.
%    2. run attached v2structDemo2.m file and read comments in file for a suggestion of
%       how to use v2struct in managing input to other functions with improved usability.
%
%% Revision history
%    2011-05-19, Adi N., Creation
%    2011-05-29, Adi N., Update structure added, some documentation and demo function changes
%    2011-06-02, Adi N., Fixed updating structure functionality
%    2011-06-05, Adi N., Added functionality: avoid overwritring existing variables, added
%                        unpacking examples to demo1 .m file.
%    2011-06-30, Adi N., fieldNames usage corrected, now must include a specific string to
%                        be triggered. Documentation enhanced. Code tweaked.
%    2011-07-14, Adi N., Fixed bug in packing with variables only
%    2011-08-14, Adi N., Clarified warning and error when packing/unpacking with
%                        fieldNames.
%    2011-09-12, Adi N., Added easy packing of all variables in caller workspace (thanks
%                        to Vesa Lehtinen for the suggestion), fixed bug in warning
%                        handling in packing case, edited comments.
%
%    Inspired by the function: mmv2truct - D.C. Hanselman, University of Maine, Orono, ME
%    04469 4/28/99, 9/29/99, renamed 10/19/99 Mastering MATLAB 5, Prentice Hall,
%    ISBN 0-13-858366-8

% parse input for field names
if isempty(varargin)
   gotCellArrayOfStrings = false;
   toUnpackRegular = false;
   toUnpackFieldNames = false;
   gotFieldNames = false;
else
   gotCellArrayOfStrings = iscellstr(varargin{end});

   toUnpackRegular = (nargin == 1) && isstruct(varargin{1});
   if toUnpackRegular
      fieldNames = fieldnames(varargin{1})';
      nFields = length(fieldNames);
   end

   gotFieldNames = gotCellArrayOfStrings & any(strcmpi(varargin{end},'fieldNames'));
   if gotFieldNames
      fieldNamesRaw = varargin{end};
      % indices of cells with actual field names, leaving out the index to 'fieldNames' cell.
      indFieldNames = ~strcmpi(fieldNamesRaw,'fieldNames');
      fieldNames = fieldNamesRaw(indFieldNames);
      nFields = length(fieldNames);
   end
   toUnpackFieldNames = (nargin == 2) && isstruct(varargin{1}) && gotFieldNames;
end

% Unpack
if toUnpackRegular || toUnpackFieldNames

   struct = varargin{1};
   assert(isequal(length(struct),1) , 'Single input nust be a scalar structure.');
   CallerWS = evalin('caller','whos'); % arguments in caller work space

   % update fieldNames according to 'avoidOverWrite' flag field.
   if isfield(struct,'avoidOverWrite')
      indFieldNames = ~ismember(fieldNames,{CallerWS(:).name,'avoidOverWrite'});
      fieldNames = fieldNames(indFieldNames);
      nFields = length(fieldNames);
   end

   if toUnpackRegular % Unpack with regular fields order
      if nargout == 0 % assign in caller
         for iField = 1:nFields
            assignin('caller',fieldNames{iField},struct.(fieldNames{iField}));
         end
      else % dump into variables
         for iField = 1:nargout
            varargout{iField} = struct.(fieldNames{iField});
         end
      end

   elseif toUnpackFieldNames % Unpack with fields according to fieldNames
      if nargout == 0 % assign in caller, by comparing fields to fieldNames
         for iField = 1:nFields
            assignin('caller',fieldNames{iField},struct.(fieldNames{iField}));
         end
      else % dump into variables
         assert( isequal(nFields, nargout) , ['Number of output arguments',...
            ' does not match number of field names in cell array']);
         for iField = 1:nFields
            varargout{iField} = struct.(fieldNames{iField});
         end
      end
   end

% Pack
else
   % build cell array of input names
   CallerWS = evalin('caller','whos');
   inputNames = cell(1,nargin);
   for iArgin = 1:nargin
      inputNames{iArgin} = inputname(iArgin);
   end
   nInputs = length(inputNames);

      % look for 'nameOfStruct2Update' variable and get the structure name
   if ~any(strcmpi(inputNames,'nameOfStruct2Update')) % no nameOfStruct2Update
      nameStructArgFound = false;
      validVarargin = varargin;
   else % nameOfStruct2Update found
      nameStructArgFound = true;
      nameStructArgLoc = strcmp(inputNames,'nameOfStruct2Update');
      nameOfStruct2Update = varargin{nameStructArgLoc};
      % valid varargin with just the inputs to pack and fieldNames if exists
      validVarargin = varargin(~strcmpi(inputNames,'nameOfStruct2Update'));
      % valid inputNames with just the inputs name to pack and fieldNames if exists
      inputNames = inputNames(~strcmpi(inputNames,'nameOfStruct2Update'));
      nInputs = length(inputNames);
      % copy structure from caller workspace to enable its updating
      if ismember(nameOfStruct2Update,{CallerWS(:).name}) % verify existance
         S = evalin('caller',nameOfStruct2Update);
      else
         error(['Bad input. Structure named ''',nameOfStruct2Update,...
            ''' was not found in workspace'])
      end
   end

   % when there is no input or the input is only variables and perhaps
   % also nameOfStruct2Update
   if ~gotFieldNames
      % no input, pack all of variables in caller workspace
      if isequal(nInputs, 0)
         for iVar = 1:length(CallerWS)
            S.(CallerWS(iVar).name) = evalin('caller',CallerWS(iVar).name);
         end
         % got input, check input names and pack
      else
         for iInput = 1:nInputs
            if gotCellArrayOfStrings % called with a cell array of strings
               errMsg = sprintf(['Bad input in cell array of strings.'...
                           '\nIf you want to pack (or unpack) using this cell array as'...
                           ' designated names'...
                           '\nof the structure''s fields, add a cell with the string'...
                           ' ''fieldNames'' to it.']);
            else
               errMsg = sprintf(['Bad input in argument no. ', int2str(iArgin),...
                                 ' - explicit argument.\n'...
                          'Explicit arguments can only be called along with a matching'...
                          '\n''fieldNames'' cell array of strings.']);
            end
            assert( ~isempty(inputNames{iInput}), errMsg);
            S.(inputNames{iInput}) = validVarargin{iInput};
         end

         % issue warning for possible wrong usage when packing with an input of cell array of
         % strings as the last input without it containing the string 'fieldNames'.
         if gotCellArrayOfStrings
            name = inputNames{end};
            % input contains structure and a cell array of strings
            if (nargin == 2) && isstruct(varargin{1})
               msgStr = [inputNames{1},''' and ''',inputNames{2},''' were'];
               % input contains any arguments with an implicit cell array of strings
            else
               msgStr = [name, ''' was'];
            end
            warnMsg = ['V2STRUCT - ''%s packed in the structure.'...
               '\nTo avoid this warning do not put ''%s'' as last v2struct input.'...
               '\nIf you want to pack (or unpack) using ''%s'' as designated names'...
               ' of the'...
               '\nstructure''s fields, add a cell with the string ''fieldNames'' to'...
               ' ''%s''.'];
            fprintf('\n')
            warning('MATLAB:V2STRUCT:cellArrayOfStringNotFieldNames',warnMsg,msgStr,...
                     name,name,name)
         end
      end
   % fieldNames cell array exists in input
   elseif gotFieldNames
      nVarToPack = length(varargin)-1-double(nameStructArgFound);
      if nVarToPack == 0 % no variables to pack
         for iField = 1:nFields
            S.(fieldNames{iField}) = evalin('caller',fieldNames{iField});
         end

         % else - variables to pack exist
         % check for correct number of fields vs. variables to pack
      elseif ~isequal(nFields,nVarToPack)
         error(['Bad input. Number of strings in fieldNames does not match',...
                'number of input arguments for packing.'])
      else
         for iField = 1:nFields
            S.(fieldNames{iField}) = validVarargin{iField};
         end
      end

   end % if ~gotFieldNames

if nargout == 0
   assignin( 'caller', 'Sv2struct',S );
else
   varargout{1} = S;
end

end % if nargin


function dep = cfg_vout_file_fplist(job)

% function dep = cfg_vout_file_fplist(job)
%
% Virtual outputs for cfg_run_file_fplist. Struct with fields .files and
% .dirs. See help on cfg_getfile.
%
% This code is part of a batch job configuration system for MATLAB. See
%      help matlabbatch
% for a general overview.
%_______________________________________________________________________
% Copyright (C) 2007 Freiburg Brain Imaging

% Volkmar Glauche
% $Id: cfg_vout_file_fplist.m 3990 2010-07-13 10:27:17Z volkmar $

rev = '$Rev: 3990 $'; %#ok

dep(1)            = cfg_dep;
if ~ischar(job.filter) || strcmp(job.filter,'<UNDEFINED>')
    dep(1).sname  = 'Selected Files';
else
    dep(1).sname  = sprintf('Selected Files (%s)', job.filter);
end
dep(1).src_output = substruct('.','files');
dep(1).tgt_spec   = cfg_findspec({{'class','cfg_files','strtype','e'}}); % This will display for both file and dir inputs. There is no notion of "no directories" in matching.
dep(2)            = cfg_dep;
dep(2).sname      = 'Subdirectories';
dep(2).src_output = substruct('.','dirs');
dep(2).tgt_spec   = cfg_findspec({{'filter','dir','strtype','e'}});


function vl_compilenn(varargin)
%VL_COMPILENN Compile the MatConvNet toolbox.
%   The `vl_compilenn()` function compiles the MEX files in the
%   MatConvNet toolbox. See below for the requirements for compiling
%   CPU and GPU code, respectively.
%
%   `vl_compilenn('OPTION', ARG, ...)` accepts the following options:
%
%   `EnableGpu`:: `false`
%      Set to true in order to enable GPU support.
%
%   `Verbose`:: 0
%      Set the verbosity level (0, 1 or 2).
%
%   `Debug`:: `false`
%      Set to true to compile the binaries with debugging
%      information.
%
%   `CudaMethod`:: Linux & Mac OS X: `mex`; Windows: `nvcc`
%      Choose the method used to compile the CUDA code. There are two
%      methods:
%
%      * The **`mex`** method uses the MATLAB MEX command with the
%        configuration file
%        `<MatConvNet>/matlab/src/config/mex_CUDA_<arch>.[sh/xml]`
%        This configuration file is in XML format since MATLAB 8.3
%        (R2014a) and is a Shell script for earlier versions. This
%        is, principle, the preferred method as it uses the
%        MATLAB-sanctioned compiler options.
%
%      * The **`nvcc`** method calls the NVIDIA CUDA compiler `nvcc`
%        directly to compile CUDA source code into object files.
%
%        This method allows to use a CUDA toolkit version that is not
%        the one that officially supported by a particular MATALB
%        version (see below). It is also the default method for
%        compilation under Windows and with CuDNN.
%
%   `CudaRoot`:: guessed automatically
%      This option specifies the path to the CUDA toolkit to use for
%      compilation.
%
%   `EnableImreadJpeg`:: `true`
%      Set this option to `true` to compile `vl_imreadjpeg`.
%
%   `EnableDouble`:: `true`
%      Set this option to `true` to compile the support for DOUBLE
%      data types.
%
%   `ImageLibrary`:: `libjpeg` (Linux), `gdiplus` (Windows), `quartz` (Mac)
%      The image library to use for `vl_impreadjpeg`.
%
%   `ImageLibraryCompileFlags`:: platform dependent
%      A cell-array of additional flags to use when compiling
%      `vl_imreadjpeg`.
%
%   `ImageLibraryLinkFlags`:: platform dependent
%      A cell-array of additional flags to use when linking
%      `vl_imreadjpeg`.
%
%   `EnableCudnn`:: `false`
%      Set to `true` to compile CuDNN support. See CuDNN
%      documentation for the Hardware/CUDA version requirements.
%
%   `CudnnRoot`:: `'local/'`
%      Directory containing the unpacked binaries and header files of
%      the CuDNN library.
%
%   ## Compiling the CPU code
%
%   By default, the `EnableGpu` option is switched to off, such that
%   the GPU code support is not compiled in.
%
%   Generally, you only need a 64bit C/C++ compiler (usually Xcode, GCC or
%   Visual Studio for Mac, Linux, and Windows respectively). The
%   compiler can be setup in MATLAB using the
%
%      mex -setup
%
%   command.
%
%   ## Compiling the GPU code
%
%   In order to compile the GPU code, set the `EnableGpu` option to
%   `true`. For this to work you will need:
%
%   * To satisfy all the requirements to compile the CPU code (see
%     above).
%
%   * A NVIDIA GPU with at least *compute capability 2.0*.
%
%   * The *MATALB Parallel Computing Toolbox*. This can be purchased
%     from Mathworks (type `ver` in MATLAB to see if this toolbox is
%     already comprised in your MATLAB installation; it often is).
%
%   * A copy of the *CUDA Devkit*, which can be downloaded for free
%     from NVIDIA. Note that each MATLAB version requires a
%     particular CUDA Devkit version:
%
%     | MATLAB version | Release | CUDA Devkit  |
%     |----------------|---------|--------------|
%     | 8.2            | 2013b   | 5.5          |
%     | 8.3            | 2014a   | 5.5          |
%     | 8.4            | 2014b   | 6.0          |
%     | 8.6            | 2015b   | 7.0          |
%     | 9.0            | 2016a   | 7.5          |
%
%     Different versions of CUDA may work using the hack described
%     above (i.e. setting the `CudaMethod` to `nvcc`).
%
%   The following configurations have been tested successfully:
%
%   * Windows 7 x64, MATLAB R2014a, Visual C++ 2010, 2013 and CUDA Toolkit
%     6.5. VS 2015 CPU version only (not supported by CUDA Toolkit yet).
%   * Windows 8 x64, MATLAB R2014a, Visual C++ 2013 and CUDA
%     Toolkit 6.5.
%   * Mac OS X 10.9, 10.10, 10.11, MATLAB R2013a to R2016a, Xcode, CUDA
%     Toolkit 5.5 to 7.5.
%   * GNU/Linux, MATALB R2014a/R2015a/R2015b/R2016a, gcc/g++, CUDA Toolkit 5.5/6.5/7.5.
%
%   Compilation on Windows with MinGW compiler (the default mex compiler in
%   Matlab) is not supported. For Windows, please reconfigure mex to use
%   Visual Studio C/C++ compiler.
%   Furthermore your GPU card must have ComputeCapability >= 2.0 (see
%   output of `gpuDevice()`) in order to be able to run the GPU code.
%   To change the compute capabilities, for `mex` `CudaMethod` edit
%   the particular config file.  For the 'nvcc' method, compute
%   capability is guessed based on the GPUDEVICE output. You can
%   override it by setting the 'CudaArch' parameter (e.g. in case of
%   multiple GPUs with various architectures).
%
%   See also: [Compliling MatConvNet](../install.md#compiling),
%   [Compiling MEX files containing CUDA
%   code](http://mathworks.com/help/distcomp/run-mex-functions-containing-cuda-code.html),
%   `vl_setup()`, `vl_imreadjpeg()`.

% Copyright (C) 2014-16 Karel Lenc and Andrea Vedaldi.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

% Get MatConvNet root directory
root = fileparts(fileparts(mfilename('fullpath'))) ;
addpath(fullfile(root, 'matlab')) ;

% --------------------------------------------------------------------
%                                                        Parse options
% --------------------------------------------------------------------

opts.enableGpu        = false;
opts.enableImreadJpeg = true;
opts.enableCudnn      = false;
opts.enableDouble     = true;
opts.imageLibrary = [] ;
opts.imageLibraryCompileFlags = {} ;
opts.imageLibraryLinkFlags = [] ;
opts.verbose          = 0;
opts.debug            = false;
opts.cudaMethod       = [] ;
opts.cudaRoot         = [] ;
opts.cudaArch         = [] ;
opts.defCudaArch      = [...
  '-gencode=arch=compute_20,code=\"sm_20,compute_20\" '...
  '-gencode=arch=compute_30,code=\"sm_30,compute_30\"'];
opts.cudnnRoot        = 'local/cudnn' ;
opts = vl_argparse(opts, varargin);

% --------------------------------------------------------------------
%                                                     Files to compile
% --------------------------------------------------------------------

arch = computer('arch') ;
if isempty(opts.imageLibrary)
  switch arch
    case 'glnxa64', opts.imageLibrary = 'libjpeg' ;
    case 'maci64', opts.imageLibrary = 'quartz' ;
    case 'win64', opts.imageLibrary = 'gdiplus' ;
  end
end
if isempty(opts.imageLibraryLinkFlags)
  switch opts.imageLibrary
    case 'libjpeg', opts.imageLibraryLinkFlags = {'-ljpeg'} ;
    case 'quartz', opts.imageLibraryLinkFlags = {'-framework Cocoa -framework ImageIO'} ;
    case 'gdiplus', opts.imageLibraryLinkFlags = {'gdiplus.lib'} ;
  end
end

lib_src = {} ;
mex_src = {} ;

% Files that are compiled as CPP or CU depending on whether GPU support
% is enabled.
if opts.enableGpu, ext = 'cu' ; else ext='cpp' ; end
lib_src{end+1} = fullfile(root,'matlab','src','bits',['data.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['datamex.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnconv.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnfullyconnected.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnsubsample.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnpooling.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnnormalize.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnbnorm.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnbias.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnbilinearsampler.' ext]) ;
lib_src{end+1} = fullfile(root,'matlab','src','bits',['nnroipooling.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnconv.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnconvt.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnpool.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnnormalize.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnbnorm.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnbilinearsampler.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_nnroipool.' ext]) ;
mex_src{end+1} = fullfile(root,'matlab','src',['vl_taccummex.' ext]) ;
switch arch
  case {'glnxa64','maci64'}
    % not yet supported in windows
    mex_src{end+1} = fullfile(root,'matlab','src',['vl_tmove.' ext]) ;
end

% CPU-specific files
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','im2row_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','subsample_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','copy_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','pooling_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','normalize_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','bnorm_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','tinythread.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','bilinearsampler_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','roipooling_cpu.cpp') ;
lib_src{end+1} = fullfile(root,'matlab','src','bits','imread.cpp') ;

% GPU-specific files
if opts.enableGpu
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','im2row_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','subsample_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','copy_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','pooling_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','normalize_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','bnorm_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','bilinearsampler_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','roipooling_gpu.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','datacu.cu') ;
  mex_src{end+1} = fullfile(root,'matlab','src','vl_cudatool.cu') ;
end

% cuDNN-specific files
if opts.enableCudnn
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','nnconv_cudnn.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','nnbias_cudnn.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','nnpooling_cudnn.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','nnbilinearsampler_cudnn.cu') ;
  lib_src{end+1} = fullfile(root,'matlab','src','bits','impl','nnbnorm_cudnn.cu') ;
end

% Other files
if opts.enableImreadJpeg
  mex_src{end+1} = fullfile(root,'matlab','src', ['vl_imreadjpeg.' ext]) ;
  mex_src{end+1} = fullfile(root,'matlab','src', ['vl_imreadjpeg_old.' ext]) ;
  lib_src{end+1} = fullfile(root,'matlab','src', 'bits', 'impl', ['imread_' opts.imageLibrary '.cpp']) ;
end

% --------------------------------------------------------------------
%                                                   Setup CUDA toolkit
% --------------------------------------------------------------------

if opts.enableGpu
  opts.verbose && fprintf('%s: * CUDA configuration *\n', mfilename) ;

  % Find the CUDA Devkit
  if isempty(opts.cudaRoot), opts.cudaRoot = search_cuda_devkit(opts) ; end
  opts.verbose && fprintf('%s:\tCUDA: using CUDA Devkit ''%s''.\n', ...
                          mfilename, opts.cudaRoot) ;

  opts.nvccPath = fullfile(opts.cudaRoot, 'bin', 'nvcc') ;
  switch arch
    case 'win64', opts.cudaLibDir = fullfile(opts.cudaRoot, 'lib', 'x64') ;
    case 'maci64', opts.cudaLibDir = fullfile(opts.cudaRoot, 'lib') ;
    case 'glnxa64', opts.cudaLibDir = fullfile(opts.cudaRoot, 'lib64') ;
    otherwise, error('Unsupported architecture ''%s''.', arch) ;
  end

  % Set the nvcc method as default for Win platforms
  if strcmp(arch, 'win64') && isempty(opts.cudaMethod)
    opts.cudaMethod = 'nvcc';
  end

  % Activate the CUDA Devkit
  cuver = activate_nvcc(opts.nvccPath) ;
  opts.verbose && fprintf('%s:\tCUDA: using NVCC ''%s'' (%d).\n', ...
                          mfilename, opts.nvccPath, cuver) ;

  % Set the CUDA arch string (select GPU architecture)
  if isempty(opts.cudaArch), opts.cudaArch = get_cuda_arch(opts) ; end
  opts.verbose && fprintf('%s:\tCUDA: NVCC architecture string: ''%s''.\n', ...
                          mfilename, opts.cudaArch) ;
end

if opts.enableCudnn
  opts.cudnnIncludeDir = fullfile(opts.cudnnRoot, 'include') ;
  switch arch
    case 'win64', opts.cudnnLibDir = fullfile(opts.cudnnRoot, 'lib', 'x64') ;
    case 'maci64', opts.cudnnLibDir = fullfile(opts.cudnnRoot, 'lib') ;
    case 'glnxa64', opts.cudnnLibDir = fullfile(opts.cudnnRoot, 'lib64') ;
    otherwise, error('Unsupported architecture ''%s''.', arch) ;
  end
end

% --------------------------------------------------------------------
%                                                     Compiler options
% --------------------------------------------------------------------

% Build directories
mex_dir = fullfile(root, 'matlab', 'mex') ;
bld_dir = fullfile(mex_dir, '.build');
if ~exist(fullfile(bld_dir,'bits','impl'), 'dir')
  mkdir(fullfile(bld_dir,'bits','impl')) ;
end

% Compiler flags
flags.cc = {} ;
flags.ccpass = {} ;
flags.ccoptim = {} ;
flags.link = {} ;
flags.linklibs = {} ;
flags.linkpass = {} ;
flags.nvccpass = {char(opts.cudaArch)} ;

if opts.verbose > 1
  flags.cc{end+1} = '-v' ;
end
if opts.debug
  flags.cc{end+1} = '-g' ;
  flags.nvccpass{end+1} = '-O0' ;
else
  flags.cc{end+1} = '-DNDEBUG' ;
  flags.nvccpass{end+1} = '-O3' ;
end
if opts.enableGpu
  flags.cc{end+1} = '-DENABLE_GPU' ;
end
if opts.enableCudnn
  flags.cc{end+1} = '-DENABLE_CUDNN' ;
  flags.cc{end+1} = ['-I"' opts.cudnnIncludeDir '"'] ;
end
if opts.enableDouble
  flags.cc{end+1} = '-DENABLE_DOUBLE' ;
end
flags.link{end+1} = '-lmwblas' ;
switch arch
  case {'maci64'}
  case {'glnxa64'}
    flags.linklibs{end+1} = '-lrt' ;
  case {'win64'}
    % VisualC does not pass this even if available in the CPU architecture
    flags.cc{end+1} = '-D__SSSE3__' ;
end

if opts.enableImreadJpeg
  flags.cc = horzcat(flags.cc, opts.imageLibraryCompileFlags) ;
  flags.linklibs = horzcat(flags.linklibs, opts.imageLibraryLinkFlags) ;
end

if opts.enableGpu
  flags.link = horzcat(flags.link, {['-L"' opts.cudaLibDir '"'], '-lcudart', '-lcublas'}) ;
  switch arch
    case {'maci64', 'glnxa64'}
      flags.link{end+1} = '-lmwgpu' ;
    case 'win64'
      flags.link{end+1} = '-lgpu' ;
  end
  if opts.enableCudnn
    flags.link{end+1} = ['-L"' opts.cudnnLibDir '"'] ;
    flags.link{end+1} = '-lcudnn' ;
  end
end

switch arch
  case {'maci64'}
    flags.ccpass{end+1} = '-mmacosx-version-min=10.9' ;
    flags.linkpass{end+1} = '-mmacosx-version-min=10.9' ;
    flags.ccoptim{end+1} = '-mssse3 -ffast-math' ;
    flags.nvccpass{end+1} = '-Xcompiler -fPIC' ;

    if opts.enableGpu
      flags.linkpass{end+1} = sprintf('-Wl,-rpath -Wl,"%s"', opts.cudaLibDir) ;
    end
    if opts.enableGpu && opts.enableCudnn
      flags.linkpass{end+1} = sprintf('-Wl,-rpath -Wl,"%s"', opts.cudnnLibDir) ;
    end

    if opts.enableGpu && cuver < 70000
      % CUDA prior to 7.0 on Mac require GCC libstdc++ instead of the native
      % clang libc++. This should go away in the future.
      flags.ccpass{end+1} = '-stdlib=libstdc++' ;
      flags.linkpass{end+1} = '-stdlib=libstdc++' ;
      if  ~verLessThan('matlab', '8.5.0')
        % Complicating matters, MATLAB 8.5.0 links to clang's libc++ by
        % default when linking MEX files overriding the option above. We
        % force it to use GCC libstdc++
        flags.linkpass{end+1} = '-L"$MATLABROOT/bin/maci64" -lmx -lmex -lmat -lstdc++' ;
      end
    end

  case {'glnxa64'}
    flags.ccoptim{end+1} = '-mssse3 -ftree-vect-loop-version -ffast-math -funroll-all-loops' ;
    flags.nvccpass{end+1} = '-Xcompiler -fPIC -D_FORCE_INLINES' ;

    if opts.enableGpu
      flags.linkpass{end+1} = sprintf('-Wl,-rpath -Wl,"%s"', opts.cudaLibDir) ;
    end
    if opts.enableGpu && opts.enableCudnn
      flags.linkpass{end+1} = sprintf('-Wl,-rpath -Wl,"%s"', opts.cudnnLibDir) ;
    end

  case {'win64'}
    flags.nvccpass{end+1} = '-Xcompiler /MD' ;
    cl_path = fileparts(check_clpath()); % check whether cl.exe in path
    flags.nvccpass{end+1} = sprintf('--compiler-bindir "%s"', cl_path) ;
end

% --------------------------------------------------------------------
%                                                        Command flags
% --------------------------------------------------------------------

flags.mexcc = horzcat(flags.cc, ...
                      {'-largeArrayDims'}, ...
                      {['CXXFLAGS=$CXXFLAGS ' strjoin(flags.ccpass)]}, ...
                      {['CXXOPTIMFLAGS=$CXXOPTIMFLAGS ' strjoin(flags.ccoptim)]}) ;
if ~ispc, flags.mexcc{end+1} = '-cxx'; end

% mex: compile GPU
flags.mexcu= horzcat({'-f' mex_cuda_config(root)}, ...
                     flags.cc, ...
                     {'-largeArrayDims'}, ...
                     {['CXXFLAGS=$CXXFLAGS ' quote_nvcc(flags.ccpass) ' ' strjoin(flags.nvccpass)]}, ...
                     {['CXXOPTIMFLAGS=$CXXOPTIMFLAGS ' quote_nvcc(flags.ccoptim)]}) ;

% mex: link
flags.mexlink = horzcat(flags.cc, flags.link, ...
                        {'-largeArrayDims'}, ...
                        {['LDFLAGS=$LDFLAGS ', strjoin(flags.linkpass)]}, ...
                        {['LINKLIBS=', strjoin(flags.linklibs), ' $LINKLIBS']}) ;

% nvcc: compile GPU
flags.nvcc = horzcat(flags.cc, ...
                     {opts.cudaArch}, ...
                     {sprintf('-I"%s"', fullfile(matlabroot, 'extern', 'include'))}, ...
                     {sprintf('-I"%s"', fullfile(matlabroot, 'toolbox','distcomp','gpu','extern','include'))}, ...
                     {quote_nvcc(flags.ccpass)}, ...
                     {quote_nvcc(flags.ccoptim)}, ...
                     flags.nvccpass) ;

if opts.verbose
  fprintf('%s: * Compiler and linker configurations *\n', mfilename) ;
  fprintf('%s: \tintermediate build products directory: %s\n', mfilename, bld_dir) ;
  fprintf('%s: \tMEX files: %s/\n', mfilename, mex_dir) ;
  fprintf('%s: \tMEX options [CC CPU]: %s\n', mfilename, strjoin(flags.mexcc)) ;
  fprintf('%s: \tMEX options [LINK]: %s\n', mfilename, strjoin(flags.mexlink)) ;
end
if opts.verbose && opts.enableGpu
  fprintf('%s: \tMEX options [CC GPU]: %s\n', mfilename, strjoin(flags.mexcu)) ;
end
if opts.verbose && opts.enableGpu && strcmp(opts.cudaMethod,'nvcc')
  fprintf('%s: \tNVCC options [CC GPU]: %s\n', mfilename, strjoin(flags.nvcc)) ;
end
if opts.verbose && opts.enableImreadJpeg
  fprintf('%s: * Reading images *\n', mfilename) ;
  fprintf('%s: \tvl_imreadjpeg enabled\n', mfilename) ;
  fprintf('%s: \timage library: %s\n', mfilename, opts.imageLibrary) ;
  fprintf('%s: \timage library compile flags: %s\n', mfilename, strjoin(opts.imageLibraryCompileFlags)) ;
  fprintf('%s: \timage library link flags: %s\n', mfilename, strjoin(opts.imageLibraryLinkFlags)) ;
end

% --------------------------------------------------------------------
%                                                              Compile
% --------------------------------------------------------------------

% Intermediate object files
srcs = horzcat(lib_src,mex_src) ;
for i = 1:numel(horzcat(lib_src, mex_src))
  [~,~,ext] = fileparts(srcs{i}) ; ext(1) = [] ;
  objfile = toobj(bld_dir,srcs{i});
  if strcmp(ext,'cu')
    if strcmp(opts.cudaMethod,'nvcc')
      nvcc_compile(opts, srcs{i}, objfile, flags.nvcc) ;
    else
      mex_compile(opts, srcs{i}, objfile, flags.mexcu) ;
    end
  else
    mex_compile(opts, srcs{i}, objfile, flags.mexcc) ;
  end
  assert(exist(objfile, 'file') ~= 0, 'Compilation of %s failed.', objfile);
end

% Link into MEX files
for i = 1:numel(mex_src)
  objs = toobj(bld_dir, [mex_src(i), lib_src]) ;
  mex_link(opts, objs, mex_dir, flags.mexlink) ;
end

% Reset path adding the mex subdirectory just created
vl_setupnn() ;

% --------------------------------------------------------------------
%                                                    Utility functions
% --------------------------------------------------------------------

% --------------------------------------------------------------------
function objs = toobj(bld_dir,srcs)
% --------------------------------------------------------------------
str = fullfile('matlab','src') ;
multiple = iscell(srcs) ;
if ~multiple, srcs = {srcs} ; end
objs = cell(1, numel(srcs));
for t = 1:numel(srcs)
  i = strfind(srcs{t},str);
  objs{t} = fullfile(bld_dir, srcs{t}(i+numel(str):end)) ;
end
if ~multiple, objs = objs{1} ; end
objs = regexprep(objs,'.cpp$',['.' objext]) ;
objs = regexprep(objs,'.cu$',['.' objext]) ;
objs = regexprep(objs,'.c$',['.' objext]) ;

% --------------------------------------------------------------------
function mex_compile(opts, src, tgt, mex_opts)
% --------------------------------------------------------------------
mopts = {'-outdir', fileparts(tgt), src, '-c', mex_opts{:}} ;
opts.verbose && fprintf('%s: MEX CC: %s\n', mfilename, strjoin(mopts)) ;
mex(mopts{:}) ;

% --------------------------------------------------------------------
function nvcc_compile(opts, src, tgt, nvcc_opts)
% --------------------------------------------------------------------
nvcc_path = fullfile(opts.cudaRoot, 'bin', 'nvcc');
nvcc_cmd = sprintf('"%s" -c "%s" %s -o "%s"', ...
                   nvcc_path, src, ...
                   strjoin(nvcc_opts), tgt);
opts.verbose && fprintf('%s: NVCC CC: %s\n', mfilename, nvcc_cmd) ;
status = system(nvcc_cmd);
if status, error('Command %s failed.', nvcc_cmd); end;

% --------------------------------------------------------------------
function mex_link(opts, objs, mex_dir, mex_flags)
% --------------------------------------------------------------------
mopts = {'-outdir', mex_dir, mex_flags{:}, objs{:}} ;
opts.verbose && fprintf('%s: MEX LINK: %s\n', mfilename, strjoin(mopts)) ;
mex(mopts{:}) ;

% --------------------------------------------------------------------
function ext = objext()
% --------------------------------------------------------------------
% Get the extension for an 'object' file for the current computer
% architecture
switch computer('arch')
  case 'win64', ext = 'obj';
  case {'maci64', 'glnxa64'}, ext = 'o' ;
  otherwise, error('Unsupported architecture %s.', computer) ;
end

% --------------------------------------------------------------------
function conf_file = mex_cuda_config(root)
% --------------------------------------------------------------------
% Get mex CUDA config file
mver = [1e4 1e2 1] * sscanf(version, '%d.%d.%d') ;
if mver <= 80200, ext = 'sh' ; else ext = 'xml' ; end
arch = computer('arch') ;
switch arch
  case {'win64'}
    config_dir = fullfile(matlabroot, 'toolbox', ...
                          'distcomp', 'gpu', 'extern', ...
                          'src', 'mex', arch) ;
  case {'maci64', 'glnxa64'}
    config_dir = fullfile(root, 'matlab', 'src', 'config') ;
end
conf_file = fullfile(config_dir, ['mex_CUDA_' arch '.' ext]);
fprintf('%s:\tCUDA: MEX config file: ''%s''\n', mfilename, conf_file);

% --------------------------------------------------------------------
function cl_path = check_clpath()
% --------------------------------------------------------------------
% Checks whether the cl.exe is in the path (needed for the nvcc). If
% not, tries to guess the location out of mex configuration.
cc = mex.getCompilerConfigurations('c++');
if isempty(cc)
  error(['Mex is not configured.'...
    'Run "mex -setup" to configure your compiler. See ',...
    'http://www.mathworks.com/support/compilers ', ...
    'for supported compilers for your platform.']);
end
cl_path = fullfile(cc.Location, 'VC', 'bin', 'amd64');
[status, ~] = system('cl.exe -help');
if status == 1
  warning('CL.EXE not found in PATH. Trying to guess out of mex setup.');
  prev_path = getenv('PATH');
  setenv('PATH', [prev_path ';' cl_path]);
  status = system('cl.exe');
  if status == 1
    setenv('PATH', prev_path);
    error('Unable to find cl.exe');
  else
    fprintf('Location of cl.exe (%s) successfully added to your PATH.\n', ...
      cl_path);
  end
end

% -------------------------------------------------------------------------
function paths = which_nvcc()
% -------------------------------------------------------------------------
switch computer('arch')
  case 'win64'
    [~, paths] = system('where nvcc.exe');
    paths = strtrim(paths);
    paths = paths(strfind(paths, '.exe'));
  case {'maci64', 'glnxa64'}
    [~, paths] = system('which nvcc');
    paths = strtrim(paths) ;
end

% -------------------------------------------------------------------------
function cuda_root = search_cuda_devkit(opts)
% -------------------------------------------------------------------------
% This function tries to to locate a working copy of the CUDA Devkit.

opts.verbose && fprintf(['%s:\tCUDA: searching for the CUDA Devkit' ...
                    ' (use the option ''CudaRoot'' to override):\n'], mfilename);

% Propose a number of candidate paths for NVCC
paths = {getenv('MW_NVCC_PATH')} ;
paths = [paths, which_nvcc()] ;
for v = {'5.5', '6.0', '6.5', '7.0', '7.5', '8.0', '8.5', '9.0'}
  switch computer('arch')
    case 'glnxa64'
      paths{end+1} = sprintf('/usr/local/cuda-%s/bin/nvcc', char(v)) ;
    case 'maci64'
      paths{end+1} = sprintf('/Developer/NVIDIA/CUDA-%s/bin/nvcc', char(v)) ;
    case 'win64'
      paths{end+1} = sprintf('C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v%s\\bin\\nvcc.exe', char(v)) ;
  end
end
paths{end+1} = sprintf('/usr/local/cuda/bin/nvcc') ;

% Validate each candidate NVCC path
for i=1:numel(paths)
  nvcc(i).path = paths{i} ;
  [nvcc(i).isvalid, nvcc(i).version] = validate_nvcc(paths{i}) ;
end
if opts.verbose
  fprintf('\t| %5s | %5s | %-70s |\n', 'valid', 'ver', 'NVCC path') ;
  for i=1:numel(paths)
    fprintf('\t| %5d | %5d | %-70s |\n', ...
            nvcc(i).isvalid, nvcc(i).version, nvcc(i).path) ;
  end
end

% Pick an entry
index = find([nvcc.isvalid]) ;
if isempty(index)
  error('Could not find a valid NVCC executable\n') ;
end
[~, newest] = max([nvcc(index).version]);
nvcc = nvcc(index(newest)) ;
cuda_root = fileparts(fileparts(nvcc.path)) ;

if opts.verbose
  fprintf('%s:\tCUDA: choosing NVCC compiler ''%s'' (version %d)\n', ...
          mfilename, nvcc.path, nvcc.version) ;
end

% -------------------------------------------------------------------------
function [valid, cuver]  = validate_nvcc(nvccPath)
% -------------------------------------------------------------------------
[status, output] = system(sprintf('"%s" --version', nvccPath)) ;
valid = (status == 0) ;
if ~valid
  cuver = 0 ;
  return ;
end
match = regexp(output, 'V(\d+\.\d+\.\d+)', 'match') ;
if isempty(match), valid = false ; return ; end
cuver = [1e4 1e2 1] * sscanf(match{1}, 'V%d.%d.%d') ;

% --------------------------------------------------------------------
function cuver = activate_nvcc(nvccPath)
% --------------------------------------------------------------------

% Validate the NVCC compiler installation
[valid, cuver] = validate_nvcc(nvccPath) ;
if ~valid
  error('The NVCC compiler ''%s'' does not appear to be valid.', nvccPath) ;
end

% Make sure that NVCC is visible by MEX by setting the MW_NVCC_PATH
% environment variable to the NVCC compiler path
if ~strcmp(getenv('MW_NVCC_PATH'), nvccPath)
  warning('Setting the ''MW_NVCC_PATH'' environment variable to ''%s''', nvccPath) ;
  setenv('MW_NVCC_PATH', nvccPath) ;
end

% In some operating systems and MATLAB versions, NVCC must also be
% available in the command line search path. Make sure that this is%
% the case.
[valid_, cuver_] = validate_nvcc('nvcc') ;
if ~valid_ || cuver_ ~= cuver
  warning('NVCC not found in the command line path or the one found does not matches ''%s''.', nvccPath);
  nvccDir = fileparts(nvccPath) ;
  prevPath = getenv('PATH') ;
  switch computer
    case 'PCWIN64', separator = ';' ;
    case {'GLNXA64', 'MACI64'}, separator = ':' ;
  end
  setenv('PATH', [nvccDir separator prevPath]) ;
  [valid_, cuver_] = validate_nvcc('nvcc') ;
  if ~valid_ || cuver_ ~= cuver
    setenv('PATH', prevPath) ;
    error('Unable to set the command line path to point to ''%s'' correctly.', nvccPath) ;
  else
    fprintf('Location of NVCC (%s) added to your command search PATH.\n', nvccDir) ;
  end
end

% -------------------------------------------------------------------------
function str = quote_nvcc(str)
% -------------------------------------------------------------------------
if iscell(str), str = strjoin(str) ; end
str = strrep(strtrim(str), ' ', ',') ;
if ~isempty(str), str = ['-Xcompiler ' str] ; end

% --------------------------------------------------------------------
function cudaArch = get_cuda_arch(opts)
% --------------------------------------------------------------------
opts.verbose && fprintf('%s:\tCUDA: determining GPU compute capability (use the ''CudaArch'' option to override)\n', mfilename);
try
  gpu_device = gpuDevice();
  arch_code = strrep(gpu_device.ComputeCapability, '.', '');
  cudaArch = ...
      sprintf('-gencode=arch=compute_%s,code=\\\"sm_%s,compute_%s\\\" ', ...
              arch_code, arch_code, arch_code) ;
catch
  opts.verbose && fprintf(['%s:\tCUDA: cannot determine the capabilities of the installed GPU; ' ...
                      'falling back to default\n'], mfilename);
  cudaArch = opts.defCudaArch;
end


function [metabolite_structure,IDsAdded,InchiKeyList,InchiStringList] = generateInchiFromMol(metabolite_structure,folder, inchiKey, smiles,formula,startSearch,endSearch)
% This function adds inchistrings to the metabolite structure entries if
% mol files are available (in folder) and no inchistring is present in
% metabolite structure for a given entry. It also calculated inchikeys and
% smiles if desired.
%
% INPUT
% metabolite_structure  metabolite structure
% folder                folder containing the mol files
% inchiKey              if 1 (default), then inchiKey will be generated
%                       from mol file
% smiles                if 1 (default), then smiles will be generated
%                       from mol file
% formula               if 1 (default), then inchiKey will be generated
%                       from inchistring file
% startSearch           specify where the search should start in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
% endSearch             specify where the search should end in the
%                       metabolite structure. Must be numeric (optional, default: all metabolites
%                       in the structure will be search for)
%
% OUTPUT
% metabolite_structure  Updated metabolite structure
% IDsAddeed             List of IDs added to the metabolite structure
% InchiKeyList          List of inchikeys added
% InchiStringList       List of inchistrings added
%
%
% Ines Thiele, 2020-2021

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

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

% folder    folder containing all mol files
annotationSource = ['Mol files from ' folder];
annotationType = 'automatic';

Mets = fieldnames(metabolite_structure);

if ~exist('startSearch','var')
    startSearch = 1;
end
if ~exist('endSearch','var')
    endSearch = length(Mets);
end
cnt = 1;
for i = startSearch : endSearch

    VMHId{cnt,1} = Mets{i};
    if isfield(metabolite_structure.(Mets{i}),'VMHId')
        VMHId{cnt,2} = metabolite_structure.(Mets{i}).VMHId;
    else
        VMHId{cnt,2} = Mets{i};
        VMHId{cnt,2} =regexprep(  VMHId{i,2},'VMH_','');
        VMHId{cnt,2} =regexprep(  VMHId{i,2},'__','_');
    end
    cnt = cnt + 1;
end
a = 1;
IDsAdded = ''; a =  1;
InchiStringList = ''; b =1;
InchiKeyList = ''; c= 1;
%folder = 'FileDumps/molFiles/molFiles_AGORA2/';
files = dir(folder);
space =' ';

% this could be accelerated
for i = 1 :length(files)
    met = regexprep(files(i).name,'.mol','');
    match = VMHId(find(ismember(VMHId(:,2),met),1),1);

    if contains(files(i).name,'.mol') && ~isempty(match)

        metabolite_structure.(match{1}).hasmolfile = 1;
        metabolite_structure.(match{1}).hasmolfile_source = [annotationSource,':',annotationType,':',datestr(now)];

        if isempty(metabolite_structure.(match{1}).inchiString) || length(find(isnan(metabolite_structure.(match{1}).inchiString)))>0
            if ismac
                [status, result]=system(strcat('/usr/local/bin/obabel',[space folder filesep files(i).name],' -o inchi -h' ));
            elseif ispc % pc
                [status, result]=system(strcat('obabel',[space folder filesep files(i).name],' -o inchi -h' ));
            elseif isunix
                [status, result]=system(strcat('obabel',[space folder filesep files(i).name],' -o inchi -h' ));
            end

            if contains(result,'1 molecule converted')
                %result = regexprep(result,'1 molecule converted','');
                [tok,rem] = strtok(result,'=');
                result = regexprep(rem,'1 molecule converted','');
                result = regexprep(result,'\n','');
                if strfind(result,'==')
                    tokr = split(result,'==');
                    result = tokr{1};
                end
                if ~isempty(result)
                     inchiString = strcat('InChI',result);
                else
                    inchiString = {};
                end
                % find metabolite in metabolite_structure
                if ~isempty(inchiString)
                    inchiString = strcat('InChI',result);
                    metabolite_structure.(match{1}).inchiString = inchiString;
                    metabolite_structure.(match{1}).inchiString_source = [annotationSource,':',annotationType,':',datestr(now)];
                    IDsAdded{a,1} = match{1};
                    IDsAdded{a,2} = 'inchiString';
                    IDsAdded{a,3} = metabolite_structure.(match{1}).inchiString;
                    a = a + 1;

                    InchiStringList{b,1} = match{1};
                    InchiStringList{b,2} = strcat(folder,files(i).name);
                    InchiStringList{b,3} = metabolite_structure.(match{1}).inchiString;
                    InchiStringList{b,4} = metabolite_structure.(match{1}).inchiString_source;
                    b = b + 1;
                end

                if inchiKey
                    if(isempty(metabolite_structure.(match{1}).inchiKey) || length(find(isnan(metabolite_structure.(match{1}).inchiKey)))>0)
                        if    ~isempty(metabolite_structure.(match{1}).inchiString) &&  length(find(isnan(metabolite_structure.(match{1}).inchiString)))==0
                            inchiString =  metabolite_structure.(match{1}).inchiString;
                            [result] = convertInchiString2format(inchiString,'inchiKey');
                            metabolite_structure.(match{1}).inchiKey = result;
                            metabolite_structure.(match{1}).inchiKey_source = [annotationSource,':',annotationType,':',datestr(now)];
                            IDsAdded{a,1} = match{1};
                            IDsAdded{a,2} = 'inchiKey';
                            IDsAdded{a,3} = metabolite_structure.(match{1}).inchiKey;
                            a = a + 1;

                            InchiKeyList{c,1} = match{1};
                            InchiKeyList{c,2} = strcat(folder,files(i).name);
                            InchiKeyList{c,3} = metabolite_structure.(match{1}).inchiKey;
                            InchiKeyList{c,4} = metabolite_structure.(match{1}).inchiKey_source;
                            c = c + 1;
                        end
                    end
                end
                if smiles
                    if (isempty(metabolite_structure.(match{1}).smile) || length(find(isnan(metabolite_structure.(match{1}).smile)))>0)
                        if ~isempty(metabolite_structure.(match{1}).inchiString) &&  length(find(isnan(metabolite_structure.(match{1}).inchiString)))==0
                            inchiString =  metabolite_structure.(match{1}).inchiString;

                            [result] = convertInchiString2format(inchiString,'smiles');
                            result = regexprep(result,'\W$','');
                            metabolite_structure.(match{1}).smile = result;
                            metabolite_structure.(match{1}).smile_source = [annotationSource,':',annotationType,':',datestr(now)];
                            IDsAdded{a,1} = match{1};
                            IDsAdded{a,2} = 'smiles';
                            IDsAdded{a,3} = metabolite_structure.(match{1}).smile;
                            a = a + 1;
                        end
                    end
                end
            end
        end
    end
end
if formula
    [metabolite_structure] = addMetFormulaCharge(metabolite_structure,startSearch,endSearch);
end


function varargout = GUIEditSegyHeader(varargin)
% GUIEditSegyHeader : GUI for editing the SGY header
%
% Call :
%   SH=GUIEditSegyHeader(SH);
%
%
% Example :
%   [Data,STH,SH]=ReadSegy('841_m.sgy');
%   SH=GUIEditSegyHeader(SH);
%
%

% GUIEDITSEGYHEADER Application M-file for GUIEditSegyTraceHeader.fig
%    FIG = GUIEDITSEGYHEADER launch GUIEditSegyTraceHeader GUI.
%    GUIEDITSEGYHEADER('callback_name', ...) invoke the named callback.

% Last Modified by GUIDE v2.5 03-Mar-2003 15:33:07

if (nargin == 0)|(isstruct(varargin{1}))  % LAUNCH GUI

	fig = openfig(mfilename,'reuse');

	% Use system color scheme for figure:
	set(fig,'Color',get(0,'defaultUicontrolBackgroundColor'));

	% Generate a structure of handles to pass to callbacks, and store it.
	handles = guihandles(fig);
	guidata(fig, handles);

    if nargin>0,
       data = guihandles(fig); % initialize it to contain handles
       data.SegyHeader=varargin{1};
       data.SegyHeaderOrig=varargin{1};
       guidata(fig,data);

       set(fig,'HandleVisibility','On')
       GUIEditSegyHeader('actionSetHeader',fig,handles)
       GUIEditSegyHeader('actionUpdateHeader',fig,handles)
       set(fig,'HandleVisibility','CallBack')

    end

	% Wait for callbacks to run and window to be dismissed:
	uiwait(fig);

	if nargout > 0
	  data=guidata(fig);
	  if isstruct(data.SegyHeader)
	    varargout{1}=data.SegyHeader;
	  else
	    varargout{1} = fig;
	  end
    end

    close(handles.figure1);
	set(handles.figure1,'Visible','off')

elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK

	try
		if (nargout)
			[varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
		else
			feval(varargin{:}); % FEVAL switchyard
		end
	catch
		disp(lasterr);
	end

end

%| ABOUT CALLBACKS:
%| GUIDE automatically appends subfunction prototypes to this file, and
%| sets objects' callback properties to call them through the FEVAL
%| switchyard above. This comment describes that mechanism.
%|
%| Each callback subfunction declaration has the following form:
%| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN)
%|
%| The subfunction name is composed using the object's Tag and the
%| callback type separated by '_', eSweepFrequencyStart.g. 'slider2_Callback',
%| 'figure1_CloseRequestFcn', 'axis1_ButtondownFcn'.
%|
%| H is the callback object's handle (obtained using GCBO).
%|
%| EVENTDATA is empty, but reserved for future use.
%|
%| HANDLES is a structure containing handles of components in GUI using
%| tags as fieldnames, eSweepFrequencyStart.g. handles.figure1, handles.slider2. This
%| structure is created at GUI startup using GUIHANDLES and stored in
%| the figure's application data using GUIDATA. A copy of the structure
%| is passed to each callback.  You can store additional information in
%| this structure at GUI startup, and you can change the structure
%| during callbacks.  Call guidata(h, handles) after changing your
%| copy to replace the stored original so that subsequent callbacks see
%| the updates. Type "help guihandles" and "help guidata" for more
%| information.
%|
%| VARARGIN contains any extra arguments you have passed to the
%| callback. Specify the extra arguments by editing the callback
%| property in the inspector. By default, GUIDE sets the property to:
%| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo))
%| Add any extra arguments after the last argument, before the final
%| closing parenthesis.

% --------------------------------------------------------------------

% --------------------------------------------------------------------
function varargout = figure1_ResizeFcn(h, eventdata, handles, varargin)

% --------------------------------------------------------------------
function varargout = pbDone_Callback(h, eventdata, handles, varargin)
uiresume;

% --------------------------------------------------------------------
function varargout = pbCancel_Callback(h, eventdata, handles, varargin)
data=guidata(h);
data.SegyHeader=data.SegyHeaderOrig;
guidata(h,data);
uiresume;

clear name;
function actionSetHeader(h,handles)
data=guidata(h);
SH=data.SegyHeader;

% Revision
revision=SH.SegyFormatRevisionNumber;
r=1; % Default old revisoin
if revision>50, r=2;end
if revision==0, r=1;end
for i=1:length(SH.Rev), Revision{i}=SH.Rev(i).name;end
set(handles.popSegyFormatRevision,'String',Revision);

clear name;
% DataSampleFormat
DSF=SH.Rev(r).DataSampleFormat;
for i=1:length(DSF), name{i}=DSF(i).name;end
set(handles.popDataSampleFormat,'String',name);

clear name;
% TraceSorting
TS=SH.Rev(r).TraceSorting;
for i=1:length(TS), name{i}=TS(i).name;end
set(handles.popTraceSorting,'String',name);

clear name;
% Sweep Type
ST=SH.Rev(r).SweepType;
for i=1:length(ST), name{i}=ST(i).name;end
set(handles.popSweepType,'String',name);

clear name;
% BinaryGain
BG=SH.Rev(r).BinaryGain;
for i=1:length(BG), name{i}=BG(i).name;end
set(handles.popBinaryGain,'String',name);

clear name;
% TaperType
TT=SH.Rev(r).TaperType;
for i=1:length(TT), name{i}=TT(i).name;end
set(handles.popTaperType,'String',name);

clear name;
% CorrelatedDataTraces
CD=SH.Rev(r).CorrelatedDataTraces;
for i=1:length(CD), name{i}=CD(i).name;end
set(handles.popCorrelatedDataTraces,'String',name);

clear name;
% AmplitudeRecoveryMethod
ARM=SH.Rev(r).AmplitudeRecoveryMethod;
for i=1:length(ARM), name{i}=ARM(i).name;end
set(handles.popAmplitudeRecoveryMethod,'String',name);

clear name;
% MeasurementSystem
M=SH.Rev(r).MeasurementSystem;
for i=1:length(M), name{i}=M(i).name;end
set(handles.popMeasurementSystem,'String',name);

clear name;
% ImpulseSignalPolarity
ISR=SH.Rev(r).ImpulseSignalPolarity;
for i=1:length(ISR), name{i}=ISR(i).name;end
set(handles.popImpulseSignalPolarity,'String',name);

clear name;
% popVibratoryPolarityCode
VPC=SH.Rev(r).VibratoryPolarityCode;
for i=1:length(VPC), name{i}=VPC(i).name;end
set(handles.popVibratoryPolarityCode,'String',name);

clear name;
% popFixedLengthTraceFlag
FLT=SH.Rev(r).FixedLengthTraceFlag;
for i=1:length(FLT), name{i}=FLT(i).name;end
set(handles.popFixedLengthTraceFlag,'String',name);

% EDIT BOXES
set(handles.eJob,'String',SH.Job);
set(handles.eLine,'String',SH.Line);
set(handles.eReel,'String',SH.Reel);
set(handles.eDataTracePerEnsemble,'String',SH.DataTracePerEnsemble);
set(handles.eAuxiliaryTracePerEnsemble,'String',SH.AuxiliaryTracePerEnsemble);
set(handles.edt,'String',SH.dt);
set(handles.edtOrig,'String',SH.dtOrig);
set(handles.ens,'String',SH.ns);
set(handles.ensOrig,'String',SH.nsOrig);
set(handles.eEnsembleFold,'String',SH.EnsembleFold);
set(handles.eVerticalSumCode,'String',SH.VerticalSumCode);

set(handles.eSweepFrequencyStart,'String',SH.SweepFrequencyStart);
set(handles.eSweepFrequencyEnd,'String',SH.SweepFrequencyEnd);
set(handles.eSweepLength,'String',SH.SweepLength);
set(handles.eSweepChannel,'String',SH.SweepChannel);
set(handles.eSweepTaperlengthStart,'String',SH.SweepTaperlengthStart);
set(handles.eSweepTaperLengthEnd,'String',SH.SweepTaperLengthEnd);

%---------------------------------------------------------
function r=actionGetrevision(h);
data=guidata(h);
SH=data.SegyHeader;
% Revision
revision=SH.SegyFormatRevisionNumber;
r=1; % Default old revisoin
if revision>50, r=2;end
if revision==0, r=1;end

%---------------------------------------------------------
function actionUpdateHeader(h,handles)
data=guidata(h);

SH=data.SegyHeader;

% Revision
revision=SH.SegyFormatRevisionNumber;
r=1; % Default old revisoin
if revision>50, r=2;end
if revision==0, r=1;end
set(handles.popSegyFormatRevision,'value',r);

% DataSampleFormat
DSF=SH.Rev(r).DataSampleFormat;
if length(get(handles.popDataSampleFormat,'String'))<SH.DataSampleFormat,
    SH.DataSampleFormat=1;
    disp([mfilename,' : Using DataSampleFormat ',num2str(SH.DataSampleFormat)])
end
set(handles.popDataSampleFormat,'value',SH.DataSampleFormat);

% MAYBE THE NEXT LINES COULD BE USED FOR MORE HEADERS
% TraceSorting
TraceSortingSelect=1;
for i=1:length(SH.Rev(r).TraceSorting)
    if SH.TraceSorting==SH.Rev(r).TraceSorting(i).value;
        TraceSortingSelect=i;
        SH.TraceSorting;
    end
end

if TraceSortingSelect>length(get(handles.popTraceSorting,'String'));
    TraceSortingSelect=1;
end
set(handles.popTraceSorting,'value',TraceSortingSelect);

SweepTypeSelect=1;
for i=1:length(SH.Rev(r).SweepType)
    if SH.SweepType==SH.Rev(r).SweepType(i).value;
        SweepTypeSelect=i;
        SH.SweepType;
    end
end
if SweepTypeSelect>length(get(handles.popSweepType,'String'));
    SweepTypeSelect=1;
end
set(handles.popSweepType,'value',SweepTypeSelect);

data.SegyHeader=SH;
guidata(h,data)

% --------------------------------------------------------------------
function varargout = popSegyFormatRevision_Callback(h, eventdata, handles, varargin)
data=guidata(h);
sel=get(h,'value');
data.SegyHeader.SegyFormatRevisionNumber =  data.SegyHeader.Rev(sel).SegyFormatRevisionNumber;

guidata(h,data);
actionSetHeader(h,handles)
actionUpdateHeader(h,handles)

% --------------------------------------------------------------------
function varargout = popDataSampleFormat_Callback(h, eventdata, handles, varargin)
data=guidata(h);
sel=get(h,'value');
data.SegyHeader.DataSampleFormat=get(h,'value');
guidata(h,data);

% --------------------------------------------------------------------
function varargout = popTraceSorting_Callback(h, eventdata, handles, varargin)
data=guidata(h);
TraceSortingSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.TraceSorting=data.SegyHeader.Rev(r).TraceSorting(TraceSortingSelect).value;
guidata(h,data);

function popSweepType_Callback(h, eventdata, handles)
data=guidata(h);
SweepTypeSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.SweepType=data.SegyHeader.Rev(r).SweepType(SweepTypeSelect).value;
guidata(h,data);

function popTaperType_Callback(h, eventdata, handles)
data=guidata(h);
TaperTypeSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.TaperType=data.SegyHeader.Rev(r).TaperType(TaperTypeSelect).value;
guidata(h,data);
actionUpdateHeader(h,handles)

function popCorrelatedDataTraces_Callback(h, eventdata, handles)
data=guidata(h);
CorrelatedDataTracesSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.CorrelatedDataTraces=data.SegyHeader.Rev(r).CorrelatedDataTraces(CorrelatedDataTracesSelect).value;
guidata(h,data);

function popBinaryGain_Callback(h, eventdata, handles)
data=guidata(h);
BinaryGainSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.BinaryGain=data.SegyHeader.Rev(r).BinaryGain(BinaryGainSelect).value;
guidata(h,data);

function popAmplitudeRecoveryMethod_Callback(h, eventdata, handles)
data=guidata(h);
AmplitudeRecoveryMethodSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.AmplitudeRecoveryMethod=data.SegyHeader.Rev(r).AmplitudeRecoveryMethod(AmplitudeRecoveryMethodSelect).value;
guidata(h,data);

function varargout = popMeasurementSystem_Callback(h, eventdata, handles, varargin)
data=guidata(h);
MSSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.MeasurementSystem=data.SegyHeader.Rev(r).MeasurementSystem(MSSelect).value;
guidata(h,data);

function popImpulseSignalPolarity_Callback(h, eventdata, handles)
data=guidata(h);
ImpulseSignalPolaritySelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.ImpulseSignalPolarity=data.SegyHeader.Rev(r).ImpulseSignalPolarity(ImpulseSignalPolaritySelect).value;
guidata(h,data);

function popVibratoryPolarityCode_Callback(h, eventdata, handles)
data=guidata(h);
VibratoryPolarityCodeSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.VibratoryPolarityCode=data.SegyHeader.Rev(r).VibratoryPolarityCode(VibratoryPolarityCodeSelect).value;
guidata(h,data);

% --- Executes on selection change in popFixedLengthTraceFlag.
function popFixedLengthTraceFlag_Callback(h, eventdata, handles)
data=guidata(h);
FixedLengthTraceFlagSelect=get(h,'value');
r=actionGetrevision(h);
data.SegyHeader.FixedLengthTraceFlag=data.SegyHeader.Rev(r).FixedLengthTraceFlag(FixedLengthTraceFlagSelect).value;
guidata(h,data);

% --- Executes on button press in pbEditTextualFileHeader.
function pbEditTextualFileHeader_Callback(h, eventdata, handles)

data=guidata(h);
data.SegyHeader=GUIEditTextualFileHeader(data.SegyHeader);
guidata(h,data);

function eJob_Callback(hObject, eventdata, handles)

function eLine_Callback(hObject, eventdata, handles)

function eReel_Callback(hObject, eventdata, handles)

function eDataTracePerEnsemble_Callback(hObject, eventdata, handles)

function eAuxiliaryTracePerEnsemble_Callback(hObject, eventdata, handles)

function edt_Callback(hObject, eventdata, handles)

function edtOrig_Callback(hObject, eventdata, handles)

% --- Executes during object creation, after setting all properties.
function ens_CreateFcn(hObject, eventdata, handles)

% --- Executes on button press in ens.
function ens_Callback(hObject, eventdata, handles)

% --- Executes on button press in ensOrig.
function ensOrig_Callback(hObject, eventdata, handles)

% --- Executes on button press in eEnsembleFold.
function eEnsembleFold_Callback(hObject, eventdata, handles)

% --- Executes on button press in eVerticalSumCode.
function eVerticalSumCode_Callback(hObject, eventdata, handles)

% --- Executes on button press in eSweepFrequencyStart.
function eSweepFrequencyStart_Callback(hObject, eventdata, handles)

function eSweepFrequencyEnd_Callback(hObject, eventdata, handles)

function eSweepLength_Callback(hObject, eventdata, handles)

function eSweepChannel_Callback(hObject, eventdata, handles)

function eSweepTaperlengthStart_Callback(hObject, eventdata, handles)

function eSweepTaperLengthEnd_Callback(hObject, eventdata, handles)


function s_out = structmerge(s1, s2, varargin)
% STRUCTMERGE - Merge struct variables into a common struct
%
%  S_OUT = STRUCTMERGE(S1, S2, ...)
%
%  Merges the structures S1 and S2 into a common structure S_OUT
%  such that S_OUT has all of the fields of S1 and S2. When
%  S1 and S2 share the same fieldname, the value of S2 is taken.
%
%  The behavior of the function can be altered by passing additional
%  arguments as name/value pairs.
%
%  Parameter (default)     | Description
%  ------------------------------------------------------------
%  ErrorIfNewField (0)     | (0/1) Is it an error if S2 contains a
%                          |  field that is not present in S1?
%
%  See also: STRUCT

ErrorIfNewField = 0;

assign(varargin{:});

f1 = fieldnames(s1);
f2 = fieldnames(s2);

if ErrorIfNewField,
	[c,f1i] = setdiff(f2,f1);
	if ~isempty(c),
		error(['Some fields of the second structure are not in the first: ' cell2str(c) '.']);
	end;
end;

s_out = s1;

for i=1:length(f2),
	s_out = setfield(s_out,f2{i},getfield(s2,f2{i}));
end;


function mr = mrCreateEmpty;
% Create an empty mr struct.
%
% mr = mrCreateEmpty;
%
% ras 07/05.
mr.data = [];
mr.name = '';
mr.path = '';
mr.hdr = [];
mr.info.scanner = '';
mr.info.subject = '';
mr.info.subjectSex = '';
mr.info.subjectAge = [];
mr.info.date = '';
mr.info.scanStart = [];
mr.info.examNum = [];
mr.info.protocol = '';
mr.info.coil = '';
mr.format = '';
mr.spaces = [];
mr.voxelSize = [];
mr.dims = [];
mr.extent = [];
mr.dimUnits = '';
mr.dataUnits = '';
mr.dataRange = [];
mr.phaseFlag = 0;  % by default, set to 1 otherwise
mr.params = [];
mr.comments = '';

return


%  M file that starts a Mosaic Browser displaying
%  the HTML version of the ZMAP Users Guide
%
%  Stefan Wiemer   6/96

%    !netscape /Seis/A/stefan/zmapwww/title.htm  &

report_this_filefun(mfilename('fullpath'));
disp('Attempting to open browser - please be patient...');
disp('If the browser does not open, please open the browser first and try again, or open the file ./zmapwww/title.hmt manually');
do = [ 'web ' hodi '/zmapwww/title.htm ;' ];
err=['errordlg('' Error while opening, please open the browser first and try again or open the file ./zmapwww/title.hmt manually'');'];
eval(do,err)


%% set up path

addpath ../misc;
addpath ../filehandling

%rootdatadir = '../experiments/wildtype/results';
% if ispc,
%   rootdatadir = 'C:\Data\JAABA\groundtruth_pBDPGAL4U_data';
% else
%   rootdatadir = '/groups/branson/bransonlab/projects/JAABA/data/groundtruth_pBDPGAL4U_data';
% end
rootdatadir='/groups/branson/home/riveraalbam/Desktop/experiments';
%% parameters

% experiment_names = {
%   'EXT_CSMH_None_Rig1Plate15BowlA_20120519T170213'
%   'FCF_attP2_1500062_None_Rig1Plate15BowlA_20120519T172815'
%   'FCF_cantons_1500002_None_Rig1Plate15BowlA_20120519T160453'
%   };
% experiment_names = {
%   'pBDPGAL4U_TrpA_Rig1Plate10BowlB_20110609T091905'
%   'pBDPGAL4U_TrpA_Rig1Plate15BowlD_20120210T152130'
%   'pBDPGAL4U_TrpA_Rig2Plate17BowlA_20120202T145723'
%   'pBDPGAL4U_TrpA_Rig2Plate17BowlC_20110811T133805'
%   'pBDPGAL4U_TrpA_Rig2Plate17BowlD_20111130T094443'
% };
% experiment_names = {
%   'GMR_14C07_AE_01_TrpA_Rig1Plate15BowlB_20120404T141159'
%   'GMR_14C07_AE_01_TrpA_Rig2Plate14BowlD_20110511T152004'
%   'GMR_34H05_AE_01_TrpA_Rig1Plate10BowlC_20110623T090005'
%   'GMR_34H05_AE_01_TrpA_Rig1Plate15BowlA_20120222T145008'
%   'GMR_71G01_AE_01_TrpA_Rig2Plate14BowlA_20110707T154658'
%   'GMR_71G01_AE_01_TrpA_Rig2Plate14BowlD_20110707T154929'
%   'GMR_71G01_AE_01_TrpA_Rig2Plate17BowlD_20110916T155353'
% };
experiment_names={'/CantonS/CantonSA' '/Dvirilis/virilisA' 'Dmercatorum/mercatorumA'}
expdirs = cellfun(@(x) fullfile(rootdatadir,x),experiment_names,'UniformOutput',false);

%I don't have these
% classifierparamsfiles = {'../experiments/JAABA_classifier_params1.txt'
%   '../experiments/JAABA_classifier_params2.txt'
%   '../experiments/JAABA_classifier_params_wing.txt'};
classifierparamsfiles={'/groups/branson/home/riveraalbam/Desktop/experiments/classifierslistNov25.txt'};
nintervals = 20;
nframesperseg = 500;
keyword = 'larvaspecies';
timestamp = datestr(now,'yyyymmddTHHMMSS');
figpos = [100,100,1280,720];
forcecompute = false;
%what is going to be written in the movie
% behaviorname_dict = {'pivot_tail','TailPivot'
%   'pivot_head','CenterPivot'
%   'AttemptedCopulation','AttemptedCop'};
behaviorname_dict = {'Crawling','Crawling' 'Head_casting','HeadCasting' 'Turning','Turning' 'Allburrows','Burrowing'};

% behaviororder = {
%   'WingFlick'
%   'Copulation'
%   'AttemptedCop'
%   'WingExtension'
%   'Chase'
%   'Touch'
%   'Righting'
%   'Jump'
%   'Walk'
%   'Crabwalk'
%   'CenterPivot'
%   'TailPivot'
%   'Backup'
%   'Stop'
%   'WingGrooming'
%   };
MakeJAABAResultsMovie(expdirs,classifierparamsfiles,...
   'nframesperseg',nframesperseg,...
   'targets',targets,...
   'framestarts',framestarts,...
   'aviname',aviname,...
    'behaviorname_dict',behaviorname_dict,...
   'fixedaxis','false');
   %'figpos',figpos,...

%% make sure all the scores are computed, up-to-date

% classifierparams = ReadClassifierParamsFile(classifierparamsfiles);
%
% nbehaviors = numel(classifierparams);
% dobreak = false;
% for i = 1:nbehaviors,
%   if dobreak,
%     break;
%   end
%   classifierfilecurr = classifierparams(i).classifierfile;
%   configfilecurr = classifierparams(i).configfile;
%   behavior = classifierparams(i).behaviors.names;
%   if iscell(behavior),
%     behavior = sprintf('%s_',behavior{:});
%     behavior = behavior(1:end-1);
%   end
%   tmp = load(classifierfilecurr);
%   classifier_timestamp = tmp.classifierTS;
%   for j = 1:numel(expdirs),
%     if dobreak,
%       break;
%     end
%     scoresfile_curr = fullfile(expdirs{j},classifierparams(i).file.scorefilename);
%     docompute = false;
%     if ~exist(scoresfile_curr,'file'),
%       fprintf('Scores file %s does not exist.\n',scoresfile_curr);
%       docompute = true;
%     else
%       tmp = load(scoresfile_curr);
%       scores_timestamp = tmp.timestamp;
%       if scores_timestamp ~= classifier_timestamp,
%         keyboard;
%         if forcecompute,
%           docompute = true;
%         else
%           if scores_timestamp > classifier_timestamp,
%             question = sprintf('Scores are newer than classifier for %s, %s. Recompute scores?',behavior,experiment_names{j});
%             def = 'No';
%           else
%             question = sprintf('Scores are older than classifier for %s, %s. Recompute scores?',behavior,experiment_names{j});
%             def = 'Yes';
%           end
%           res = questdlg(question,'Recompute?','Yes','No','Cancel',def);
%           if strcmpi(res,'Yes'),
%             docompute = true;
%           elseif strcmpi(res,'Cancel'),
%             dobreak = true;
%             break;
%           end
%         end
%       end
%     end
%
%     if docompute,
%       fprintf('Computing %s...\n',scoresfile_curr);
%       JAABADetect(expdirs{j},'classifierfiles',{classifierfilecurr},'configfiles',{configfilecurr});
%     end
%
%   end
%
% end

%% choose what to show

% %behaviorsuse = {'Chase','Jump','WingGrooming','Righting','Backup','pivot_tail','pivot_head','WingExtension','WingFlick','AttenptedCopulation'};
% %behaviorsuse = {'WingExtension','WingFlick','AttemptedCopulation','Copulation','pivot_head'};
% behaviorsuse={'Crawling','Head_casting','Turning', 'Allburrows'};
% weightingscheme = 'sumlogsumscore';
%
% [targets,expdirs_chosen,framestarts,nframesperseg] = ...
%   ChooseExampleIntervals(expdirs,classifierparamsfiles,...
%   'nintervals',nintervals,'nframesperseg',nframesperseg,...
%   'weightingscheme',weightingscheme);
%
% % sort based on expdir, then framestart
% maxframestart = max(framestarts);
% [~,~,expi] = unique(expdirs_chosen);
% tmp = expi'*10*maxframestart + framestarts;
% [~,order] = sort(tmp);
% targets = targets(order);
% expdirs_chosen = expdirs_chosen(order);
% framestarts = framestarts(order);
% [unique_expdirs,~,expi] = unique(expdirs_chosen);
%
% for i = 1:nintervals,
%   fprintf('%d: %s, fly %d, frame %d\n',i,expdirs_chosen{i},targets(i),framestarts(i));
% end

% %% make the movies
%
% avibasename = sprintf('JAABAResultsMovie_%s_%s',keyword,timestamp);
%
% for i = 1:nintervals,
%   [~,experiment_name] = fileparts(expdirs_chosen{i});
%   aviname = sprintf('%s_%02d_%s_%02d.avi',avibasename,i,experiment_name,targets(i));
%   MakeJAABAResultsMovie(expdirs_chosen{i},classifierparamsfiles,...
%     'nframesperseg',nframesperseg,...
%     'targets',targets(i),...
%     'framestarts',framestarts(i),...
%     'aviname',aviname,...
%     'figpos',figpos,...
%     'behaviorname_dict',behaviorname_dict);
% end
%
% %% BDP
%
% keyword = 'larvaspecies';
% expi = 2;
% expdir = expdirs{expi};
% avibasename = sprintf('JAABAResultsMovie_%s_%s',keyword,timestamp);
% targets = [9,18,3,17];
% framestarts = [9646,10500,14201,14650];
%
% [~,experiment_name] = fileparts(expdirs_chosen{expi});
% aviname = sprintf('%s_%02d_%s.avi',avibasename,expi,experiment_name);
% MakeJAABAResultsMovie(expdir,classifierparamsfiles,...
%   'nframesperseg',nframesperseg,...
%   'targets',targets,...
%   'framestarts',framestarts,...
%   'aviname',aviname,...
%   'figpos',figpos,...
%   'printnone',false,...
%   'behaviorname_dict',behaviorname_dict,...
%   'behaviororder',behaviororder,...
%   'textprefix','pBDPGAL4U',...
%   'textsuffix','1/3rd speed');
%
% %%
%
% keyword = 'larvaspecies';
% expi = 2;
% expi = 5;
% expdir = expdirs{expi};
% avibasename = sprintf('JAABAResultsMovie_%s_%s',keyword,timestamp);
% targets = [9,18,3,17];
% targets = [10,20];
% framestarts = [9646,10500,14201,14650];
% framestarts = [4396,7345];
%
% [~,experiment_name] = fileparts(expdir);
% aviname = sprintf('%s_%02d_%s.avi',avibasename,expi,experiment_name);
% MakeJAABAResultsMovie(expdir,classifierparamsfiles,...
%   'nframesperseg',nframesperseg,...
%   'targets',targets,...
%   'framestarts',framestarts,...
%   'aviname',aviname,...
%   'figpos',figpos,...
%   'printnone',false,...
%   'behaviorname_dict',behaviorname_dict,...
%   'behaviororder',behaviororder,...
%   'textprefix','R71G01',...
%   'textsuffix','1/6th speed');


classdef DataFilesClass < handle

    properties
        files;
        filesErr;
        filetype;
        dirFormats;
        err;
        errmsg;
        rootdir;
        config;
        nfiles;
        logger
    end

    properties (Access = private)
        lookupTable
    end

    methods

        % ----------------------------------------------------
        function obj = DataFilesClass(varargin)
            obj.files = FileClass.empty();
            obj.filesErr = FileClass.empty();
            obj.filetype = '';
            obj.rootdir = pwd;
            obj.nfiles = 0;
            obj.err = -1;
            obj.errmsg = {};
            obj.dirFormats = struct('type',0, 'choices',{{}});

            global logger
            global cfg

            logger = InitLogger(logger);
            cfg    = InitConfig(cfg);

            obj.logger = logger;
            obj.lookupTable = [];

            skipconfigfile = false;
            askToFixNameConflicts = [];

            if nargin==0
                return
            end
            if nargin==1
                obj.rootdir = varargin{1};
            end
            if nargin==2
                obj.rootdir = varargin{1};
                obj.filetype = varargin{2};
            end
            if nargin==3
                obj.rootdir = varargin{1};
                obj.filetype = varargin{2};
                if strcmp(varargin{3}, 'standalone')
                    skipconfigfile = true;
                end
            end
            if nargin==4
                obj.rootdir = varargin{1};
                obj.filetype = varargin{2};
                if strcmp(varargin{3}, 'standalone')
                    skipconfigfile = true;
                end
                askToFixNameConflicts = varargin{4};
            end

            if obj.filetype(1) == '.'
                obj.filetype(1) = '';
            end
            obj.rootdir = filesepStandard(obj.rootdir,'full');

            % Configuration parameters
            obj.config = struct('RegressionTestActive','','AskToFixNameConflicts',1);
            if skipconfigfile==false
                str = cfg.GetValue('Regression Test Active');
                if strcmp(str,'true')
                    obj.config.RegressionTestActive=true;
                else
                    obj.config.RegressionTestActive=false;
                end
            else
                obj.config.RegressionTestActive=false;
            end
            obj.config.SuppressErrorChecking = false;
            if ~isempty(askToFixNameConflicts)
                obj.config.AskToFixNameConflicts = askToFixNameConflicts;
            elseif strcmp(cfg.GetValue('Fix File Name Conflicts'), sprintf('don''t ask again'))
                obj.config.AskToFixNameConflicts = 0;
            end

            if ~obj.config.AskToFixNameConflicts
                obj.config.SuppressErrorChecking = true;
            end

            if nargin==0
                return;
            end

            obj.err = 0;

            obj.InitFolderFromats();
            obj.GetDataSet();
        end

        % -----------------------------------------------------------------------------------
        function GetDataSet(obj)
            if exist(obj.rootdir, 'dir')~=7
                error('Invalid subject folder: ''%s''', obj.rootdir);
            end

            for ii = 1:length(obj.dirFormats.choices)
                obj.InitLookupTable();
                obj.FindDataSet(ii);

                % Remove any files that cannot pass the basic test of loading
                % its data
                obj.ErrorCheck();
                if ~isempty(obj.files)
                    break
                end
            end

            obj.ErrorCheckFinal();
        end

        % -----------------------------------------------------------------------------------
        function InitFolderFromats(obj)
            obj.dirFormats.choices = {

                %%%% 1. Flat #1
                {
                ['*_run*.', obj.filetype];
                }

                %%%% 2. Flat #2
                {
                ['*.', obj.filetype];
                }

                %%%% 3. Subjects
                {
                '*';
                ['*.', obj.filetype];
                }

                %%%% 4. BIDS #1,    sub-<label>[_ses-<label>][_task-<label>][_run-<index>]_nirs.snirf
                {
                'sub-*';
                'ses-*';
                ['nirs/sub-*_run-*_nirs.', obj.filetype];
                }

                %%%% 5. BIDS #2
                {
                'sub-*';
                ['nirs/sub-*_run-*_nirs.', obj.filetype];
                }

                %%%% 6. BIDS #3
                {
                'sub-*';
                ['nirs/sub-*_*_nirs.', obj.filetype];
                }

                %%%% 7. BIDS #4
                {
                '*';
                ['nirs/sub-*_*_nirs.', obj.filetype];
                }

                %%%% 8. BIDS folder structure
                {
                'sub-*';
                'ses-*';
                ['nirs/sub-*_run-*_nirs.', obj.filetype];
                }

                %%%% 9. BIDS-like folder structure without file naming restrictions
                {
                'sub-*';
                'ses-*';
                ['nirs/*.', obj.filetype];
                }

                %%%% 10. BIDS-like folder structure without nirs sub-folder
                {
                'sub-*';
                'ses-*';
                ['*.', obj.filetype];
                }

                };
        end

        % ----------------------------------------------------
        function FindDataSet(obj, iFormat, iPattern, parentdir)
            if ~exist('iFormat','var')
                iFormat = 1;
            end
            if ~exist('iPattern','var')
                iPattern = 1;
            end
            if ~exist('parentdir','var')
                parentdir = obj.rootdir;
            end

            if iFormat > length(obj.dirFormats.choices)
                return
            end
            if iPattern > length(obj.dirFormats.choices{iFormat})
                return
            end
            parentdir = filesepStandard(parentdir);
            pattern = obj.dirFormats.choices{iFormat}{iPattern};

            dirs = mydir([parentdir, pattern], obj.rootdir);

            dirnamePrev = '';
            for ii = 1:length(dirs)
                if dirs(ii).IsEmpty()
                    continue;
                end
                if dirs(ii).IsFile()
                    if strcmp(pattern, '*')
                        continue
                    end
                    if includes(dirs(ii).name, obj.filetype)
                        if ~strcmp(dirs(ii).name, dirnamePrev)
                            obj.AddParentDirs(dirs(ii));
                        end
                        obj.AddFile(dirs(ii));
                    end

                    if obj.dirFormats.type == 0
                        obj.dirFormats.type = iFormat;
                    end
                elseif dirs(ii).IsDir()
                    obj.FindDataSet(iFormat, iPattern+1, [parentdir, dirs(ii).filename])
                end
                dirnamePrev = dirs(ii).name;
            end
        end

        % ----------------------------------------------------
        function AddParentDirs(obj, dirname)
            pathrel = getPathRelative([dirname.rootdir, dirname.name], obj.rootdir);
            subdirs = str2cell_fast(pathrel, {'/','\'});
            N = length(subdirs);
            for ii = 1:N-1
                if strcmp(subdirs{ii}, 'nirs')
                    continue;
                end
                pathrel2 = buildpathfrompathparts(subdirs(1:ii));
                if obj.SearchLookupTable(pathrel2)
                    continue;
                end
                obj.files(end+1) = FileClass([obj.rootdir, '/', pathrel2], obj.rootdir);
                obj.AddLookupTable(obj.files(end).name)
            end
        end

        % ----------------------------------------------------
        function AddFile(obj, file)
            obj.files(end+1) = file;
            obj.nfiles = obj.nfiles+1;
            obj.AddLookupTable(obj.files(end).filename)
        end

        % ----------------------------------------------------
        function ErrorCheckName(obj)
            for ii = length(obj.files):-1:1
                if obj.files(ii).ErrorCheckName()<0
                    q = obj.AskToFixNameConflicts(ii);
                    if q == 1
                        obj.files(ii).FixNameConflict();
                    else
                        obj.files(ii).NameConflictFixed();
                    end
                end
                if obj.files(ii).GetError()<0
                    obj.err = -1;
                end
            end
        end

        % ----------------------------------------------------
        function ErrorCheckFinal(obj)
            obj.ErrorCheckName();

            % Find all acquisition files in group folder
            fileNames = findTypeFiles(obj.rootdir, ['.', obj.filetype]);

            % Make alist of all files excluded from current data set
            for ii = 1:length(fileNames)
                filefound = false;

                for jj = 1:length(obj.files)
                    if pathscompare(fileNames{ii}, [obj.rootdir, obj.files(jj).name])
                        filefound = true;
                        break;
                    end
                end

                for jj = 1:length(obj.filesErr)
                    if pathscompare(fileNames{ii}, [obj.rootdir, obj.filesErr(jj).name])
                        filefound = true;
                        break;
                    end
                end

                if ~filefound
                    obj.filesErr(end+1) = FileClass(fileNames{ii});
                    obj.filesErr(end).SetError('Invalid File Name');
                end
            end

        end

        % --------------------------------------------------------------------------
        function answer = AskToFixNameConflicts(obj, ii)
            global cfg

            ConfigFileClass
            answer = 0;
            if obj.config.AskToFixNameConflicts == 0
                obj.files(ii).NameConflictFixed();
                return
            end
            q = MenuBox(obj.GetErrorMsg(ii), {'YES','NO'},[],[],'askEveryTimeOptions');
            if q(1) == 0
                return;
            end
            if length(q)>1 && q(2) == 1
                cfg.SetValue('Fix File Name Conflicts', sprintf('don''t ask again'));
                cfg.Save()
                obj.config.AskToFixNameConflicts = 0;
            end
            if q(1)==2
                obj.files(ii).NameConflictFixed();
            end
            answer = q(1);
        end

        % -----------------------------------------------------
        function errmsg = GetErrorMsg(obj, ii)
            p1      = fileparts(obj.files(ii).GetName());
            [p2,f2] = fileparts(filesepStandard(obj.files(ii).rootdir,'nameonly:file'));
            [~,f3]  = fileparts(p2);
            if isfile_private(obj.files(ii).GetName())
                filetype = 'file'; %#ok<*PROPLC>
            else
                filetype = 'folder';
            end
            containingFolder = '';
            if obj.files(ii).GetError() == -1
                containingFolder = p1;
            end
            if obj.files(ii).GetError() == -2
                containingFolder = f2;
            end
            if obj.files(ii).GetError() == -3
                containingFolder = f3;
            end
            msg{1} = sprintf('WARNING: The current %s (%s) has the same name as the folder (%s) containing it. ', filetype, obj.files(ii).GetName(), containingFolder);
            msg{2} = sprintf('All %ss should have a different name than the folder containing them, otherwise ', ['F',filetype(2:end)]);
            msg{3} = sprintf('it may cause incorrect results in processing. Do you want to rename this %s?', filetype);
            errmsg = [msg{:}];
        end

        % -------------------------------------------------------
        function pushbuttonLoadDataset_Callback(~, hObject)
            hp = get(hObject,'parent');
            hc = get(hp,'children');
            for ii = 1:length(hc)

                if strcmp(get(hc(ii),'tag'),'pushbuttonLoad')
                    hButtnLoad = hc(ii);
                elseif strcmp(get(hc(ii),'tag'),'pushbuttonSelectAnother')
                    hButtnSelectAnother = hc(ii);
                end

            end

            if hObject==hButtnLoad
                delete(hButtnSelectAnother);
            elseif hObject==hButtnSelectAnother
                delete(hButtnLoad);
            end
            delete(hp);
        end

        % ----------------------------------------------------------
        function b = IsEmpty(obj)
            if isempty(obj.files)
                b = true;
            else
                b = false;
            end
        end

        % ----------------------------------------------------------
        function found = ConvertedFrom(obj, src)
            found = zeros(length(src.files), 1);
            for ii = 1:length(src.files)
                if src.files(ii).isdir
                    found(ii) = -1;
                    continue;
                end
                [ps, fs] = fileparts(src.files(ii).name);
                for jj = 1:length(obj.files)
                    [pd, fd] = fileparts(obj.files(jj).name);
                    if strcmp(filesepStandard([ps,'/',fs], 'nameonly'), filesepStandard([pd,'/',fd], 'nameonly'))
                        found(ii) = 1;
                        break;
                    end
                end
            end
        end

        % ----------------------------------------------------------
        function ErrorCheck(obj)
            errorIdxs = [];

            if isempty(obj.files)
                return
            end

            % Assume constructor name follows from name of data format type
            constructor = sprintf('%sClass', [upper(obj.filetype(1)), obj.filetype(2:end)]);

            % Make sure function by that name exists; otherwise no way to
            % use it to check loadability
            if isempty(which(constructor))
                return;
            end

            % Try to create object of data filetype and load data into it
            msg = 'Please wait while we check group folder for valid data files ...';
            hwait = waitbar_improved(0, msg);
            dataflag = false;
            for ii = 1:length(obj.files)
                if obj.files(ii).isdir
                    continue;
                end
                filename = [obj.rootdir, obj.files(ii).name]; %#ok<NASGU>
                eval( sprintf('o = %s(filename);', constructor) );
                if  o.GetError() < 0
                    obj.logger.Write('DataFilesClass.ErrorCheck - ERROR: In file "%s" %s. File will not be added to data set\n', obj.files(ii).name, o.GetErrorMsg());
                    errorIdxs = [errorIdxs, ii]; %#ok<AGROW>
                elseif  contains(o.GetErrorMsg(), '''data'' field corrupt and unusable')
                    obj.logger.Write('DataFilesClass.ErrorCheck - WARNING: In file "%s" %s. File will not be added to data set\n', obj.files(ii).name, o.GetErrorMsg());
                    errorIdxs = [errorIdxs, ii]; %#ok<AGROW>
                elseif  contains(o.GetErrorMsg(), '''data'' field is invalid')
                    obj.logger.Write('DataFilesClass.ErrorCheck - WARNING: In file "%s" %s. File will not be added to data set\n', obj.files(ii).name, o.GetErrorMsg());
                    errorIdxs = [errorIdxs, ii]; %#ok<AGROW>
                elseif ~isempty(o.GetErrorMsg())
                    obj.logger.Write('DataFilesClass.ErrorCheck - WARNING: In file  "%s"  %s. File will be added anyway.\n', obj.files(ii).name, o.GetErrorMsg());
                    dataflag = true;
                else
                    dataflag = true;
                end
                if ~isempty(o.GetErrorMsg())
                    obj.files(ii).SetError(o.GetErrorMsg());
                end
                hwait = waitbar_improved(ii/length(obj.files), hwait, msg);
            end

            if dataflag==false
                obj.files = FileClass.empty();
            else
                for jj = 1:length(errorIdxs)
                    obj.filesErr(end+1) = obj.files(errorIdxs(jj)).copy;
                end
            	obj.files(errorIdxs) = [];
                obj.nfiles = obj.nfiles - length(errorIdxs);
            end
            close(hwait);
        end

        % ----------------------------------------------------------
        function err = GetError(obj)
            err = -1;
            if isempty(obj)
                return;
            end
            err = obj.err;
        end

        % ----------------------------------------------------------
        function PrintFolderStructure(obj, options)
            if ~exist('options','var')
                options = '';
            end
            stepsize = 3;
            obj.logger.Write('\n');
            obj.logger.Write('DataTreeClass - Data Set Folder Structure:\n');
            for ii = 1:length(obj.files)
                k = length(find(obj.files(ii).name=='/'));
                if ii<10
                    j=3;
                elseif ii>9 && ii<100
                    j=2;
                else
                    j=3;
                end
                if optionExists(options, 'flat')
                    obj.logger.Write(sprintf('%d.%s%s\n', ii, blanks(j), obj.files(ii).name));
                else
                    if ii<10
                        j=3;
                    elseif ii>9 && ii<100
                        j=2;
                    else
                        j=3;
                    end
                    if optionExists(options, 'numbered')
                        n = k*stepsize+stepsize+j;
                        obj.logger.Write(sprintf('%d.%s%s\n', ii, blanks(n), obj.files(ii).filename));
                    else
                        n = k*stepsize+stepsize;
                        obj.logger.Write(sprintf('%s%s\n', blanks(n), obj.files(ii).filename));
                    end
                end
            end
            obj.logger.Write('\n');
        end

    end

    methods (Access = private)

        % ----------------------------------------------------------
        function InitLookupTable(obj)
            width = 4;
            if isempty(obj.lookupTable)
                obj.lookupTable = int32(zeros((10^width)-1, 1));
            else
                obj.lookupTable(:) = 0;
            end
        end

        % ----------------------------------------------------------
        function AddLookupTable(obj, str)
            n = round(log10(length(obj.lookupTable)));
            obj.lookupTable(string2hash(str, n)) = 1;
        end

        % ----------------------------------------------------------
        function b = SearchLookupTable(obj, str)
            n = round(log10(length(obj.lookupTable)));
            b = obj.lookupTable(string2hash(str, n));
        end

    end
end


function varargout=texthvc(varargin)
%
% For calling details please see v_texthvc.m
%
% This dummy routine is included for backward compatibility only
% and will be removed in a future release of voicebox. Please use
% v_texthvc.m in future and/or update with v_voicebox_update.m
%
%      Copyright (C) Mike Brookes 2018
%      Version: $Id: texthvc.m 10863 2018-09-21 15:39:23Z dmb $
%
if nargout
    varargout=cell(1,nargout);
    [varargout{:}]=v_texthvc(varargin{:});
else
    v_texthvc(varargin{:});
end