function [mri, seg, hdr] = read_asa_mri(fn)

% READ_ASA_MRI reads an ASA format MRI file
%
% Use as
%   [mri, seg, hdr] = read_asa_mri(filename)
%
% The raw image data is returned, together with the position of the
% external head markers in raw image coordinates.
%
% In the ASA default PAN (pre-auricular/nasion) coordinate system
%   PointOnPositiveYAxis -> LPA
%   PointOnNegativeYAxis -> RPA
%   PointOnPositiveXAxis -> nasion

% Copyright (C) 2002, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

hdr.Nrows     = read_ini(fn, 'NumberRows=', '%d');
hdr.Ncolumns  = read_ini(fn, 'NumberColumns=', '%d');
hdr.Nslices   = read_ini(fn, 'NumberSlices=', '%d');
hdr.rows      = read_ini(fn, 'Rows=', '%s');
hdr.columns   = read_ini(fn, 'Columns=', '%s');
hdr.slices    = read_ini(fn, 'Slices=', '%s');
hdr.distance  = read_ini(fn, 'Distance=', '%f', 3);
hdr.mrifile   = read_ini(fn, 'Matrix', '%s');
hdr.segfile   = read_ini(fn, 'Segmentation', '%s');
hdr.posy      = read_ini(fn, 'PointOnPositiveYAxis', '%f', 3);
hdr.negy      = read_ini(fn, 'PointOnNegativeYAxis', '%f', 3);
hdr.posx      = read_ini(fn, 'PointOnPositiveXAxis', '%f', 3);
hdr.voxposy   = read_ini(fn, 'VoxelOnPositiveYAxis', '%f', 3);
hdr.voxnegy   = read_ini(fn, 'VoxelOnNegativeYAxis', '%f', 3);
hdr.voxposx   = read_ini(fn, 'VoxelOnPositiveXAxis', '%f', 3);
hdr.segfile   = read_ini(fn, 'Segmentation', '%s', 1);

dim = [hdr.Ncolumns hdr.Nrows hdr.Nslices];
mri = [];
seg = [];

% the data files are at the same location as the mri file, locate path
[path, name, ext] = fileparts(fn);

% this temporary needs 8x as much storage!!!
if ~isempty(hdr.mrifile)
  mrifile = fullfile(path, hdr.mrifile);
  mri = zeros(dim);
  fid = fopen_or_error(mrifile, 'rb', 'ieee-le');
  mri(:) = fread(fid, prod(dim), 'uint8');
  mri = uint8(mri);
  fclose(fid);
end

% this temporary needs 8x as much storage!!!
if ~isempty(hdr.segfile)
  segfile = fullfile(path, hdr.segfile);
  seg = zeros(dim);
  fid = fopen_or_error(segfile, 'rb', 'ieee-le');
  seg(:) = fread(fid, prod(dim), 'uint8');
  seg = uint8(seg);
  fclose(fid);
end

% flip the orientation of the MRI data, the result should be
% 'coronal(occipital-frontal)'
% 'horizontal(inferior-superior)'
% 'sagittal(right-left)'

if strcmp(hdr.columns, 'coronal(frontal-occipital)')
  hdr.columns = 'coronal(occipital-frontal)';
  mri = flipdim(mri, 1);
  seg = flipdim(seg, 1);
elseif strcmp(hdr.columns, 'horizontal(superior-inferior)')
  hdr.columns = 'horizontal(inferior-superior)';
  mri = flipdim(mri, 1);
  seg = flipdim(seg, 1);
elseif strcmp(hdr.columns, 'sagittal(left-right)')
  hdr.columns = 'sagittal(right-left)';
  mri = flipdim(mri, 1);
  seg = flipdim(seg, 1);
end

if strcmp(hdr.rows, 'coronal(frontal-occipital)')
  hdr.rows = 'coronal(occipital-frontal)';
  mri = flipdim(mri, 2);
  seg = flipdim(seg, 2);
elseif strcmp(hdr.rows, 'horizontal(superior-inferior)')
  hdr.rows = 'horizontal(inferior-superior)';
  mri = flipdim(mri, 2);
  seg = flipdim(seg, 2);
elseif strcmp(hdr.rows, 'sagittal(left-right)')
  hdr.rows = 'sagittal(right-left)';
  mri = flipdim(mri, 2);
  seg = flipdim(seg, 2);
end

if strcmp(hdr.slices, 'coronal(frontal-occipital)')
  hdr.slices = 'coronal(occipital-frontal)';
  mri = flipdim(mri, 3);
  seg = flipdim(seg, 3);
elseif strcmp(hdr.slices, 'horizontal(superior-inferior)')
  hdr.slices = 'horizontal(inferior-superior)';
  mri = flipdim(mri, 3);
  seg = flipdim(seg, 3);
elseif strcmp(hdr.slices, 'sagittal(left-right)')
  hdr.slices = 'sagittal(right-left)';
  mri = flipdim(mri, 3);
  seg = flipdim(seg, 3);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% swap the orientations of the MRI data, the result should be fixed
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 1st dimension corresponds to columns, which should be 'coronal(occipital-frontal)'
% 2st dimension corresponds to rows,    which should be 'sagittal(right-left)'
% 3rd dimension corresponds to slices,  which should be 'horizontal(inferior-superior)'

if     strcmp(hdr.columns, 'coronal(occipital-frontal)')
  orientation(1) = 1;
elseif strcmp(hdr.columns, 'sagittal(right-left)')
  orientation(1) = 2;
elseif strcmp(hdr.columns, 'horizontal(inferior-superior)')
  orientation(1) = 3;
end

if     strcmp(hdr.rows, 'coronal(occipital-frontal)')
  orientation(2) = 1;
elseif strcmp(hdr.rows, 'sagittal(right-left)')
  orientation(2) = 2;
elseif strcmp(hdr.rows, 'horizontal(inferior-superior)')
  orientation(2) = 3;
end

if     strcmp(hdr.slices, 'coronal(occipital-frontal)')
  orientation(3) = 1;
elseif strcmp(hdr.slices, 'sagittal(right-left)')
  orientation(3) = 2;
elseif strcmp(hdr.slices, 'horizontal(inferior-superior)')
  orientation(3) = 3;
end

mri = ipermute(mri, orientation);
seg = ipermute(seg, orientation);
hdr.rows    = 'sagittal(right-left)';
hdr.columns = 'coronal(occipital-frontal)';
hdr.slices  = 'horizontal(inferior-superior)';

% recompute the dimensions after all the swapping
hdr.Nrows    = size(mri, 1);
hdr.Ncolumns = size(mri, 2);
hdr.Nslices  = size(mri, 3);
dim = size(mri);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% if possible, create the accompanying homogenous coordinate transformation matrix
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% In case of PointOn..., ASA counts voxels from the center of the MRI
% and in case of VoxelOn..., ASA counts voxels from the corner of the MRI
% In both cases, ASA starts counting at [0 0 0], which is C convention
% whereas I want to count from the 1st voxel and number that with [1 1 1]
if ~isempty(hdr.posx) && ~isempty(hdr.negy) && ~isempty(hdr.posy)
  offset = (dim + [1 1 1])/2;
  hdr.fiducial.mri.nas = hdr.posx + offset;
  hdr.fiducial.mri.lpa = hdr.posy + offset;
  hdr.fiducial.mri.rpa = hdr.negy + offset;
else
  offset = [1 1 1];
  hdr.fiducial.mri.nas = hdr.voxposx + offset;
  hdr.fiducial.mri.lpa = hdr.voxposy + offset;
  hdr.fiducial.mri.rpa = hdr.voxnegy + offset;
end

% use the headcoordinates function (roboos/misc) to compute the transformaton matrix
hdr.transformMRI2Head = ft_headcoordinates(hdr.fiducial.mri.nas, hdr.fiducial.mri.lpa, hdr.fiducial.mri.rpa, 'asa');
hdr.transformHead2MRI = inv(hdr.transformMRI2Head);

% compute the fiducials in head coordinates
hdr.fiducial.head.nas = ft_warp_apply(hdr.transformMRI2Head, hdr.fiducial.mri.nas, 'homogenous');
hdr.fiducial.head.lpa = ft_warp_apply(hdr.transformMRI2Head, hdr.fiducial.mri.lpa, 'homogenous');
hdr.fiducial.head.rpa = ft_warp_apply(hdr.transformMRI2Head, hdr.fiducial.mri.rpa, 'homogenous');


%kLine 'Line Object Menuform'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros Line.pane file
%
% Parameters:
%
% Example: kLine( {})
%
% Khoros helpfile follows below:
% .begin tagged
%
% .item "The Line Visual Object"
% A line visual object supports the display of a line annotation.  The endpoints
% of the line are specified in world coordinates.  The  world coordinates are
% specified by the parent.  By default, these world coordinates are from 0 to 1
% unless otherwise specified.  So, for example, if the line is created within
% a manager object, its world coordinates are from 0 to 1.  However, if the line
% is created over an image, the world coordinates will be dictated by the size of
% the image; if it created over a plot, the world coordinates will be dictated
% by the plot.
%
% .item "Line Width"
% This list selection allows you to set the line width used by
% the line object. Line widths range from "Extra Fine" to "Extra Wide".
%
% .item "Line Type"
% This list selection lets you set the line type used on
% the line object. There are seven line types, including "Solid",
% "Dotted", "Dot Dash", "Short Dash", "Long Dash", "Odd Dash", and
% "Grid Dotted".
%
% .item "Line Color"
% This stringlist selection lets you set the foreground color
% of the line object, using the color name.
%
% .item "X Begin"
% This double value specifies the x location of the beginning of
% the line segment in world coordinates.
%
% .item "X End"
% This double value specifies the x location of the end of
% the line segment in world coordinates.
%
% .item "Y Begin"
% This double value specifies the y location of the beginning of
% the line segment in world coordinates.
%
% .item "Y End"
% This double value specifies the y location of the end of
% the line segment in world coordinates.
%
% .end tagged

function varargout = kLine(varargin)
Inputs={};
if nargin ==0
  arglist={'',''};
elseif nargin ==1
  arglist=varargin{1};
