function [ avw, machine ] = avw_img_read(fileprefix,IMGorient,machine,verbose)

% avw_img_read - read Analyze format data image (*.img)
%
% [ avw, machine ] = avw_img_read(fileprefix,[orient],[machine],[verbose])
%
% fileprefix - a string, the filename without the .img extension
%
% orient - read a specified orientation, integer values:
%
%          '', use header history orient field
%          0,  transverse unflipped (LAS*)
%          1,  coronal unflipped (LA*S)
%          2,  sagittal unflipped (L*AS)
%          3,  transverse flipped (LPS*)
%          4,  coronal flipped (LA*I)
%          5,  sagittal flipped (L*AI)
%
% where * follows the slice dimension and letters indicate +XYZ
% orientations (L left, R right, A anterior, P posterior,
% I inferior, & S superior).
%
% Some files may contain data in the 3-5 orientations, but this
% is unlikely. For more information about orientation, see the
% documentation at the end of this .m file.  See also the
% AVW_FLIP function for orthogonal reorientation.
%
% machine - a string, see machineformat in fread for details.
%           The default here is 'ieee-le' but the routine
%           will automatically switch between little and big
%           endian to read any such Analyze header.  It
%           reports the appropriate machine format and can
%           return the machine value.
%
% verbose - the default is to output processing information to the command
%           window.  If verbose = 0, this will not happen.
%
% Returned values:
%
% avw.hdr - a struct with image data parameters.
% avw.img - a 3D matrix of image data (double precision).
%
% A returned 3D matrix will correspond with the
% default ANALYZE coordinate system, which
% is Left-handed:
%
% X-Y plane is Transverse
% X-Z plane is Coronal
% Y-Z plane is Sagittal
%
% X axis runs from patient right (low X) to patient Left (high X)
% Y axis runs from posterior (low Y) to Anterior (high Y)
% Z axis runs from inferior (low Z) to Superior (high Z)
%
% The function can read a 4D Analyze volume, but only if it is in the
% axial unflipped orientation.
%
% See also: avw_hdr_read (called by this function),
%           avw_view, avw_write, avw_img_write, avw_flip
%

% $Revision$ $Date: 2009/01/14 09:24:45 $

% Licence:  GNU GPL, no express or implied warranties
% History:  05/2002, Darren.Weber@flinders.edu.au
%                    The Analyze format is copyright
%                    (c) Copyright, 1986-1995
%                    Biomedical Imaging Resource, Mayo Foundation
%           01/2003, Darren.Weber@flinders.edu.au
%                    - adapted for matlab v5
%                    - revised all orientation information and handling
%                      after seeking further advice from AnalyzeDirect.com
%           03/2003, Darren.Weber@flinders.edu.au
%                    - adapted for -ve pixdim values (non standard Analyze)
%           07/2004, chodkowski@kennedykrieger.org, added ability to
%                    read volumes with dimensionality greather than 3.
%  a >3D volume cannot be flipped.  and error is thrown if a volume of
%  greater than 3D (ie, avw.hdr.dime.dim(1) > 3) requests a data flip
%  (ie, avw.hdr.hist.orient ~= 0 ).  i pulled the transfer of read-in
%  data (tmp) to avw.img out of any looping mechanism.  looping is not
%  necessary as the data is already in its correct orientation.  using
%  'reshape' rather than looping should be faster but, more importantly,
%  it allows the reading in of N-D volumes. See lines 270-280.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if ~exist('IMGorient','var'), IMGorient = ''; end
if ~exist('machine','var'), machine = 'ieee-le'; end
if ~exist('verbose','var'), verbose = 1; end

if isempty(IMGorient), IMGorient = ''; end
if isempty(machine), machine = 'ieee-le'; end
if isempty(verbose), verbose = 1; end

if ~exist('fileprefix','var'),
  msg = sprintf('...no input fileprefix - see help avw_img_read\n\n');
  ft_error(msg);
end
if contains(fileprefix, '.hdr')
  fileprefix = strrep(fileprefix,'.hdr','');
end
if contains(fileprefix, '.img')
  fileprefix = strrep(fileprefix,'.img','');
end

% MAIN

% Read the file header
[ avw, machine ] = avw_hdr_read(fileprefix,machine,verbose);

avw = read_image(avw,IMGorient,machine,verbose);

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [ avw ] = read_image(avw,IMGorient,machine,verbose)

fid = fopen_or_error(sprintf('%s.img',avw.fileprefix),'r',machine);

if verbose,
    ver = '[$Revision$]';
    fprintf('\nAVW_IMG_READ [v%s]\n',ver(12:16));  tic;
end

% short int bitpix;    /* Number of bits per pixel; 1, 8, 16, 32, or 64. */
% short int datatype      /* Datatype for this image set */
% /*Acceptable values for datatype are*/
% #define DT_NONE             0
% #define DT_UNKNOWN          0    /*Unknown data type*/
% #define DT_BINARY           1    /*Binary             ( 1 bit per voxel)*/
% #define DT_UNSIGNED_CHAR    2    /*Unsigned character ( 8 bits per voxel)*/
% #define DT_SIGNED_SHORT     4    /*Signed short       (16 bits per voxel)*/
% #define DT_SIGNED_INT       8    /*Signed integer     (32 bits per voxel)*/
% #define DT_FLOAT           16    /*Floating point     (32 bits per voxel)*/
% #define DT_COMPLEX         32    /*Complex (64 bits per voxel; 2 floating point numbers)/*
% #define DT_DOUBLE          64    /*Double precision   (64 bits per voxel)*/
% #define DT_RGB            128    /*A Red-Green-Blue datatype*/
% #define DT_ALL            255    /*Undocumented*/

switch double(avw.hdr.dime.bitpix),
  case  1,   precision = 'bit1';
  case  8,   precision = 'uchar';
  case 16,   precision = 'int16';
  case 32,
    if     isequal(avw.hdr.dime.datatype, 8), precision = 'int32';
    else                                      precision = 'single';
    end
  case 64,   precision = 'double';
  otherwise,
    precision = 'uchar';
    if verbose, fprintf('...precision undefined in header, using ''uchar''\n'); end
end

% read the whole .img file into matlab (faster)
if verbose,
    fprintf('...reading %s Analyze %s image format.\n',machine,precision);
end
fseek(fid,0,'bof');
% adjust for matlab version
ver = version;
ver = str2num(ver(1));
if ver < 6,
  tmp = fread(fid,inf,sprintf('%s',precision));
else
  tmp = fread(fid,inf,sprintf('%s=>double',precision));
end
fclose(fid);

% Update the global min and max values
avw.hdr.dime.glmax = max(double(tmp));
avw.hdr.dime.glmin = min(double(tmp));

%---------------------------------------------------------------
% Now partition the img data into xyz

% --- first figure out the size of the image

% short int dim[ ];      /* Array of the image dimensions */
%
% dim[0]      Number of dimensions in database; usually 4.
% dim[1]      Image X dimension;  number of pixels in an image row.
% dim[2]      Image Y dimension;  number of pixel rows in slice.
% dim[3]      Volume Z dimension; number of slices in a volume.
% dim[4]      Time points; number of volumes in database.

PixelDim = double(avw.hdr.dime.dim(2));
RowDim   = double(avw.hdr.dime.dim(3));
SliceDim = double(avw.hdr.dime.dim(4));
TimeDim  = double(avw.hdr.dime.dim(5));

PixelSz  = double(avw.hdr.dime.pixdim(2));
RowSz    = double(avw.hdr.dime.pixdim(3));
SliceSz  = double(avw.hdr.dime.pixdim(4));
TimeSz   = double(avw.hdr.dime.pixdim(5));

% ---- NON STANDARD ANALYZE...

% Some Analyze files have been found to set -ve pixdim values, eg
% the MNI template avg152T1_brain in the FSL etc/standard folder,
% perhaps to indicate flipped orientation?  If so, this code below
% will NOT handle the flip correctly!
if PixelSz < 0,
  ft_warning('X pixdim < 0 !!! resetting to abs(avw.hdr.dime.pixdim(2))');
  PixelSz = abs(PixelSz);
  avw.hdr.dime.pixdim(2) = single(PixelSz);
end
if RowSz < 0,
  ft_warning('Y pixdim < 0 !!! resetting to abs(avw.hdr.dime.pixdim(3))');
  RowSz = abs(RowSz);
  avw.hdr.dime.pixdim(3) = single(RowSz);
end
if SliceSz < 0,
  ft_warning('Z pixdim < 0 !!! resetting to abs(avw.hdr.dime.pixdim(4))');
  SliceSz = abs(SliceSz);
  avw.hdr.dime.pixdim(4) = single(SliceSz);
end

% ---- END OF NON STANDARD ANALYZE

% --- check the orientation specification and arrange img accordingly
if ~isempty(IMGorient),
  if ischar(IMGorient),
    avw.hdr.hist.orient = uint8(str2num(IMGorient));
  else
    avw.hdr.hist.orient = uint8(IMGorient);
  end
end

if isempty(avw.hdr.hist.orient),
  msg = [ '...unspecified avw.hdr.hist.orient, using default 0\n',...
      '   (check image and try explicit IMGorient option).\n'];
  fprintf(msg);
  avw.hdr.hist.orient = uint8(0);
end

% --- check if the orientation is to be flipped for a volume with more
% --- than 3 dimensions.  this logic is currently unsupported so throw
% --- an error.  volumes of any dimensionality may be read in *only* as
% --- unflipped, ie, avw.hdr.hist.orient == 0
if ( TimeDim > 1 ) && (avw.hdr.hist.orient ~= 0 ),
   msg = [ 'ERROR: This volume has more than 3 dimensions *and* ', ...
           'requires flipping the data.  Flipping is not supported ', ...
           'for volumes with dimensionality greater than 3.  Set ', ...
           'avw.hdr.hist.orient = 0 and flip your volume after ', ...
           'calling this function' ];
   msg = ft_error( '%s (%s).', msg, mfilename );
   ft_error( msg );
end

