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`.
%
%   `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.
%
%   `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 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 requirement 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 |
%     |----------------|---------|-------------|
%     | 2013b          | 2013b   | 5.5         |
%     | 2014a          | 2014a   | 5.5         |
%     | 2014b          | 2014b   | 6.0         |
%
%     A 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 and CUDA Toolkit
%     6.5 (unable to compile with Visual C++ 2013).
%   * Windows 8 x64, MATLAB R2014a, Visual C++ 2013 and CUDA
%     Toolkit 6.5.
%   * Mac OS X 10.9 and 10.10, MATLAB R2013a and R2013b, Xcode, CUDA
%     Toolkit 5.5.
%   * GNU/Linux, MATALB R2014a, gcc, CUDA Toolkit 5.5.
%
%   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-15 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.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 = {'LDFLAGS=$LDFLAGS -framework Cocoa -framework ImageIO'} ;
    case 'gdiplus', opts.imageLibraryLinkFlags = {'-lgdiplus'} ;
  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]) ;
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]) ;

% 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') ;

% 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','datacu.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') ;
end

% Other files
if opts.enableImreadJpeg
  mex_src{end+1} = fullfile(root,'matlab','src', ['vl_imreadjpeg.' 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

  % 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.link = {} ;
if opts.verbose > 1
  flags.cc{end+1} = '-v' ;
  flags.link{end+1} = '-v' ;
end
if opts.debug
  flags.cc{end+1} = '-g' ;
else
  flags.cc{end+1} = '-DNDEBUG' ;
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
flags.link{end+1} = '-lmwblas' ;
switch arch
  case {'maci64', 'glnxa64'}
  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.link = horzcat(flags.link, opts.imageLibraryLinkFlags) ;
end

if opts.enableGpu
  flags.link{end+1} = ['-L' opts.cudaLibDir] ;
  flags.link{end+1} = '-lcudart' ;
  flags.link{end+1} = '-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

% For the MEX command
flags.link{end+1} = '-largeArrayDims' ;
flags.mexcc = flags.cc ;
flags.mexcc{end+1} = '-largeArrayDims' ;
flags.mexcc{end+1} = '-cxx' ;
if strcmp(arch, 'maci64') && 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.mexcc{end+1} = 'CXXFLAGS=$CXXFLAGS -stdlib=libstdc++' ;
  flags.link{end+1} = 'LDFLAGS=$LDFLAGS -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.link{end+1} = 'LINKLIBS=$LINKLIBS -L"$MATLABROOT/bin/maci64" -lmx -lmex -lmat -lstdc++' ;
  end
end
if strcmp(arch, 'maci64') && opts.enableGpu
  % Mac OS X 10.11 disables LD_LIBRARY_PATH for security reason. To
  % address this issue, we need to rpath the required CUDA and
  % cuDNN libraries.
  flags.link{end+1} = sprintf('LDFLAGS=$LDFLAGS -Wl,-rpath -Wl,"%s"', opts.cudaLibDir) ;
  if opts.enableCudnn
    flags.link{end+1} = sprintf('LDFLAGS=$LDFLAGS -Wl,-rpath -Wl,"%s"', opts.cudnnLibDir) ;
  end
end
if opts.enableGpu
  flags.mexcu = flags.cc ;
  flags.mexcu{end+1} = '-largeArrayDims' ;
  flags.mexcu{end+1} = '-cxx' ;
  flags.mexcu(end+1:end+2) = {'-f' mex_cuda_config(root)} ;
  flags.mexcu{end+1} = ['NVCCFLAGS=' opts.cudaArch '$NVCC_FLAGS'] ;
end

% For the cudaMethod='nvcc'
if opts.enableGpu && strcmp(opts.cudaMethod,'nvcc')
  flags.nvcc = flags.cc ;
  flags.nvcc{end+1} = ['-I"' fullfile(matlabroot, 'extern', 'include') '"'] ;
  flags.nvcc{end+1} = ['-I"' fullfile(matlabroot, 'toolbox','distcomp','gpu','extern','include') '"'] ;
  if opts.debug
    flags.nvcc{end+1} = '-O0' ;
  end
  flags.nvcc{end+1} = '-Xcompiler' ;
  switch arch
    case {'maci64', 'glnxa64'}
      flags.nvcc{end+1} = '-fPIC' ;
    case 'win64'
      flags.nvcc{end+1} = '/MD' ;
      check_clpath(); % check whether cl.exe in path
  end
  flags.nvcc{end+1} = opts.cudaArch;
end

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 compiler options: %s\n', mfilename, strjoin(flags.mexcc)) ;
  fprintf('%s: \tMEX linker options: %s\n', mfilename, strjoin(flags.link)) ;
end
if opts.verbose & opts.enableGpu
  fprintf('%s: \tMEX compiler options (CUDA): %s\n', mfilename, strjoin(flags.mexcu)) ;
end
if opts.verbose & opts.enableGpu & strcmp(opts.cudaMethod,'nvcc')
  fprintf('%s: \tNVCC compiler options: %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) = [] ;
  if strcmp(ext,'cu')
    if strcmp(opts.cudaMethod,'nvcc')
      nvcc_compile(opts, srcs{i}, toobj(bld_dir,srcs{i}), flags.nvcc) ;
    else
      mex_compile(opts, srcs{i}, toobj(bld_dir,srcs{i}), flags.mexcu) ;
    end
  else
    mex_compile(opts, srcs{i}, toobj(bld_dir,srcs{i}), flags.mexcc) ;
  end
end

% Link into MEX files
for i = 1:numel(mex_src)
  [~,base,~] = fileparts(mex_src{i}) ;
  objs = toobj(bld_dir, {mex_src{i}, lib_src{:}}) ;
  mex_link(opts, objs, mex_dir, flags.link) ;
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
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 = strrep(objs,'.cpp',['.' objext]) ;
objs = strrep(objs,'.cu',['.' objext]) ;
objs = strrep(objs,'.c',['.' objext]) ;

% --------------------------------------------------------------------
function objs = 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 obj = 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 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.
status = system('cl.exe -help');
if status == 1
  warning('CL.EXE not found in PATH. Trying to guess out of mex setup.');
  cc = mex.getCompilerConfigurations('c++');
  if isempty(cc)
    error('Mex is not configured. Run "mex -setup".');
  end
  prev_path = getenv('PATH');
  cl_path = fullfile(cc.Location, 'VC','bin','x86_amd64');
  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(opts)
% -------------------------------------------------------------------------
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: seraching 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(opts)] ;
for v = {'5.5', '6.0', '6.5', '7.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
nvcc = nvcc(index(1)) ;
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 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 inplanes = rtGetInplanes(examNum,ipSeries,doCrop);
%
% inplanes = rtGetInplanes(examNum,inplaneSeriesNum,[doCrop]);
%
% For pseudo real-time, copy over the DICOM files
% from the scanner for the selected inplane
% anatomical series (to the session Raw/Inplane dir),
% then read them in as in InitAnatomy and save/return
% the anat struct.
%
% ras 04/05
if ieNotDefined('examNum')
    examNum = input('Enter exam #: ');
end
if ieNotDefined('ipSeries')
    ipSeries = input('Enter series for Inplanes: ');
end
if ieNotDefined('doCrop')
    doCrop = input('Crop Inplanes? [1 for yes, 0 for no]: ');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% run a unix command to ssh over to    %
% the control machine (lcmr3) and get  %
% the DICOM images:                    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% first, the command to be executed at lcmr3:
xferCmd = sprintf(['/export/home/sdc/bin/lx_ximg ',...
                   'E%iS%iIall -d /usr/g/mrraw'],...
                   examNum,ipSeries);
% now, the ssh command to call this remotely
cmd = sprintf('ssh sdc@lcmr3 %s',xferCmd);

% execute
unix(cmd)

% now, make sure there's a local Raw/Inplane dir:
if ~exist(fullfile(pwd,'Raw'),'dir')
    mkdir Raw
    disp('Made Raw')
end

if ~exist(fullfile(pwd,'Raw','Anatomy'),'dir')
    mkdir Raw Anatomy
    disp('Made Raw/Anatomy')
end

if ~exist(fullfile(pwd,'Raw','Anatomy','Inplane'),'dir')
    mkdir Raw/Anatomy Inplane
    disp('Made Raw/Anatomy/Inplane')
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Xfer the files to the local Raw dir %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% find the file names
pattern = sprintf('/lcmr3/mrraw/E%iS%i*.MR.dcm',examNum,ipSeries);
w = dir(pattern);
nFiles = length(w);
if nFiles==0
    error('No matching files found!')
end
for i = 1:nFiles
    srcFile = sprintf('E%iS%iI%i.MR.dcm',examNum,ipSeries,i);
    tgtFile = sprintf('Raw/Anatomy/Inplane/I%03d.dcm',i);
    cmd = sprintf('cp /lcmr3/mrraw/%s %s',srcFile,tgtFile);
    unix(cmd);
    fprintf('Got %s \n',tgtFile);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Now get the inplanes for mrVista %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[anat, inplanes] = InitAnatomy(pwd,'Raw',doCrop);

if doCrop==0
    %%% GUM
    inplanes.crop = [1 1; inplanes.fullSize];
    inplanes.cropSize = [inplanes.fullSize];
else
    [anat, inplanes] = CropInplanes('Raw',anat,inplanes,[1 1]);
end

% save the anat image locally
if ~exist(fullfile(pwd,'Inplane'),'dir')
    disp('Making Inplane directory');
    mkdir Inplane
end
save('Inplane/anat','anat');
disp('Saved Inplane/anat.mat file.')

% update mrSESSION
if exist('mrSESSION.mat','file')
    load mrSESSION
    mrSESSION.inplanes = inplanes;
    save mrSESSION mrSESSION -append
end

return


function DeleteDataFiles(varargin)

% Syntax:
%
%   DeleteDataFiles()
%   DeleteDataFiles(dirname)
%   DeleteDataFiles(dirname, datafiles0)
%   DeleteDataFiles(dirname, format)
%   DeleteDataFiles(dirname, datafiles0, options)
%   DeleteDataFiles(dirname, format, options)
%
% Description:
%
%   Delete all .<format extension> files in group folder. DeleteDataFiles will find all
%   .<format extension> data acquisition files in the group folder. If dirname is not supplied it'll
%   treat the current working directory as the group folder.
%
% Examples:
%
%   1. Delete all .snirf files in the Homer3 Examples directory
%
%       DeleteDataFiles('C:\jdubb\workspaces\Homer3\DataTree\AcquiredData\Snirf\Examples', '.snirf')
%
%   2. Delete all .nirs files files in the current group folder
%
%       DeleteDataFiles(pwd, '.nirs)
%
%

global supportedFormats
if isempty(supportedFormats)
    supportedFormats = {
    '.snirf',0;
    '.nirs',0;
    };
end

% Set argument defaults
dirname = filesepStandard(pwd);
format = supportedFormats{1};
datafiles0 = [];
options = 'delete';

% Parse arguments
if nargin==1
    dirname = filesepStandard(varargin{1},'full');
elseif nargin==2
    dirname = filesepStandard(varargin{1},'full');
    if ischar(varargin{2})
        format = varargin{2};
    else
        datafiles0 = varargin{2};
    end
elseif nargin==3
    dirname = filesepStandard(varargin{1}, 'full');
    if ischar(varargin{2})
        format = varargin{2};
    else
        datafiles0 = varargin{2};
    end
    options = varargin{3};
end
if isempty(datafiles0)
    datafiles0 = DataFilesClass(dirname, format, 'standalone').files;
end

% Get final list of data files
datafiles = mydir(dirname);
if iscell(datafiles0)
    for ii = 1:length(datafiles0)
        datafiles(ii) = mydir([dirname, datafiles0{ii}]);
    end
elseif ischar(datafiles0)
    datafiles = mydir([dirname, datafiles0]);
elseif isa(datafiles0, 'FileClass')
    datafiles = datafiles0;
end

% Delete data files
for ii = 1:length(datafiles)
    if datafiles(ii).isdir
        continue;
    end
    if strcmp(options, 'delete')
        fprintf('Deleting %s\n', [datafiles(ii).rootdir, '/', datafiles(ii).name]);
        delete([datafiles(ii).rootdir, '/', datafiles(ii).name]);
        delete([datafiles(ii).rootdir, '/*_events.tsv']);
    elseif strcmp(options, 'move')
        fprintf('Moving %s to %s\n', [datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', datafiles(ii).name, '.old']);
        movefile([datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', datafiles(ii).name, '.old']);
    elseif strcmp(options, 'restore')
        [pname, fname] = fileparts(datafiles(ii).name);
        fprintf('Restoring %s to %s\n', [datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', pname, '/', fname]);
        movefile([datafiles(ii).rootdir, '/', datafiles(ii).name], [datafiles(ii).rootdir, '/', pname, '/', fname]);
    end
    pause(0.25);
end


function convertDicom2Vista_UU_donders(subjectName,SessionName,dicomServer)
% convertDicom2Vista_UU_donders(subjectName,SessionName,dicomServer)
%
% adapted from prepareUCSFParnassusUniversal
%
% e.g. prepareUCSFParnassusUniversal('norcia','LOCMT',2);
% The 1st argument is the subject name (corresponds to that subject's
% anatomy directory under /raid/MRI/anatomy/
% The 2nd argument is a plain text string to tag the exam. Avoid slashes,
% minus signs, spaces etc.
% The third argument indicates which dicom server the data came from. In
% theory we can auto-detec this but for now use
% 1: For NIC data via MPRAGE
% 2: For NIC data via Hurricane
% 3: For Donders' data
%
%
% This is a script to recon the dicom files that come from the UCSF MRI
% scanner at China Basin
% The first part uses the medcon program to convert those files into
% analyze format. After that, we use FSL to do some preprocessing like
% motion correction.
% The final step is to generate a mrLoadRet project from the set of 4d
% Analyze files.
% Motion correction is performed in several stages: We first do within-scan
% MC and store all the resulting transform matrices for each scan. Then we
% do between-scan MC using the mean image from each scan to align everything to the
% first scan in the session. We store the transforms for these alignments
% as well.
%
% Then we go back and combine the within and between scan transforms and
% re-apply the entire transform to the original data set. In this way we
% avoid re-sampling the original data more than we have to.
% EXAMPLE: prepareUCSF('appelbaum','LOC_Localizer');
% 2006.04.20 Mark Schira wrote it (mark@ski.org)
% 2007.06.13 Alex Wade modified it for use with data from Parnassus NIC
% Siemens scanner. Uses some ideas from dicom2analyze_v4.m : SPM and GazLab
% people
% Issue is that Siemens system uses mosaic image format: Each dicom file
% has an entire volume in it sometimes (and sometimes not in the case of
% anatomies..)
% The SPM5 routine dicom2analyze takes care of this.
% The critical step is...
%  % convert dicoms to analyzer
%        hdr = spm_dicom_headers(files);
%        spm_dicom_convert(hdr,opts,root_dir,format);
% Rather than using medcon (great though medcon is!)
% This 'universal' version is supposed to cope with the slightly different
% filename conventions in the two DICOM suervers 'hurricane' and 'MPRAGE'
% 080907: Can now use any directory names inside the RawDicom subdirectory

%%

% we need spm
if(isempty(which('spm_dicom_headers')))
    error('Requires spm2+ tools!');
end

if not(exist('subjectName','var'))
    error('need the lastname of subject as input');
end

if not(exist('SessionName','var'))
    SessionName='Whatever';
end

if not(exist('dicomServer','var'))
    error ('You must  specify a dicom server index: 1 is UCSF NIC / MPRAGE, 2 is UCSF NIC / Hurricane, 3 is Donders Institute');
end

ActionList={'Nothing','Inplanes','Functional','RefFunc','Anatomical','DTI'};

%%  Opening dialogs
% We present the user with a list of options. Not all stages have to be
% performed at once.

initOptions = {'clean previous files (be careful!)',...
    'DB Query',...
    'Do Detect data',...
    'Generate MLR structure',...
    'Generare BV structure',...
    'Motion cor. in scan',...
    'Motion cor. between scan',...
    'Perform blocked analysis',...
    'Perform event related analysis',...
    'Clean up, delete analyze files',...
    'Clean up. Zip the Dicom files',...
    };
initReply=[0 0 1 1 0 0 0 0 0 0 0]; % These are the defaults
initReply = buttondlg('mrInit UU-Data', initOptions,initReply);
if isempty(initReply), return; end

pause(0.2);
cleanfirst=initReply(1);
doDBQuery=initReply(2); %#ok<NASGU>
detectData=initReply(3);
doMLR=initReply(4);
doBV=initReply(5);%#ok<NASGU>
motionInScan=initReply(6);%#ok<NASGU>
motionBetween=initReply(7);%#ok<NASGU>
blockedData=initReply(8);%#ok<NASGU>
eventData=initReply(9);%#ok<NASGU>
doCleanUp=initReply(10);%#ok<NASGU>
doPackDicom=initReply(11);%#ok<NASGU>

baseDir = pwd;

%% we use some unix commands
operatingSystem=computer;
if not(strcmp(operatingSystem(1:4),'GLNX'))
    error('This function  is for Linux machines only');
end

%% remove analyze file from previous runs

if cleanfirst
    ! rm ./*/*/*/*.hdr
    ! rm ./*/*/*/*.img
    ! rm -r ./Inplane
    ! rm -r ./Gray
    ! rm -r ./Volume
    ! rm mrSESSION*.mat
    ! rm prepare*vars.mat
end

%% Automatic detection of the Directories, Inplanes etc
if detectData
    disp('Reading the directory structure...');
    RawDir = './Raw/Functionals/';
    baseDirListing=dir(RawDir);

    % Sort this so that it is in ascending numeric order
    nDirs=length(baseDirListing);
    tempDirName=zeros(nDirs,1);

    for t=1:length(baseDirListing);
        switch dicomServer
            case {1,3}		% MPRAGE
                tempDirNum = str2double( baseDirListing(t).name(find(baseDirListing(t).name=='_',1,'last')+1:end) );

            otherwise
                tempDirNum=str2double(baseDirListing(t).name);
        end

        if (tempDirNum>0) && isfinite(tempDirNum)
            tempDirName(t)=tempDirNum;
        end

    end

    [tempDirName,i]=sort(tempDirName); % Because we don't know how DIR might order the output
    baseDirListing=baseDirListing(i); % Apply the sort

    baseDirListing=baseDirListing(3:end); % Strip off stuff that didn't start with a number - like . and ..
    disp({baseDirListing.name}')

    foundScans(1).DirName='';
    foundScans(1).Files=0;
    foundScans(1).Slices=0;
    foundScans(1).Volumes=0;
    foundScans(1).Sequence='';
    foundScans(1).Action='';
    foundScans(1).Filenames='';
    foundScans(1).SkipVols='';
    foundScans(1).Cycles='';
    foundScans(1).Filename4d='';
    foundScans(1).lastfile='';

    inplanedir=0;

    for thisEntry=1:length(baseDirListing)
        fprintf('\nProcessing %d',thisEntry);

        if baseDirListing(thisEntry).isdir

            thisdir=baseDirListing(thisEntry).name;

            switch dicomServer
                case 1 % Coming from MPRAGE at the NIC
                    tempdir=dir([baseDir,filesep,RawDir,filesep,baseDirListing(thisEntry).name,filesep,'*.dcm']); % Only list the Dicom files.
                case 2 % Coming from Hurricane
                    tempdir=dir([baseDir,filesep,RawDir,filesep,baseDirListing(thisEntry).name,filesep,'*.DCM']); % Only list the Dicom files.
                case 3 % Coming from Donders
                     tempdir=dir([baseDir,filesep,RawDir,filesep,baseDirListing(thisEntry).name,filesep,'*.IMA']); % Only list the Dicom files.
                otherwise
                    error('No valid dicomServer type specified');
            end

            foundScans(thisEntry).RawDir = RawDir;
            foundScans(thisEntry).DicomServer = dicomServer;
            foundScans(thisEntry).Filenames=tempdir(1).name;
            foundScans(thisEntry).Filenames=...
            foundScans(thisEntry).Filenames(1:find(foundScans(thisEntry).Filenames=='I'));
            foundScans(thisEntry).DirName=baseDirListing(thisEntry).name;
            foundScans(thisEntry).Files=length(tempdir);
            header=dicominfo([baseDir,filesep,RawDir,filesep,baseDirListing(thisEntry).name,filesep,tempdir(1).name]);
            if (isfield(header,'Private_0019_100a')) % This is a functional image stored in Mosaic format
                disp(tempdir);
                foundScans(thisEntry).Slices=header.Private_0019_100a;%Private_0021_104f;
                foundScans(thisEntry).Volumes=(length(tempdir));%/foundScans(thisdir).Slices;
            else % Otherwise it's a T1 image stored slice-wise
                foundScans(thisEntry).Slices=(length(tempdir));
                foundScans(thisEntry).Volumes=1;
            end

%             if (sum(findstr(header.SeriesDescription,'ep2d'))) && (foundScans(thisEntry).Volumes>40) % Currently, this is hard-coded. We should be more flexible in case we change the protocol name.
            if (~isempty(strfind(header.SeriesDescription,'ep2d')) || ~isempty(strfind(header.SeriesDescription,'bas_MoCo'))) && (foundScans(thisEntry).Volumes>40) % Currently, this is hard-coded. We should be more flexible in case we change the protocol name.
                foundScans(thisEntry).Action='Functional';
            else
                foundScans(thisEntry).Action='Nothing';
            end
            if strcomp(header.SeriesDescription,'inplanes')
                inplanedir=thisdir;
            end

            foundScans(thisEntry).Sequence=header.SeriesDescription;

        end
    end
    if not(inplanedir==0)
        foundScans(inplanedir).Action='Inplanes';
    else
        disp(sprintf('[%s]:No Inplanes found',mfilename));
    end
    foundScans=selectInitParameters(foundScans);

else %if not scan the directory one would have to read the info from a previous run

    foundScans=selectInitParameters(foundScans);
end

%% The above does seem to work quite well. Now we can pass the dir names
% into the SPM5 dicom converters to generate NIFTI or ANALYZE format
% data...
%
% Recon all the functionals to Analyse

disp(sprintf('[%s]:Reconning all scans...',mfilename));drawnow;
procScans = cell(numel(foundScans),1);
hdrList   = cell(numel(foundScans),1);
for thisDir=1:numel(foundScans)
    if strcmp(foundScans(thisDir).Action,'Functional') ||  strcmp(foundScans(thisDir).Action,'RefFunc')
        tic
        [procScans{thisDir},hdrList{thisDir}]=Dicom2Analyze_SPM5(foundScans(thisDir));
        toc
    end
end
%
%
% for thisDir=1:length(foundScans)
%     if strcmp(foundScans(thisDir).Action,'Inplanes') ||  strcmp(foundScans(thisDir).Action,'Anatomical')
%         outFileName=[foundScans(thisDir).Filenames,'_a4d.hdr']; % We will generate a 3d analyze (Nifti) file from anatomicals
%
%         tic
%         [procScans{thisDir},hdrlist{thisDir}]=Dicom2ThreeDAnalyze_SPM5(foundScans(thisDir),pwd,dicomServer);
%         toc
%
%     end
% end
% datestr(now)
% clear thisDir:

%%

cd (baseDir)
if doMLR
    disp('Making directories');
    !mkdir Inplane
    !mkdir Volume
    !mkdir Gray
end

save prepareUUvars

status=mrInitRetFromSPM(foundScans,subjectName,SessionName);

disp('Result of mrInitRetFromSPM');
disp(status);

if blockedData
    mrvista('inplane');
    inplane=getSelectedInplane;
    inplane=computeMeanMap(inplane,0);
    inplane=computeCorAnal(inplane,0);
    inplane = selectDataType(inplane,2);
    inplane=computeMeanMap(inplane,0);
    inplane=computeCorAnal(inplane,0);
    %    closeInplaneWindow();
    datestr(now)
end


function strs = slstrsplit(srcstr, delimiters)
%SLSTRSPLIT splits a string into cell array of strings by delimiters
%
% $ Syntax $
%   - strs = slstrsplit(srcstr, delimiters)
%
% $ Arguments $
%   - srcstr:       the source string
%   - delimiters:   the array of delimiting chars
%
% $ Description $
%   - strs = slstrsplit(srcstr, delimiters) splits the source string into
%     a cell array of parts, which are delimited by the chars in
%     delimiters.
%
% $ Remarks $
%   - If for adjacent delimiters, the between will not will extracted.
%   - No further processing is applied, you can use functions like
%     slcompresstext to achieve these goals.
%
% $ History $
%   - Created by Dahua Lin, on Aug 13, 2006
%

%% determine delimiter positions

n0 = length(srcstr);
is_delimiter = false(n0, 1);

nd = length(delimiters);
for i = 1 : nd
    ch = delimiters(i);
    is_delimiter(srcstr == ch) = true;
end

dps = find(is_delimiter);
dps = dps(:)';

%% extract parts

if isempty(dps)
    strs = {srcstr};
else
    sps = [1, dps+1];
    eps = [dps-1, n0];
    fv = find(sps <= eps);
    if ~isempty(fv)
        sps = sps(fv);
        eps = eps(fv);
        np = length(fv);
        strs = cell(np, 1);
        for i = 1 : np
            strs{i} = srcstr(sps(i):eps(i));
        end
    else
        strs = {};
    end
end


function addToPath(arg)

% Adds the MFE Toolbox to the current path and optionally saves the path
silent = false;
if nargin>0 && strcmpi(arg,'-silent')
    silent = true;
end

dirList = {'bootstrap',...
    'crosssection',...
    'distributions',...
    'GUI',...
    'multivariate',...
    'tests',...
    'timeseries',...
    'univariate',...
    'utility',...
    'realized',...
    };
optionalDirList = {'duplication'};
mexDir = 'dlls';

for i=1:length(dirList)
    addpath(fullfile(pwd,dirList{i}));
end

user_entry = '';
while ~ismember({user_entry},{'Y','N'}) && ~silent
    user_entry = input('Add MATLAB work-a-like functions? [Y/N(default)] ','s');
    user_entry = upper(user_entry);
    if isempty(user_entry)
        user_entry = 'N';
    end
end

if strcmpi(user_entry,'Y')
    for i=1:length(optionalDirList)
        addpath(fullfile(pwd,optionalDirList{i}));
    end
end

user_entry = '';
while ~ismember({user_entry},{'Y','N'}) && ~silent
    user_entry = input('Make path permanent? [Y/N (default)] ','s');
    user_entry = upper(user_entry);
    if isempty(user_entry)
        user_entry = 'N';
    end
end

if strcmpi(user_entry,'Y')
    try
        savepath
    catch ME
        warning('MFEToolbox:Path',['There was a problem saving the path.  The error was: \n' ME.message])
    end
end

if strcmpi(computer,'PCWIN64')
    user_entry = '';
    while ~ismember({user_entry},{'Y','N'}) && ~silent
        user_entry = input('Add MEX files to path? [Y(default)/N] ','s');
        user_entry = upper(user_entry);
        if isempty(user_entry)
            user_entry = 'Y';
        end
    end
    if strcmp(user_entry,'Y') || silent
        addpath(fullfile(pwd,mexDir));
    end
end


%% #######################     HEADER START    ############################
%*************************************************************************
%
% Filename:				Retrieve_Directory_Structure.m
%
% Author:				A. Mering
% Created:				22-Feb-2013
%
% Changed on:			XX-XX-XXXX  by USERNAME		SHORT CHANGE DESCRIPTION
%						XX-XX-XXXX  by USERNAME		SHORT CHANGE DESCRIPTION
%
%*************************************************************************
%
% Description:
%		Recursively analyze the directory structure and get all files according to defined file type(s).
%
%
% Input parameter:
%		- root_dir:			Root directory from where to start the analysis
%		- file_types:		Cell array of file types to be take into account
%
% Output parameter:
%		- file_structure:	Structure containing information about all the files occuring
%		- num_files_below:  Number of files matching search expression within lower lying directories
%		- exclusion_list:	List of file names to be excluded
%
%*************************************************************************
%
% Intrinsic Subfunctions
%		- none
%
% Intrinsic Callbacks
%		- none
%
% #######################      HEADER END     ############################

%% #######################    FUNCTION START   ############################
function [directory_structure, num_files] = Retrieve_Directory_Structure(root_dir, file_types, exclusion_list)

%% Input Validation
% start with input checks

% Is the root_dir of type char and does the directory exist?
if ~ischar(root_dir) || ~exist(root_dir, 'dir')
	error('Input directory is not an existing directory!')
end

% Is filetype of type cell?
if ~iscell(file_types)
	% no? then make a cell out of it
	file_types = {file_types};
end

% Are all cell elements of type char?
if ~all(cellfun(@ischar, file_types))
	error('Invalid input for file types')
end

% Are all cell elements of the form '*....'?
if any(cellfun('isempty', regexp(file_types, '^\*\..+$')))
	error('File types should be of the form ''*.xyz''!')
end

%% PostProcess input parameters
% append '$' to identify files via regexp
file_types = cellfun(@(x) sprintf('%s$',x), file_types, 'UniformOutput', false);
file_types = regexprep(file_types, '\$*$', '\$');

%% Initialize
root_content = dir(root_dir);

max_num_found_files_string_length = ceil(log10(sum(~cell2mat({root_content.isdir})+1)));
max_num_found_subdirs_string_length = ceil(log10(sum(cell2mat({root_content.isdir})+1)));

num_found_files = 0;
num_found_subdirs = 0;
num_dir_level_below = 0;
num_files_below = 0;
num_files = 0;

directory_structure = struct();

%% Get directory content
for n = 1 : size(root_content)
	current_item = root_content(n);

	% Check for directory -> Recursion
	if current_item.isdir

		if isempty(regexp(current_item.name, '^\.{1,2}$', 'once'))
			[directory_content, num_files_within] = Retrieve_Directory_Structure([root_dir,'\',current_item.name], file_types, exclusion_list);

			num_files_below = num_files_below + num_files_within;

			% Only append directories with found items
			if ~isempty(fieldnames(directory_content))

				num_dir_level_below = max([num_dir_level_below, directory_content.Statistics_Num_Dir_Level_Below + 1]);

				num_found_subdirs = num_found_subdirs + 1;

				structure_label = sprintf(['Directory%0',num2str(max_num_found_subdirs_string_length),'i'],num_found_subdirs);

				directory_structure.(structure_label) = directory_content;
				directory_structure.(structure_label).DirName = current_item.name;

				directory_structure.(structure_label) = orderfields(directory_structure.(structure_label));
			end
		end

	else

		% Check filetypes

		if ~ismember(current_item.name, exclusion_list)
			found_type_pos = find(~cellfun('isempty', regexp(current_item.name, regexprep(file_types, '^\*', '\'))),1);

			if any(strcmp(file_types, '*.*$')) || ~isempty(found_type_pos)
				num_found_files = num_found_files + 1;

				structure_label = sprintf(['File%0',num2str(max_num_found_files_string_length),'i'],num_found_files);

				directory_structure.(structure_label) = current_item.name;

			end
		end
	end

end

num_files = num_files_below + num_found_files;

directory_structure = orderfields(directory_structure);
if num_found_subdirs  ~= 0 || num_found_files ~= 0
	directory_structure.Statistics_Num_Dirs = num_found_subdirs;
	directory_structure.Statistics_Num_Dir_Level_Below = num_dir_level_below;
	directory_structure.Statistics_Num_Files_Current = num_found_files;
	directory_structure.Statistics_Num_Files_Below = num_files_below;

	[~,directory_structure.DirName ] = fileparts(root_dir);
end

% #######################     FUNCTION END    ############################

%% #######################  SUBFUNCTION START  ############################

% #######################   SUBFUNCTION END   ############################

%% #######################    CALLBACK START   ############################

% #######################     CALLBACK END    ############################


%kill_spaces Remove all spaces from given string.

function r = kill_spaces(s)

r = s(setdiff(1:length(s),strfind(s,' ')));