else error('Usage: [out1,..] = kLine(arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={};
maxval={};
minval={};
istoggle=[];
was_set=istoggle * 0;
paramtype={};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=0; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

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


function jnii=jnifticreate(varargin)
%
%    jnii=jnifticreate
%       or
%    jnii=jnifticreate('header1', value1, 'header2', value2, ...)
%    jnii=jnifticreate(img, 'header1', value1, ...)
%
%    Create a default JNIfTI structure with default header and image volume
%
%    author: Qianqian Fang (q.fang <at> neu.edu)
%
%    input:
%        img: set the jnii.NIFTIData section
%        'header_i': the header subfield name defined in the JNIfTI
%                    specification, see https://github.com/fangq/jnifti
%        value_i: set the value for the specified JNIfTI header field
%
%    output:
%        jnii: without any input, jnii gives the default jnii header
%              if img is given, jnii also includes the NIFTIData field
%
%
%    this file is part of JNIfTI specification: https://github.com/fangq/jnifti
%
%    License: Apache 2.0, see https://github.com/fangq/jnifti for details
%

jnii=struct(encodevarname('_DataInfo_'),struct(),'NIFTIHeader',struct(), 'NIFTIData', []);

% jnii.NIFTIHeader.NIIHeaderSize=  0;
% jnii.NIFTIHeader.A75DataTypeName=   'uint8';
% jnii.NIFTIHeader.A75DBName=      '';
% jnii.NIFTIHeader.A75Extends=     0;
% jnii.NIFTIHeader.A75SessionError='';
% jnii.NIFTIHeader.A75Regular=     0;
jnii.NIFTIHeader.DimInfo.Freq=   0;
jnii.NIFTIHeader.DimInfo.Phase=  0;
jnii.NIFTIHeader.DimInfo.Slice=  0;
jnii.NIFTIHeader.Dim=            [];
jnii.NIFTIHeader.Param1=         0;
jnii.NIFTIHeader.Param2=         0;
jnii.NIFTIHeader.Param3=         0;
jnii.NIFTIHeader.Intent=         '';
jnii.NIFTIHeader.DataType=       'uint8';
jnii.NIFTIHeader.BitDepth=       8;
jnii.NIFTIHeader.FirstSliceID=   1;
jnii.NIFTIHeader.VoxelSize=      [1,1,1,1];
jnii.NIFTIHeader.Orientation=    struct('x','r','y','a','z','s');
% jnii.NIFTIHeader.NIIByteOffset=  0;
jnii.NIFTIHeader.ScaleSlope=     0;
jnii.NIFTIHeader.ScaleOffset=    0;
jnii.NIFTIHeader.LastSliceID=    1;
jnii.NIFTIHeader.SliceType=      '';
jnii.NIFTIHeader.Unit=           struct('L','mm','T', 's');
jnii.NIFTIHeader.MaxIntensity=   255;
jnii.NIFTIHeader.MinIntensity=   0;
jnii.NIFTIHeader.SliceTime=      1;
jnii.NIFTIHeader.TimeOffset=     0;
% jnii.NIFTIHeader.A75GlobalMax=  255;
% jnii.NIFTIHeader.A75GlobalMin=   0;
jnii.NIFTIHeader.Description=    '';
% jnii.NIFTIHeader.AuxFile=        '';
jnii.NIFTIHeader.QForm=          0;
jnii.NIFTIHeader.SForm=          1;
jnii.NIFTIHeader.Quatern.b=      0;
jnii.NIFTIHeader.Quatern.c=      0;
jnii.NIFTIHeader.Quatern.d=      0;
jnii.NIFTIHeader.QuaternOffset.x=0;
jnii.NIFTIHeader.QuaternOffset.y=0;
jnii.NIFTIHeader.QuaternOffset.z=0;
jnii.NIFTIHeader.Affine(1,:)=    [1 0 0 0];
jnii.NIFTIHeader.Affine(2,:)=    [0 1 0 0];
jnii.NIFTIHeader.Affine(3,:)=    [0 0 1 0];
jnii.NIFTIHeader.Name=           'default';
jnii.NIFTIHeader.NIIFormat=      'jnifti';
% jnii.NIFTIHeader.NIIExtender=    [0,0,0,0];

datainfo.JNIFTIVersion='0.5';
datainfo.Comment='Created by JNIFTY Toolbox (https://github.com/NeuroJSON/jnifty)';
datainfo.AnnotationFormat='https://github.com/NeuroJSON/jnifti/blob/master/JNIfTI_specification.md';
datainfo.SerialFormat='http://json.org';
datainfo.Parser=struct('Python',[], ...
                       'MATLAB',[], ...
                       'JavaScript', 'https://github.com/NeuroJSON/jsdata',...
                       'CPP', 'https://github.com/NeuroJSON/json',...
                       'C', 'https://github.com/NeuroJSON/ubj');
datainfo.Parser.Python={'https://pypi.org/project/jdata','https://pypi.org/project/bjdata'};
datainfo.Parser.MATLAB={'https://github.com/NeuroJSON/jnifty','https://github.com/NeuroJSON/jsonlab'};
datainfo.JNIFTIVersion='0.5';
jnii.(encodevarname('_DataInfo_'))=datainfo;

if(nargin==0)
    return;
end

img=[];
pid=1;
if(~ischar(varargin{1}))
    img=varargin{1};
    pid=2;
end

if(~isempty(varargin))
     for i=pid:2:length(varargin)
         jnii.NIFTIHeader.(varargin{i})=varargin{i+1};
     end
end

if(~isnumeric(img) && ~islogical(img))
    error('img input must be a numerical or logical array');
end

jnii.NIFTIHeader.Dim=size(img);
jnii.NIFTIHeader.DataType=class(img);
info=whos('img');
jnii.NIFTIHeader.BitDepth=info.bytes/numel(img)*8;
jnii.NIFTIHeader.MinIntensity=min(img(:));
jnii.NIFTIHeader.MaxIntensity=max(img(:));

jnii.NIFTIData=img;


addpath './lib';
addpath './lib/gnss';
addpath './lib/calbiration';
addpath './lib/plot';
addpath './lib/rotation';
addpath './lib/geo';
savepath


function ExpFiles = io_expand_filenames(FileFilter, Files)
% Expands a list of paths :
%    1) If it is a file => keep it unchanged
%    2) If they are .ds/ directory with .meg4/.res4 files,
%       or ERPCenter directories with .hdr/.erp files,
%       => keep them as "files to open"
%    3) Else : add all the data files they contains (subdirectories included)

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

% Returned variable
ExpFiles = {};
% Get file format
FileFormat = char(FileFilter.getFormatName());
% Loop on all the selected items
for i = 1:length(Files)
    % DIRECTORIES
    if isdir(Files{i})
        isDirFile = 0;
        % If CTF/ERP format
        if ismember(FileFormat, {'CTF', 'EEG-ERPCENTER', 'EEG-NEURALYNX', 'EEG-NEURONE'})
            switch (FileFormat)
                case 'CTF'
                    % Check if there are .meg4 or .res4 files in directory
                    dirFiles = [dir(bst_fullfile(Files{i}, '*.meg4'));
                                dir(bst_fullfile(Files{i}, '*.res4'))];
                case 'EEG-ERPCENTER'
                    % Check if there are .hdr file in directory
                    dirFiles = dir(bst_fullfile(Files{i}, '*.hdr'));
                case 'EEG-NEURALYNX'
                    % Check if there are .ncs or .nse files in directory
                    dirFiles = [dir(bst_fullfile(Files{i}, '*.ncs'));
                                dir(bst_fullfile(Files{i}, '*.nse'))];
                case 'EEG-NEURONE'
                    % Get binary files 1-9.bin
                    dirFiles = [dir(bst_fullfile(Files{i}, '1.bin')); dir(bst_fullfile(Files{i}, '2.bin')); dir(bst_fullfile(Files{i}, '3.bin')); dir(bst_fullfile(Files{i}, '4.bin')); dir(bst_fullfile(Files{i}, '5.bin'));
                                dir(bst_fullfile(Files{i}, '6.bin')); dir(bst_fullfile(Files{i}, '7.bin')); dir(bst_fullfile(Files{i}, '8.bin')); dir(bst_fullfile(Files{i}, '9.bin'))];
            end
            j = 1;
            while (~isDirFile && (j <= length(dirFiles)))
                if FileFilter.accept(java.io.File(bst_fullfile(Files{i}, dirFiles(j).name)))
                    isDirFile = 1;
                    ExpFiles = cat(2, ExpFiles, Files{i});
                else
                    j = j + 1;
                end
            end
        end

        % If directory is not a CTF/ERP dir to be opened
        if ~isDirFile
            % Get all files in this directory
            dirFiles = dir(bst_fullfile(Files{i}, '*'));
            % Build a new files list
            dirFullFiles = {};
            for j = 1:length(dirFiles)
                % Skip all the files that start with a '.'
                if (dirFiles(j).name(1) == '.')
                    continue;
                end
                % Skip all the 4D system files
                if strcmpi(FileFormat, '4D') && ismember(dirFiles(j).name, {'config', 'hs_file'})
                    continue;
                end
                dirFullFiles{end + 1} = bst_fullfile(Files{i}, dirFiles(j).name);
            end
            % And call function recursively
            ExpFiles = cat(2, ExpFiles, io_expand_filenames(FileFilter, dirFullFiles));
        end

    % SINGLE FILE
    else
        % Test if is accepted by FileFilter
        if FileFilter.accept(java.io.File(Files{i}))
            % If single file is CTF : get the parent
            if strcmpi(FileFormat, 'CTF') || strcmpi(FileFormat, 'EEG-NEURALYNX')
                ExpFiles{end+1} = bst_fileparts(Files{i});
            else
                ExpFiles{end+1} = Files{i};
            end
        end
    end
end


%NAVIERSTOKES Navier-Stokes equations physics mode.

% Copyright 2013-2022 Precise Simulation, Ltd.


function [ fn ] = getBatchWrapper_ucf101_flow(opts, numThreads, trainopts)

fn = @(imdb,batch,valopts,frameList) getBatch(imdb,batch,opts,numThreads, trainopts, valopts) ;
end

% -------------------------------------------------------------------------
function [im,labels] = getBatch(imdb, batch, opts, numThreads, trainopts, moreopts)
% -------------------------------------------------------------------------
opts.nFrames = imdb.images.nFrames(batch);

images = cell(1,numel(opts.nFrames));
labels = imdb.images.label(batch) ;
for k = 1:numel(opts.nFrames)
  images{k}  = strcat([imdb.images.name{batch(k)}(1:end-4) filesep], images{k} ) ;
end
if ~isempty(moreopts) || nargin < 6
  for f = fieldnames(moreopts)'
    f = char(f) ;
    trainopts.(f) = moreopts.(f);
  end
end

opts.nFramesPerVid = trainopts.nFramesPerVid;
opts.numAugments = trainopts.numAugments;
opts.frameSample = trainopts.frameSample;
opts.augmentation =  trainopts.augmentation;

if isfield(trainopts, 'temporalStride')
  opts.temporalStride =  trainopts.temporalStride(randi(numel(trainopts.temporalStride))) ; % shuffle;
end
if isfield(trainopts, 'nFrameStack')
  opts.nFrameStack = trainopts.nFrameStack;
else
  opts.nFrameStack = trainopts.nFramesPerVid;
end
if isfield(trainopts, 'keepFramesDim')
  keepFramesDim = trainopts.keepFramesDim;
else
  keepFramesDim = false;
end
if isfield(trainopts, 'cheapResize')
  opts.cheapResize =  trainopts.cheapResize;
end