switch double(avw.hdr.hist.orient),

  case 0, % transverse unflipped

    % orient = 0:  The primary orientation of the data on disk is in the
    % transverse plane relative to the object scanned.  Most commonly, the fastest
    % moving index through the voxels that are part of this transverse image would
    % span the right-left extent of the structure imaged, with the next fastest
    % moving index spanning the posterior-anterior extent of the structure.  This
    % 'orient' flag would indicate to Analyze that this data should be placed in
    % the X-Y plane of the 3D Analyze Coordinate System, with the Z dimension
    % being the slice direction.

    % For the 'transverse unflipped' type, the voxels are stored with
    % Pixels in 'x' axis (varies fastest) - from patient right to left
    % Rows in   'y' axis                  - from patient posterior to anterior
    % Slices in 'z' axis                  - from patient inferior to superior

    if verbose, fprintf('...reading axial unflipped orientation\n'); end

    % -- This code will handle nD files
    dims = double( avw.hdr.dime.dim(2:end) );
    % replace dimensions of 0 with 1 to be used in reshape
    idx = find( dims == 0 );
    dims( idx ) = 1;
    avw.img = reshape( tmp, dims );

    % -- The code above replaces this
    %         avw.img = zeros(PixelDim,RowDim,SliceDim);
    %
    %         n = 1;
    %         x = 1:PixelDim;
    %         for z = 1:SliceDim,
    %             for y = 1:RowDim,
    %                 % load Y row of X values into Z slice avw.img
    %                 avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
    %                 n = n + PixelDim;
    %             end
    %         end

    % no need to rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim

case 1, % coronal unflipped

    % orient = 1:  The primary orientation of the data on disk is in the coronal
    % plane relative to the object scanned.  Most commonly, the fastest moving
    % index through the voxels that are part of this coronal image would span the
    % right-left extent of the structure imaged, with the next fastest moving
    % index spanning the inferior-superior extent of the structure.  This 'orient'
    % flag would indicate to Analyze that this data should be placed in the X-Z
    % plane of the 3D Analyze Coordinate System, with the Y dimension being the
    % slice direction.

    % For the 'coronal unflipped' type, the voxels are stored with
    % Pixels in 'x' axis (varies fastest) - from patient right to left
    % Rows in   'z' axis                  - from patient inferior to superior
    % Slices in 'y' axis                  - from patient posterior to anterior

    if verbose, fprintf('...reading coronal unflipped orientation\n'); end

    avw.img = zeros(PixelDim,SliceDim,RowDim);

    n = 1;
    x = 1:PixelDim;
    for y = 1:SliceDim,
      for z = 1:RowDim,
        % load Z row of X values into Y slice avw.img
        avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
        n = n + PixelDim;
      end
    end

    % rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim
    avw.hdr.dime.dim(2:4) = int16([PixelDim,SliceDim,RowDim]);
    avw.hdr.dime.pixdim(2:4) = single([PixelSz,SliceSz,RowSz]);

  case 2, % sagittal unflipped

    % orient = 2:  The primary orientation of the data on disk is in the sagittal
    % plane relative to the object scanned.  Most commonly, the fastest moving
    % index through the voxels that are part of this sagittal image would span the
    % posterior-anterior extent of the structure imaged, with the next fastest
    % moving index spanning the inferior-superior extent of the structure.  This
    % 'orient' flag would indicate to Analyze that this data should be placed in
    % the Y-Z plane of the 3D Analyze Coordinate System, with the X dimension
    % being the slice direction.

    % For the 'sagittal unflipped' type, the voxels are stored with
    % Pixels in 'y' axis (varies fastest) - from patient posterior to anterior
    % Rows in   'z' axis                  - from patient inferior to superior
    % Slices in 'x' axis                  - from patient right to left

    if verbose, fprintf('...reading sagittal unflipped orientation\n'); end

    avw.img = zeros(SliceDim,PixelDim,RowDim);

    n = 1;
    y = 1:PixelDim;         % posterior to anterior (fastest)

    for x = 1:SliceDim,     % right to left (slowest)
      for z = 1:RowDim,   % inferior to superior

        % load Z row of Y values into X slice avw.img
        avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
        n = n + PixelDim;
      end
    end

    % rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim
    avw.hdr.dime.dim(2:4) = int16([SliceDim,PixelDim,RowDim]);
    avw.hdr.dime.pixdim(2:4) = single([SliceSz,PixelSz,RowSz]);

    %--------------------------------------------------------------------------------
    % Orient values 3-5 have the second index reversed in order, essentially
    % 'flipping' the images relative to what would most likely become the vertical
    % axis of the displayed image.
    %--------------------------------------------------------------------------------

  case 3, % transverse/axial flipped

    % orient = 3:  The primary orientation of the data on disk is in the
    % transverse plane relative to the object scanned.  Most commonly, the fastest
    % moving index through the voxels that are part of this transverse image would
    % span the right-left extent of the structure imaged, with the next fastest
    % moving index spanning the *anterior-posterior* extent of the structure.  This
    % 'orient' flag would indicate to Analyze that this data should be placed in
    % the X-Y plane of the 3D Analyze Coordinate System, with the Z dimension
    % being the slice direction.

    % For the 'transverse flipped' type, the voxels are stored with
    % Pixels in 'x' axis (varies fastest) - from patient right to Left
    % Rows in   'y' axis                  - from patient anterior to Posterior *
    % Slices in 'z' axis                  - from patient inferior to Superior

    if verbose, fprintf('...reading axial flipped (+Y from Anterior to Posterior)\n'); end

    avw.img = zeros(PixelDim,RowDim,SliceDim);

    n = 1;
    x = 1:PixelDim;
    for z = 1:SliceDim,
      for y = RowDim:-1:1, % flip in Y, read A2P file into P2A 3D matrix

        % load a flipped Y row of X values into Z slice avw.img
        avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
        n = n + PixelDim;
      end
    end

    % no need to rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim

  case 4, % coronal flipped

    % orient = 4:  The primary orientation of the data on disk is in the coronal
    % plane relative to the object scanned.  Most commonly, the fastest moving
    % index through the voxels that are part of this coronal image would span the
    % right-left extent of the structure imaged, with the next fastest moving
    % index spanning the *superior-inferior* extent of the structure.  This 'orient'
    % flag would indicate to Analyze that this data should be placed in the X-Z
    % plane of the 3D Analyze Coordinate System, with the Y dimension being the
    % slice direction.

    % For the 'coronal flipped' type, the voxels are stored with
    % Pixels in 'x' axis (varies fastest) - from patient right to Left
    % Rows in   'z' axis                  - from patient superior to Inferior*
    % Slices in 'y' axis                  - from patient posterior to Anterior

    if verbose, fprintf('...reading coronal flipped (+Z from Superior to Inferior)\n'); end

    avw.img = zeros(PixelDim,SliceDim,RowDim);

    n = 1;
    x = 1:PixelDim;
    for y = 1:SliceDim,
      for z = RowDim:-1:1, % flip in Z, read S2I file into I2S 3D matrix

        % load a flipped Z row of X values into Y slice avw.img
        avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
        n = n + PixelDim;
      end
    end

    % rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim
    avw.hdr.dime.dim(2:4) = int16([PixelDim,SliceDim,RowDim]);
    avw.hdr.dime.pixdim(2:4) = single([PixelSz,SliceSz,RowSz]);

  case 5, % sagittal flipped

    % orient = 5:  The primary orientation of the data on disk is in the sagittal
    % plane relative to the object scanned.  Most commonly, the fastest moving
    % index through the voxels that are part of this sagittal image would span the
    % posterior-anterior extent of the structure imaged, with the next fastest
    % moving index spanning the *superior-inferior* extent of the structure.  This
    % 'orient' flag would indicate to Analyze that this data should be placed in
    % the Y-Z plane of the 3D Analyze Coordinate System, with the X dimension
    % being the slice direction.

    % For the 'sagittal flipped' type, the voxels are stored with
    % Pixels in 'y' axis (varies fastest) - from patient posterior to Anterior
    % Rows in   'z' axis                  - from patient superior to Inferior*
    % Slices in 'x' axis                  - from patient right to Left

    if verbose, fprintf('...reading sagittal flipped (+Z from Superior to Inferior)\n'); end

    avw.img = zeros(SliceDim,PixelDim,RowDim);

    n = 1;
    y = 1:PixelDim;

    for x = 1:SliceDim,
      for z = RowDim:-1:1, % flip in Z, read S2I file into I2S 3D matrix

        % load a flipped Z row of Y values into X slice avw.img
        avw.img(x,y,z) = tmp(n:n+(PixelDim-1));
        n = n + PixelDim;
      end
    end

    % rearrange avw.hdr.dime.dim or avw.hdr.dime.pixdim
    avw.hdr.dime.dim(2:4) = int16([SliceDim,PixelDim,RowDim]);
    avw.hdr.dime.pixdim(2:4) = single([SliceSz,PixelSz,RowSz]);

  otherwise

    ft_error('unknown value in avw.hdr.hist.orient, try explicit IMGorient option.');

end

if verbose, t=toc; fprintf('...done (%5.2f sec).\n\n',t); end

return

% This function attempts to read the orientation of the
% Analyze file according to the hdr.hist.orient field of the
% header.  Unfortunately, this field is optional and not
% all programs will set it correctly, so there is no guarantee,
% that the data loaded will be correctly oriented.  If necessary,
% experiment with the 'orient' option to read the .img
% data into the 3D matrix of avw.img as preferred.
%

% (Conventions gathered from e-mail with support@AnalyzeDirect.com)
%
% 0  transverse unflipped
%       X direction first,  progressing from patient right to left,
%       Y direction second, progressing from patient posterior to anterior,
%       Z direction third,  progressing from patient inferior to superior.
% 1  coronal unflipped
%       X direction first,  progressing from patient right to left,
%       Z direction second, progressing from patient inferior to superior,
%       Y direction third,  progressing from patient posterior to anterior.
% 2  sagittal unflipped
%       Y direction first,  progressing from patient posterior to anterior,
%       Z direction second, progressing from patient inferior to superior,
%       X direction third,  progressing from patient right to left.
% 3  transverse flipped
%       X direction first,  progressing from patient right to left,
%       Y direction second, progressing from patient anterior to posterior,
%       Z direction third,  progressing from patient inferior to superior.
% 4  coronal flipped
%       X direction first,  progressing from patient right to left,
%       Z direction second, progressing from patient superior to inferior,
%       Y direction third,  progressing from patient posterior to anterior.
% 5  sagittal flipped
%       Y direction first,  progressing from patient posterior to anterior,
%       Z direction second, progressing from patient superior to inferior,
%       X direction third,  progressing from patient right to left.