if isfield(trainopts, 'prefetch')
    opts.prefetch = trainopts.prefetch;
end
if isfield(trainopts, 'frameList')
    opts.frameList = trainopts.frameList;
end

im = cnn_ucf101_get_flow_batch(images, opts, ...
                            'numThreads', numThreads, ...
                            'augmentation', opts.augmentation , ...
                          'imageDir', imdb.flowDir) ;
if iscell(im), return; end

if nargout == 1 % to work with dag code
  if ndims(im) > 4 && ~keepFramesDim
    sz = size(im);
    nFrames = sz(5:end);
    im = permute(im, [1 2 3 5 4]);
    im = reshape(im, sz(1), sz(2), sz(3), []);
  end
  im = {'input', im, 'label', labels } ;
end

end


function opts = mkopts_bj(T);

opts.algorithm = 'bj';
opts.use_kd_tree = 0;
opts.sis = 20;
opts.initial_K = T;
opts.do_greedy = 0;
opts.do_split = 0;
opts.do_merge = 0;
opts.do_sort = 0;

% Local Variables: ***
% mode: matlab ***
% End: ***


% ------------------------------------------------------------------------
%  Copyright (C)
%  Universitat Politecnica de Catalunya BarcelonaTech (UPC) - Spain
%  University of California Berkeley (UCB) - USA
%
%  Jordi Pont-Tuset <jordi.pont@upc.edu>
%  Pablo Arbelaez <arbelaez@berkeley.edu>
%  June 2014
% ------------------------------------------------------------------------
% This file is part of the MCG package presented in:
%    Arbelaez P, Pont-Tuset J, Barron J, Marques F, Malik J,
%    "Multiscale Combinatorial Grouping,"
%    Computer Vision and Pattern Recognition (CVPR) 2014.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
%
% Script to compute MCG or SCG candidates on a whole dataset
%
% ------------------------------------------------------------------------
function im2mcg_all(mode, database, gt_set)
if nargin==0
    mode = 'fast';
end
if nargin<2
    database = 'pascal2012';
end
if nargin<3
    gt_set = 'val2012';
end

% Create out folder
if strcmp(mode,'fast')
    res_dir = fullfile(root_dir,'datasets',database,'SCG');
elseif strcmp(mode,'accurate')
    res_dir = fullfile(root_dir,'datasets',database,'MCG');
else
    error('Unknown mode for MCG: Possibilities are ''fast'' or ''accurate''')
end
if ~exist(res_dir,'dir')
    mkdir(res_dir);
end

% Load which images to consider from the database (train, val, etc.)
im_ids = database_ids(database,gt_set);

% Sweep all images and process them in parallel
num_workers = 4;
if isempty(gcp('nocreate'))
    parpool('local', num_workers)
end
num_images = length(im_ids);
parfor im_id = 1:num_images
    % File to store the candidates
    res_file   = fullfile(res_dir,[im_ids{im_id} '.mat']);

    % Do not recompute if already computed
    if ~exist(res_file,'file')
        % Read the image
        image = get_image(database, im_ids{im_id});

        % Call the MCG code
        candidates = im2mcg(image,mode);

        % Store the masks results
        parsave(res_file,candidates);
    end
end
delete(gcp('nocreate'))
end

function parsave(res_file,candidates)
    scores = candidates.scores; %#ok<NASGU>
    bboxes = candidates.bboxes; %#ok<NASGU>
    superpixels = candidates.superpixels; %#ok<NASGU>
    labels = candidates.labels; %#ok<NASGU>
    save(res_file,'scores','bboxes','superpixels','labels');
end


% Main layout-manager class
classdef GridLayout < handle

    properties
        Container

        NumRows
        NumCols
        RowHeight
        ColWidth
        HGap
        VGap
        LMargin
        RMargin
        TMargin
        BMargin

        % Sizing policies
        % 0: Absolute
        % 1: Proportional
        % 2: Automatic

        RowHeightPolicy
        RowHeightAbsolute
        RowHeightWeight

        ColWidthPolicy
        ColWidthAbsolute
        ColWidthWeight
    end

    properties (Constant)
        MinRowHeight = 5
        MinColWidth = 5
    end

    properties (Dependent)
        ActualWidth
        ActualHeight
        RequiredWidth
        RequiredHeight
    end

    properties (SetAccess = private)
        Cell  % Cell container
    end

    methods
        function Obj = GridLayout(Parent, varargin)
            Names = { ...
                'NumRows' ...
                'NumCols' ...
                'RowHeight' ...
                'ColWidth' ...
                'CellColor' ...
                'Gap' ...
                'HGap' ...
                'VGap' ...
                'Margin' ...
                'LMargin' ...
                'RMargin' ...
                'TMargin' ...
                'BMargin'};
            NamesCell = CellProps.GetCellParamNames();
            [P, PC] = ParseArgs(varargin, Names, NamesCell);

            assert(~isempty(Parent), ...
                'Parent handle cannot be empty.');

            Obj.Container = uicontainer( ...
                'Parent', Parent, ...
                'ResizeFcn', @(hsrc,ev)Update(Obj,true), ...
                'BackgroundColor', P.CellColor);
            Position = getpixelposition(Obj.Container);
            setappdata(Obj.Container,'Position',Position);

            % NumRows
            DefaultNumRows = 1;
            if ~isempty(P.RowHeight) && iscell(P.RowHeight)
                DefaultNumRows = length(P.RowHeight);
            end
            Obj.NumRows = GetArg(P,'NumRows',DefaultNumRows);
            % NumCols
            DefaultNumCols = 1;
            if ~isempty(P.ColWidth) && iscell(P.ColWidth)
                DefaultNumCols = length(P.ColWidth);
            end
            Obj.NumCols = GetArg(P,'NumCols',DefaultNumCols);
            % RowHeight
            Value = GetArg(P,'RowHeight','*');
            if ~iscell(Value)
                Obj.RowHeight = cell(1,Obj.NumRows);
                [Obj.RowHeight{1:end}] = deal(Value);
            else
                Obj.RowHeight = P.RowHeight;
            end
            % ColWidth
            Value = GetArg(P,'ColWidth','*');
            if ~iscell(Value)
                Obj.ColWidth = cell(1,Obj.NumCols);
                [Obj.ColWidth{1:end}] = deal(Value);
            else
                Obj.ColWidth = P.ColWidth;
            end
            % HGap
            Obj.HGap = GetArg(P,'HGap',5);
            % VGap
            Obj.VGap = GetArg(P,'VGap',5);
            % Gap (overrides HGap, VGap)
            if ~isempty(P.Gap)
                Obj.HGap = P.Gap;
                Obj.VGap = P.Gap;
            end
            % Margin
            if isempty(P.Margin)
                DefaultLMargin = Obj.HGap;
                DefaultRMargin = Obj.HGap;
                DefaultTMargin = Obj.VGap;
                DefaultBMargin = Obj.VGap;
            else
                DefaultLMargin = P.Margin;
                DefaultRMargin = P.Margin;
                DefaultTMargin = P.Margin;
                DefaultBMargin = P.Margin;
            end
            Obj.LMargin = GetArg(P,'LMargin',DefaultLMargin);
            Obj.RMargin = GetArg(P,'RMargin',DefaultRMargin);
            Obj.TMargin = GetArg(P,'TMargin',DefaultTMargin);
            Obj.BMargin = GetArg(P,'BMargin',DefaultBMargin);

            % Create array of cell containers
            Obj.Cell = zeros(Obj.NumRows,Obj.NumCols);
            for RIdx = 1:Obj.NumRows
                for CIdx = 1:Obj.NumCols
                    ThisCell = uicontainer( ...
                        'Parent', Obj.Container, ...
                        'Units', 'pixels');
                    if ~isempty(P.CellColor)
                        set(ThisCell, 'BackgroundColor', P.CellColor);
                    end
                    Props = CellProps(PC);
                    setappdata(ThisCell,'Props',Props);
                    setappdata(ThisCell,'RSpan',[RIdx RIdx]);
                    setappdata(ThisCell,'CSpan',[CIdx CIdx]);
                    Obj.Cell(RIdx,CIdx) = ThisCell;
                end
            end

            % RowHeightPolicy
            Obj.RowHeightPolicy   = zeros(1,Obj.NumRows);
            Obj.RowHeightAbsolute = zeros(1,Obj.NumRows);
            Obj.RowHeightWeight   = zeros(1,Obj.NumRows);
            for RIdx = 1:Obj.NumRows
                RHeight = Obj.RowHeight{RIdx};
                if isnumeric(RHeight)
                    % Absolute
                    Obj.RowHeightPolicy(RIdx) = 0;
                    Obj.RowHeightAbsolute(RIdx) = max(RHeight,Obj.MinRowHeight);
                elseif ischar(RHeight) && RHeight(end) == '*'
                    % Proportional
                    Obj.RowHeightPolicy(RIdx) = 1;
                    Weight = RHeight(1:end-1);
                    if isempty(Weight)
                        Weight = '1';
                    end
                    Obj.RowHeightWeight(RIdx) = str2double(Weight);
                elseif ischar(RHeight) && isequal(RHeight,'a')
                    % Automatic
                    Obj.RowHeightPolicy(RIdx) = 2;
                else
                    error('Illegal RowHeight definition.');
                end
            end

            % ColWidthPolicy
            Obj.ColWidthPolicy   = zeros(1,Obj.NumCols);
            Obj.ColWidthAbsolute = zeros(1,Obj.NumCols);
            Obj.ColWidthWeight   = zeros(1,Obj.NumCols);
            for CIdx = 1:Obj.NumCols
                CWidth = Obj.ColWidth{CIdx};
                if isnumeric(CWidth)
                    % Absolute
                    Obj.ColWidthPolicy(CIdx) = 0;
                    Obj.ColWidthAbsolute(CIdx) = max(CWidth,Obj.MinColWidth);
                elseif ischar(CWidth) && CWidth(end) == '*'
                    % Proportional
                    Obj.ColWidthPolicy(CIdx) = 1;
                    Weight = CWidth(1:end-1);
                    if isempty(Weight)
                        Weight = '1';
                    end
                    Obj.ColWidthWeight(CIdx) = str2double(Weight);
                elseif ischar(CWidth) && isequal(CWidth,'a')
                    % Automatic
                    Obj.ColWidthPolicy(CIdx) = 2;
                else
                    error('Illegal ColWidth definition.');
                end
            end

            if ~isempty(Obj.RequiredHeight)
                HeightDiff = Obj.RequiredHeight-Obj.ActualHeight;
                Position = getpixelposition(Parent);
                Position(4) = Position(4) + HeightDiff;
                setpixelposition(Parent, Position);
            end
        end

        function MergeCells(Obj, RSpan, CSpan)
            if isempty(RSpan)
                RSpan = [1 Obj.NumRows];
            end
            if isscalar(RSpan)
                RSpan = [RSpan RSpan];
            end
            if isempty(CSpan)
                CSpan = [1 Obj.NumCols];
            end
            if isscalar(CSpan)
                CSpan = [CSpan CSpan];
            end
            for RIdx = RSpan(1):RSpan(2)
                for CIdx = CSpan(1):CSpan(2)
                    ThisCell = Obj.Cell(RIdx,CIdx);
                    if RIdx == RSpan(1) && CIdx == CSpan(1)
                        setappdata(ThisCell,'RSpan',RSpan);
                        setappdata(ThisCell,'CSpan',CSpan);
                    else
                        delete(ThisCell);
                    end
                end
            end
        end

        function RemoveCells(Obj, RSpan, CSpan)
            if isempty(RSpan)
                RSpan = [1 Obj.NumRows];
            end
            if isscalar(RSpan)
                RSpan = [RSpan RSpan];
            end
            if isempty(CSpan)
                CSpan = [1 Obj.NumCols];
            end
            if isscalar(CSpan)
                CSpan = [CSpan CSpan];
            end
            for RIdx = RSpan(1):RSpan(2)
                for CIdx = CSpan(1):CSpan(2)
                    ThisCell = Obj.Cell(RIdx,CIdx);
                    if ishghandle(ThisCell)
                        delete(ThisCell);
                    end
                end
            end
        end

        function FormatCells(Obj, RSpan, CSpan, varargin)
            if isscalar(RSpan)
                RSpan = [RSpan RSpan];
            end
            if isscalar(CSpan)
                CSpan = [CSpan CSpan];
            end
            for RIdx = RSpan(1):RSpan(2)
                for CIdx = CSpan(1):CSpan(2)
                    ThisCell = Obj.Cell(RIdx,CIdx);
                    if ~ishghandle(ThisCell)
                        continue; % Cell is removed
                    end
                    GridLayout.FormatCell(ThisCell, varargin{:});
                end
            end
        end

        function Update(Obj, IsCallback)
            if nargin < 2
                IsCallback = false;
            end
            % Layout size
            NewPosition = getpixelposition(Obj.Container);
            OldPosition = getappdata(Obj.Container,'Position');
            if isequal(NewPosition,OldPosition) && IsCallback
                return;
            end
            setappdata(Obj.Container,'Position',NewPosition);
            LayoutWidth  = NewPosition(3);
            LayoutHeight = NewPosition(4);

            % Get children cells; as a vector!
            Cells = get(Obj.Container,'Children');
            % Get their row and column spans
            RSpan = zeros(length(Cells),2);
            CSpan = zeros(length(Cells),2);
            for i = 1:length(Cells)
                RSpan(i,:) = getappdata(Cells(i),'RSpan');
                CSpan(i,:) = getappdata(Cells(i),'CSpan');
            end

            % Determine column widths
            CWidthSum = LayoutWidth-(Obj.NumCols-1)*Obj.VGap-Obj.LMargin-Obj.RMargin;
            CWidth = zeros(1,Obj.NumCols);
            % 1. Absolute
            CMask = Obj.ColWidthPolicy == 0;
            CWidth(CMask) = Obj.ColWidthAbsolute(CMask);
            % 2. Automatic
            for CIdx = find(Obj.ColWidthPolicy == 2)
                MaxWidth = 0;
                Mask = CSpan(:,1) == CIdx & CSpan(:,2) == CIdx;
                assert(sum(Mask) > 0, ...
                    'Any column with automatic width must have at least one non-merged cell.');
                for MyCell = Cells(Mask)'
                    Props = getappdata(MyCell,'Props');
                    Width = Props.LMargin+Props.RMargin;
                    Child = get(MyCell,'Children');
                    if ~isempty(Child)
                        ChildSize = get(Child,'Position');
                        Width = Width + ChildSize(3);
                    end
                    MaxWidth = max(MaxWidth,Width);
                end
                CWidth(CIdx) = MaxWidth;
            end
            % 3. Proportional
            CWidthProportionalMask = Obj.ColWidthPolicy == 1;
            CWidthSumProportional = CWidthSum - sum(CWidth);
            CWidthSumProportional = max(CWidthSumProportional,0);
            CWidthWeight = Obj.ColWidthWeight(CWidthProportionalMask);
            CWidth(CWidthProportionalMask) = CWidthSumProportional*CWidthWeight/sum(CWidthWeight);
            CWidth = max(CWidth, Obj.MinColWidth);

            % Determine row heights
            RHeightSum = LayoutHeight-(Obj.NumRows-1)*Obj.HGap-Obj.TMargin-Obj.BMargin;
            RHeight = zeros(1,Obj.NumRows);
            % 1. Absolute
            RMask = Obj.RowHeightPolicy == 0;
            RHeight(RMask) = Obj.RowHeightAbsolute(RMask);
            % 2. Automatic
            for RIdx = find(Obj.RowHeightPolicy == 2)
                MaxHeight = 0;
                Mask = RSpan(:,1) == RIdx & RSpan(:,2) == RIdx;
                assert(sum(Mask) > 0, ...
                    'Any row with automatic height must have at least one non-merged cell.');
                for MyCell = Cells(Mask)'
                    Props = getappdata(MyCell,'Props');
                    Height = Props.TMargin+Props.BMargin;
                    Child = get(MyCell,'Children');
                    if ~isempty(Child)
                        ChildSize = get(Child,'Position');
                        Height = Height + ChildSize(4);
                    end
                    MaxHeight = max(MaxHeight,Height);
                end
                RHeight(RIdx) = MaxHeight;
            end
            % 3. Proportional
            RMask = Obj.RowHeightPolicy == 1;
            RHeightSumProportional = RHeightSum - sum(RHeight);
            RHeightSumProportional = max(RHeightSumProportional,0);
            Weight = Obj.RowHeightWeight(RMask);
            RHeight(RMask) = RHeightSumProportional*Weight/sum(Weight);
            RHeight = max(RHeight, Obj.MinRowHeight);

            % Horizontal and vertical offsets for each cell
            CellOffsetH = cumsum([Obj.LMargin CWidth(1:end-1)+Obj.VGap]);
            CellOffsetV = LayoutHeight-cumsum([RHeight(1)+Obj.BMargin RHeight(2:end)+Obj.HGap]);

            % Set cell positions
            for i = 1:length(Cells)
                RIdx  = RSpan(i,1):RSpan(i,2);
                CIdx  = CSpan(i,1):CSpan(i,2);

                set(Cells(i), ...
                    'Position', [ ...
                        CellOffsetH(CIdx(1)) ...
                        CellOffsetV(RIdx(end)) ...
                        sum(CWidth(CIdx))+Obj.VGap*(length(CIdx)-1) ...
                        sum(RHeight(RIdx))+Obj.HGap*(length(RIdx)-1)]);

                GridLayout.ResizeCell(Cells(i));
            end
        end

        function Value = get.ActualWidth(Obj)
            Position = getpixelposition(Obj.Container);
            Value = Position(3);
        end
        function Value = get.ActualHeight(Obj)
            Position = getpixelposition(Obj.Container);
            Value = Position(4);
        end

        function Value = get.RequiredWidth(Obj)
            if any(Obj.ColWidthPolicy == 1)
                Value = [];
                return;
            end
            Mask = Obj.ColWidthPolicy == 0;
            Value = sum(Obj.ColWidthAbsolute(Mask)) + Obj.HGap*(Obj.NumCols-1) + Obj.LMargin + Obj.RMargin;
        end
        function Value = get.RequiredHeight(Obj)
            if any(Obj.RowHeightPolicy == 1)
                Value = [];
                return;
            end
            Mask = Obj.RowHeightPolicy == 0;
            Value = sum(Obj.RowHeightAbsolute(Mask)) + Obj.VGap*(Obj.NumRows-1) + Obj.TMargin + Obj.BMargin;
        end
    end

    methods (Static)
        function FormatCell(Cell, varargin)
            assert(isscalar(Cell) && ishghandle(Cell), ...
                'Cell must be a hghandle.');
            Props = getappdata(Cell,'Props');
            for i = 1:2:length(varargin)
                Props.(varargin{i}) = varargin{i+1};
            end
            setappdata(Cell,'Props',Props);
        end
    end

    methods (Static, Access = private)
        function ResizeCell(Src)
            Child = get(Src,'Children');
            if isempty(Child)
                return;
            end

            % Special case for axes with legend
            % The legend has the same parent as the axes!
            if ~isscalar(Child)
                assert(numel(Child) == 2 && ...
                    ishghandle(Child(1),'axes') && strcmp(get(Child(1),'Tag'),'legend') && ...
                    ishghandle(Child(2),'axes'), ...
                    'If Cell has two children, they must be an ''axes'' and its legend.');
                Child = Child(2);
            end

            % Special treatment for axes objects
            IsChildAxes = ishghandle(Child,'axes');
            if IsChildAxes
                OldAxesUnits = get(Child,'Units');
                set(Child,'Units','Pixels');
                UseOuterPosition = strcmp(get(Child,'ActivePositionProperty'),'outerposition');
                if UseOuterPosition
                    ChildPosition = get(Child,'OuterPosition');
                else
                    ChildPosition = get(Child,'Position');
                end
            else
                ChildPosition = get(Child,'Position');
            end

            % Child offset and size
            ChildOffset = ChildPosition(1:2);
            ChildSize = ChildPosition(3:4);
            % Cell size
            CellPosition = getpixelposition(Src);
            CellSize = CellPosition(3:4);
            % Cell propoerties
            Props = getappdata(Src,'Props');
            % Horizontal alignment
            HAlign  = Props.HAlign;
            LMargin = Props.LMargin;
            RMargin = Props.RMargin;
            if strcmp(HAlign,'Left')
                ChildOffset(1) = LMargin;
            elseif strcmp(HAlign,'Right')
                ChildOffset(1) = CellSize(1)-ChildSize(1)-RMargin;
            elseif strcmp(HAlign,'Center')
                ChildOffset(1) = (CellSize(1)-ChildSize(1))/2;
            else % Stretch
                ChildOffset(1) = LMargin;
                ChildSize(1) = CellSize(1)-LMargin-RMargin;
            end
            % Vertical alignment
            VAlign  = Props.VAlign;
            TMargin = Props.TMargin;
            BMargin = Props.BMargin;
            if strcmp(VAlign,'Bottom')
                ChildOffset(2) = BMargin;
            elseif strcmp(VAlign,'Top')
                ChildOffset(2) = CellSize(2)-ChildSize(2)-TMargin;
            elseif strcmp(VAlign,'Center')
                ChildOffset(2) = (CellSize(2)-ChildSize(2))/2;
            else % Stretch
                ChildOffset(2) = BMargin;
                ChildSize(2) = CellSize(2)-TMargin-BMargin;
            end
            % Prevent non-positive sizes
            ChildSize = max(ChildSize,.1);

            % Update child position
            ChildPosition = [ChildOffset ChildSize];
            if IsChildAxes
                if UseOuterPosition
                    set(Child,'OuterPosition',ChildPosition);
                else
                    set(Child,'Position',ChildPosition);
                end
                set(Child,'Units',OldAxesUnits);
            else
                set(Child,'Position',ChildPosition);
            end
        end

    end