%----------------------------------------------------------------------------
% From ANALYZE documentation...
%
% The ANALYZE coordinate system has an origin in the lower left
% corner. That is, with the subject lying supine, the coordinate
% origin is on the right side of the body (x), at the back (y),
% and at the feet (z). This means that:
%
% +X increases from right (R) to left (L)
% +Y increases from the back (posterior,P) to the front (anterior, A)
% +Z increases from the feet (inferior,I) to the head (superior, S)
%
% The LAS orientation is the radiological convention, where patient
% left is on the image right.  The alternative neurological
% convention is RAS (also Talairach convention).
%
% A major advantage of the Analzye origin convention is that the
% coordinate origin of each orthogonal orientation (transverse,
% coronal, and sagittal) lies in the lower left corner of the
% slice as it is displayed.
%
% Orthogonal slices are numbered from one to the number of slices
% in that orientation. For example, a volume (x, y, z) dimensioned
% 128, 256, 48 has:
%
%   128 sagittal   slices numbered 1 through 128 (X)
%   256 coronal    slices numbered 1 through 256 (Y)
%    48 transverse slices numbered 1 through  48 (Z)
%
% Pixel coordinates are made with reference to the slice numbers from
% which the pixels come. Thus, the first pixel in the volume is
% referenced p(1,1,1) and not at p(0,0,0).
%
% Transverse slices are in the XY plane (also known as axial slices).
% Sagittal slices are in the ZY plane.
% Coronal slices are in the ZX plane.
%
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
% E-mail from support@AnalyzeDirect.com
%
% The 'orient' field in the data_history structure specifies the primary
% orientation of the data as it is stored in the file on disk.  This usually
% corresponds to the orientation in the plane of acquisition, given that this
% would correspond to the order in which the data is written to disk by the
% scanner or other software application.  As you know, this field will contain
% the values:
%
% orient = 0 transverse unflipped
% 1 coronal unflipped
% 2 sagittal unflipped
% 3 transverse flipped
% 4 coronal flipped
% 5 sagittal flipped
%
% It would be vary rare that you would ever encounter any old Analyze 7.5
% files that contain values of 'orient' which indicate that the data has been
% 'flipped'.  The 'flipped flag' values were really only used internal to
% Analyze to precondition data for fast display in the Movie module, where the
% images were actually flipped vertically in order to accommodate the raster
% paint order on older graphics devices.  The only cases you will encounter
% will have values of 0, 1, or 2.
%
% As mentioned, the 'orient' flag only specifies the primary orientation of
% data as stored in the disk file itself.  It has nothing to do with the
% representation of the data in the 3D Analyze coordinate system, which always
% has a fixed representation to the data.  The meaning of the 'orient' values
% should be interpreted as follows:
%
% orient = 0:  The primary orientation of the data on disk is in the
% transverse plane relative to the object scanned.  Most commonly, the fastest
% moving index through the voxels that are part of this transverse image would
% span the right-left extent of the structure imaged, with the next fastest
% moving index spanning the posterior-anterior extent of the structure.  This
% 'orient' flag would indicate to Analyze that this data should be placed in
% the X-Y plane of the 3D Analyze Coordinate System, with the Z dimension
% being the slice direction.
%
% orient = 1:  The primary orientation of the data on disk is in the coronal
% plane relative to the object scanned.  Most commonly, the fastest moving
% index through the voxels that are part of this coronal image would span the
% right-left extent of the structure imaged, with the next fastest moving
% index spanning the inferior-superior extent of the structure.  This 'orient'
% flag would indicate to Analyze that this data should be placed in the X-Z
% plane of the 3D Analyze Coordinate System, with the Y dimension being the
% slice direction.
%
% orient = 2:  The primary orientation of the data on disk is in the sagittal
% plane relative to the object scanned.  Most commonly, the fastest moving
% index through the voxels that are part of this sagittal image would span the
% posterior-anterior extent of the structure imaged, with the next fastest
% moving index spanning the inferior-superior extent of the structure.  This
% 'orient' flag would indicate to Analyze that this data should be placed in
% the Y-Z plane of the 3D Analyze Coordinate System, with the X dimension
% being the slice direction.
%
% Orient values 3-5 have the second index reversed in order, essentially
% 'flipping' the images relative to what would most likely become the vertical
% axis of the displayed image.
%
% Hopefully you understand the difference between the indication this 'orient'
% flag has relative to data stored on disk and the full 3D Analyze Coordinate
% System for data that is managed as a volume image.  As mentioned previously,
% the orientation of patient anatomy in the 3D Analyze Coordinate System has a
% fixed orientation relative to each of the orthogonal axes.  This orientation
% is completely described in the information that is attached, but the basics
% are:
%
% Left-handed coordinate system
%
% X-Y plane is Transverse
% X-Z plane is Coronal
% Y-Z plane is Sagittal
%
% X axis runs from patient right (low X) to patient left (high X)
% Y axis runs from posterior (low Y) to anterior (high Y)
% Z axis runs from inferior (low Z) to superior (high Z)
%
%----------------------------------------------------------------------------

%----------------------------------------------------------------------------
% SPM2 NOTES from spm2 webpage: One thing to watch out for is the image
% orientation. The proper Analyze format uses a left-handed co-ordinate
% system, whereas Talairach uses a right-handed one. In SPM99, images were
% flipped at the spatial normalisation stage (from one co-ordinate system
% to the other). In SPM2b, a different approach is used, so that either a
% left- or right-handed co-ordinate system is used throughout. The SPM2b
% program is told about the handedness that the images are stored with by
% the spm_flip_analyze_images.m function and the defaults.analyze.flip
% parameter that is specified in the spm_defaults.m file. These files are
% intended to be customised for each site. If you previously used SPM99
% and your images were flipped during spatial normalisation, then set
% defaults.analyze.flip=1. If no flipping took place, then set
% defaults.analyze.flip=0. Check that when using the Display facility
% (possibly after specifying some rigid-body rotations) that:
%
% The top-left image is coronal with the top (superior) of the head displayed
% at the top and the left shown on the left. This is as if the subject is viewed
% from behind.
%
% The bottom-left image is axial with the front (anterior) of the head at the
% top and the left shown on the left. This is as if the subject is viewed from above.
%
% The top-right image is sagittal with the front (anterior) of the head at the
% left and the top of the head shown at the top. This is as if the subject is
% viewed from the left.
%----------------------------------------------------------------------------


function hout=suptitle(str, varargin)
%SUPTITLE Puts a title above all subplots.
%	SUPTITLE('text') adds text to the top of the figure
%	above all subplots (a "super title"). Use this function
%	after all subplot commands.

% Drea Thomas 6/15/95 drea@mathworks.com
% John Cristion 12/13/00 modified
% Mark Histed 03/13/04 histed@mit.edu: fix disappearing legend on last plot
%
% $Id: suptitle.m,v 1.2 2004/03/13 22:17:47 histed Exp $

% Warning: If the figure or axis units are non-default, this
% will break.

if exist('varargin', 'var')
    for ii = 1:2:length(varargin)
       switch lower(varargin{ii})
           case {'fs', 'fontsize'}
               fs = varargin{ii+1};
       end
    end
end

% Parameters used to position the supertitle.

% Amount of the figure window devoted to subplots
plotregion = .92;

% Y position of title in normalized coordinates
titleypos  = .95;

% Fontsize for supertitle
if ~exist('fs', 'var') fs = get(gcf,'defaultaxesfontsize'); end

% Fudge factor to adjust y spacing between subplots
fudge=1;

haold = gca;
figunits = get(gcf,'units');

% Get the (approximate) difference between full height (plot + title
% + xlabel) and bounding rectangle.

	if (~strcmp(figunits,'pixels')),
		set(gcf,'units','pixels');
		pos = get(gcf,'position');
		set(gcf,'units',figunits);
	else,
		pos = get(gcf,'position');
	end
	ff = (fs-4)*1.27*5/pos(4)*fudge;

        % The 5 here reflects about 3 characters of height below
        % an axis and 2 above. 1.27 is pixels per point.

% Determine the bounding rectange for all the plots

% h = findobj('Type','axes');

% findobj is a 4.2 thing.. if you don't have 4.2 comment out
% the next line and uncomment the following block.

h = findobj(gcf,'Type','axes');  % Change suggested by Stacy J. Hills

% If you don't have 4.2, use this code instead
%ch = get(gcf,'children');
%h=[];
%for i=1:length(ch),
%  if strcmp(get(ch(i),'type'),'axes'),
%    h=[h,ch(i)];
%  end
%end

max_y=0;
min_y=1;

oldtitle =0;
for i=1:length(h),
	if (~strcmp(get(h(i),'Tag'),'suptitle')),
		pos=get(h(i),'pos');
		if (pos(2) < min_y), min_y=pos(2)-ff/5*3;end;
		if (pos(4)+pos(2) > max_y), max_y=pos(4)+pos(2)+ff/5*2;end;
	else,
		oldtitle = h(i);
	end
end

if max_y > plotregion,
	scale = (plotregion-min_y)/(max_y-min_y);
	for i=1:length(h),
		pos = get(h(i),'position');
		pos(2) = (pos(2)-min_y)*scale+min_y;
		pos(4) = pos(4)*scale-(1-scale)*ff/5*3;
		set(h(i),'position',pos);
	end
end

np = get(gcf,'nextplot');
set(gcf,'nextplot','add');
if (oldtitle),
	delete(oldtitle);
end
ha=axes('pos',[0 1 1 1],'visible','off','Tag','suptitle');
ht=text(.5,titleypos-1,str);set(ht,'horizontalalignment','center','fontsize',fs);
set(gcf,'nextplot',np);
axes(haold);

% fix legend if one exists
legH = legend;
if ~isempty(legH)
    axes(legH);
end

if nargout,
	hout=ht;
end


%% Stiffness Tensor
%
%% PLEASE HELP AND ADD CONTENT HERE
%


function out = isDiag(~)
%ISDIAG   Returns 1 for a SPINOP.
%
% See also SPINOP.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

out = 1;

end


%DRAWMARKER  Draws a marker on a predefined position in an image
%
%     img = cv.drawMarker(img, pos)
%     img = cv.drawMarker(..., 'OptionName', optionValue, ...)
%
% ## Input
% * __img__ Input image.
% * __pos__ The point where the crosshair is positioned `[x,y]`.
%
% ## Output
% * __img__ Output image.
%
% ## Options
% * __Color__ Line color. Default is a black color.
% * __MarkerType__ The specific type of marker you want to use. One of:
%   * __Cross__ (`+`) (default) A crosshair marker shape.
%   * __TiltedCross__ (`x`) A 45 degree tilted crosshair marker shape.
%   * __Star__ (`*`) A star marker shape, combination of cross and tilted
%     cross.
%   * __Diamond__ (`d`) A diamond marker shape.
%   * __Square__ (`s`) A square marker shape.
%   * __TriangleUp__ (`^`) An upwards pointing triangle marker shape.
%   * __TriangleDown__ (`v`) A downwards pointing triangle marker shape.
% * __MarkerSize__ The length of the marker axis. Default is 20 pixels.
% * __Thickness__ Line thickness. default 1
% * __LineType__ Type of the line. One of:
%   * __4__ 4-connected line
%   * __8__ 8-connected line (default)
%   * __AA__ anti-aliased line
%
% The function cv.drawMarker draws a marker on a given position in the image.
% For the moment several marker types are supported, see `MarkerType` for more
% information.
%
% See also: cv.line, cv.circle
%


function [outputImage, selectedSlices, selectedVolumes] = ...
    select4D(this, varargin)