end

% EOF


% Read NHDR/NRRD files
%
% headerInfo = nhdr_nrrd_read(nrrdFileName, bReadData)
%
% Inputs:
% * nrrdFileName (char): path to NHDR/NRRD file, either a detached header,
% a detached header pointing to detached data files or a NRRD standalone
% file with header and data included
% * bReadData (bool): set to true to read the data and store it in
% headerInfo.data, set to false to just import the header without the data
%
% Outputs:
% * headerInfo (struct): contains all the fields specified in the file
% header. If data was read it is contained in the 'data' field. That
% structure can be fed to the nhdr_nrrd_write module as is and produce
% valid NHDR/NRRD files.
%
% Format definition:
% http://teem.sourceforge.net/nrrd/format.html
%
% A few supported NRRD features:
% - detached headers with all variants of 'data file:'
% - raw, txt/text/ascii, gz/gzip encodings
% - definition of space and orientation
% - handling of diffusion-weighted MRI data with '<key>:=<value>' lines
%   'modality:=DWMRI', 'DWMRI_b-value:=' and 'DWMRI_gradient_0000:=',
%   'DWMRI_gradient_0001:=', 'DWMRI_gradient_0002:=', etc.
%   (see https://www.na-mic.org/wiki/NAMIC_Wiki:DTI:Nrrd_format)
%
%
% Other features:
% - exits cleanly upon error (no file should be left open)
% - informative error messages whenever possible
% - warnings printed to console
% - NHDR/NRRD writer fills out missing fields by inspecting the data
%   whenever possible
%
%
% Unsupported features:
%
% In general, any header field that we were unable to parse is reported
% in a message printed to the console. Specific examples of
% unsupported features include:
%
% - reading data along more than 1 dimension or along a dimension other
%   than the slowest (last) axis specified by the optional <subdim>, as in
%   'data file: <format> <min> <max> <step> [<subdim>]' or 'data file: LIST
%   [<subdim>]'
% - storing all the comments found in headers
% - hex encoding
% - bz2/bzip2 encoding
% - byte skip; can only accept -1 with raw encoding, 0 for all other
%   encodings
% - line skip: can only accept 0
% - checking that field specifications of the form '<field>: <desc>'
%   appear no more than once in the NRRD header (unlike '<key>:=<value>'
%   lines)
%
% Date: July 2018
% Author: Gaetan Rensonnet
%
%
%
% Notes :
%
% This is intended as a more comprehensive and accurate implementation of
% the NRRD format specification than most of the Matlab scripts that have
% been proposed so far. We try to fail graciously when an unsupported
% feature of the NRRD format is encountered. One of our main contributions
% is to propose a writer module which is compatible with the read module,
% in that the output of one can be given as an argument to the other to
% read or produce equivalent NHDR/NRRD files. This is still a version with
% much room for improvement.
%
% The following contributions inspired parts of our Matlab read/write
% modules:
%
% 1. The body of the writer module was pretty much written from scratch but
% the general structure of the reader's main body is based on the Matlab
% functions maReadNrrdHeader.m and maReadNrrdData.m by marc@bwh.harvard.edu
% and kquintus@bwh.harvard.edu (unpublished). Many additions were made and
% a few bugs fixed.
%
% 2. Jeff Mather's NRRD reader
% (http://nl.mathworks.com/matlabcentral/fileexchange/34653-nrrd-format-file-reader)
% and
% http://jeffmatherphotography.com/dispatches/2012/02/writing-a-file-reader-in-matlab/)
% provided the auxiliary functions:
% - adjustEndian
% - getDatatype, which we renamed nrrd_getMatlabDataType and now throws
% a gracious error if it encounters 'block'-type data,
% - readData was adapted to include a cross-platform fix to delete
% temporary files when using gzip encoding. David Feng's fix used a
% Windows-specific command to delete temporary files
% (https://nl.mathworks.com/matlabcentral/fileexchange/50830-nrrd-format-file-reader).
%
% 3. mdcacio's nrrdWriter
% (https://nl.mathworks.com/matlabcentral/fileexchange/48621-nrrdwriter-filename--matrix--pixelspacing--origin--encoding-)
% provided the auxiliary functions:
% - writeData(): we got rid of the 'unexpected end of input stream when
% attempting to gunzip the file' error when using gzip encoding, which we
% later found had been fixed by Quan Chen independenlty and in a similar
% manner.
% - setDatatype(), which we renamed get_nrrd_datatype() and is the
% reciprocal of getDatatype
%

function headerInfo = nhdr_nrrd_read(nrrdFileName, bReadData)

[mainFpath,mainFileName,mainFext] = fileparts(nrrdFileName);

headerInfo = struct();

% default header:
headerInfo.content = mainFileName;        % default value, overwritten if content field is set
headerInfo.data = [];

fidr = fopen(nrrdFileName, 'r');

if (fidr == -1)
    error('ABORT: %s does not exist.\n', nrrdFileName);
end

try
    if ~(strcmpi(mainFext, '.nhdr') || strcmpi(mainFext, '.nrrd'))
        warning('%s looks like a %s file, not a nhdr or nrrd file.\n', nrrdFileName, mainFext );
    end

    % Magic line
    cs  = fgetl(fidr);
    assert(numel(cs) >= 8 && isequal(cs(1:4), 'NRRD'),...
        'Bad signature. First line should be magic line of type "NRRD000X" with X an integer between 1 and 5.'); % FIXME should throw an error if a bad integer is provided
    nrrd_version = sscanf(cs(5:end), '%d');
    if nrrd_version > 5
        error('This reader only supports versions of the NRRD file format up to 5. Detected %d.', nrrd_version)
    end

    % Always optional: defining orientation
    define_orientation = 0;         % internal-use variable

    % Parse header
    while ~feof(fidr)

        cs = fgetl(fidr);         % content string

        if isempty(cs)
            % End of header
            break;
        end

        if foundKeyword( 'CONTENT:', cs )

            headerInfo.content = strtrim( cs( length('CONTENT:')+1:end ) );

        elseif foundKeyword('TYPE:', cs )

            headerInfo.type = strtrim( cs( length('TYPE:')+1:end ) );

        elseif foundKeyword('ENDIAN:', cs )

            headerInfo.endian = strtrim( cs( length('ENDIAN:')+1:end ) );

        elseif foundKeyword('ENCODING:', cs )

            headerInfo.encoding = strtrim( cs( length('ENCODING:')+1:end ) );

        elseif foundKeyword('DIMENSION:', cs )

            headerInfo.dimension = sscanf( cs( length('DIMENSION:')+1:end ), '%i' );

        elseif foundKeyword('SIZES:', cs )

            iSizes = sscanf( cs(length('SIZES:')+1:end), '%i' );
            headerInfo.sizes = iSizes(:)';      % store as row vector

        elseif foundKeyword('KINDS:', cs )

            headerInfo.kinds = extractStringList( cs(length('KINDS:')+1:end) ); % bug fixed with extractStringList where 2 entries are present
            % FIXME: check that axis sizes match each kind according to nrrd standard

        elseif foundKeyword('SPACE:', cs )
            % Starts defining orientation (either space or space dimension,
            % not both)
            define_orientation = 1;

            if isfield(headerInfo, 'spacedimension')
                fprintf(['WARNING nhdr_nrrd_read %s:\n ''space'' field specifier will ' ...
                    'be checked for consistency but will be ignored afterwards ' ...
                    'because ''space dimension'' was specified before.\n'], fopen(fidr));
            end

            tmp_space = strtrim( cs( length('SPACE:')+1:end ) );
            tmp_spacedimension = nrrd_getSpaceDimensions(tmp_space);
            if tmp_spacedimension <= 0
                error('%s: unrecognized ''space'' descriptor ''%s''.', fopen(fidr), tmp_space)
            end

            if isfield(headerInfo, 'spacedimension')
                % internal_spacedimension already set
                if internal_spacedimension ~= tmp_spacedimension
                    error(['%s: ''space'' field specifier implies a spatial ' ...
                            '(world) dimension equal to %d, which differs from ' ...
                            'the ''space dimension'' field specifier set to %d.'],...
                            fopen(fidr), tmp_spacedimension, internal_spacedimension)
                end
                % if no inconsistencies found, just ignore space field
            else
                % Set space info for the first time:
                headerInfo.space = tmp_space;
                internal_spacedimension = tmp_spacedimension;  % for internal use only
            end

        elseif foundKeyword('SPACE DIMENSION:', cs)
            % Starts defining orientation (either space or space dimension,
            % not both)
            define_orientation = 1;
            if isfield(headerInfo, 'space')
                fprintf(['WARNING nhdr_nrrd_read %s:\n ''space dimension'' field specifier ' ...
                    ' will be checked for consistency but will be ignored afterwards ' ...
                    'because ''space'' was specified before.\n'],...
                    fopen(fidr));
            end

            tmp_spacedimension = sscanf( cs( length('SPACE DIMENSION:')+1:end), '%i' );
            if numel(tmp_spacedimension) ~= 1
                error(['%s: ''space dimension'' should be specified as one' ...
                    ' integer number. Found %d element(s) instead.'],...
                      fopen(fidr), numel(tmp_spacedimension))
            end
            if tmp_spacedimension <= 0
                error('%s: ''space dimension'' should be specified as a strictly positive integer (found %d).',...
                                fopen(fidr), tmp_spacedimension)
            end

            if isfield(headerInfo, 'space')
                if tmp_spacedimension ~= internal_spacedimension
                    error(['%s: ''space dimension'' field specifier set to %d, ' ...
                        'which differs from the space (world) dimension implied by the ' ...
                        '''space'' field specifier which is equal to %d.'],...
                            fopen(fidr), tmp_spacedimension, internal_spacedimension)
                end
                % if no inconsistencies found, just ignore space dimension
                % field
            else
                % Set space info for the first time:
                headerInfo.spacedimension = tmp_spacedimension;
                internal_spacedimension = tmp_spacedimension;  % for internal use only
            end

        elseif foundKeyword('SPACE DIRECTIONS:', cs )
            % Required if orientation defined but must come after space or
            % space dimension
            % space directions: <vector[0]> <vector[1]> ... <vector[dim-1]>
            % The format of the <vector> is as follows. The vector is
            % delimited by "(" and ")", and the individual components are
            % comma-separated.
            if ~define_orientation
               error('%s: field specifier ''space directions'' cannot be set before ''space'' or ''space dimension''.',fopen(fidr))
            end

            space_dir_tmp = strtrim(cs(length('SPACE DIRECTIONS:')+1:end));     % remove leading and trailing spaces

            spacedir_vecs = strsplit(space_dir_tmp); %  cell array of strings after split at {' ','\f','\n','\r','\t','\v'}
            SD_data = zeros(internal_spacedimension, internal_spacedimension);

            % Check each vector: either none or (f1,...,f_spaceDim) with fi
            % a floating-point number
            cnt_space_vectors = 0;
            for i = 1:length(spacedir_vecs)
                none_start_index = strfind(lower(spacedir_vecs{i}), 'none');
                if ~isempty(none_start_index)
                    % Axis-specific entry contains substring none
                    if ~strcmpi(spacedir_vecs{i}, 'none')
                        fprintf(['WARNING nhdr_nrrd_read: detected %s instead of ' ...
                            'expected none for axis %d of the per-axis field specifications "space directions:".\n' ...
                            ' There should be no quotation marks, parentheses or any other characters, just plain none.\n'],...
                            spacedir_vecs{i}, i);
                        % Clean none vector specification
                        spacedir_vecs{i} = 'none';
                    end
                else
                    % Axis-specific entry is a numerical vector
                    cnt_space_vectors = cnt_space_vectors + 1;
                    if cnt_space_vectors > internal_spacedimension
                        error(['%s:\n ''space directions'' field specifier: ' ...
                            'number of space vectors detected exceeds space (world)' ...
                            ' dimension, which is equal to %d.'],...
                            fopen(fidr), internal_spacedimension)
                    end
                    btw_parentheses = spacedir_vecs{i}(1) == '(' ...
                                        && spacedir_vecs{i}(end) == ')';
                    axis_vector = regexprep(spacedir_vecs{i}, '[()]', ''); % stripped off all parens
                    if ~btw_parentheses
                        fprintf(['WARNING nhdr_nrrd_read: vector should be delimited ' ...
                                'by parentheses for axis %d of the per-axis field ' ...
                                'specifications "space directions:".\n' ...
                                ' At least one missing parenthesis in ''%s''.\n'],...
                                i, spacedir_vecs{i})
                    end
                    % Clean up by forcing single enclosing parentheses:
                    spacedir_vecs{i} = ['(',  axis_vector,  ')'];
                    % Check vector and extract numerical data
                    vector_entries = strsplit(axis_vector, ',');
                    if length(vector_entries) ~= internal_spacedimension
                        error(['%s:\n vector for data axis %d (space axis %d) of the ' ...
                                'per-axis field specifications "space directions:" should ' ...
                                'contain %d entries corresponding to the space (or world) dimension ' ...
                                'specified in the "space" or "space dimension" field specification.' ...
                                ' Found %d here.'],...
                                fopen(fidr), i, cnt_space_vectors, internal_spacedimension, ...
                                length(vector_entries))
                    end
                    for j = 1:length(vector_entries)
                        vector_entry = sscanf(vector_entries{j}, '%f');
                        if isempty(vector_entry)
                            error(['%s\n in field specification "space directions:",  ' ...
                                'vector for data axis %d (space axis %d) too short. ' ...
                                'Detected %d entries instead of expected %d corresponding to ' ...
                                'space (world) dimension.'],...
                                fopen(fidr), i, cnt_space_vectors, j-1, internal_spacedimension)
                        end
                        SD_data(j, cnt_space_vectors) = vector_entry;
                    end
                end
            end
            % Store array of cleaned up strings
            headerInfo.spacedirections = spacedir_vecs;               % cell array of strings, ideally of the form {'(1,0,0)' '(0,2,0.1)' 'none'  '(0.1,0.1,1.1)'} if internal_spacedimension==3

            % Extract numerical data more leniently:
            SD_data_chk = strrep(space_dir_tmp, 'none', '' );                       % ignore "none" entries
            SD_data_chk = extractNumbersWithout(SD_data_chk, {'(',')',',', '"', ''''} );           % detects numbers up to first non numerical entry
            if numel(SD_data_chk) ~= (internal_spacedimension)^2
                   error(['Expected ''space directions'' to specify a %d-by-%d matrix ' ...
                       '(%d elements in total) in agreement with world space dimension.' ...
                       ' Found %d element(s) instead.\n'],...
                            internal_spacedimension, internal_spacedimension,...
                            (internal_spacedimension)^2, numel(SD_data_chk));
            end

            % Sanity check
            if ~isequal(SD_data_chk(:), SD_data(:))
               error(['%s:\n ''space directions'' field specifier: couldn''t' ...
                   ' read space vectors. Please refer to the NRRD format definition.'],...
                   fopen(fidr))
            end
            headerInfo.spacedirections_matrix = SD_data;
            % Correctness of field specification is checked below after
            % whole header is parsed because we need to be sure that the
            % "dimension" basic field specification was set

        elseif foundKeyword('SPACE UNITS:', cs )
            % Always optional, must come after space or space dimension
            if define_orientation ~= 1
                error('Field specification ''space units'' cannot be specified before ''space'' or ''space dimension''.')
            end

            space_units_tmp = strrep( cs(length('SPACE UNITS:')+1:end), 'none', '');        % ignore none entries
            % FIXME:  ideally, check that the sum of elements including none and
            % " " matches headerInfo.dimension, i.e. the total dimension, as
            % specified in the standard. Standard a bit unclear: should
            % unknown units be specified with "???", "none" or "" ? (quotes
            % seem to be required).

            headerInfo.spaceunits = extract_spaceunits_list( space_units_tmp );

            if length(headerInfo.spaceunits) ~= internal_spacedimension
               error(['Expected ''space units'' to contain %d elements enclosed in double quotes' ...
                   ' to match the ''space'' or ''space dimension'' field but found the following ' ...
                   '%d element(s):\n%s'],...
                   internal_spacedimension, length(headerInfo.spaceunits), sprintf('%s\t',headerInfo.spaceunits{:}))
            end

        elseif foundKeyword('SPACE ORIGIN:', cs )
            % Always optional, must come after space or space dimension

            assert(define_orientation==1, ...
                    sprintf(['%s: field ''space origin'' cannot be specified' ...
                            ' before ''space'' or ''space dimension''.'], ...
                            fopen(fidr)));

            iSO = extractNumbersWithout( cs(length('SPACE ORIGIN:')+1:end), {'(',')',','} );

            assert(numel(iSO) == internal_spacedimension,...
                    sprintf(['%s: expected ''space origin'' to specify a ' ...
                            '%d-element vector to match the ''space'' or ' ...
                            '''space dimension'' field but found %d ' ...
                            'element(s).'],...
                            fopen(fidr),internal_spacedimension, numel(iSO)) );

            headerInfo.spaceorigin = iSO(:);

        elseif foundKeyword('MEASUREMENT FRAME:', cs )
            % Always optional, must come after space or space dimension

            assert(define_orientation==1,...
                    sprintf(['%s: field ''measurement frame'' cannot be ' ...
                            'specified before ''space'' or ''space dimension''.'],...
                            fopen(fidr)));

            measframe_str = strrep( cs(length('MEASUREMENT FRAME:')+1:end), 'none', '');

            iMF = extractNumbersWithout( measframe_str, {'(',')',','} ); % fails if non number entries are not previously removed

            assert(numel(iMF) == (internal_spacedimension)^2,...
                sprintf(['%s: expected ''measurement frame'' to specify a ' ...
                        '%d-by-%d matrix (%d total elements) but found %d ' ...
                        'element(s).'],...
                        fopen(fidr), internal_spacedimension, ...
                        internal_spacedimension, (internal_spacedimension)^2,...
                        numel(iMF)));

            headerInfo.measurementframe = reshape(iMF(:), [internal_spacedimension, internal_spacedimension]);

            % FIXME: this will gladly accept '1 0 0 0 1 0 0 0 1' instead of
            % '(1,0,0) (0,1,0)  (0,0,1)' for instance. But it should have
            % length spacedimension according to standard so this is not too bad.

        elseif foundKeyword('THICKNESSES:', cs )

            sThicknesses = extractStringList( cs(length('THICKNESSES:')+1:end) ); % fixed bug with extractStringList where 2 entries are present
            iThicknesses = [];
            lenThicknesses = length( sThicknesses );
            for iI=1:lenThicknesses
                iThicknesses = [iThicknesses, str2double(sThicknesses{iI}) ];
            end
            headerInfo.thicknesses = iThicknesses;

        elseif foundKeyword('CENTERINGS:', cs )

            headerInfo.centerings = extractStringList( cs(length('CENTERINGS:')+1:end ) ); % fixed bug with extractStringList where 2 entries are present

        elseif foundKeyword('LINE SKIP:', cs) || foundKeyword('LINESKIP', cs)

            if foundKeyword('LINE SKIP:', cs)
                headerInfo.lineskip = sscanf( cs( length('LINE SKIP:')+1:end ), '%d' );
            else
                headerInfo.lineskip = sscanf( cs( length('LINESKIP:')+1:end ), '%d' );
            end
            assert(headerInfo.lineskip >= 0,...
                    sprintf(['Field lineskip or line skip should be greater' ...
                            ' than or equal to zero, detected %d.'], ...
                            headerInfo.lineskip));

        elseif foundKeyword('BYTE SKIP:', cs) || foundKeyword('BYTESKIP:', cs)

            if foundKeyword('BYTE SKIP:', cs)
                headerInfo.byteskip = sscanf( cs( length('BYTE SKIP:')+1:end ), '%d' );
            else
                headerInfo.byteskip = sscanf( cs( length('BYTESKIP:')+1:end ), '%d' );
            end
            assert(headerInfo.byteskip >= -1, ...
                    sprintf(['Field byteskip or byte skip can only take ' ...
                            'non-negative integer values or -1, detected %d.'], ...
                            headerInfo.byteskip));

        elseif foundKeyword('MODALITY', cs )

            headerInfo.modality = strtrim( extractKeyValueString( cs(length('MODALITY')+1:end ) ) );

        elseif foundKeyword('DWMRI_B-VALUE', cs )

            headerInfo.bvalue = str2double( extractKeyValueString( cs(length('DWMRI_B-VALUE')+1:end ) ) );

        elseif foundKeyword('DWMRI_GRADIENT_', cs )

            [iGNr, dwiGradient] = extractGradient(cs(length('DWMRI_GRADIENT_')+1:end ));
            headerInfo.gradients(iGNr+1,:) = dwiGradient;
            % FIXME: make it more general than numbering limited to 4 digits?

        elseif foundKeyword('DATA FILE:', cs ) || foundKeyword('DATAFILE:', cs)
            % This tells us it is a detached header and ends it. 3 possibilities here

            field_value = strtrim( cs(length('DATA FILE:')+1:end) );
            if foundKeyword('DATAFILE:', cs)
                field_value = strtrim( cs(length('DATAFILE:')+1:end) );
            end

            [filelist, LIST_mode, subdim] = extract_datafiles(field_value);

            headerInfo.datafiles = filelist;

            % In LIST mode, filelist is empty and is filled by reading the
            % rest of the header
            if LIST_mode
                datafile_cnt = 0;
                while ~feof(fidr)
                    cs = fgetl(fidr);
                    if isempty(cs)
                        break;
                    end
                    datafile_cnt = datafile_cnt + 1;
                    headerInfo.datafiles{datafile_cnt} = cs;
                end
            end

            % We could technically break the loop here bc data file/datafile is
            % supposed to close a header; however I think it does no harm to
            % keep parsing (in LIST_mode, end of file reached anyways)

        else

            % see if we are dealing with a comment

            csTmp = strtrim( cs );
            if csTmp(1)~='#' && ~strcmp(cs(1:4),'NRRD')
                fprintf('WARNING nhdr_nrrd_read: Could not parse input line: ''%s'' \n', cs );
                % REVIEW: is it better to blindly write it to the output
                % structure in order to be able to write the exact same file
                % later on ?
            end
        end

    end

    % Check for required fields
    % REVIEW: should this be checked only when the data is read? People
    % might be interested in just parsing the header no matter what is
    % in it...)
    assert(isfield(headerInfo, 'sizes'), 'Missing required ''sizes'' field in header');
    assert(isfield(headerInfo, 'dimension'), 'Missing required ''dimension'' field in header');
    assert(isfield(headerInfo, 'encoding'), 'Missing required ''encoding'' field in header');
    assert(isfield(headerInfo, 'type'), 'Missing required ''type'' field in header');

    % Check other cross-field dependencies, etc.
    % TODO: assert lengths of all
    % fields specified, check that kinds and axis sizes match, etc.
    if isfield(headerInfo, 'byteskip') && headerInfo.byteskip == -1
        assert( strcmpi(headerInfo.encoding, 'raw'), ...
            sprintf('byte skip value of -1 is only valid with raw encoding. See definition of NRRD File Format for more information.\n'));
    end

    matlabdatatype = nrrd_getMatlabDataType(headerInfo.type);

    %  endian field required if data defined on 2 bytes or more
    if ~(any(strcmpi(headerInfo.encoding,{'txt', 'text', 'ascii'})) || any(strcmpi(matlabdatatype, {'int8', 'uint8'})))
        assert(isfield(headerInfo, 'endian'), 'Missing required ''endian'' field in header');
    else
        if ~isfield(headerInfo,'endian')
            headerInfo.endian = 'little';    % useless but harmless, just for code compatibility because endian field may be accessed later on while reading data
        end
    end

    % Space and orientation information
    if define_orientation
        assert(isfield(headerInfo, 'spacedirections'), ...
                ['Missing field ''space directions'', required if either' ...
                ' ''space'' or ''space dimension'' is set.']);
        if length(headerInfo.spacedirections) ~= headerInfo.dimension
            fprintf(['WARNING nhdr_nrrd_read %s:\n Unexpected format found for ''space directions'' specifier.\n',...
                    ' Expected none entries and vectors delimited by parentheses such as (1.2,0.2,0.25), ', ...
                    'the number of entries being equal to the dimension field specification.\n',...
                    ' See definition of NRRD File Format for more information.\n'], fopen(fidr));
        end
    end

    % TODO: add support for positive line skips
    if isfield(headerInfo, 'lineskip') && headerInfo.lineskip > 0
        assert(isfield(headerInfo, 'byteskip') && headerInfo.byteskip == -1, ...
            sprintf(['lineskip option is currently not supported and can ' ...
                    'only be set to zero unless raw encoding is used and ' ...
                    'byte skip is set to -1 (which cancels the effect of ' ...
                    'lineskip altogether).']));
    end

    % TODO: add support for positive byte skips (see line skip)
    if isfield(headerInfo, 'byteskip') && ~strcmpi(headerInfo.encoding,'raw')
        assert( headerInfo.byteskip == 0, ...
            sprintf('byte skip option with non raw encoding is currently not supported and can only be set to zero.\n'));
    end
    if isfield(headerInfo, 'byteskip') && strcmpi(headerInfo.encoding, 'raw')
        assert( headerInfo.byteskip == -1, ...
            sprintf('non-negative byte skip values with raw encoding are currently not supported; byte skip can only be set to -1.\n'));
    end

catch me
    % Clean up before raising error
    fclose(fidr);
    rethrow(me);
end

% Read the data. Detect if data is in the file or in a detached data file
% and check what type of detached file it is if need be.
if bReadData
    N_data_tot = prod(headerInfo.sizes);
    if isfield(headerInfo, 'datafiles')
        % This is a detached header file

        % We no longer need to read the header (closing it before reading
        % all the detached data files is safer)
        fclose(fidr);

        % TODO: we currently only read slices along the slowest axis (i.e.,
        % last coordinate)
        if ~isempty(subdim) && subdim~=headerInfo.dimension
            error(['(detached header): reading data from slices along axis other than the last' ...
                ' (i.e. slowest) one, is currently not supported.\n' ...
                'Last argument [<subdim>] in ''data file'' or ''datafile'' field ' ...
                'should be removed or set equal to %d, which is the detected' ...
                ' ''dimension'' field value, for now.'],...
                        headerInfo.dimension);
        end

        % Read data chunk by chunk from detached data files
        N_data_files = length(headerInfo.datafiles);
        assert(mod(N_data_tot, N_data_files)==0, ...
            sprintf(['Number of detected data files (%d) does not divide total' ...
                    ' number of values contained in data %d obtained from prod(sizes=[%s]).\n'],...
                    N_data_files, N_data_tot, sprintf('%d ',headerInfo.sizes)));

        N_data_per_file = N_data_tot/N_data_files;

        headerInfo.data = zeros(headerInfo.sizes, matlabdatatype);      % specify right type of zeros, otherwise double by default

        for i = 1:N_data_files

            % Check type of detached data file
            [~,fname_data,ext_data] = fileparts(headerInfo.datafiles{i});        % redundant because done above

            data_ind = (i-1)*N_data_per_file+1:i*N_data_per_file;

            if strcmpi(ext_data,'.nhdr')

                error(['datafile %di/%d: nhdr file should not be used as ' ...
                        'detached data file.'], ...
                        i, length(headerInfo.datafiles));

            elseif strcmpi(ext_data, '.nrrd')
                % Detached NRRD file

                bRead_Detached_Data = true;
                tmp_struct = nhdr_nrrd_read(fullfile(mainFpath, ...
                                            [fname_data, ext_data]), ...
                                            bRead_Detached_Data);   % recursive call
                % TODO: check the rest of the structure for
                % inconsistencies with metadata from header
                assert(N_data_files==1 || headerInfo.dimension == tmp_struct.dimension +1, ...
                    sprintf(['Detached header %s: the number of dimensions in detached ' ...
                            'nrrd data file %d/%d (%s) should be one fewer than that of' ...
                            ' the header file.\nDetected %d instead of %d.\nDifferent ' ...
                            'datafile dimensions as specified by the nrrd standard are' ...
                            ' not supported as of now.\n'],...
                            fopen(fidr), i, N_data_files, headerInfo.datafiles{i},...
                            tmp_struct.dimension, headerInfo.dimension-1 ));

                headerInfo.data(data_ind) = tmp_struct.data(:);

                % Store detached data header for last detached file seen,
                % assuming that all are the same. Do not store data though
                % as it would be redundant.
                tmp_struct = rmfield(tmp_struct, 'data');
                headerInfo.detached_header = tmp_struct;

            else
                % e.g., detached .raw file

                fid_data = fopen( fullfile(mainFpath, [fname_data, ext_data]), 'r');
                if( fid_data < 1 )
                    error(['While reading detached header file %s:\ndetached ' ...
                           'data file number %d/%d (%s) could not be opened.'], ...
                           nrrdFileName, i, N_data_files, headerInfo.datafiles{i});
                end
                try
                    tmp_data = readData(fid_data, N_data_per_file, headerInfo.encoding, matlabdatatype);
                    fclose(fid_data);
                catch me_detached
                    fclose(fid_data);
                    rethrow(me_detached);
                end

                tmp_data = adjustEndian(tmp_data, headerInfo.endian);

                headerInfo.data(data_ind) = tmp_data(:);
            end

        end

    else
        % This is a NRRD standalone file: read data directly from it
        try
            headerInfo.data = readData(fidr, N_data_tot, headerInfo.encoding, matlabdatatype);
            fclose(fidr);
        catch me_detached
            fclose(fidr);
            rethrow(me_detached);
        end
        headerInfo.data = adjustEndian(headerInfo.data, headerInfo.endian);
        headerInfo.data = reshape(headerInfo.data, headerInfo.sizes(:)');   % data into expected form. Transpose required by reshape function bc size vectors need to be row vectors
    end
else
    fclose(fidr);
end

end

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

% ====================================================================
% --- Auxiliary functions -------------------------------------------%
% ====================================================================

function [iGNr, dwiGradient] = extractGradient( st )

% first get the gradient number

iGNr = str2num( st(1:4) ); % FIX numbering limited to 4 digits (synchronize with writer module)

% find where the assignment is

assgnLoc = strfind( st, ':=' );

if ( isempty(assgnLoc) )
    dwiGradient = [];
    return;
else

    dwiGradient = sscanf( st(assgnLoc+2:end), '%f' );

end

end

% Return part of string after :=
function kvs = extractKeyValueString( st )

assgnLoc = strfind( st, ':=' );

if ( isempty(assgnLoc) )
    kvs = [];
    return;
else

    kvs = st(assgnLoc(1)+2:end);

end

end

% Turn space-separated list into cell array of strings.
function sl = extractStringList( strList )
sl = strsplit(strtrim(strList)); % old Matlab file exchange version had a strange bug with lists of length 2
end

% Store in an array the list of numbers separated by the tokens listed in
% the withoutTokens cell array
function iNrs = extractNumbersWithout( inputString, withoutTokens )

auxStr = inputString;

for iI=1:length( withoutTokens )

    auxStr = strrep( auxStr, withoutTokens{iI}, ' ' );

end

iNrs = sscanf( auxStr, '%f' );

end

% Return true if the string keyWord is the beginning of the content string
% cs (ignoring case)
function fk = foundKeyword( keyWord, cs )
lenKeyword = length( keyWord );
fk = (lenKeyword <= length(cs)) && strcmpi( cs(1:lenKeyword), keyWord);
end

% Return cell array of strings with space units in the form {mm, km, m}
% from input of the form "mm " "mm" " m" where undesired blank spaces may
% have crept in. The double quotes are removed in the process but will be
% added by the nhdr/nrrd writer function.
function su_ca = extract_spaceunits_list( fieldValue )
fv_trimmed = strtrim( fieldValue );
su_ca = strsplit(fv_trimmed, '"');                              % units are delimited by double quotes
su_ca = su_ca(~ ( strcmp(su_ca, '') | strcmp(su_ca, ' ') ) );   % remove empty or blank space strings
for i = 1:length(su_ca)
    su_ca{i} = strtrim( su_ca{i} );
end
end

% Extract data file list into a cell array from the datafile or data file
% field in a NHDR file, stripped off its leading and trailing spaces;
% LIST_mode is set to one if a list of files closes the header;
% subdim is empty by default and may be set through either of these field
% specifications:
% data file: <format> <min> <max> <step> [<subdim>]
% data file: LIST [<subdim>]

function [filelist, LIST_mode, subdim] = extract_datafiles( field_string)

field_string = strtrim(field_string);
filelist = {};
LIST_mode = 0;
subdim = [];
if length(field_string)>= 4 && strcmpi(field_string(1:4), 'LIST')
    % LIST mode
    subdim = sscanf(field_string(5:end), '%d'); % assert 1<=dimensions
    LIST_mode = 1;
    return;
else
    % single detached data file or multiple files written in concise form, non LIST mode
    str_lst = strsplit(field_string); % without delimiters specified, splits at any sequence in the set  {' ','\f','\n','\r','\t','\v'}
    if length(str_lst) == 1
        % Single detached data file:
        filelist{1} = str_lst{1};
        % subdim does not make sense here since all of the data is
        % contained in the deatached data file
    elseif any(length(str_lst)==[0, 2, 3]) || length(str_lst)>5
        % Invalid cases (2, 3 or striclty more than 5 entries)
        error(['error in ''data list'' (or ''data list'') field, in non' ...
                ' ''LIST'' mode:\nexpected  ''<filename>'' or ''<format> ' ...
                '<min> <max> <step> [<subdim>]'' but found instead ''%s'' ' ...
                '(%d elements instead of 1, 4 or 5).'],...
                sprintf('%s ',str_lst{:}), length(str_lst));
    else
        % Multiple detached data files with filenames including integral
        % values generated by min:step:max

        str_format = str_lst{1};
        id_min = sscanf(str_lst{2}, '%d');
        id_max = sscanf(str_lst{3}, '%d');
        step = sscanf(str_lst{4}, '%d');

        % check format and indexing values
        assert(step~=0,...
                sprintf(['detached data files with names specified by ' ...
                        'sprintf()-like format:  step should be strictly positive' ...
                        ' or negative, not zero.']));
        if step < 0
            assert(id_min >= id_max, ...
                    sprintf(['detached data files with names specified by ' ...
                            'sprintf()-like format:  when step is <0, min ' ...
                            'should be larger than or equal to max, here we ' ...
                            'found min=%d < max=%d.'],id_min, id_max));
        else
            assert(id_min <= id_max,...
                    sprintf(['detached data files with names specified by ' ...
                            'sprintf()-like format: : when step is >0, min ' ...
                            'should be smaller than or equal to max, here we ' ...
                            'found min=%d > max=%d.'],id_min, id_max));
        end

        % populate data file list
        fileIDs = id_min:step:id_max;
        filelist = cell(length(fileIDs), 1);
        for i = 1:length(fileIDs)  % does not necessarily incude <max>, it cannot be larger than <max>
            expanded_fname = sprintf(str_format, fileIDs(i));
            filelist{i} = expanded_fname;
        end

        if length(str_lst) == 5
            subdim = sscanf(str_lst{5}, '%d');
        end
    end
end

end

% Read data in a column vector from open file with pointer fidIn at the
% beginning of the data to read. The other arguments specify the number of
% elements to read (determined in main body of reader function above), data
% file encoding (specified in NHDR/NRRD header) and the Matlab type the
% data should be converted to (determined in main body of reader function
% above).
function data = readData(fidIn, Nelems, meta_encoding, matlabdatatype)

switch (meta_encoding)
    case {'raw'}
        % This implicitely assumes that byteskip was set to -1 in raw
        % encoding
        data_bytes = (Nelems)*sizeOf(matlabdatatype);   % bytes of useful info to return in data array
        fseek(fidIn,0,'eof');                           % set position pointer to end of file
        tot_bytes_file = ftell(fidIn);                  % current location of the position pointer (I believe as a byte offset from beginning of file)
        fseek(fidIn,tot_bytes_file-data_bytes,'bof');   % make sure position pointer is right before useful data starts

        % Just read binary file (original version of the code)
        data = fread(fidIn, inf, [matlabdatatype '=>' matlabdatatype]);

    case {'gzip', 'gz'}

        tmp = fread(fidIn, Inf, 'uint8=>uint8'); % byte per byte

        tmpBase = tempname(pwd);
        tmpFile = [tmpBase '.gz'];
        fidTmp = fopen(tmpFile, 'wb');  % this creates tmpFile
        assert(fidTmp > 3, 'Could not open temporary file for GZIP decompression')

        try
            fwrite(fidTmp, tmp, 'uint8');
        catch me
            fclose(fidTmp);
            delete(tmpFile);
            rethrow(me);
        end
        fclose(fidTmp);

        try
            gunzip(tmpFile); % this creates tmpBase (tmpFile without the .gz)
        catch me
            delete(tmpFile);
            rethrow(me);
        end
        delete(tmpFile);

        fidTmp = fopen(tmpBase, 'rb');
        % cleaner = onCleanup(@() fclose(fidTmp));
        try
            data = readData(fidTmp, Nelems, 'raw', matlabdatatype); % recursive call
        catch me
            fclose(fidTmp);
            delete(tmpBase);
            rethrow(me);
        end
        fclose(fidTmp);
        delete(tmpBase);

    case {'txt', 'text', 'ascii'}

        data = fscanf(fidIn, '%f');
        data = cast(data, matlabdatatype);

    otherwise
        assert(false, 'Unsupported encoding')
end
% Check number of read elements (sometimes there is an offest of about 1)
assert(Nelems == numel(data),...
        sprintf(['Error reading binary content of %s: detected %d elements' ...
                ' instead of %d announced in header.'],...
                fopen(fidIn), numel(data), Nelems));
end

% Switch byte ordering according to endianness specified in nhdr/nrrd file.
% The metadata should just contain a field 'endian' set to 'little' or
% 'big'.
function data = adjustEndian(data, meta_endian)

[~,~,endian] = computer();

needToSwap = (isequal(endian, 'B') && isequal(lower(meta_endian), 'little')) || ...
    (isequal(endian, 'L') && isequal(lower(meta_endian), 'big'));

if (needToSwap)
    data = swapbytes(data);
end
end

% Size of Matlab's numeric classes in bytes
% See: https://stackoverflow.com/questions/16104423/size-of-a-numeric-class

function numBytes = sizeOf(dataClass)

    % create temporary variable of data type specified
    eval(['var = ' dataClass '(0);']);

    % Use the functional form of whos, and get the number of bytes
    W = whos('var');
    numBytes = W.bytes;

end


function tapas_uniqc_new_struct(varargin)
% Creates a new script (e.g. tests/examples) including header.
% tapas_uniqc_new_struct(funname) opens the editor and pastes the content
% of a user-defined template into the file funname.m.
%
%   Example
%       tapas_uniqc_new_struct myscript
%           OR
%       tapas_uniqc_new_struct myscript author
%
%   opens the editor and pastes the following
%
% 	% script myscript
% 	%MYSCRIPT  One-line description here, please.
% 	%
% 	%   Example
% 	%   MYSCRIPT
% 	%
% 	%   See also
%
% 	% Author: Your name
% 	% Created: 2005-09-22
% 	% Copyright 2005 Your company.
%
%   See also edit, mfiletemplate

% Author: Peter (PB) Bodin
% Created: 2005-09-22
% Modified: 2019-01-05 (Saskia Bollmann and Lars Kasper)

	% See the variables repstr, repwithstr and tmpl to figure out how
	% to design your own template.
	% Edit tmpl to your liking, if you add more tokens in tmpl, make
	% sure to add them in repstr and repwithstr as well.

	% I made this function just for fun to check out some java handles to
	% the editor. It would probably be better to fprintf the template
	% to a new file and then call edit, since the java objects might change
	% names between versions.

	switch nargin
		case 0
			edit
			warning('tapas_uniqc_new_struct without argument is the same as edit')
			return;
		case 1
			fname=varargin{:};
			edit(fullfile(pwd,fname));
            authors = 'Saskia Bollmann & Lars Kasper'; %default authors, set this further down in function authors
        case 2
            fname = varargin{1};
            authors = varargin{2};
		otherwise
			error('tapas:uniqc:TooManyInputArguments', ...
                'too many input arguments')
	end

	try lasterror
		edhandle=com.mathworks.mlservices.MLEditorServices;

        % R2009a => 2009.0, R2009b = 2009.5
        vs = version('-release');
        v = str2double(vs(1:4));
        if vs(5)=='b'
            v = v + 0.5;
        end

        if v < 2009.0
            edhandle.builtinAppendDocumentText(strcat(fname,'.m'),parse(fname,authors));
        else
            edhandle.getEditorApplication.getActiveEditor.appendText(parse(fname, authors));
        end
	catch
		rethrow(lasterror)
	end

	function out = parse(func, authors)

		tmpl={ ...
  'classdef $filename'
            '% This is a predefined options/parameter structure for CLASS_OR_FUNCTION'
            '%'
            '%   outputStruct = $filename(input)'
            '%'
            '%'
            '%   See also CLASS_OR_FUNCTION'
            ' '
            '% Author:   $author'
            '% Created:  $date'
            '% Copyright (C) $year $institute'
            '%                    $company'
            '%'
            '% This file is part of the TAPAS UniQC Toolbox, which is released'
            '% under the terms of the GNU General Public License (GPL), version 3. '
            '% You can redistribute it and/or modify it under the terms of the GPL'
            '% (either version 3 or, at your option, any later version).'
            '% For further details, see the file COPYING or'
            '%  <http://www.gnu.org/licenses/>.'
            ''
            'properties'
            ''
            '   %INSERT_PROPERTY_DESCRIPTION_ABOVE_EACH_PROPERTY'
            ''
            'end'
            'methods'
            '   function this = $filename(varargin)'
            '   % Constructor of class, allows ''PropertyName'', ''PropertyValue'''
            '   % pairs and predefined shortcuts'
            '   % everything else set to default ([])'
            '       if nargin'
            '           for cnt = 1:nargin/2 % save them to object properties'
            '               this.(varargin{2*cnt-1}) = varargin{2*cnt};'
            '           end'
            '       end'
            '   end'
            'end'
            'end'
            };

		repstr={...
			'$filename'
			'$FILENAME'
			'$date'
			'$year'
			'$author'
            '$institute'
			'$company'};

		repwithstr={...
			func
			upper(func)
			datestr(now,29)
			datestr(now,10)
			authors
			'Institute for Biomedical Engineering'
            'University of Zurich and ETH Zurich'};

		for k = 1:numel(repstr)
			tmpl = strrep(tmpl,repstr{k},repwithstr{k});
		end
		out = sprintf('%s\n',tmpl{:});
	end
end