% Creates new image from selected data range, allows interactive picking of
% volumes/slices via clicking on montage
%
%   Y = MrImage()
%   Y.select4D(inputs)
%
% This is a method of class MrImage.
%
% IN
%
%   varargin    'ParameterName', 'ParameterValue'-pairs for the following
%               properties:
%               'method'            'manual' or 'indexed' (default)
%                                   if 'manual', montage plot is presented
%                                   to select4D slices/volumes
%                                   if 'indexed', extraction options (s.b.)
%                                   are used
%               'fixedWithinFigure' 'slices' or 'volumes' (default)
%                                   Defines which dimension is plotted
%                                   within the same figure when doing
%                                   manual selection
%               'combine'           'and'/'all' or 'or'/'once' (default)
%                                   Specifies whether combination over the
%                                   not-fixed dimension (e.g. slices, if
%                                   fixedWithinFigure = 'volumes') shall be
%                                   performed via an
%                                   and-operation
%                                   i.e. selection only if slice/volume
%                                   selected in all montages
%                                   or an or-operation
%                                   i.e. selection as soon as slice/volume
%                                   selected in at least one montage
%
%
%               Parameters for data extraction:
%
%               'signalPart'        for complex data, defines which signal
%                                   part shall be extracted for plotting
%                                       'all'       - do not change data (default)
%                                       'abs'       - absolute value
%                                       'phase'     - phase of signal
%                                       'real'      - real part of signal
%                                       'imag'      - imaginary part of
%                                                     signal
%               'plotMode',         transformation of data before plotting
%                                   'linear' (default), 'log'
%               'selectedX'         [1, nPixelX] vector of selected
%                                   pixel indices in 1st image dimension
%               'selectedY'         [1, nPixelY] vector of selected
%                                   pixel indices in 2nd image dimension
%               'selectedVolumes'   [1,nVols] vector of selected volumes to
%                                             be displayed
%               'selectedSlices'    [1,nSlices] vector of selected slices to
%                                               be displayed
%                                   choose Inf to display all volumes
%               'sliceDimension'    (default: 3) determines which dimension
%                                   shall be plotted as a slice
%               'exclude'           false (default) or true
%                                   if true, selection will be inverted, i.e.
%                                   selectedX/Y/Slices/Volumes will NOT be
%                                   extracted, but all others in dataset
%               'rotate90'          default: 0; 0,1,2,3; rotates image
%                                   by multiple of 90 degrees AFTER
%                                   flipping slice dimensions
% OUT
%
% EXAMPLE
%   select4D
%
%   See also MrImage MrImage.extract_plot4D_data

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-12-01
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (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/>.
%

switch nargin
    case 0
        defaults.method     = 'manual';
    otherwise
        defaults.method     = 'indexed';
end

defaults.fixedWithinFigure  = 'slices';
defaults.exclude            = 'false';

[argsUsed, argsExtract] = tapas_uniqc_propval(varargin, defaults);
tapas_uniqc_strip_fields(argsUsed);

if strcmpi(method, 'manual')
    % plot and click montage
    switch fixedWithinFigure

        %% Montage plot and selection per selected slice
        case {'slice', 'slices'}

            %% Take slice selection, if given
            if isfield(argsExtract, 'selectedSlices') && ...
                    ~isinf(argsExtract.selectedSlices)
                selectedSlices = argsExtract.selectedSlices;
            else
                nSlices = this.geometry.nVoxels(3);
                selectedSlices = 1:nSlices;
            end

            argsExtract.fixedWithinFigure = 'slice';

            %% Montage plot per slice
            for indSlice = selectedSlices
                argsExtract.selectedSlices = indSlice;

                this.plot(argsExtract);

                % Now pick volumes from montage via clicking

            end
        case {'volume', 'volumes'}
    end

else
    [dataSelected, displayRange] = this.extract_plot_data(argsExtract);
end

outputImage                     = this.copyobj('exclude', 'data');
outputImage.data                = dataSelected;
nVoxelsOriginal                 = size(dataSelected);
nVoxelsOriginal(end+1:4)        = 1;

% Update nVoxels,FOV; keep resolution
outputImage.update_geometry_dim_info('nVoxels', nVoxelsOriginal);


% set Matlab path and TOOLBOX_PATH environment variable
addpath(fullfile(pwd, 'matlab'));
setenv('TOOLBOX_PATH', pwd);


function [net,stats] = cnn_train_dag(net, imdb, getBatch, varargin)
%CNN_TRAIN_DAG Demonstrates training a CNN using the DagNN wrapper
%    CNN_TRAIN_DAG() is similar to CNN_TRAIN(), but works with
%    the DagNN wrapper instead of the SimpleNN wrapper.

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

opts.expDir = fullfile('data','exp') ;
opts.continue = true ;
opts.batchSize = 256 ;
opts.numSubBatches = 1 ;
opts.train = [] ;
opts.val = [] ;
opts.gpus = [] ;
opts.prefetch = false ;
opts.numEpochs = 300 ;
opts.learningRate = 0.001 ;
opts.weightDecay = 0.0005 ;
opts.momentum = 0.9 ;
opts.saveMomentum = true ;
opts.nesterovUpdate = false ;
opts.randomSeed = 0 ;
opts.profile = false ;
opts.parameterServer.method = 'mmap' ;
opts.parameterServer.prefix = 'mcn' ;

opts.derOutputs = {'objective', 1} ;
opts.extractStatsFn = @extractStats ;
opts.plotStatistics = true;
opts = vl_argparse(opts, varargin) ;

if ~exist(opts.expDir, 'dir'), mkdir(opts.expDir) ; end
if isempty(opts.train), opts.train = find(imdb.images.set==1) ; end
if isempty(opts.val), opts.val = find(imdb.images.set==2) ; end
if isnan(opts.train), opts.train = [] ; end
if isnan(opts.val), opts.val = [] ; end

% -------------------------------------------------------------------------
%                                                            Initialization
% -------------------------------------------------------------------------

evaluateMode = isempty(opts.train) ;
if ~evaluateMode
    if isempty(opts.derOutputs)
        error('DEROUTPUTS must be specified when training.\n') ;
    end
end

% -------------------------------------------------------------------------
%                                                        Train and validate
% -------------------------------------------------------------------------

modelPath = @(ep) fullfile(opts.expDir, sprintf('net-epoch-%d.mat', ep));
modelFigPath = fullfile(opts.expDir, 'net-train.pdf') ;

start = opts.continue * findLastCheckpoint(opts.expDir) ;
if start >= 1
    fprintf('%s: resuming by loading epoch %d\n', mfilename, start) ;
    [net, state, stats] = loadState(modelPath(start)) ;
else
    state = [] ;
end

for epoch=start+1:opts.numEpochs

    % Set the random seed based on the epoch and opts.randomSeed.
    % This is important for reproducibility, including when training
    % is restarted from a checkpoint.

    rng(epoch + opts.randomSeed) ;
    prepareGPUs(opts, epoch == start+1) ;

    % Train for one epoch.
    params = opts ;
    params.epoch = epoch ;
    params.learningRate = opts.learningRate(min(epoch, numel(opts.learningRate))) ;
    params.train = opts.train(randperm(numel(opts.train))) ; % shuffle
    params.val = opts.val(randperm(numel(opts.val))) ;
    params.imdb = imdb ;
    params.getBatch = getBatch ;

    if numel(opts.gpus) <= 1
        [net, state] = processEpoch(net, state, params, 'val') ;

        [net, state] = processEpoch(net, state, params, 'train') ;
        [net, state] = processEpoch(net, state, params, 'val') ;
        if ~evaluateMode
            saveState(modelPath(epoch), net, state) ;
        end
        lastStats = state.stats ;
    else
        spmd
            [net, state] = processEpoch(net, state, params, 'train') ;
            [net, state] = processEpoch(net, state, params, 'val') ;
            if labindex == 1 && ~evaluateMode
                saveState(modelPath(epoch), net, state) ;
            end
            lastStats = state.stats ;
        end
        lastStats = accumulateStats(lastStats) ;
    end

    stats.train(epoch) = lastStats.train ;
    stats.val(epoch) = lastStats.val ;
    clear lastStats ;
    saveStats(modelPath(epoch), stats) ;

    if opts.plotStatistics
        switchFigure(1) ; clf ;
        plots = setdiff(...
            cat(2,...
            fieldnames(stats.train)', ...
            fieldnames(stats.val)'), {'num', 'time'}) ;
        for p = plots
            p = char(p) ;
            values = zeros(0, epoch) ;
            leg = {} ;
            for f = {'train', 'val'}
                f = char(f) ;
                if isfield(stats.(f), p)
                    tmp = [stats.(f).(p)] ;
                    values(end+1,:) = tmp(1,:)' ;
                    leg{end+1} = f ;
                end
            end
            subplot(1,numel(plots),find(strcmp(p,plots))) ;
            plot(1:epoch, values','o-') ;
            xlabel('epoch') ;
            title(p) ;
            legend(leg{:}) ;
            grid on ;
        end
        drawnow ;
        print(1, modelFigPath, '-dpdf') ;
    end
end

% With multiple GPUs, return one copy
if isa(net, 'Composite'), net = net{1} ; end

% -------------------------------------------------------------------------
function [net, state] = processEpoch(net, state, params, mode)
% -------------------------------------------------------------------------
% Note that net is not strictly needed as an output argument as net
% is a handle class. However, this fixes some aliasing issue in the
% spmd caller.

% initialize with momentum 0
if isempty(state) || isempty(state.momentum)
    state.momentum = num2cell(zeros(1, numel(net.params))) ;
end

% move CNN  to GPU as needed
numGpus = numel(params.gpus) ;
if numGpus >= 1
    net.move('gpu') ;
    state.momentum = cellfun(@gpuArray, state.momentum, 'uniformoutput', false) ;
end
if numGpus > 1
    parserv = ParameterServer(params.parameterServer) ;
    net.setParameterServer(parserv) ;
else
    parserv = [] ;
end

% profile
if params.profile
    if numGpus <= 1
        profile clear ;
        profile on ;
    else
        mpiprofile reset ;
        mpiprofile on ;
    end
end

num = 0 ;
epoch = params.epoch ;
subset = params.(mode) ;
adjustTime = 0 ;

stats.num = 0 ; % return something even if subset = []
stats.time = 0 ;

start = tic ;
for t=1:params.batchSize:numel(subset)
    fprintf('%s: epoch %02d: %3d/%3d:', mode, epoch, ...
        fix((t-1)/params.batchSize)+1, ceil(numel(subset)/params.batchSize)) ;
    batchSize = min(params.batchSize, numel(subset) - t + 1) ;

    for s=1:params.numSubBatches
        % get this image batch and prefetch the next
        batchStart = t + (labindex-1) + (s-1) * numlabs ;
        batchEnd = min(t+params.batchSize-1, numel(subset)) ;
        batch = subset(batchStart : params.numSubBatches * numlabs : batchEnd) ;
        num = num + numel(batch) ;
        if numel(batch) == 0, continue ; end

        inputs = params.getBatch(params.imdb, batch) ;

        if params.prefetch
            if s == params.numSubBatches
                batchStart = t + (labindex-1) + params.batchSize ;
                batchEnd = min(t+2*params.batchSize-1, numel(subset)) ;
            else
                batchStart = batchStart + numlabs ;
            end
            nextBatch = subset(batchStart : params.numSubBatches * numlabs : batchEnd) ;
            params.getBatch(params.imdb, nextBatch) ;
        end

        if strcmp(mode, 'train')
            net.mode = 'normal' ;
            net.accumulateParamDers = (s ~= 1) ;
            net.eval(inputs, params.derOutputs, 'holdOn', s < params.numSubBatches) ;
        else
            net.mode = 'test' ;
            net.vars(11).precious = 1;
            net.eval(inputs) ;
        end

        % test in batch
%         imt = inputs{2};
%         labels = inputs{end};
%         score_batch = squeeze(net.vars(11).value);
%         [~, predLabel_batch] = max(score_batch, [], 1);
%         acc_batch = 1-mean(labels(:)==predLabel_batch(:));
%         % test individually
%         predLabel_single = zeros(1, length(labels));
%         score_single = zeros(10, length(labels));
%         for iii = 1:length(labels)
%             curLabel = labels(iii);
%             curInput = {inputs{1}, imt(:,:,:,iii), inputs{3}, curLabel};
%
%             net.vars(11).precious = 1;
%             net.eval(curInput);
%
%             curA = squeeze(net.vars(11).value);
%             score_single(:, iii) = curA(:);
%             [~, curPredLabel] = max(curA, [], 1);
%             predLabel_single(iii) = curPredLabel;
%         end
%         acc_single = 1-mean(labels(:)==predLabel_single(:));
%
%         fprintf('\nacc in batch form: %.4f\n', acc_batch);
%         fprintf('acc in single form: %.4f\n', acc_single);
%         fprintf('score difference: %.4f\n', norm(score_single(:)-score_batch(:)));

    end

    % Accumulate gradient.
    if strcmp(mode, 'train')
        if ~isempty(parserv), parserv.sync() ; end
        state = accumulateGradients(net, state, params, batchSize, parserv) ;
    end

    % Get statistics.
    time = toc(start) + adjustTime ;
    batchTime = time - stats.time ;
    stats.num = num ;
    stats.time = time ;
    stats = params.extractStatsFn(stats,net) ;
    currentSpeed = batchSize / batchTime ;
    averageSpeed = (t + batchSize - 1) / time ;
    if t == 3*params.batchSize + 1
        % compensate for the first three iterations, which are outliers
        adjustTime = 4*batchTime - time ;
        stats.time = time + adjustTime ;
    end

    fprintf(' %.1f (%.1f) Hz', averageSpeed, currentSpeed) ;
    for f = setdiff(fieldnames(stats)', {'num', 'time'})
        f = char(f) ;
        fprintf(' %s: %.3f', f, stats.(f)) ;
    end
    fprintf('\n') ;
end

% Save back to state.
state.stats.(mode) = stats ;
if params.profile
    if numGpus <= 1
        state.prof.(mode) = profile('info') ;
        profile off ;
    else
        state.prof.(mode) = mpiprofile('info');
        mpiprofile off ;
    end
end
if ~params.saveMomentum
    state.momentum = [] ;
else
    state.momentum = cellfun(@gather, state.momentum, 'uniformoutput', false) ;
end

net.reset() ;
net.move('cpu') ;

% -------------------------------------------------------------------------
function state = accumulateGradients(net, state, params, batchSize, parserv)
% -------------------------------------------------------------------------
numGpus = numel(params.gpus) ;
otherGpus = setdiff(1:numGpus, labindex) ;

for p=1:numel(net.params)

    if ~isempty(parserv)
        parDer = parserv.pullWithIndex(p) ;
    else
        parDer = net.params(p).der ;
    end

    switch net.params(p).trainMethod

        case 'average' % mainly for batch normalization
            thisLR = net.params(p).learningRate ;
            net.params(p).value = vl_taccum(...
                1 - thisLR, net.params(p).value, ...
                (thisLR/batchSize/net.params(p).fanout),  parDer) ;

        case 'gradient'
            thisDecay = params.weightDecay * net.params(p).weightDecay ;
            thisLR = params.learningRate * net.params(p).learningRate ;

            if thisLR>0 || thisDecay>0
                % Normalize gradient and incorporate weight decay.
                parDer = vl_taccum(1/batchSize, parDer, ...
                    thisDecay, net.params(p).value) ;

                % Update momentum.
                state.momentum{p} = vl_taccum(...
                    params.momentum, state.momentum{p}, ...
                    -1, parDer) ;

                % Nesterov update (aka one step ahead).
                if params.nesterovUpdate
                    delta = vl_taccum(...
                        params.momentum, state.momentum{p}, ...
                        -1, parDer) ;
                else
                    delta = state.momentum{p} ;
                end

                % Update parameters.
                net.params(p).value = vl_taccum(...
                    1,  net.params(p).value, thisLR, delta) ;
            end
        otherwise
            error('Unknown training method ''%s'' for parameter ''%s''.', ...
                net.params(p).trainMethod, ...
                net.params(p).name) ;
    end
end

% -------------------------------------------------------------------------
function stats = accumulateStats(stats_)
% -------------------------------------------------------------------------

for s = {'train', 'val'}
    s = char(s) ;
    total = 0 ;

    % initialize stats stucture with same fields and same order as
    % stats_{1}
    stats__ = stats_{1} ;
    names = fieldnames(stats__.(s))' ;
    values = zeros(1, numel(names)) ;
    fields = cat(1, names, num2cell(values)) ;
    stats.(s) = struct(fields{:}) ;

    for g = 1:numel(stats_)
        stats__ = stats_{g} ;
        num__ = stats__.(s).num ;
        total = total + num__ ;

        for f = setdiff(fieldnames(stats__.(s))', 'num')
            f = char(f) ;
            stats.(s).(f) = stats.(s).(f) + stats__.(s).(f) * num__ ;

            if g == numel(stats_)
                stats.(s).(f) = stats.(s).(f) / total ;
            end
        end
    end
    stats.(s).num = total ;
end

% -------------------------------------------------------------------------
function stats = extractStats(stats, net)
% -------------------------------------------------------------------------
sel = find(cellfun(@(x) isa(x,'dagnn.Loss'), {net.layers.block})) ;
for i = 1:numel(sel)
    stats.(net.layers(sel(i)).outputs{1}) = net.layers(sel(i)).block.average ;
end

% -------------------------------------------------------------------------
function saveState(fileName, net_, state)
% -------------------------------------------------------------------------
net = net_.saveobj() ;
save(fileName, 'net', 'state') ;

% -------------------------------------------------------------------------
function saveStats(fileName, stats)
% -------------------------------------------------------------------------
if exist(fileName)
    save(fileName, 'stats', '-append') ;
else
    save(fileName, 'stats') ;
end

% -------------------------------------------------------------------------
function [net, state, stats] = loadState(fileName)
% -------------------------------------------------------------------------
load(fileName, 'net', 'state', 'stats') ;
net = dagnn.DagNN.loadobj(net) ;
if isempty(whos('stats'))
    error('Epoch ''%s'' was only partially saved. Delete this file and try again.', ...
        fileName) ;
end

% -------------------------------------------------------------------------
function epoch = findLastCheckpoint(modelDir)
% -------------------------------------------------------------------------
list = dir(fullfile(modelDir, 'net-epoch-*.mat')) ;
tokens = regexp({list.name}, 'net-epoch-([\d]+).mat', 'tokens') ;
epoch = cellfun(@(x) sscanf(x{1}{1}, '%d'), tokens) ;
epoch = max([epoch 0]) ;

% -------------------------------------------------------------------------
function switchFigure(n)
% -------------------------------------------------------------------------
if get(0,'CurrentFigure') ~= n
    try
        set(0,'CurrentFigure',n) ;
    catch
        figure(n) ;
    end
end

% -------------------------------------------------------------------------
function clearMex()
% -------------------------------------------------------------------------
clear vl_tmove vl_imreadjpeg ;

% -------------------------------------------------------------------------
function prepareGPUs(opts, cold)
% -------------------------------------------------------------------------
numGpus = numel(opts.gpus) ;
if numGpus > 1
    % check parallel pool integrity as it could have timed out
    pool = gcp('nocreate') ;
    if ~isempty(pool) && pool.NumWorkers ~= numGpus
        delete(pool) ;
    end
    pool = gcp('nocreate') ;
    if isempty(pool)
        parpool('local', numGpus) ;
        cold = true ;
    end

end
if numGpus >= 1 && cold
    fprintf('%s: resetting GPU\n', mfilename)
    clearMex() ;
    if numGpus == 1
        gpuDevice(opts.gpus)
    else
        spmd
            clearMex() ;
            gpuDevice(opts.gpus(labindex))
        end
    end
end


 function jf_assert(varargin)
%function jf_assert(command)
% verify that the command (evaluated within caller) returns true.
% if not, print error message.

if nargin < 1, ir_usage, end
if nargin == 1 && streq(varargin{1}, 'test'), jf_assert_test, return, end

arg = [varargin{:}]; % handle cases with spaces like 'jf_assert x == y'

[name line] = caller_name;
if isempty(name)
	str = '';
else
	str = sprintf(' at %d in "%s"', line, name);
end

try
	tmp = evalin('caller', arg);
catch
	error(['%s was unable to evaluate "%s"' str], mfilename, arg)
end

% note: use isequal, not issame!
if isscalar(tmp) && islogical(tmp)
	if ~tmp
		fail(['jf_assert of "%s" was untrue' str], arg)
%		dbup
%		dbstack
%		keyboard
	end
else
tmp
	whos
	error(['jf_assert of "%s" did not return logical scalar' str], arg)
end

function jf_assert_test
jf_assert 7 == 7


% SARImageWriter is an abstract superclass describing the family of all
% writers of SAR raster data.  It was desgined particularly with complex
% SAR data in mind, but subclasses could be built to write detected SAR
% data as well.
%
% Written by: Wade Schwartzkopf, NGA/ID
%
% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////

classdef SARImageWriter < handle
    % Public class data.  Anyone has access to these in a read-only fashion.
    properties(SetAccess=protected, GetAccess=public)
        sicdmeta; % Collection and image formation metadata
        filename;
    end

    methods
        % Constructor.  Since this superclass constructor requires
        % arguments, constructors for derived classes will typically call
        % this explicitly (MATLAB's default is to call the superclass
        % constructor without argument.) to assure that the internal
        % metatdata is correctly set on creation.
        function obj = SARImageWriter(filename, sicdmeta)
            % Fundamental properties
            obj.filename = filename;
            obj.sicdmeta = sicdmeta;
        end
    end

    % All subclasses need a way to write pixel data
    methods (Abstract)
        status = write_chip(obj, data, start_indices);
    end

    methods (Static)
        % Utility function that mimics the behavior of MATLAB's FSEEK
        % function, but allows for seeking past the end of a file (just
        % appends zeros to the end of the file).
        status = fseek(fid, offset, origin);
    end

end

% //////////////////////////////////////////
% /// CLASSIFICATION: UNCLASSIFIED       ///
% //////////////////////////////////////////


% VL_ERFILL  Fill extremal region
%  MEMBERS=VL_ERFILL(I,ER) returns the list MEMBERS of the pixels which
%  belongs to the extremal region represented by the pixel ER.
%
%  The selected region is the one that contains pixel ER and of
%  intensity I(ER).
%
%  I must be of class UINT8 and ER must be a (scalar) index of the
%  region representative point.
%
%  See also: VL_MSER(), VL_HELP().

% Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).


function l=v_gmmlpdf(varargin)
%V_GMMLPDF obsolete function - please use GAUSSMIXP instead
l=v_gaussmixp(varargin{1:nargin});


function [resample] = resampledesign(cfg, design)

% RESAMPLEDESIGN returns a resampling matrix, in which each row can be
% used to resample either the original design matrix or the original data.
% The random resampling is done given user-specified constraints on the
% experimental design, e.g. to swap within paired observations but not
% between pairs.
%
% Use as
%   [resample] = randomizedesign(cfg, design)
% where the configuration can contain
%   cfg.resampling       = 'permutation' or 'bootstrap'
%   cfg.numrandomization = number (e.g. 300), can be 'all' in case of two conditions
%   cfg.ivar             = number or list with indices, independent variable(s)
%   cfg.uvar             = number or list with indices, unit variable(s)
%   cfg.wvar             = number or list with indices, within-cell variable(s)
%   cfg.cvar             = number or list with indices, control variable(s)
%
% The "Independent variable" codes the condition number. Since the data is
% assumed to be independent from the condition number any reshuffeling of
% the condition number is allowed and ivar does NOT affect the resampling
% outcome.
%
% The "Unit of observation variable" corresponds to the subject number (in a
% within-subject manipulation) or the trial number (in a within-trial
% manipulation). It is best understood by considering that it corresponds
% to the "pairing" of the data in a paired T-test or repeared measures
% ANOVA. The uvar affects the resampling outcome in the way that only
% resamplings within one unit of observation are returned (e.g. swap
% conditions within a subject, not over subjects).
%
% The "Within-cell variable" corresponds to the grouping of the data in
% cells, where the multiple observations in a groups should not be broken
% apart. This for example applies to multiple tapers in a spectral estimate
% of a single trial of data (the "rpttap" dimension), where different
% tapers should not be shuffled separately. Another example is a blocked
% fMRI design, with a different condition in each block and multiple
% repetitions of the same condition within a block. Assuming that there is
% a slow HRF that convolutes the trials within a block, you can shuffle the
% blocks but not the individual trials in a block.
%
% The "Control variable" can be seen as the opposite from the within-cell
% variable: it allows you to specify blocks in which the resampling should
% be done, at the same time controlling that repetitions are not shuffled
% between different control blocks.
%
% See also FT_STATISTICS_MONTECARLO

% Copyright (C) 2005-2020, 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$

ft_checkopt(cfg, 'ivar', {'numericscalar', 'numericvector', 'empty'});
ft_checkopt(cfg, 'uvar', {'numericscalar', 'numericvector', 'empty'});
ft_checkopt(cfg, 'wvar', {'numericscalar', 'numericvector', 'empty'});
ft_checkopt(cfg, 'cvar', {'numericscalar', 'numericvector', 'empty'});
ft_checkopt(cfg, 'numrandomization', {'numericscalar', 'char'});
ft_checkopt(cfg, 'resampling', {'char'});

cfg.ivar = ft_getopt(cfg, 'ivar'); % the default is empty
cfg.uvar = ft_getopt(cfg, 'uvar'); % the default is empty
cfg.wvar = ft_getopt(cfg, 'wvar'); % the default is empty
cfg.cvar = ft_getopt(cfg, 'cvar'); % the default is empty

% the default is 'no', consistent with the situation prior to 14 Sept 2011
% FIXME this needs finalization
efficient = ft_getopt(cfg, 'efficient', 'no');

% if size(design,1)>size(design,2)
%   % this function wants the replications in the column direction
%   % the matrix seems to be transposed
%   design = transpose(design);
% end

Nvar  = size(design,1);   % number of factors or regressors
Nrepl = size(design,2);   % number of replications

if ~isempty(intersect(cfg.ivar, cfg.uvar)), ft_warning('there is an intersection between cfg.ivar and cfg.uvar'); end
if ~isempty(intersect(cfg.ivar, cfg.wvar)), ft_warning('there is an intersection between cfg.ivar and cfg.wvar'); end
if ~isempty(intersect(cfg.ivar, cfg.cvar)), ft_warning('there is an intersection between cfg.ivar and cfg.cvar'); end
if ~isempty(intersect(cfg.uvar, cfg.wvar)), ft_warning('there is an intersection between cfg.uvar and cfg.wvar'); end
if ~isempty(intersect(cfg.uvar, cfg.cvar)), ft_warning('there is an intersection between cfg.uvar and cfg.cvar'); end
if ~isempty(intersect(cfg.wvar, cfg.cvar)), ft_warning('there is an intersection between cfg.wvar and cfg.cvar'); end

fprintf('total number of measurements     = %d\n', Nrepl);
fprintf('total number of variables        = %d\n', Nvar);
fprintf('number of independent variables  = %d\n', length(cfg.ivar));
fprintf('number of unit variables         = %d\n', length(cfg.uvar));
fprintf('number of within-cell variables  = %d\n', length(cfg.wvar));
fprintf('number of control variables      = %d\n', length(cfg.cvar));
fprintf('using a %s resampling approach\n', cfg.resampling);

if ~isempty(cfg.cvar)
  % the different levels of the control variable indicate the blocks in which the resampling can be done
  % the replications should not be resampled over the blocks
  blocklevel = unique(design(cfg.cvar,:)', 'rows')';
  for i=1:size(blocklevel,2)
    blocksel{i} = find(all(design(cfg.cvar,:)==repmat(blocklevel(:,i), 1, Nrepl), 1));
    blocklen(i) = length(blocksel{i});
  end
  for i=1:size(blocklevel,2)
    fprintf('------------------------------------------------------------\n');
    if length(cfg.cvar)>1
      fprintf('resampling the subset where the control variable level is [%s]\n', num2str(blocklevel(:,i)));
    else
      fprintf('resampling the subset where the control variable level is %s\n', num2str(blocklevel(:,i)));
    end
    % use recursion to resample the replications within each block
    tmpcfg = cfg;
    tmpcfg.cvar = [];
    blockres{i} = blocksel{i}(resampledesign(tmpcfg, design(:, blocksel{i})));
  end
  % concatenate the blocks and return the result
  resample = cat(2, blockres{:});
  return
end

if isnumeric(cfg.numrandomization) && cfg.numrandomization==0
  % no randomizations are requested, return an empty shuffling matrix
  resample = zeros(0,Nrepl);
  return;
end

if ~isempty(cfg.wvar)
  % keep the elements within a cell together, e.g. multiple tapers in a trial
  % this is realized by replacing the design matrix temporarily with a smaller version
  blkmeas = unique(design(cfg.wvar,:)', 'rows')';
  for i=1:size(blkmeas,2)
    blksel{i} = find(all(design(cfg.wvar,:)==repmat(blkmeas(:,i), 1, Nrepl), 1));
    blklen(i) = length(blksel{i});
  end
  if any(blklen~=blklen(1))
    ft_error('the number of repetitions per block should be constant');
  end
  for i=1:size(blkmeas,2)
    if any(diff(design(:, blksel{i}), 1, 2)~=0)
      ft_error('the design matrix variables should be constant within a block');
    end
  end
  orig_design = design;
  orig_Nrepl  = Nrepl;
  % replace the design matrix by a blocked version
  design = zeros(size(design,1), size(blkmeas,2));
  Nrepl  = size(blkmeas,2);
  for i=1:size(blkmeas,2)
    design(:,i) = orig_design(:, blksel{i}(1));
  end
end

% do some validity checks
if Nvar==1 && ~isempty(cfg.uvar)
  ft_error('A within-units shuffling requires a at least one unit variable and at least one independent variable');
end

if isempty(cfg.uvar) && strcmp(cfg.resampling, 'permutation')
  % reshuffle the colums of the design matrix
  if ischar(cfg.numrandomization) && strcmp(cfg.numrandomization, 'all')
    % systematically shuffle the columns in the design matrix
    Nperm = prod(1:Nrepl);
    fprintf('creating all possible permutations (%d)\n', Nperm);
    resample = perms(1:Nrepl);
  elseif ~ischar(cfg.numrandomization)
    % randomly shuffle the columns in the design matrix the specified number of times
    resample = zeros(cfg.numrandomization, Nrepl);
    for i=1:cfg.numrandomization
      resample(i,:) = randperm(Nrepl);
    end
  end

elseif isempty(cfg.uvar) && strcmp(cfg.resampling, 'bootstrap')
  % randomly draw with replacement, keeping the number of elements the same in each class
  % only the test under the null-hypothesis (h0) is explicitely implemented here
  % but the h1 test can be achieved using a control variable
  resample = zeros(cfg.numrandomization, Nrepl);
  for i=1:cfg.numrandomization
    resample(i,:) = randsample(1:Nrepl, Nrepl, true);
  end

elseif ~isempty(cfg.uvar) && strcmp(cfg.resampling, 'permutation')
  % reshuffle the colums of the design matrix, keep the rows of the design matrix with the unit variable intact
  unitlevel = unique(design(cfg.uvar,:)', 'rows')';
  for i=1:size(unitlevel,2)
    unitsel{i} = find(all(design(cfg.uvar,:)==repmat(unitlevel(:,i), 1, Nrepl), 1));
    unitlen(i) = length(unitsel{i});
  end
  if length(cfg.uvar)==1
    fprintf('repeated measurement in variable %d over %d levels\n', cfg.uvar, length(unitlevel));
    fprintf('number of repeated measurements in each level is '); fprintf('%d ', unitlen); fprintf('\n');
  else
    fprintf('repeated measurement in mutiple variables over %d levels\n', length(unitlevel));
    fprintf('number of repeated measurements in each level is '); fprintf('%d ', unitlen); fprintf('\n');
  end
  fprintf('the maximum number of unique permutations is %d\n', prod(unitlen));

  if ischar(cfg.numrandomization) && strcmp(cfg.numrandomization, 'all')
    % create all possible permutations by systematic assignment
    if any(unitlen~=2)
      % it would be possible to also implement it for other cases
      % but so far ther has not been a concrete need for it
      ft_error('cfg.numrandomization=''all'' is only supported for two repeated measurements');
    end
    Nperm = 2^(length(unitlevel));
    resample = zeros(Nperm, Nrepl);
    for i=1:Nperm
      flip  = dec2bin( i-1, length(unitlevel));
      for j=1:length(unitlevel)
        if     strcmp('0', flip(j))
          resample(i, unitsel{j}(1)) = unitsel{j}(1);
          resample(i, unitsel{j}(2)) = unitsel{j}(2);
        elseif strcmp('1', flip(j))
          resample(i, unitsel{j}(1)) = unitsel{j}(2);
          resample(i, unitsel{j}(2)) = unitsel{j}(1);
        end
      end
    end
    fprintf('generated all %d possible permutations\n', 2^(length(unitlevel)));

  elseif ~ischar(cfg.numrandomization)
    % see https://github.com/fieldtrip/fieldtrip/issues/1313
    if cfg.numrandomization > prod(unitlen)
      ft_warning('the number of randomizations (%d) is larger than the maximum number of unique permutations (%d), better use cfg.numrandomization=''all''', cfg.numrandomization, prod(unitlen))
    elseif cfg.numrandomization/prod(unitlen) > 0.5
      ft_warning('the number of randomizations (%d) is close to the maximum number of unique permutations (%d), better use cfg.numrandomization=''all''', cfg.numrandomization, prod(unitlen))
    end

    % create the desired number of permutations by random shuffling
    resample = zeros(cfg.numrandomization, Nrepl);
    for i=1:cfg.numrandomization
      for j=1:length(unitlevel)
        resample(i, unitsel{j}) = unitsel{j}(randperm(length(unitsel{j})));
      end
    end
    fprintf('generated %d random permutations\n', cfg.numrandomization);
  end

elseif length(cfg.uvar)==1 && strcmp(cfg.resampling, 'bootstrap') && isempty(cfg.cvar)
  % randomly draw with replacement, keeping the number of elements the same in each class
  % only the test under the null-hypothesis (h0) is explicitely implemented here
  % but the h1 test can be achieved using a control variable

  % FIXME allow for length(cfg.uvar)>1, does it make sense in the first place
  % bootstrap the units of observation
  units = design(cfg.uvar,:);
  for k = 1:length(unique(units))
    sel = find(units==k);
    indx(:,k) = sel;
    Nrep(k)   = length(sel);
  end
  resample = zeros(cfg.numrandomization, Nrepl);

  %sanity check on number of repetitions
  if any(Nrep~=Nrep(1)), ft_error('all units of observation should have an equal number of repetitions'); end

  if max(units(:))<20
    ft_warning('fewer than 20 units warrants explicit checking of double occurrences of ''bootstraps''');
    checkunique = 1;
  else
    checkunique = 0;
  end

  if ~checkunique
    for i=1:cfg.numrandomization
      tmp           = randsample(1:Nrepl/Nrep(1), Nrepl/Nrep(1), true);
      for k=1:size(indx,1)
        resample(i,indx(k,:)) = indx(k,tmp);
      end
    end
  else
    tmp = zeros(cfg.numrandomization*10, Nrepl/Nrep(1));
    for i=1:cfg.numrandomization*10
      tmp(i,:) = sort(randsample(1:Nrepl/Nrep(1), Nrepl/Nrep(1), true));
    end

    tmp = unique(tmp, 'rows');
    fprintf('found %d unique rows in bootstrap matrix of %d bootstraps', size(tmp,1), cfg.numrandomization*10);

    if size(tmp,1)<cfg.numrandomization
      fprintf('using only %d unique bootstraps\n', size(tmp,1));
      cfg.numrandomization = size(tmp,1);
      index = 1:size(tmp,1);
    else
      index = randperm(size(tmp,1));
      index = index(1:cfg.numrandomization);
    end

    tmp = tmp(index,:);
    for i=1:cfg.numrandomization
      for k=1:size(indx,1)
        resample(i,indx(k,:)) = indx(k,tmp(i,:));
      end
    end

  end

else
  ft_error('Unsupported configuration for resampling.');
end

if ~isempty(cfg.wvar)
  % switch back to the original design matrix and expand the resample ordering
  % matrix so that it reorders all elements within a cell together
  Nrepl  = orig_Nrepl;
  expand = zeros(size(resample,1), Nrepl);
  for i=1:size(resample,1)
    expand(i,:) = cat(2, blksel{resample(i,:)});
  end
  resample = expand;
  % replace the blocked version of the design by the original one
  design = orig_design;
  Nrepl  = orig_Nrepl;
end

% in some cases it is possible to reduce the number of permutations by taking only the ones that result in unique condition sequences
% but important is that the relative requencies of the condition sequences remains the same
if strcmp(efficient, 'yes')
  if numel(cfg.ivar)<1
    ft_error('this reqiures at least one independent variable to be specified (ivar)');
  end
  if numel(cfg.uvar)>0
    ft_error('this is not yet supported in combination with a unit of observation (uvar)');
  end

  original = zeros(size(resample,1), numel(cfg.ivar)*size(resample,2));
  for i=1:size(resample,1)
    for j=1:numel(cfg.ivar)
      sel = (1:Nrepl) + (j-1)*Nrepl;
      original(i,sel) = design(cfg.ivar(j), resample(i,:));
    end
  end

  [reduced, indx1, indx2] = unique(original, 'rows');
  % this returns the reduced condition sequences, where
  % indx1 contains for the reduced row number the original (last) row number
  % indx2 contains for the original condition sequences the reduced row number
  frequency = zeros(size(reduced,1),1);
  for i=1:size(reduced,1)
    frequency(i) = sum(indx2==i);
  end

  if all(frequency==frequency(1))
    % the relative frequency of the reduced condition sequences is the same
    % as the relative frequency of the original condition sequences, which
    % means that the reduced set of permutations is appropriate
    fprintf('using the reduced set of permutations (%d) instead of the original permutations (%d)\n', size(reduced,1), size(resample,1));
    resample = resample(indx1,:);
  else
    % don't use the reduced set of permutations
    fprintf('the reduced set has different relative frequencies of the conditions, retaining the original permutations\n')
  end
end % if efficient


function MD5 = CalcMD5(Data, InClass, OutClass)  %#ok<STOUT,INUSD>
% 128 bit MD5 checksum: file, string, byte stream [MEX]
% This function calculates a 128 bit checksum for arrays and files.
% Digest = CalcMD5(Data, [InClass], [OutClass])
% INPUT:
%   Data:   Data array or file name. Either numerical or CHAR array.
%           Currently only files and arrays with up to 2^32 bytes (2.1GB) are
%           accepted.
%   InClass: String to declare the type of the 1st input.
%           Optional. Default: 'Char'.
%           'File': [Data] is a file name as string. The digest is calculated
%                   for this file.
%           'Char': [Data] is a char array to calculate the digest for. Only the
%                   ASCII part of the Matlab CHARs is used, such that the digest
%                   is the same as if the array is written to a file as UCHAR,
%                   e.g. with FWRITE.
%           'Unicode': All bytes of the input [Data] are used to calculate the
%                   digest. This is the standard for numerical input.
%   OutClass: String, format of the output. Just the first character matters.
%           Optional, default: 'hex'.
%           'hex': [1 x 32] string as lowercase hexadecimal number.
%           'HEX': [1 x 32] string as uppercase hexadecimal number.
%           'Dec': [1 x 16] double vector with UINT8 values.
%           'Base64': [1 x 22] string, encoded to base 64 (A:Z,a:z,0:9,+,/).
%
% OUTPUT:
%   Digest: A 128 bit number is replied in a format depending on [OutClass].
%           The chance, that different data sets have the same MD5 sum is about
%           2^128 (> 3.4 * 10^38). Therefore MD5 can be used as "finger-print"
%           of a file rather than e.g. CRC32.
%
% EXAMPLES:
%   Three methods to get the MD5 of a file:
%   1. Direct file access (recommended):
%     MD5 = CalcMD5(which('CalcMD5.m'), 'File')
%   2. Import the file to a CHAR array (binary mode for exact line breaks!):
%     FID = fopen(which('CalcMD5.m'), 'rb');
%     S   = fread(FID, inf, 'uchar=>char');
%     fclose(FID);
%     MD5 = CalcMD5(S, 'char')
%   3. Import file as a byte stream:
%     FID = fopen(which('CalcMD5.m'), 'rb');
%     S   = fread(FID, inf, 'uint8=>uint8');
%     fclose(FID);
%     MD5 = CalcMD5(S, 'unicode');  // 'unicode' can be omitted here
%
%   Test string:
%     CalcMD5(char(0:511), 'char', 'HEX')
%       => F5C8E3C31C044BAE0E65569560B54332
%     CalcMD5(char(0:511), 'unicode', 'HEX')
%       => 3484769D4F7EBB88BBE942BB924834CD
%
% Tested: Matlab 6.5, 7.7, 7.8, WinXP, [UnitTest]
% Author: Jan Simon, Heidelberg, (C) 2009-2010 J@n-Simon.De
% License: This program is derived from the RSA Data Security, Inc.
%          MD5 Message Digest Algorithm, RFC 1321, R. Rivest, April 1992
%
% See also CalcCRC32.
% Michael Kleder has published a Java call to compute the MD5 (and further
% check sums): http://www.mathworks.com/matlabcentral/fileexchange/8944

% $JRev: R5.00j V:015 Sum:zh2gTrvHwbd7 Date:17-Dec-2009 02:46:53 $
% $File: CalcMD5\CalcMD5.m $
% History:
% 015: 15-Dec-2009 16:53, BUGFIX: UINT32 has 32 bits on 64 bit systems now.
%      Thanks to Sebastiaan Breedveld!

% If the current Matlab path is the parent folder of this script, the
% MEX function is not found - change the current directory!
error(['JSim:', mfilename, ':NoMex'], 'Cannot find MEX script.');


classdef LatexTable < handle
  properties
    rowSuffixes
    rowPrefixes
    colPrefixes
    entries
    r
    c
  end

  methods
    function obj = begin(obj)
      obj.rowSuffixes = {} ;
      obj.rowPrefixes = {''} ;
      obj.colPrefixes = {''} ;
      obj.entries = {} ;
      obj.r = 1 ;
      obj.c = 1 ;
    end
    function obj = pf(obj, str, varargin)
      obj.entries{obj.r,obj.c} = sprintf(str, varargin{:}) ;
      obj.c = obj.c + 1 ;
      if obj.r == 1, obj.colPrefixes{obj.c} = '' ; end
    end
    function obj = endl(obj)
      obj.rowSuffixes{obj.r} = sprintf('\\\\\n') ;
      obj.c = 1 ;
      obj.r = obj.r + 1 ;
      obj.rowPrefixes{obj.r} = '' ;
    end
    function obj = vline(obj)
      obj.colPrefixes{obj.c} = horzcat(obj.colPrefixes{obj.c}, '|') ;
    end
    function obj = hline(obj)
      obj.rowPrefixes{obj.r} = horzcat(obj.rowPrefixes{obj.r}, sprintf('\\hline\n')) ;
    end
    function move(obj, r, c)
      obj.r = r ;
      obj.c = c ;
    end
    function str = end(obj)
      str = {} ;
      nc = size(obj.entries,2) ;
      nr = size(obj.entries,1) ;
      sizes = cellfun(@(x) numel(x), obj.entries) ;
      widths = max(sizes,[],1) ;

      str{end+1} = '\begin{tabular}{' ;
      for c=1:nc
        str{end+1} = [obj.colPrefixes{c} 'c'] ;
      end
      str{end+1} = sprintf('%s}\n', obj.colPrefixes{nc+1}) ;

      for r = 1:nr
        str{end+1} = obj.rowPrefixes{r} ;
        for c = 1:nc
          format = sprintf('%%%ds', widths(c)) ;
          if c > 1, format = [' &' format] ; end
          str{end+1} = sprintf(format, obj.entries{r,c}) ;
        end
        str{end+1} = obj.rowSuffixes{r} ;
      end
      str{end+1} = obj.rowPrefixes{nr+1} ;
      str{end+1} = sprintf('\\end{tabular}\n') ;
      str = horzcat(str{:}) ;
    end
  end
end


%
% Set the default parameters for a specific experiment (protocol/subject)
%
% Parameters
% ----------
% protocol : string
%   Folder for a specific acquisition protocol, possible containing different subjects
% subject : string
%   Folder of the specific subject
%
function AMICO_SetSubject( protocol, subject )

	global AMICO_data_path
	global CONFIG

	CONFIG = [];

	CONFIG.protocol			= protocol;
	CONFIG.subject			= subject;

	CONFIG.DATA_path		= fullfile( AMICO_data_path, CONFIG.protocol, CONFIG.subject );
	CONFIG.dwiFilename		= fullfile( CONFIG.DATA_path, 'DWI.nii' );
	CONFIG.maskFilename     = fullfile( CONFIG.DATA_path, 'roi_mask.nii' );
	CONFIG.dim              = [];
	CONFIG.pixdim           = [];
	CONFIG.OUTPUT_path      = [];

	CONFIG.schemeFilename	= fullfile( CONFIG.DATA_path, 'DWI.scheme' );
	CONFIG.scheme			= [];
    CONFIG.b0_thr           = 1;

	CONFIG.OPTIMIZATION     = [];


function doseV = applyIMRTCompression(params, doseV)
%"applyCompression"
%   Applies compression specified in the IM structure to the doseV vector
%   and returns it.
%
%   Based on code by JOD and CZ
%
%JRA 30 Aug 04
%
%Usage:
%   function doseV = applyCompression(IM, doseV)
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

switch lower(params.ScatterMethod)
    case 'exponential'
        %Apply "exponential" compression.
        maxD = max(doseV);
        doseVLow = doseV;
        doseVLow(doseVLow > params.Scatter.Threshold*maxD) = 0;
        lowInd = find(doseVLow);

        if ~isempty(lowInd)
            [sortDose, ind] = sort(doseVLow(lowInd)); %%
            maxOfSorted = max(sortDose(:));
            normSortDose = sortDose/maxOfSorted;
            coins = rand(length(normSortDose), 1);
            keepers = find(normSortDose > coins);

            doseV(doseV < params.Scatter.Threshold*maxD) = 0;
            doseV(lowInd(ind(keepers))) = doseVLow(lowInd(ind(keepers)));% + evenDose;
        else
            doseV;
        end

    case 'random' %Also known as "probabilistic".
        maxD = max(doseV);
        Step = params.Scatter.RandomStep;
        doseVLow = doseV;
        doseVLow(doseVLow > params.Scatter.Threshold*maxD) = 0;
        lowInd = find(doseVLow);
        N = floor(length(lowInd)/Step);

        downInd = round(rand(N,1) * (Step-1) + 1);
        tmp = cumsum([0;ones(N-1,1)]*Step);
        downInd = downInd + tmp;
%         for p = 1 : N-1;
%             downInd(p) = lowInd(ceil(Step*rand) + Step*(p-1));
%         end
        if exist('doseVLowDown')
            clear doseVLowDown;
        end
        doseVLowDown  = zeros(size(doseV));
        f = 1:N-1;
%         for f = 1:N-1
            doseVLowDown(downInd(f)) = doseVLow(downInd(f));
%         end
        doseV(doseV < params.Scatter.Threshold*maxD) = 0;
        doseV = doseV + doseVLowDown;
    case 'threshold'
        maxD = max(doseV);
        doseV(doseV < params.Scatter.Threshold*maxD) = 0;
    otherwise
        error('Invalid compression method in IM.params.ScatterMethod.');
end


function remove = spm_cfg_eeg_remove_bad_trials
% configuration file for copying
%__________________________________________________________________________
% Copyright (C) 2009-2012 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_cfg_eeg_remove_bad_trials.m 5377 2013-04-02 17:07:57Z vladimir $

%--------------------------------------------------------------------------
% D
%--------------------------------------------------------------------------
D        = cfg_files;
D.tag    = 'D';
D.name   = 'File Name';
D.filter = 'mat';
D.num    = [1 1];
D.help   = {'Select the M/EEG mat file.'};

%--------------------------------------------------------------------------
% prefix
%--------------------------------------------------------------------------
prefix         = cfg_entry;
prefix.tag     = 'prefix';
prefix.name    = 'Filename Prefix';
prefix.help    = {'Specify the string to be prepended to the filenames of the output dataset. Default prefix is ''r''.'};
prefix.strtype = 's';
prefix.num     = [1 Inf];
prefix.val     = {'r'};

%--------------------------------------------------------------------------
% remove
%--------------------------------------------------------------------------
remove          = cfg_exbranch;
remove.tag      = 'remove';
remove.name     = 'Remove bad trials';
remove.val      = {D, prefix};
remove.help     = {'Removes bad trials and re-orders trials to conform to condlist'}';
remove.prog     = @eeg_remove;
remove.vout     = @vout_eeg_remove;
remove.modality = {'EEG'};

%==========================================================================
function out = eeg_remove(job)
% construct the S struct
S           = job;
S.D         = S.D{1};
out.D       = spm_eeg_remove_bad_trials(S);
out.Dfname  = {fullfile(out.D)};

%==========================================================================
function dep = vout_eeg_remove(job)
% return dependencies
dep(1)            = cfg_dep;
dep(1).sname      = 'Cleaned M/EEG data';
dep(1).src_output = substruct('.','D');
dep(1).tgt_spec   = cfg_findspec({{'strtype','e'}});

dep(2)            = cfg_dep;
dep(2).sname      = 'Cleaned M/EEG datafile';
dep(2).src_output = substruct('.','Dfname');
dep(2).tgt_spec   = cfg_findspec({{'filter','mat'}});