function varargout = process_evt_simple( varargin )
% PROCESS_EVT_SIMPLE: Convert extended events to simple events
%
% USAGE:  OutputFiles = process_evt_simple('Run', sProcess, sInput)

% @=============================================================================
% 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, 2015-2018

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Convert to simple event';
    sProcess.Category    = 'File';
    sProcess.SubGroup    = 'Events';
    sProcess.Index       = 63;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/EventMarkers#Other_menus';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data', 'raw', 'matrix'};
    sProcess.OutputTypes = {'data', 'raw', 'matrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Event name
    sProcess.options.eventname.Comment = 'Event names: ';
    sProcess.options.eventname.Type    = 'text';
    sProcess.options.eventname.Value   = '';
    % Time offset
    sProcess.options.method.Comment = {'Keep the start of the events', 'Keep the middle of the events', 'Keep the end of the events'};
    sProcess.options.method.Type    = 'radio';
    sProcess.options.method.Value   = 1;
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFile = Run(sProcess, sInput) %#ok<DEFNU>
    % Return all the input files
    OutputFile = {sInput.FileName};

    % ===== GET OPTIONS =====
    % Time offset
    switch (sProcess.options.method.Value)
        case 1,  Method = 'start';
        case 2,  Method = 'middle';
        case 3,  Method = 'end';
    end
    % Event names
    EvtNames = strtrim(str_split(sProcess.options.eventname.Value, ',;'));
    if isempty(EvtNames)
        bst_report('Error', sProcess, [], 'No events selected.');
        return;
    end

    % ===== LOAD FILE =====
    % Get file descriptor
    isRaw = strcmpi(sInput.FileType, 'raw');
    % Load the raw file descriptor
    if isRaw
        DataMat = in_bst_data(sInput.FileName, 'F');
        sEvents = DataMat.F.events;
        sFreq = DataMat.F.prop.sfreq;
    else
        DataMat = in_bst_data(sInput.FileName, 'Events', 'Time');
        sEvents = DataMat.Events;
        sFreq = 1 ./ (DataMat.Time(2) - DataMat.Time(1));
    end
    % If no markers are present in this file
    if isempty(sEvents)
        bst_report('Error', sProcess, sInput, 'This file does not contain any event.');
        return;
    end
    % Find event names
    iEvtList = [];
    for i = 1:length(EvtNames)
        iEvt = find(strcmpi(EvtNames{i}, {sEvents.label}));
        if isempty(iEvt)
            bst_report('Warning', sProcess, sInput, 'This file does not contain any event.');
        elseif isempty(sEvents(iEvt).times)
            bst_report('Warning', sProcess, sInput, ['Event category "' sEvents(iEvt).label '" is empty.']);
        elseif (size(sEvents(iEvt).times,1) == 1)
            bst_report('Warning', sProcess, sInput, ['Event category "' sEvents(iEvt).label '" already contains simple events.']);
        else
            iEvtList(end+1) = iEvt;
        end
    end
    % No events to process
    if isempty(iEvtList)
        bst_report('Error', sProcess, sInput, 'No events to process.');
        return;
    end

    % ===== PROCESS EVENTS =====
    for i = 1:length(iEvtList)
        switch Method
            case 'start'
                sEvents(iEvtList(i)).times = sEvents(iEvtList(i)).times(1,:);
            case 'middle'
                sEvents(iEvtList(i)).times = mean(sEvents(iEvtList(i)).times,1);
            case 'end'
                sEvents(iEvtList(i)).times = sEvents(iEvtList(i)).times(2,:);
        end
        sEvents(iEvtList(i)).times = round(sEvents(iEvtList(i)).times .* sFreq) / sFreq;
    end

    % ===== SAVE RESULT =====
    % Report results
    if isRaw
        DataMat.F.events = sEvents;
    else
        DataMat.Events = sEvents;
    end
    % Only save changes if something was change
    bst_save(file_fullpath(sInput.FileName), DataMat, 'v6', 1);
end


function midi = readmidi(filename, rawbytes)
% midi = readmidi(filename, rawbytes)
% midi = readmidi(filename)
%
% Read MIDI file and store in a Matlab structure
% (use midiInfo.m to see structure detail)
%
% Inputs:
%  filename - input MIDI file
%  rawbytes - 0 or 1: Include raw bytes in structure
%             This info is redundant, but can be
%             useful for debugging. default=0
%

% Copyright (c) 2009 Ken Schutte
% more info at: http://www.kenschutte.com/midi

if (nargin<2)
  rawbytes=0;
end

fid = fopen(filename);
[A count] = fread(fid,'uint8');
fclose(fid);

if (rawbytes) midi.rawbytes_all = A; end

% realtime events: status: [F8, FF].  no data bytes
%clock, undefined, start, continue, stop, undefined, active
%sensing, systerm reset

% file consists of "header chunk" and "track chunks"
%   4B  'MThd' (header) or 'MTrk' (track)
%   4B  32-bit unsigned int = number of bytes in chunk, not
%       counting these first 8

% HEADER CHUNK --------------------------------------------------------
% 4B 'Mthd'
% 4B length
% 2B file format
%    0=single track, 1=multitrack synchronous, 2=multitrack asynchronous
%    Synchronous formats start all tracks at the same time, while asynchronous formats can start and end any track at any time during the score.
% 2B track cout (must be 1 for format 0)
% 2B num delta-time ticks per quarter note
%

if ~isequal(A(1:4)',[77 84 104 100])  % double('MThd')
    error('File does not begin with header ID (MThd)');
end

header_len = decode_int(A(5:8));
if (header_len == 6)
else
    error('Header length != 6 bytes.');
end

format = decode_int(A(9:10));
if (format==0 || format==1 || format==2)
     midi.format = format;
else
    error('Format does not equal 0,1,or 2');
end

num_tracks = decode_int(A(11:12));
if (format==0 && num_tracks~=1)
    error('File is format 0, but num_tracks != 1');
end

time_unit = decode_int(A(13:14));
if (bitand(time_unit,2^15)==0)
  midi.ticks_per_quarter_note = time_unit;
else
  error('Header: SMPTE time format found - not currently supported');
end

if (rawbytes)
  midi.rawbytes_header = A(1:14);
end

% end header parse ----------------------------------------------------

% BREAK INTO SEPARATE TRACKS ------------------------------------------
% midi.track(1).data = [byte byte byte ...];
% midi.track(2).date = ...
% ...
%
% Track Chunks---------
% 4B 'MTrk'
% 4B length (after first 8B)
%
ctr = 15;
for i=1:num_tracks

  if ~isequal(A(ctr:ctr+3)',[77 84 114 107])  % double('MTrk')
    error(['Track ' num2str(i) ' does not begin with track ID=MTrk']);
  end
  ctr = ctr+4;

  track_len = decode_int(A(ctr:ctr+3));
  ctr = ctr+4;

  % have track.rawbytes hold initial 8B also...
  track_rawbytes{i} = A((ctr-8):(ctr+track_len-1));

  if (rawbytes)
    midi.track(i).rawbytes_header = A(ctr-8:ctr-1);
  end

  ctr = ctr+track_len;
end
% ----------------------------------------------------------------------

% Events:
%  - meta events: start with 'FF'
%  - MIDI events: all others

% MIDI events:
%  optional command byte + 0,1,or 2 bytes of parameters
%  "running mode": command byte omitted.
%
% all midi command bytes have MSB=1
% all data for inside midi command have value <= 127 (ie MSB=0)
% -> so can determine running mode
%
% meta events' data may have any values (meta events have to set
% len)
%

% 'Fn' MIDI commands:
%  no chan. control the entire system
%F8 Timing Clock
%FA start a sequence
%FB continue a sequence
%FC stop a sequence

% Meta events:
%  1B 0xFF
%  1B event type
%  1B length of additional data
%  ?? additional data
%

% "channel mode messages"
% have same code as "control change": 0xBn
%  but uses reserved controller numbers 120-127
%

%Midi events consist of an optional command byte
% followed by zero, one or two bytes of parameters.
% In running mode, the command can be omitted, in
% which case the last MIDI command specified is
% assumed.  The first bit of a command byte is 1,
% while the first bit of a parameter is always 0.
%   In addition, the last 4 bits of a command
%   indicate the channel to which the event should
%   be sent; therefore, there are 6 possible
%   commands (really 7, but we will discuss the x'Fn'
%   commands later) that can be specified.  They are:

% parse tracks -----------------------------------------
for i=1:num_tracks

  track = track_rawbytes{i};

  if (rawbytes); midi.track(i).rawbytes = track; end

  msgCtr = 1;
  ctr=9;  % first 8B were MTrk and length
  while (ctr < length(track_rawbytes{i}))

    clear currMsg;
    currMsg.used_running_mode = 0;
    % note:
    %  .used_running_mode is necessary only to
    %  be able to reconstruct a file _exactly_ from
    %  the 'midi' structure.  this is helpful for
    %  debugging since write(read(filename)) can be
    %  tested for exact replication...
    %

    ctr_start_msg = ctr;

    [deltatime,ctr] = decode_var_length(track, ctr);

    % ?
    %if (rawbytes)
    %  currMsg.rawbytes_deltatime = track(ctr_start_msg:ctr-1);
    %end

    % deltaime must be 1-4 bytes long.
    % could check here...

    % CHECK FOR META EVENTS ------------------------
    % 'FF'
    if track(ctr)==255

      type = track(ctr+1);

      ctr = ctr+2;

      % get variable length 'length' field
      [len,ctr] = decode_var_length(track, ctr);

      % note: some meta events have pre-determined lengths...
      %  we could try verifiying they are correct here.

      thedata = track(ctr:ctr+len-1);
      chan = [];

      ctr = ctr + len;

      midimeta = 0;

    else
      midimeta = 1;
      % MIDI EVENT ---------------------------

      % check for running mode:
      if (track(ctr)<128)

	% make it re-do last command:
	%ctr = ctr - 1;
	%track(ctr) = last_byte;
	currMsg.used_running_mode = 1;

	B = last_byte;
	nB = track(ctr); % ?

      else

	B  = track(ctr);
	nB = track(ctr+1);

	ctr = ctr + 1;

      end

      % nibbles:
      %B  = track(ctr);
      %nB = track(ctr+1);

      Hn = bitshift(B,-4);
      Ln = bitand(B,15);

      chan = [];

      msg_type = midi_msg_type(B,nB);

      % DEBUG:
      if (i==2)
	  if (msgCtr==1)
	    disp(msg_type);
	  end
      end

      switch msg_type

       case 'channel_mode'

	% UNSURE: if all channel mode messages have 2 data byes (?)
	type = bitshift(Hn,4) + (nB-120+1);
	thedata = track(ctr:ctr+1);
	chan = Ln;

	ctr = ctr + 2;

	% ---- channel voice messages:
       case 'channel_voice'

	type = bitshift(Hn,4);
	len = channel_voice_msg_len(type); % var length data:
	thedata = track(ctr:ctr+len-1);
	chan = Ln;

	% DEBUG:
	if (i==2)
	  if (msgCtr==1)
	    disp([999  Hn type])
	  end
	end

	ctr = ctr + len;

       case 'sysex'

	% UNSURE: do sysex events (F0-F7) have
	%  variable length 'length' field?

	[len,ctr] = decode_var_length(track, ctr);

	type = B;
	thedata = track(ctr:ctr+len-1);
	chan = [];

	ctr = ctr + len;

       case 'sys_realtime'

	% UNSURE: I think these are all just one byte
	type = B;
	thedata = [];
	chan = [];

      end

      last_byte = Ln + bitshift(Hn,4);

    end % end midi event 'if'

    currMsg.deltatime = deltatime;
    currMsg.midimeta = midimeta;
    currMsg.type = type;
    currMsg.data = thedata;
    currMsg.chan = chan;

    if (rawbytes)
      currMsg.rawbytes = track(ctr_start_msg:ctr-1);
    end

    midi.track(i).messages(msgCtr) = currMsg;
    msgCtr = msgCtr + 1;

  end % end loop over rawbytes
end % end loop over tracks

function val=decode_int(A)

val = 0;
for i=1:length(A)
  val = val + bitshift(A(length(A)-i+1), 8*(i-1));
end

function len=channel_voice_msg_len(type)

if     (type==128); len=2;
elseif (type==144); len=2;
elseif (type==160); len=2;
elseif (type==176); len=2;
elseif (type==192); len=1;
elseif (type==208); len=1;
elseif (type==224); len=2;
else
  disp(type); error('bad channel voice message type');
end

%
% decode variable length field (often deltatime)
%
%  return value and new position of pointer into 'bytes'
%
function [val,ptr] = decode_var_length(bytes, ptr)

keepgoing=1;
val = 0;
while (keepgoing)
  % check MSB:
  %  if MSB=1, then delta-time continues into next byte...
  if(~bitand(bytes(ptr),128))
    keepgoing=0;
  end
  % keep appending last 7 bits from each byte in the deltatime:
  val = val*128 + rem(bytes(ptr), 128);
  ptr=ptr+1;
end

%
% Read first 2 bytes of msg and
%  determine the type
%  (most require only 1st byte)
%
% str is one of:
%  'channel_mode'
%  'channel_voice'
%  'sysex'
%  'sys_realtime'
%
function str=midi_msg_type(B,nB)

Hn = bitshift(B,-4);
Ln = bitand(B,7);

% ---- channel mode messages:
%if (Hn==11 && nB>=120 && nB<=127)
if (Hn==11 && nB>=122 && nB<=127)
  str = 'channel_mode';

  % ---- channel voice messages:
elseif (Hn>=8 && Hn<=14)
  str = 'channel_voice';

  %  ---- sysex events:
elseif (Hn==15 && Ln>=0 && Ln<=7)
  str = 'sysex';

  % system real-time messages
elseif (Hn==15 && Ln>=8 && Ln<=15)
  % UNSURE: how can you tell between 0xFF system real-time
  %   message and 0xFF meta event?
  %   (now, it will always be processed by meta)
  str = 'sys_realtime';

else
  % don't think it can get here...
  error('bad midi message');
end


function varargout = miqps_matpower(varargin)
%MIQPS_MATPOWER  Deprecated, please use MIQPS_MASTER instead.

%   MATPOWER
%   Copyright (c) 2010-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

[varargout{1:nargout}] = miqps_master(varargin{:});


function c = ch_cap ( c )

%% CH_CAP capitalizes a single character.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    22 November 2003
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, character C, the character to capitalize.
%
%    Output, character C, the capitalized character.
%
  if ( 'a' <= c & c <= 'z' )
    c = c + 'A' - 'a';
  end

  return
end


function [dat] = read_edf(filename, hdr, begsample, endsample, chanindx)

% READ_EDF reads specified samples from an EDF datafile. It neglects all trial or
% data block boundaries as if the data was acquired in non-continuous mode.
%
% Note that since FieldTrip only accommodates a single sampling rate in a given
% dataset, whereas EDF allows specification of a sampling rate for each channel.  If
% there are heterogenous sampling rates then this function will automatically choose
% a subset.  If the last such channel is different from the rest, the assumption will
% be made that it is the annotation channel and the rest will be selected.  If that
% is not the case, then the largest subset of channels with a consistent sampling
% rate will be chosen.  To avoid this automatic selection process, the user may
% specify their own choice of channels using chanindx.  In this case, the automatic
% selection will only occur if the user selected channels still have heterogenous
% sampling rates.  In this case the automatic selection will occur amongst the user
% specified channels.  While reading the header the resulting channel selection
% decision will be stored in hdr.orig.chansel and the contents of this field will
% override chanindx during data reading.
%
% Use as
%   [hdr] = read_edf(filename)
% where
%    filename        name of the datafile, including the .edf extension
% This returns a header structure with the following elements
%   hdr.Fs           sampling frequency
%   hdr.nChans       number of channels
%   hdr.nSamples     number of samples per trial
%   hdr.nSamplesPre  number of pre-trigger samples in each trial
%   hdr.nTrials      number of trials
%   hdr.label        cell-array with labels of each channel
%   hdr.orig         detailled EDF header information
%
% Or use as
%   [dat] = read_edf(filename, hdr, begsample, endsample, chanindx)
% where
%    filename        name of the datafile, including the .edf extension
%    hdr             header structure, see above
%    begsample       index of the first sample to read
%    endsample       index of the last sample to read
%    chanindx        index of channels to read (optional, default is all)
% This returns a Nchans X Nsamples data matrix
%
% Or use as
%   [evt] = read_edf(filename, hdr)
% where
%    filename        name of the datafile, including the .edf extension
%    hdr             header structure, see above
% This returns an Nsamples data vector of just the annotation channel

% Copyright (C) 2006, Robert Oostenveld and others
%
% 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$

switch nargin
  case 1
    chanindx=[];
  case 2
    chanindx=[];
  case 3
    chanindx=begsample;
  case 4
end

needhdr = (nargin==1)||(nargin==3);
needevt = (nargin==2);
needdat = (nargin==5);

if needhdr
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % read the header, this code is from EEGLAB's openbdf
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  FILENAME = filename;

  % defines Seperator for Subdirectories
  SLASH='/';
  BSLASH=char(92);

  cname=computer;
  if cname(1:2)=='PC' SLASH=BSLASH; end

  fid=fopen_or_error(FILENAME,'r','ieee-le');

  EDF.FILE.FID=fid;
  EDF.FILE.OPEN = 1;
  EDF.FileName = FILENAME;

  PPos=min([max(find(FILENAME=='.')) length(FILENAME)+1]);
  SPos=max([0 find((FILENAME=='/') | (FILENAME==BSLASH))]);
  EDF.FILE.Ext = FILENAME(PPos+1:length(FILENAME));
  EDF.FILE.Name = FILENAME(SPos+1:PPos-1);
  if SPos==0
    EDF.FILE.Path = pwd;
  else
    EDF.FILE.Path = FILENAME(1:SPos-1);
  end
  EDF.FileName = [EDF.FILE.Path SLASH EDF.FILE.Name '.' EDF.FILE.Ext];

  H1=char(fread(EDF.FILE.FID,256,'uint8=>char')');
  EDF.VERSION=H1(1:8);                          % 8 Byte  Versionsnummer
  %if 0 fprintf(2,'LOADEDF: WARNING  Version EDF Format %i',ver); end
  EDF.PID = deblank(H1(9:88));                  % 80 Byte local patient identification
  EDF.RID = deblank(H1(89:168));                % 80 Byte local recording identification
  %EDF.H.StartDate = H1(169:176);               % 8 Byte
  %EDF.H.StartTime = H1(177:184);               % 8 Byte
  EDF.T0=[str2num(H1(168+[7 8])) str2num(H1(168+[4 5])) str2num(H1(168+[1 2])) str2num(H1(168+[9 10])) str2num(H1(168+[12 13])) str2num(H1(168+[15 16])) ];

  % Y2K compatibility until year 2090
  if EDF.VERSION(1)=='0'
    if EDF.T0(1) < 91
      EDF.T0(1)=2000+EDF.T0(1);
    else
      EDF.T0(1)=1900+EDF.T0(1);
    end
  else
    % in a future version, this is hopefully not needed
  end

  EDF.HeadLen = str2num(H1(185:192));  % 8 Byte  Length of Header
  % reserved = H1(193:236);            % 44 Byte
  EDF.NRec = str2num(H1(237:244));     % 8 Byte  # of data records
  EDF.Dur  = str2num(H1(245:252));     % 8 Byte  # duration of data record in sec
  EDF.NS   = str2num(H1(253:256));     % 8 Byte  # of signals

  EDF.Label      = char(fread(EDF.FILE.FID,[16,EDF.NS],'uint8=>char')');
  EDF.Transducer = char(fread(EDF.FILE.FID,[80,EDF.NS],'uint8=>char')');
  EDF.PhysDim    = char(fread(EDF.FILE.FID,[ 8,EDF.NS],'uint8=>char')');

  EDF.PhysMin= str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'uint8=>char')'));
  EDF.PhysMax= str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'uint8=>char')'));
  EDF.DigMin = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'uint8=>char')'));
  EDF.DigMax = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'uint8=>char')'));

  % check validity of DigMin and DigMax
  if (length(EDF.DigMin) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Digital Minimum\n');
    EDF.DigMin = -(2^15)*ones(EDF.NS,1);
  end
  if (length(EDF.DigMax) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Digital Maximum\n');
    EDF.DigMax = (2^15-1)*ones(EDF.NS,1);
  end
  if (any(EDF.DigMin >= EDF.DigMax))
    fprintf(2,'Warning OPENEDF: Digital Minimum larger than Maximum\n');
  end
  % check validity of PhysMin and PhysMax
  if (length(EDF.PhysMin) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Physical Minimum, taking Digital Minimum instead\n');
    EDF.PhysMin = EDF.DigMin;
  end
  if (length(EDF.PhysMax) ~= EDF.NS)
    fprintf(2,'Warning OPENEDF: Failing Physical Maximum, taking Digital Maximum instead\n');
    EDF.PhysMax = EDF.DigMax;
  end
  idx_PhysMin_ge_PhysMax = EDF.PhysMin >= EDF.PhysMax;
  if (any(idx_PhysMin_ge_PhysMax))
    tmplabel = cellfun(@(x) [x ' '], cellstr(EDF.Label(idx_PhysMin_ge_PhysMax,:)),'UniformOutput',false)';
    fprintf(2,['Warning OPENEDF: Physical Minimum larger than Maximum.\nPLEASE recheck if the scaling and polarity in the following channels are still correct if used:\n' tmplabel{:} '\n']);
    %EDF.PhysMin = EDF.DigMin;
    %EDF.PhysMax = EDF.DigMax;
  end
  EDF.PreFilt= char(fread(EDF.FILE.FID,[80,EDF.NS],'uint8=>char')');
  EDF.SPR = str2num(char(fread(EDF.FILE.FID,[8,EDF.NS],'uint8=>char')'));  % samples per data record

  fseek(EDF.FILE.FID,32*EDF.NS,0);

  EDF.Cal = (EDF.PhysMax-EDF.PhysMin)./(EDF.DigMax-EDF.DigMin);
  EDF.Cal(isnan(EDF.Cal)) = 0;
  EDF.Off = EDF.PhysMin - EDF.Cal .* EDF.DigMin;

  %tmp = find(EDF.Cal < 0);
  %EDF.Cal(tmp) = ones(size(tmp));
  %EDF.Off(tmp) = zeros(size(tmp));

  EDF.Calib=[EDF.Off';(diag(EDF.Cal))];
  %EDF.Calib=sparse(diag([1; EDF.Cal]));
  %EDF.Calib(1,2:EDF.NS+1)=EDF.Off';

  EDF.SampleRate = EDF.SPR / EDF.Dur;

  EDF.FILE.POS = ftell(EDF.FILE.FID);
  if EDF.NRec == -1                            % unknown record size, determine correct NRec
    fseek(EDF.FILE.FID, 0, 'eof');
    endpos = ftell(EDF.FILE.FID);
    EDF.NRec = floor((endpos - EDF.FILE.POS) / (sum(EDF.SPR) * 2));
    fseek(EDF.FILE.FID, EDF.FILE.POS, 'bof');
    H1(237:244)=sprintf('%-8i',EDF.NRec);      % write number of records
  end

  EDF.Chan_Select=(EDF.SPR==max(EDF.SPR));
  for k=1:EDF.NS
    if EDF.Chan_Select(k)
      EDF.ChanTyp(k)='N';
    else
      EDF.ChanTyp(k)=' ';
    end
    if contains(upper(EDF.Label(k,:)),'ECG')
      EDF.ChanTyp(k)='C';
    elseif contains(upper(EDF.Label(k,:)),'EKG')
      EDF.ChanTyp(k)='C';
    elseif contains(upper(EDF.Label(k,:)),'EEG')
      EDF.ChanTyp(k)='E';
    elseif contains(upper(EDF.Label(k,:)),'EOG')
      EDF.ChanTyp(k)='O';
    elseif contains(upper(EDF.Label(k,:)),'EMG')
      EDF.ChanTyp(k)='M';
    end
  end

  if isempty(chanindx)
    chanindx=1:EDF.NS;
  else
    if any(chanindx>EDF.NS)
      error('The selected channels are not present in the data');
    end
  end

  EDF.AS.spb = sum(EDF.SPR);    % Samples per Block
  bi=[0;cumsum(EDF.SPR)];

  idx=[];idx2=[];
  for k=1:EDF.NS
    idx2=[idx2, (k-1)*max(EDF.SPR)+(1:EDF.SPR(k))];
  end
  maxspr=max(EDF.SPR);
  idx3=zeros(EDF.NS*maxspr,1);
  for k=1:EDF.NS, idx3(maxspr*(k-1)+(1:maxspr))=bi(k)+ceil((1:maxspr)'/maxspr*EDF.SPR(k));end

  %EDF.AS.bi=bi;
  EDF.AS.IDX2=idx2;
  %EDF.AS.IDX3=idx3;

  % close the file
  fclose(EDF.FILE.FID);

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % convert the header to Fieldtrip-style
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  if all(EDF.SampleRate(chanindx)==EDF.SampleRate(chanindx(1)))
    chansel=chanindx;
    hdr.Fs           = EDF.SampleRate(chanindx(1));
    hdr.nChans       = length(chansel);
    hdr.label        = cellstr(EDF.Label(chansel,:));
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.NRec * EDF.SPR(chansel(1));
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = 1;
    hdr.chanunit     = cellstr(EDF.PhysDim(chansel,:));
    hdr.chantype     = repmat({'unknown'}, size(hdr.chanunit));  % start with unknown
    hdr.chantype(strcmp(hdr.chanunit, 'uV')) = {'eeg'};          % it might also be EOG, ECG, EMG, etc
    hdr.chantype(strcmp(hdr.chanunit, 'Boolean')) = {'trigger'};
    hdr.orig         = EDF;
    % this will be used on subsequent reading of data
    if length(chansel) ~= EDF.NS
      hdr.orig.chansel = chansel;
    else
      hdr.orig.chansel = 1:hdr.nChans;
    end
    hdr.orig.annotation = find(strcmp(cellstr(hdr.orig.Label), 'EDF Annotations'));

  elseif all(EDF.SampleRate(1:end-1)==EDF.SampleRate(1))
    % only the last channel has a deviant sampling frequency
    % this is the case for EGI recorded datasets that have been converted
    % to EDF+, in which case the annotation channel is the last
    chansel = find(EDF.SampleRate==EDF.SampleRate(1));
    % continue with the subset of channels that has a consistent sampling frequency
    hdr.Fs           = EDF.SampleRate(chansel(1));
    hdr.nChans       = length(chansel);
    ft_warning('Skipping "%s" as continuous data channel because of inconsistent sampling frequency (%g Hz)', deblank(EDF.Label(end,:)), EDF.SampleRate(end));
    hdr.label        = cellstr(EDF.Label(chansel,:));
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.NRec * EDF.SPR(chansel(1));
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = 1;
    hdr.orig         = EDF;
    % this will be used on subsequent reading of data
    hdr.orig.chansel    = chansel;
    hdr.orig.annotation = find(strcmp(cellstr(hdr.orig.Label), 'EDF Annotations'));

  else
    % select the sampling rate that results in the most channels
    [a, b, c] = unique(EDF.SampleRate);
    chancount = nan(size(a));
    for i=1:length(a)
      chancount(i) = sum(c==i);
    end
    [dum, indx] = max(chancount);
    chansel = find(EDF.SampleRate == a(indx));

    % continue with the subset of channels that has a consistent sampling frequency
    hdr.Fs           = EDF.SampleRate(chansel(1));
    hdr.nChans       = length(chansel);
    hdr.label        = cellstr(EDF.Label);
    hdr.label        = hdr.label(chansel);
    % it is continuous data, therefore append all records in one trial
    hdr.nSamples     = EDF.NRec * EDF.SPR(chansel(1));
    hdr.nSamplesPre  = 0;
    hdr.nTrials      = 1;
    hdr.orig         = EDF;
    % this will be used on subsequent reading of data
    hdr.orig.chansel    = chansel;
    hdr.orig.annotation = find(strcmp(cellstr(hdr.orig.Label), 'EDF Annotations'));

    ft_warning('channels with different sampling rate not supported, selecting subset of %d channels at %f Hz', length(hdr.label), hdr.Fs);
  end

  % return the header
  dat = hdr;

elseif needdat || needevt
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % read the data
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % retrieve the original header
  EDF = hdr.orig;

  % There can be an optional chansel field containing a list of predefined channels.
  % These channels are in that case also the only ones represented in the FieldTrip
  % header, which means that the other channels are simply not visible to the naive
  % user. This field can be present because the user specified an explicit channel
  % selection in FT_READ_HEADER or because the read_edf function had to automatically
  % choose a subset to cope with heterogenous sampling rates or even both.  In any
  % case, at this point in the file reading process the contents of the chansel field
  % has the proper specification for channel selection, taking into account both the
  % user channel selection as well as any correction that might have been made due to
  % heterogenous sampling rates.

  if     ~isempty(chanindx) && ~isfield(EDF, 'chansel')
    % a subset of channels should been selected from the full list of channels in the file
    chanindx = chanindx; % keep as it is
    useChanindx = true;
  elseif ~isempty(chanindx) &&  isfield(EDF, 'chansel')
    % a subset of channels should been selected from the predefined list
    chanindx = EDF.chansel(chanindx);
    useChanindx = true;
  elseif  isempty(chanindx) &&  isfield(EDF, 'chansel')
    % all channels from the predefined list should be selected
    chanindx = EDF.chansel(chanindx);
    useChanindx = true;
  elseif  isempty(chanindx) && ~isfield(EDF, 'chansel')
    %  simply select all channels that are present in the file
    chanindx = 1:EDF.NS;
    useChanindx = false;
  end

  if needevt
    % read the annotation channel, not the data channels
    chanindx = EDF.annotation;
    begsample = 1;
    endsample = EDF.SPR(EDF.annotation)*EDF.NRec;
  end

  if useChanindx
    epochlength = EDF.SPR(chanindx(1));   % in samples for the selected channel
    blocksize   = sum(EDF.SPR);           % in samples for all channels
    chanoffset  = EDF.SPR;
    chanoffset  = round(cumsum([0; chanoffset(1:end-1)]));
    nchans      = length(chanindx);       % get the selection from the subset of channels
  else
    epochlength = EDF.SPR(1);             % in samples for a single channel
    blocksize   = sum(EDF.SPR);           % in samples for all channels
    nchans      = EDF.NS;                 % use all channels
  end

  % determine the trial containing the begin and end sample
  begepoch    = floor((begsample-1)/epochlength) + 1;
  endepoch    = floor((endsample-1)/epochlength) + 1;
  nepochs     = endepoch - begepoch + 1;

  % allocate memory to hold the data
  dat = zeros(length(chanindx),nepochs*epochlength);

  % read and concatenate all required data epochs
  for i=begepoch:endepoch
    if useChanindx
      % only a subset of channels with consistent sampling frequency is read
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      % read the complete data block
      buf = readLowLevel(filename, offset, blocksize); % see below in subfunction
      for j=1:length(chanindx)
        % cut out the part that corresponds with a single channel
        dat(j,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf((1:epochlength) + chanoffset(chanindx(j)));
      end

    elseif length(chanindx)==1
      % this is more efficient if only one channel has to be read, e.g. the status channel
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      offset = offset + (chanindx-1)*epochlength*2;
      % read the data for a single channel
      buf = readLowLevel(filename, offset, epochlength); % see below in subfunction
      dat(:,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf;

    else
      % read the data from all channels, subsequently select the desired channels
      offset = EDF.HeadLen + (i-1)*blocksize*2; % in bytes
      % read the complete data block
      buf = readLowLevel(filename, offset, blocksize); % see below in subfunction
      buf = reshape(buf, epochlength, nchans);
      dat(:,((i-begepoch)*epochlength+1):((i-begepoch+1)*epochlength)) = buf(:,chanindx)';
    end
  end

  % select the desired samples
  begsample = begsample - (begepoch-1)*epochlength;  % correct for the number of bytes that were skipped
  endsample = endsample - (begepoch-1)*epochlength;  % correct for the number of bytes that were skipped
  dat = dat(:, begsample:endsample);

  % Calibrate the data
  if useChanindx
    calib = diag(EDF.Cal(chanindx));
  end
  if length(chanindx)>1
    % using a sparse matrix speeds up the multiplication
    dat = sparse(calib) * dat;
  else
    % in case of one channel the sparse multiplication would result in a sparse array
    dat = calib * dat;
  end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for reading the 16 bit values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function buf = readLowLevel(filename, offset, numwords)
is_below_2GB = offset < 2*1024^3;
read_16bit_success = true;
if is_below_2GB
  % use the external mex file, only works for <2GB
  try
    buf = read_16bit(filename, offset, numwords);
  catch e
    read_16bit_success = false;
  end
end
if ~is_below_2GB || ~read_16bit_success
  % use plain matlab, thanks to Philip van der Broek
  fp = fopen(filename,'r','ieee-le');
  status = fseek(fp, offset, 'bof');
  if status
    ft_error(['failed seeking ' filename]);
  end
  [buf,num] = fread(fp,numwords,'bit16=>double');
  fclose(fp);
  if (num<numwords)
    ft_error(['failed reading ' filename]);
    return
  end
end


function lstOut = pyrLst1OP(lstIn, fun)
%
%
%        lstOut = pyrLst1OP(lstIn, fun)
%
%
%        Input:
%           -lstIn: a list of image pyramids
%           -fun: a function to apply to lstIn
%
%        Output:
%           -lstOut: the result of the function
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     You should have received a copy of the GNU General Public License
%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
%

n = length(lstIn);

lstOut=[];

for i=1:n
    p = fun(lstIn(i));
    lstOut = [lstOut, p];
end

end


function [file] = read_ctf_ascii(filename)

% READ_CTF_ASCII reads general data from an CTF configuration file
%
% The file should be formatted like
%    Group
%    {
%      item1 : value1a value1b value1c
%      item2 : value2a value2b value2c
%      item3 : value3a value3b value3c
%      item4 : value4a value4b value4c
%    }
%
% This fileformat structure is used in
%   params.avg
%   default.hdm
%   multiSphere.hdm
%   processing.cfg
% and maybe for other files as well.

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

fid = fopen_or_error(filename, 'r');

line = '';
while ischar(line)
  line = cleanline(fgetl(fid));
  if isempty(line) || (length(line)==1 && all(line==-1))
    continue
  end

  % the line is not empty, which means that we have encountered a chunck of information
  subline = cleanline(fgetl(fid));  % read the {
  subline = cleanline(fgetl(fid));  % read the first item
  while ~contains(subline, '}')
    if ~isempty(subline)
      [item, value] = strtok(subline, ':');
      value(1) = ' ';           % remove the :
      value  = strtrim(value);
      item   = strtrim(item);

      % the item name should be a real string, otherwise I cannot put it into the structure
      if strcmp(sprintf('%d', str2num(deblank(item))), deblank(item))
        % add something to the start of the string to distinguish it from a number
        item = ['item_' item];
      end

      % the value can be either a number or a string, and is put into the structure accordingly
      if isempty(str2num(value))
        % the value appears to be a string
        eval(sprintf('file.%s.%s = [ ''%s'' ];', line, item, value));
      else
        % the value appears to be a number or a list of numbers
        eval(sprintf('file.%s.%s = [ %s ];', line, item, value));
      end

    end
    subline = cleanline(fgetl(fid));    % read the first item
  end
end

fclose(fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function line = cleanline(line)
  if isempty(line) || (length(line)==1 && all(line==-1))
    return
  end
  comment = strfind(line, '//');
  if ~isempty(comment)
    line(min(comment):end) = ' ';
  end
  line = strtrim(line);


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

%%
clear; close all; clc;

%% Syntax
% |[ux,uy]=trigradient(TRI,V,INT);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function modelKEGG = transformModel2KEGG(model,Dictionary)
% Replaces `model.mets` with `model.metKEGGID`. If no KEGGID
% defined, the empty cell will be replaced with metabolite abreviation.
%
% USAGE:
%
%    modelKEGG = transformModel2KEGG(model, Dictionary)
%
% INPUTS:
%    model:         COBRA model structure
%    Dictionary:    consists of:
%
%                     * CompAbr = Dictionary(:, 1): List of compounds abreviation (non-compartelized)
%                     * KEGGID = Dictionary(:, 2): List of KEGGIDs for compounds in `CompAbr`
%
% OUTPUT:
%    modelKEGG:     KEGG model structure
%
% .. Author: - 11-09-07 IT

if (nargin == 2)
    modelKEGG = mapKEGGID2Model(model, Dictionary);
elseif(nargin == 1)
    error('missing data');
    %modelKEGG = model;
end

modelKEGG.metsAbr = modelKEGG.mets;
modelKEGG.mets = modelKEGG.metKEGGID;

for i = 1: length(modelKEGG.metsAbr)
    if (isempty(modelKEGG.mets{i}))
        modelKEGG.mets(i)=modelKEGG.metsAbr(i);
    end
end


function exportZtdLittleR(ztd_map, lat_map, lon_map, h_map, time, out_dir, file_name, qc_ztd)
% Export a ZTD map into little_R format (input of 3Dvar)
%
% EXAMPLE
%   exportZtdLittleR(ztd_map, lat_map, lon_map, h_map, time, 'reserved/tmp', 'sar_map', 0.015)
%
% SYNTAX
%   exportZtdLittleR(ztd_map, lat_map, lon_map, h_map, time, out_dir, file_name, qc_ztd)
%
%
% info on LITTLE R export
% reference: http://www2.mmm.ucar.edu/wrf/users/wrfda/OnlineTutorial/Help/littler.html#FM

% qc_ztd = 0.015;
% ztd_map = ones(1,1);
n_row = size(ztd_map, 1);
n_col = size(ztd_map, 2);
% lat_map = randn(n_row,n_col);
% lon_map = 10 + randn(n_row,n_col);
% h_map = 100 + randn(n_row,n_col);
% time = GPS_Time(now);
% out_dir = 'reserved/tmp';
% file_name = 'sar_map';
qc_ztd = uint32(qc_ztd * 1e4); % expressed in tenths of millimeters

log = Core.getLogger();
save_on_disk = true;
no_data = -888888.00000;
qc_no_data = -888888;
end_data = -777777.00000;
end_qc = 0;
empty_data_line = sprintf('%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d\n', ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data, ...
    no_data, qc_no_data);
end_data_line = sprintf('%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d\n', ...
    end_data, end_qc, ...
    end_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc, ...
    no_data, end_qc);
tail_integers = sprintf('%7d%7d%7d\n',1,0,0);

% LITTLE R export
txt = '';
if save_on_disk
    if ~exist(out_dir, 'dir')
        log.newLine();
        log.addWarning(sprintf('The out folder does not exists\n Creating %s', out_dir));
        mkdir(out_dir);
    end
end

if save_on_disk
    file_name = sprintf('%s%c%s', out_dir, filesep, file_name);
    fid = fopen(file_name,'w');
end
i = 0;
fprintf('\n');
wb = Go_Wait_Bar.getInstance(n_col * n_row);
wb.createNewBar(sprintf('Exporting ZTD map into LITTLE_R format (%s)', time.toString('yyyy - mm - dd')));
for c = 1 : n_col
    % get the closest epoch to the sar passage
    for r = 1 : n_row
        i = i+1;
        % reference: http://www2.mmm.ucar.edu/wrf/users/wrfda/OnlineTutorial/Help/littler.html#FM
        lat = lat_map(r,c);
        lon = lon_map(r,c);
        id_dtr = n_row * (c-1) + (r-1);
        name = sprintf('CALSAR_%05d', id_dtr);
        platform = 'FM-114 GPSZD';
        source = 'goGPS software';
        h_ortho = h_map(r, c);
        obs_time = time.toString('yyyymmddHHMMSS');
        ztd = ztd_map(r,c) * 100; % expressed in cm

        if ~isnan(ztd)
            %Latitude	Longitude	ID	Name	Platform (FM�Code)note	Source	Elevation	Valid fields	Num. errors	Num. warnings	Sequence number	Num. duplicates	Is sounding?	Is bogus?	Discard?	Unix time	Julian day	Date	SLP, QC	Ref Pressure, QC	Ground Temp, QC	SST,   QC	SFC Pressure, QC	Precip, QC	Daily Max T, QC	Daily Min T, QC	Night Min T, QC	3hr Pres Change, QC	24hr Pres Change, QC	Cloud cover, QC	Ceiling, QC	Precipitable water, QC (see note)
            %F20.5	    F20.5	    A40	A40	    A40	                    A40	    F20.5	    I10            	I10	        I10	            I10	            I10	            L	            L	        L	        I10	        I10	        A20	    F13.5,  I7	F13.5,    I7	F13.5,       I7	F13.5, I7	F13.5,        I7	F13.5,  I7	F13.5,       I7	F13.5,       I7	F13.5,       I7	F13.5,           I7	F13.5,            I7	F13.5,       I7	F13.5,   I7	F13.5,              I7

            txt = sprintf('%s%20.5f%20.5f%-40s%-40s%-40s%-40s%20.5f%10d%10d%10d%10d%10d%10s%10s%10s%10d%10d%20s%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d%13.5f%7d\n%s%s%s', ...
                txt, ...
                lat, ...
                lon, ...
                num2str(id_dtr), ...
                name, ...
                platform, ...
                source, ...
                h_ortho, ...
                1, ...
                0, ...
                0, ...
                i, ...
                0, ...
                'F', ...
                'T', ...
                'F', ...
                uint32(time.getUnixTime), ...
                uint32(time.getJD), ...
                obs_time, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                no_data, qc_no_data, ...
                ztd, qc_ztd, ...
                empty_data_line, ...
                end_data_line, ...
                tail_integers);
        end
        if save_on_disk && (length(txt) > (4 * 1024))
            wb.go(r + (c-1) * n_row);
            fprintf(fid,'%s', txt);
            txt = [];
        end
    end
    wb.go(r + (c-1) * n_row);
    if save_on_disk
        fprintf(fid,'%s', txt);
        txt = [];
    end
end
wb.close();
if save_on_disk
    fclose(fid);
end


% Handles experimental focus swapping

function focus_swap(hObject, eventdata, handles)
    x_size     = str2double(get(handles.x_size_edit,'String'));
    y_size     = str2double(get(handles.y_size_edit,'String'));

    if x_size < y_size
        positionf = [.18,.065,.87,.87];
    else
        positionf = [.32,.3,.6,.65];
    end

    focused = getappdata(0,'focused');

    if focused == hObject %Return when clicking an object already in focus
        return;
    end

    current = getappdata(hObject,'pos');

    set(focused, 'Position', current);      %Swap the axes positions
    axis image
    setappdata(focused,'pos',current);

    set(hObject, 'Position', positionf);
    axis image
    setappdata(0,'focused', hObject);

    if get(handles.show_colorbar,'Value')
        pos = get(hObject, 'Position');
        colorbar('Position',[.947, pos(2), .015, pos(4)],'DrawMode','fast')
    end

    axes(focused)
    colorbar('delete');
    drawnow


%  INTERNAL FUNCTION: dirichlet distribution
%
%  Note:
%     One would rarely need to call distribution functions directly.
%     Refer to <densities.html> file for list of supported distributions.
%
%


function test_bug1667

% WALLTIME 00:20:00
% MEM 4gb
% DEPENDENCY ft_read_data ft_fetch_data

% Improvements:
% 1-make buffering from multiple sources possible
% 2-if part of the trial is already buffered, only fetch what is needed

datainfo = ref_datasets;

datanr = 13;
datafile1 = fullfile(datainfo(datanr).origdir,'original',datainfo(datanr).type,datainfo(datanr).datatype,'/',datainfo(datanr).filename);
datanr = datanr+1;
datafile2 = fullfile(datainfo(datanr).origdir,'original',datainfo(datanr).type,datainfo(datanr).datatype,'/',datainfo(datanr).filename);
hdr1 = ft_read_header(datafile1);
hdr2 = ft_read_header(datafile2);
% fetch something
tic;
dat1 = ft_read_data(datafile1, 'cache', true, 'begsample', 1, 'endsample', 1000, 'checkboundary', false, 'header', hdr1);
t11 = toc;
tic;
dat2 = ft_read_data(datafile2, 'cache', true, 'begsample', 1, 'endsample', 1000, 'checkboundary', false, 'header', hdr1);
t12 = toc;
% fetch something that is partly overlapping
tic;
dat1 = ft_read_data(datafile1, 'cache', true, 'begsample', 500, 'endsample', 1500, 'checkboundary', false, 'header', hdr1);
t21 = toc;
tic;
dat2 = ft_read_data(datafile2, 'cache', true, 'begsample', 500, 'endsample', 1500, 'checkboundary', false, 'header', hdr1);
t22 = toc;
% fetch something completely else
tic;
dat1 = ft_read_data(datafile1, 'cache', true, 'begsample', 2000, 'endsample', 3000, 'checkboundary', false, 'header', hdr1);
t31 = toc;
tic;
dat2 = ft_read_data(datafile2, 'cache', true, 'begsample', 2000, 'endsample', 3000, 'checkboundary', false, 'header', hdr1);
t32 = toc;

% compare timing, the latter should be way slower, if not ERROR!!!11


function test_suite = testIsTearDownString
%testIsTearDownString Unit tests for isTearDownString

%   Steven L. Eddins
%   Copyright 2008 The MathWorks, Inc.

initTestSuite;

function testOneStringIs
assertTrue(xunit.utils.isTearDownString('teardownfoobar'));
assertTrue(xunit.utils.isTearDownString('TearDown_foobar'));

function testOneStringIsNot
assertFalse(xunit.utils.isTearDownString('tEardown'));

function testCellArray
strs = {'teardown', 'tearup'};
assertEqual(xunit.utils.isTearDownString(strs), [true false]);
assertEqual(xunit.utils.isTearDownString(strs'), [true; false]);


%%% movobj = convert_3dresults2mov(3dmatrix,3dmatrix,3dmatrix,3dmatrix,struct)
% I - Input sequence
% L - Low-rank sequence
% S - Sparse sequence
% O - Outliers sequence
%
function [movobj] = convert_3dresults2mov(I,L,S,O,nframes)
  %warning('off','all');
  displog('Converting results to movie...');

  if(~isempty(I)) movobj.I(1:nframes) = struct('cdata', [], 'colormap', []); else movobj.I = []; end
  if(~isempty(L)) movobj.L(1:nframes) = struct('cdata', [], 'colormap', []); else movobj.L = []; end
  if(~isempty(S)) movobj.S(1:nframes) = struct('cdata', [], 'colormap', []); else movobj.S = []; end
  if(~isempty(O)) movobj.O(1:nframes) = struct('cdata', [], 'colormap', []); else movobj.O = []; end

  for i = 1 : nframes
    %
    % Convert input
    %
    if(~isempty(I))
      Input = I(:,:,i);
      Input = mat2gray(Input);
      Input = im2uint8(Input);
      rgbImage = repmat(Input,[1 1 3]);
      movobj.I(i).cdata = rgbImage;
    end
    %
    % Convert low-rank results
    %
    if(~isempty(L))
      LowRank = L(:,:,i);
      LowRank = mat2gray(LowRank);
      LowRank = im2uint8(LowRank);
      rgbImage = repmat(LowRank,[1 1 3]);
      movobj.L(i).cdata = rgbImage;
    end
    %
    % Store sparse results
    %
    if(~isempty(S))
      Sparse = S(:,:,i);
      Sparse = mat2gray(Sparse);
      Sparse = im2uint8(Sparse);
      rgbImage = repmat(Sparse,[1 1 3]);
      movobj.S(i).cdata = rgbImage;
    end
    %
    % Store foreground results
    %
    if(~isempty(O))
      Outlier = O(:,:,i);
      Outlier = mat2gray(Outlier);
      Outlier = im2uint8(Outlier);
      Outlier = medfilt2(Outlier, [5 5]);
      rgbImage = repmat(Outlier,[1 1 3]);
      movobj.O(i).cdata = rgbImage;
    end
  end

  %warning('on','all');
end


classdef FeaturesMatcher < handle
    %FEATURESMATCHER  Feature matchers class
    %
    % See also: cv.Stitcher, cv.FeaturesFinder, cv.DescriptorMatcher
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    methods
        function this = FeaturesMatcher(matcherType, varargin)
            %FEATURESMATCHER  Constructor
            %
            %     obj = cv.FeaturesMatcher(matcherType)
            %     obj = cv.FeaturesMatcher(matcherType, 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __matcherType__ One of:
            %   * __BestOf2NearestMatcher__ A "best of 2 nearest" matcher.
            %     Features matcher which finds two best matches for each
            %     feature and leaves the best one only if the ratio between
            %     descriptor distances is greater than the threshold
            %     `MatchConf`.
            %   * __BestOf2NearestRangeMatcher__
            %   * __AffineBestOf2NearestMatcher__ A "best of 2 nearest"
            %     matcher that expects affine trasformation between images.
            %     Features matcher similar to `BestOf2NearestMatcher` which
            %     finds two best matches for each feature and leaves the best
            %     one only if the ratio between descriptor distances is
            %     greater than the threshold `MatchConf`. Unlike
            %     `BestOf2NearestMatcher` this matcher uses affine
            %     transformation (affine trasformation estimate will be placed
            %     in `matches_info`).
            %
            % ## Options
            % The following are options accepted by all matchers:
            %
            % * __TryUseGPU__ Should try to use GPU or not. default false
            % * __MatchConf__ Match distances ration threshold. Confidence for
            %   feature matching step. default 0.3
            % * __NumMatchesThresh1__ Minimum number of matches required for
            %   the 2D projective transform estimation used in the inliers
            %   classification step. default 6
            % * __NumMatchesThresh2__ Minimum number of matches required for
            %   the 2D projective transform re-estimation on inliers.
            %   default 6
            %
            % The following are options for the various algorithms:
            %
            % ### `BestOf2NearestRangeMatcher`
            % * __RangeWidth__ Range width. default 5
            %
            % ### `AffineBestOf2NearestMatcher`
            % * __FullAffine__ whether to use full affine transformation with
            %   6 degress of freedom (cv.estimateAffine2D) or reduced
            %   transformation with 4 degrees of freedom
            %   (cv.estimateAffinePartial2D) using only rotation, translation
            %   and uniform scaling. default false
            %
            % See also: cv.FeaturesMatcher.match
            %
            this.id = FeaturesMatcher_(0, 'new', matcherType, varargin{:});
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.FeaturesMatcher
            %
            if isempty(this.id), return; end
            FeaturesMatcher_(this.id, 'delete');
        end

        function typename = typeid(this)
            %TYPEID  Name of the C++ type (RTTI)
            %
            %     typename = obj.typeid()
            %
            % ## Output
            % * __typename__ Name of C++ type
            %
            typename = FeaturesMatcher_(this.id, 'typeid');
        end
    end

    %% FeaturesMatcher
    methods
        function collectGarbage(this)
            %COLLECTGARBAGE  Frees unused memory allocated before if there is any
            %
            %     obj.collectGarbage()
            %
            % See also: cv.FeaturesMatcher.FeaturesMatcher
            %
            FeaturesMatcher_(this.id, 'collectGarbage');
        end

        function tf = isThreadSafe(this)
            %ISTHREADSAFE  Check if matcher is thread safe
            %
            %     tf = obj.isThreadSafe()
            %
            % ## Output
            % * __tf__ True, if it's possible to use the same matcher instance
            %   in parallel, false otherwise.
            %
            % See also: cv.FeaturesMatcher.FeaturesMatcher
            %
            tf = FeaturesMatcher_(this.id, 'isThreadSafe');
        end

        function matches_info = match(this, features1, features2)
            %MATCH  Performs images matching
            %
            %     matches_info = obj.match(features1, features2)
            %
            % ## Input
            % * __features1__ First image features. See cv.FeaturesFinder.
            % * __features2__ Second image features. See cv.FeaturesFinder.
            %
            % ## Output
            % * **matches_info** Found matches. Structure containing
            %   information about matches between two images. It's assumed
            %   that there is a transformation between those images.
            %   Transformation may be homography or affine transformation
            %   based on selected matcher. Struct with the following fields:
            %   * **src_img_idx** Images indices (optional).
            %   * **dst_img_idx** Images indices (optional).
            %   * __matches__ Matches. A 1-by-N structure array with the
            %     following fields:
            %     `{'queryIdx', 'trainIdx', 'imgIdx', 'distance'}`
            %   * **inliers_mask** Geometrically consistent matches mask.
            %   * **num_inliers** Number of geometrically consistent matches.
            %   * __H__ Estimated transformation.
            %   * __confidence__ Confidence two images are from the same
            %     panorama.
            %
            % See also: cv.FeaturesMatcher.match_pairwise,
            %  cv.FeaturesFinder.find
            %
            matches_info = FeaturesMatcher_(this.id, 'match', features1, features2);
        end

        function pairwise_matches = match_pairwise(this, features, varargin)
            %MATCH_PAIRWISE  Performs images matching
            %
            %     pairwise_matches = obj.match_pairwise(features)
            %     pairwise_matches = obj.match_pairwise(features, 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __features__ Features of the source images.
            %   See cv.FeaturesFinder.
            %
            % ## Output
            % * **pairwise_matches** Found pairwise matches.
            %
            % ## Options
            % * __Mask__ Mask indicating which image pairs must be matched.
            %   default empty
            %
            % The function is parallelized with the TBB library.
            %
            % See also: cv.FeaturesMatcher.match, cv.FeaturesFinder.find
            %
            pairwise_matches = FeaturesMatcher_(this.id, 'match_pairwise', features, varargin{:});
        end
    end

    %% Auxiliary methods
    methods (Static)
        function str = matchesGraphAsString(pairwise_matches, conf_threshold)
            %MATCHESGRAPHASSTRING  Covert matches to graph
            %
            %     str = cv.FeaturesMatcher.matchesGraphAsString(pairwise_matches, conf_threshold)
            %
            % ## Input
            % * **pairwise_matches** Pairwise matches.
            % * **conf_threshold** Threshold for two images are from the same
            %   panorama confidence.
            %
            % ## Output
            % * __str__ matches graph represented in DOT language. Labels
            %   description: `Nm` is number of matches, `Ni` is number of
            %   inliers, `C` is confidence.
            %
            % Returns matches graph representation in DOT language.
            %
            str = FeaturesMatcher_(0, 'matchesGraphAsString', pairwise_matches, conf_threshold);
        end

        function indices = leaveBiggestComponent(features, pairwise_matches, conf_threshold)
            %LEAVEBIGGESTCOMPONENT  Leave biggest component
            %
            %     indices = cv.FeaturesMatcher.leaveBiggestComponent(features, pairwise_matches, conf_threshold)
            %
            % ## Input
            % * __features__ Features of the source images.
            % * **pairwise_matches** Pairwise matches.
            % * **conf_threshold** Threshold for two images are from the same
            %   panorama confidence.
            %
            % ## Output
            % * __indices__ array of image indices (0-based).
            %
            indices = FeaturesMatcher_(0, 'leaveBiggestComponent', features, pairwise_matches, conf_threshold);
        end
    end

end


function events = in_events_ricoh(sFile, EventFile)
% IN_EVENTS_RICOH: Read the events descriptions for a RICOH MEG file.
%
% USAGE:  events = in_events_ricoh(sFile, EventFile)
%
% This function is based on the Ricoh MEG reader toolbox version 1.0.
% For copyright and license information and software documentation,
% please refer to the contents of the folder brainstorm3/external/ricoh

% @=============================================================================
% 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, 2018

% Read file info using Ricoh functions
header.events     = getRHdrEvent(EventFile);       % Get information about trigger events.
header.annotation = getRHdrAnnotation(EventFile);  % Get information about annotations.

% Initialize returned structure
events = repmat(db_template('event'), 0);
% Triggers
if ~isempty(header.events)
    % All all the events types
    uniqueNames = unique({header.events.name});
    % Create events structures: one per category of event
    for i = 1:length(uniqueNames)
        % Add a new event category
        iEvt = length(events) + 1;
        % Find all the occurrences of event #iEvt
        iMrk = find(strcmpi({header.events.name}, uniqueNames{i}));
        % Get all samples
        allSamples = [header.events(iMrk).sample_no];
        % Get the epoch numbers (only ones for continuous and averaged files)
        if (sFile.header.acq.acq_type == 1) || (sFile.header.acq.acq_type == 2)
            iEpochs = ones(1, length(iMrk));
        else
            iEpochs = floor(allSamples / sFile.header.acq.frame_length) + 1;
            allSamples = allSamples - (iEpochs-1) .* sFile.header.acq.frame_length;
        end
        % Add event structure
        events(iEvt).label      = uniqueNames{i};
        events(iEvt).epochs     = iEpochs;
        events(iEvt).times      = allSamples ./ sFile.prop.sfreq;
        events(iEvt).reactTimes = [];
        events(iEvt).select     = 1;
        events(iEvt).channels   = [];
        events(iEvt).notes      = [];
    end
end
% Annotations
if ~isempty(header.annotation)
    % Get annotation label
    annotLabel = cell(1,length(header.annotation));
    for i = 1:length(header.annotation)
        if ~isempty(strtrim(header.annotation(i).comment))
            annotLabel{i} = strtrim(header.annotation(i).comment);
            annotLabel{i}(annotLabel{i} == 0) = [];
        elseif ~isempty(header.annotation(i).label) && isnumeric(header.annotation(i).label)
            annotLabel{i} = ['annot_', num2str(header.annotation(i).label)];
        else
            annotLabel{i} = 'Unknown';
        end
    end

    % All all the events types
    uniqueNames = unique(annotLabel);
    % Create events structures: one per category of event
    for i = 1:length(uniqueNames)
        % Add a new event category
        iEvt = length(events) + 1;
        % Find all the occurrences of event #iEvt
        iMrk = find(strcmpi(annotLabel, uniqueNames{i}));
        % Get all samples
        allSamples = [header.annotation(iMrk).sample_no];
        % Get the epoch numbers (only ones for continuous and averaged files)
        if (sFile.header.acq.acq_type == 1) || (sFile.header.acq.acq_type == 2)
            iEpochs = ones(1, length(iMrk));
        else
            iEpochs = floor(allSamples / sFile.header.acq.frame_length) + 1;
            allSamples = allSamples - (iEpochs-1) .* sFile.header.acq.frame_length;
        end
        % Add event structure
        events(iEvt).label      = uniqueNames{i};
        events(iEvt).epochs     = iEpochs;
        events(iEvt).times      = allSamples ./ sFile.prop.sfreq;
        events(iEvt).reactTimes = [];
        events(iEvt).select     = 1;
        events(iEvt).channels   = [];
        events(iEvt).notes      = [];
    end
end


function results = utl_run_batchjob(opts,d,appname,setnames)
% Internal: the actual processing function of bci_batchtrain.
% Results = ult_run_batchjob(Options,DatasetIndex,ApproachName,DatasetNames)
%
% This function executes one of the individual jobs generated by bci_batchtrain (application of one
% approach to one dataset).
%
% In:
%   Options : bci_batchtrain options struct
%
%   DatasetIndex : index into Options.datasets that determines which dataset to process
%
%   ApproachName : name of a field in Options.approaches that determines which approach to use
%
%   DatasetNames : names of the respective datasets for diagnostics
%
% Out:
%   Results : result data structure
%
% See also:
%   bci_batchtrain
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2011-08-19
dp;

% scaffolding to support custom error handlers (ultimately calls do_run to do the actual work)
results = [];
if strcmp(char(opts.handler),'rethrow')
    results = do_run(opts,d,appname,setnames);
else
    try
        results = do_run(opts,d,appname,setnames);
    catch e
        fprintf('Error processing data set "%s" with approach "%s".',setnames{d},appname);
        if ischar(opts.handler)
            try
                opts.handler = str2func(opts.handler);
            catch e
                error('The given error handler is malformed: %s',hlp_tostring(opts.handler,100));
            end
        end
        opts.handler(e);
    end
end

function results = do_run(opts,d,appname,setnames)
    % run a particular approach on a particular data set
    results = [];

    % determine where results would be saved
    storename = env_translatepath(strrep(strrep(opts.storepatt,'%set',setnames{d}),'%approach',appname));
    if opts.reuse && ~isempty(storename) && exist(storename,'file')

        % attempt to load existing results from disk
        fprintf('Reusing existing result for approach "%s" on set "%s".\n',appname,setnames{d});
        t0=tic; fprintf('Trying to load file %s...',storename);
        if opts.cache_loaded
            persistent result_cache; %#ok<TLEV>
            storetag = ['x' hlp_cryptohash(storename)];
            if isfield(result_cache,storetag)
                res = result_cache.(storetag);
            else
                io_load(storename);
                result_cache.(storetag) = res; %#ok<NODEF>
            end
        else
            io_load(storename);
        end
        fprintf('%.1f seconds (%.1f MB/s).\n',toc(t0),getfield(whos('res'),'bytes')/2^20/toc(t0));

    elseif ~opts.loadonly

        % train a model on the Dataset using bci_train
        [res.loss,res.model,res.stats] = bci_train(opts.trainargs{:}, 'data',opts.datasets{d}, 'approach',opts.approaches.(appname), 'markers',opts.markers);

        % optionally run bci_predict on each of the PredictSets
        if ~isempty(opts.predictsets) && ~isempty(opts.predictsets{d})
            for k=1:length(opts.predictsets{d})
                try
                    [res.pred_predictions{k},res.pred_loss(k),res.pred_stats{k},res.pred_targets{k}] = bci_predict(opts.predictargs{:},'data',opts.predictsets{d}{k},'model',res.model, 'markers',opts.markers);
                catch e
                    [res.pred_predictions{k},res.pred_loss(k),res.pred_stats{k},res.pred_targets{k}] = deal([],NaN,struct(),[]);
                    fprintf('Error computing predictions for set "%s", prediction set #%i with approach "%s".\n',setnames{d},k,appname);
                    opts.handler(e);
                end
            end
            res.pred_loss = res.pred_loss';
        end

        % save results
        if ~isempty(storename)
            t0=tic; fprintf('Saving results as %s...',storename);
            io_save(storename,opts.saveargs{:},'res');
            fprintf('%.1f seconds (%.1f MB/s).\n',toc(t0),getfield(whos('res'),'bytes')/2^20/toc(t0));
        end
    end

    % assign results to result data structure
    if ~isempty(opts.resultpatt)
        try
            statement = [strrep(strrep(opts.resultpatt,'%num',num2str(d)),'%approach',appname) ' res;'];
            eval(statement);
        catch e
            fprintf('Failed to evaluate ResultPattern (statement was "%s") with error: %s.\n',statement,e.message);
        end
    end

    % print diagnostics
    try
        if isfield(res,'pred_loss')
            fprintf('%s@%s: train loss: %.4f, prediction loss: %.4f\n',appname,setnames{d},res.loss,res.pred_loss);
        else
            fprintf('%s@%s: train loss: %.4f\n',appname,setnames{d},res.loss);
        end
    catch e
        fprintf('Failed to display loss estimates with error: %s\n',e.message);
    end


function outputImage = shuffle(this, iDimArray, shuffledSamplingPointsIndexArray)
% Shuffles sampling points of specified dimensions, and dimInfo-description
% (?)
%
%   Y = MrDataNd()
%   Y.shuffle(inputs)
%
% This is a method of class MrDataNd.
%
% IN
%
% OUT
%
% EXAMPLE
%   shuffle({'y','z'}, {[nSamples:-1:1], [1:2:nSamples, 2:2:nSamples]}),
%
%   See also MrDataNd

% Author:   Saskia Bollmann & Lars Kasper
% Created:  2019-10-10
% Copyright (C) 2019 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 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/>.

outputImage = this.copyobj;

nDims = numel(iDimArray);
for iDim = 1:nDims
    idxDim = this.dimInfo.get_dim_index(iDimArray{iDim});
    dataSelectorString = repmat({':'}, 1, this.dimInfo.nDims);
    dataSelectorStringNew = dataSelectorString;
    dataSelectorStringNew{idxDim} = shuffledSamplingPointsIndexArray{iDim};
    outputImage.data(dataSelectorString{:}) = ...
        outputImage.data(dataSelectorStringNew{:});
    % TODO: do we adapt the dimInfo as well?
end


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

function r = octave_array_to_python(a)
  t = '';

  [numrows, numcols] = size (a);

  if (numrows == 0)
    r = mat2str([]);
    return;

  elseif (numrows > 1)
    t = strcat('[', t);
    t = strcat(t, octave_array_to_python(a(1, :)));

    for i = 2:numrows
      t = strcat(t, ', ');
      t = strcat(t, octave_array_to_python(a(i, :)));
    end

    t = strcat(t, ']');

  else
    t = strcat('[', t);
    t = strcat(t, mat2str(a(1, 1)));

    for i = 2:numcols
      t = strcat(t, ', ');
      t = strcat(t, mat2str(a(1, i)));
    end

    t = strcat(t, ']');

  end

  r = t;


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

%%
clear; close all; clc;

%% Syntax
% |[I,J,K]=immesh(M);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function sF = rotate(sF, rot)

sF.tri = rotate(sF.tri,rot);

end


classdef ListSelector < wt.abstract.BaseWidget & wt.mixin.Enableable &...
        wt.mixin.FontStyled & wt.mixin.ButtonColorable &...
        wt.mixin.FieldColorable
    % Select from an array of items and add them to a list

    % Copyright 2020-2021 The MathWorks Inc.

    %% Events
    events (HasCallbackProperty, NotifyAccess = protected)

        % Triggered when a button is pushed
        ButtonPushed

        % Triggered when the value of the list selection changes
        ValueChanged

        % Triggered when the highlighted value changes
        HighlightedValueChanged

    end %events

    %% Public properties
    properties (AbortSet)

        % List of items to add to the list
        Items (1,:) string = ["Item 1" "Item 2" "Item 3" "Item 4"]

        % Data associated with  items (optional)
        ItemsData (1,:)

        % Indicates whether to allow duplicate entries in the list
        AllowDuplicates  (1,1) matlab.lang.OnOffSwitchState = false

        % Indicates whether to allow sort controls %RAJ - Future feature
        %Sortable  (1,1) matlab.lang.OnOffSwitchState = true

        % Inidicates what to do when add button is pressed (select from
        % Items or custom using ButtonPushed event or ButtonPushedFcn)
        AddSource (1,1) wt.enum.ListAddSource = wt.enum.ListAddSource.Items

    end %properties

    properties (AbortSet, Dependent)

        % Indices of displayed items that are currently added to the list
        SelectedIndex (1,:)

        % The current selection
        Value (1,:)

        % The current highlighted selection
        HighlightedValue (1,:)

    end %properties

    properties (AbortSet, Dependent, UsedInUpdate = false)

        % Width of the buttons
        ButtonWidth

    end %properties

    %% Read-Only properties
    properties (SetAccess = private)

        % Additional user buttons
        UserButtons wt.ButtonGrid

    end %properties

    %% Internal Properties
    properties ( Transient, NonCopyable, ...
            Access = {?wt.abstract.BaseWidget, ?wt.test.BaseWidgetTest} )

        % The ListBox control
        ListBox (1,1) matlab.ui.control.ListBox

        % The list sorting buttons
        ListButtons wt.ButtonGrid

        % Listen to button pushes in sections
        ButtonPushedListener event.listener

    end %properties

    %% Protected methods
    methods (Access = protected)

        function setup(obj)

            % Call superclass setup to establish the main grid
            obj.setup@wt.abstract.BaseWidget();

            % Set default size
            obj.Position(3:4) = [120 130];

            % Configure grid
            obj.Grid.Padding = 3;
            obj.Grid.ColumnWidth = {'1x',25};
            obj.Grid.RowHeight = {106,'1x'};

            % Create the list buttons
            obj.ListButtons = wt.ButtonGrid(obj.Grid);
            obj.ListButtons.Icon = ["add_24.png", "delete_24.png", "up_24.png", "down_24.png"];
            obj.ListButtons.ButtonTag = ["Add", "Remove", "Up", "Down"];
            obj.ListButtons.Layout.Column = 2;
            obj.ListButtons.Layout.Row = 1;
            obj.ListButtons.Orientation = "vertical";
            obj.ListButtons.ButtonHeight(:) = {25};

            % Create an additional button grid for custom buttons
            obj.UserButtons = wt.ButtonGrid(obj.Grid,"Icon",[]);
            obj.UserButtons.Layout.Column = 2;
            obj.UserButtons.Layout.Row = 2;
            obj.UserButtons.Orientation = "vertical";

            % Create the ListBox
            obj.ListBox = uilistbox(obj.Grid);
            obj.ListBox.Multiselect = true;
            obj.ListBox.ValueChangedFcn = @(h,e)obj.onSelectionChanged(e);
            obj.ListBox.Layout.Column = 1;
            obj.ListBox.Layout.Row = [1 2];

            % Update listeners
            obj.ButtonPushedListener = event.listener(...
                [obj.ListButtons obj.UserButtons],...
                'ButtonPushed',@(h,e)obj.onButtonPushed(e) );

            % Update the internal component lists
            obj.BackgroundColorableComponents = [obj.ListButtons, obj.UserButtons];
            obj.FontStyledComponents = [obj.ListBox, obj.UserButtons, obj.ListButtons];
            obj.EnableableComponents = [obj.ListBox, obj.UserButtons, obj.ListButtons];
            obj.ButtonColorableComponents = [obj.UserButtons obj.ListButtons];
            obj.FieldColorableComponents = [obj.ListBox];

        end %function

        function update(obj)

            % What is selected?
            selIdx = obj.SelectedIndex;

            % Update the list
            obj.ListBox.Items = obj.Items(selIdx);
            obj.ListBox.ItemsData = selIdx;

            % Update button enable states
            obj.updateEnables();

        end %function

        function updateEnables(obj)

            % Button enables
            if obj.Enable

                % What is selected?
                selIdx = obj.SelectedIndex;

                % Highlighted selection in list?
                hiliteIdx = obj.getListBoxSelectedIndex();

                % How many items selected into list
                numRows = numel(selIdx);
                numHilite = numel(hiliteIdx);

                % Toggle button enables
                obj.ListButtons.ButtonEnable = [
                    obj.AllowDuplicates || ( numel(selIdx) < numel(obj.Items) ) %Add Button
                    ~isempty(hiliteIdx) % Delete Button
                    numHilite && ( hiliteIdx(end) > numHilite ) %Up Button
                    numHilite && ( hiliteIdx(1) <= (numRows - numHilite) ) %Down Button
                    ];

            end %if obj.Enable

        end %function

        function onSelectionChanged(obj,evt)

            % Get the new and old values
            if isempty(obj.ItemsData)
                itemsData = obj.Items;
            else
                itemsData = obj.ItemsData;
            end

            if isempty(evt.PreviousValue)
                oldValue = itemsData([]);
            else
                oldValue = itemsData(evt.PreviousValue);
            end

            if isempty(evt.Value)
                newValue = itemsData([]);
            else
                newValue = itemsData(evt.Value);
            end

            % Update button enable states
            obj.updateEnables();

            % Trigger event
            evtOut = wt.eventdata.ValueChangedData(newValue, oldValue);
            notify(obj,"HighlightedValueChanged",evtOut);

        end %function

        function onButtonPushed(obj,evt)

            % Which button?
            switch evt.Tag

                case 'Add'

                    switch obj.AddSource

                        case wt.enum.ListAddSource.Items
                            obj.promptToAddListItems()

                        case wt.enum.ListAddSource.ButtonPushedFcn
                            notify(obj,"ButtonPushed",evt);

                    end %switch obj.AddSource

                case 'Remove'
                    obj.removeListBoxSelection();

                case 'Up'
                    obj.shiftListBoxIndex(-1);

                case 'Down'
                    obj.shiftListBoxIndex(1);

                otherwise
                    % Trigger event for user buttons
                    notify(obj,"ButtonPushed",evt);

            end %switch

            % Request update
            obj.requestUpdate();

        end %function

        function promptToAddListItems(obj)
            % Prompt a dialog to add items to the listbox

            % Prompt for stuff to add
            if obj.AllowDuplicates
                newSelIdx = listdlg("ListString",obj.Items);
            else
                newSelIdx = listdlg(...
                    "ListString",obj.Items,...
                    "InitialValue",obj.ListBox.ItemsData);
            end

            if isempty(newSelIdx)
                % User cancelled
                return
            elseif obj.AllowDuplicates
                newSelIdx = [obj.SelectedIndex newSelIdx];
            end

            % Was a change made?
            if ~isequal(obj.SelectedIndex, newSelIdx)

                % Get the original value
                oldValue = obj.Value;

                % Make the update
                obj.SelectedIndex = newSelIdx;

                % Trigger event
                evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
                notify(obj,"ValueChanged",evtOut);

            end %if

        end %function

        function selIdx = getListBoxSelectedIndex(obj)
            % Get the current selected row indices in the listbox

            warnState = warning('off','MATLAB:structOnObject');
            s = struct(obj.ListBox);
            warning(warnState);
            selIdx = s.SelectedIndex;
            if isequal(selIdx, -1)
                selIdx = [];
            end

        end %function

        function removeListBoxSelection(obj)
            % Removes the currently selected items from the listbox

            % What's currently selected?
            idxSel = obj.getListBoxSelectedIndex();

            % Is there something to remove?
            if ~isempty(idxSel)

                % Get the original value
                oldValue = obj.Value;

                % Remove it
                obj.ListBox.Items(idxSel) = [];
                obj.ListBox.ItemsData(idxSel) = [];

                % Trigger event
                evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
                notify(obj,"ValueChanged",evtOut);

            end %if

        end %function

        function shiftListBoxIndex(obj, shift)
            % Shift selected items up/down within a listbox
            % This assumes ItemsData contains unique values

            % What is the current order?
            selIdx = obj.getListBoxSelectedIndex();

            % Make indices to all items as they are now
            idxNew = 1:numel(obj.ListBox.Items);
            idxOld = idxNew;

            % Find the last stable item that doesn't move
            [~,idxStable] = setdiff(idxNew, selIdx, 'stable');
            if ~isempty(idxStable)
                idxFirstStable = idxStable(1);
                idxLastStable = idxStable(end);
            else
                idxFirstStable = inf;
                idxLastStable = 0;
            end

            % Which way do we loop?
            if shift > 0 %Shift to end

                for idxToMove = numel(selIdx):-1:1

                    % Calculate if there's room to move this item
                    idxThisBefore = selIdx(idxToMove);
                    thisShift = max( min(idxLastStable-idxThisBefore, shift), 0 );

                    % Where does this item move from/to
                    idxThisAfter = idxThisBefore + thisShift;

                    % Where do other items move from/to
                    idxOthersBefore = selIdx(idxToMove)+1:1:idxThisAfter;
                    idxOthersAfter = idxOthersBefore - thisShift;

                    % Move the items
                    idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);

                end

            elseif shift < 0 %Shift to start

                for idxToMove = 1:numel(selIdx)

                    % Calculate if there's room to move this item
                    idxThisBefore = selIdx(idxToMove);
                    thisShift = min( max(idxFirstStable-idxThisBefore, shift), 0 );

                    % Where does this item move from/to
                    idxThisAfter = idxThisBefore + thisShift;

                    % Where do other items move from/to
                    idxOthersBefore = idxThisAfter:1:selIdx(idxToMove)-1;
                    idxOthersAfter = idxOthersBefore - thisShift;

                    % Move the items
                    idxNew([idxThisAfter idxOthersAfter]) = idxNew([idxThisBefore idxOthersBefore]);

                end

            end %if shift > 0

            % Was a change made?
            if ~isequal(idxOld, idxNew)

                % Get the original value
                oldValue = obj.Value;

                % Make the shift
                obj.ListBox.Items = obj.ListBox.Items(idxNew);
                obj.ListBox.ItemsData = obj.ListBox.ItemsData(idxNew);

                % Trigger event
                evtOut = wt.eventdata.ValueChangedData(obj.Value, oldValue);
                notify(obj,"ValueChanged",evtOut);

            end %if

        end %function

    end %methods

    %% Accessors
    methods

        function value = get.SelectedIndex(obj)
            value = obj.ListBox.ItemsData;
        end
        function set.SelectedIndex(obj,value)
            obj.ListBox.Items = obj.Items(value);
            obj.ListBox.ItemsData = value;
        end

        function value = get.Value(obj)
            if isempty(obj.ItemsData)
                value = obj.Items(:,obj.ListBox.ItemsData);
            else
                value = obj.ItemsData(:,obj.ListBox.ItemsData);
            end
        end
        function set.Value(obj,value)
            if isempty(value)
                obj.SelectedIndex = [];
            else
                if isempty(obj.ItemsData)
                    [tf, selIdx] = ismember(value, obj.Items);
                else
                    [tf, selIdx] = ismember(value, obj.ItemsData);
                end
                if ~all(tf)
                    warning("widgets:ListSelector:InvalidValue",...
                        "Attempt to set an invalid Value to the list.")
                    selIdx(~tf) = [];
                end
                obj.SelectedIndex = selIdx;
            end
        end

        function value = get.HighlightedValue(obj)
            selIdx = obj.ListBox.Value;
            if isempty(selIdx) || ~isnumeric(selIdx)
                selIdx = [];
            end
            if isempty(obj.ItemsData)
                value = obj.Items(:,selIdx);
            else
                value = obj.ItemsData(:,selIdx);
            end
        end
        function set.HighlightedValue(obj,value)
            if isempty(obj.ItemsData)
                [~, obj.ListBox.Value] = ismember(value, obj.Items);
            else
                [~, obj.ListBox.Value] = ismember(value, obj.ItemsData);
            end
        end

        function value = get.ButtonWidth(obj)
            value = obj.Grid.ColumnWidth{2};
        end
        function set.ButtonWidth(obj,value)
            obj.Grid.ColumnWidth{2} = value;
        end

    end %methods

end % classdef


% CELLSRC2DB Constitute a database compatible with the ScatNet
%   convention with a cell of one object per image and a src
%
% Usage
%   db = cellsrc2db(cell, src);
function db = cellsrc2db(cell, src)
	db.src = src;
	db.features = cell2mat(cell);
	for i = 1:numel(cell)
		db.indices{i} = i;
	end
end


function eztools()
    %  put up eztools menu on current plot
    %
    %
    %  Richard G. Cobb  3/96

    %%%%%%%%%%%%%%%%%% Begin Figure Menu %%%%%%%%%%%%

    FigMenu = uimenu('Label','Figure');

    uimenu(FigMenu,'Label','Page Setup','Callback','pageset');
    uimenu(FigMenu,'Label','Labels','Separator','on','Callback','ezlabels');
    uimenu(FigMenu,'Label','Legend/Lines','Separator','on','Callback','ezlegend');

    uimenu(FigMenu,'Label','XY Locator','Separator','on','Callback','crosshair');
    uimenu(FigMenu,'Label','Add Arrows','Callback','ezarrow','Separator','on');

    % Colormap

    ColorEdit = uimenu(FigMenu,'Label','Edit ColorMap','Separator','on','Callback','makecmap(gcf,''fig'')');

    ColorMenu = uimenu(FigMenu,'Label','ColorMap','Separator','on');

    uimenu(ColorMenu,'Label','Default','Callback',...
        'colormap(''default'')');
    uimenu(ColorMenu,'Label','HSV','Callback',...
        'colormap(''hsv'')');
    uimenu(ColorMenu,'Label','summer','Callback',...
        'colormap(''summer'')');
    uimenu(ColorMenu,'Label','Gray','Callback',...
        'colormap(''gray'')');
    uimenu(ColorMenu,'Label','Hot','Callback',...
        'colormap(''hot'')');
    uimenu(ColorMenu,'Label','Cool','Callback',...
        'colormap(''cool'')');
    uimenu(ColorMenu,'Label','Copper','Callback',...
        'colormap(''copper'')');
    uimenu(ColorMenu,'Label','Pink','Callback',...
        'colormap(''pink'')');
    uimenu(ColorMenu,'Label','Jet','Callback',...
        'colormap(''jet'')');

    uimenu(FigMenu,'Label','Refresh','Separator','on','Callback','refresh');
    uimenu(FigMenu,'Label','WYSIWYG','Separator','on','Callback','wysiwyg');

    uimenu(FigMenu,'Label','Clear Figure',...
        'Callback','delete(findobj(''type'',''axes''))');

    %%%%%%%%%%%%%%%%%% Begin AXIS Menu %%%%%%%%%%%%

    AxMenu = uimenu('Label','Axis');
    uimenu(AxMenu,'Label','EZ-Axes','Callback','ezaxes');

    uimenu(AxMenu,'Label','Clear','Callback','cla');
    uimenu(AxMenu,'Label','Grid','Callback','grid');
    if(strcmp(axis('state'),'manual'))
        uimenu(AxMenu,'Label','Auto',...
            'Callback','axiscall(5,0)');
    else
        uimenu(AxMenu,'Label','Freeze',...
            'Callback','axiscall(5,0)');
    end
    AspectMenu = uimenu(AxMenu,'Label','Aspect');
    uimenu(AspectMenu,'Label','Normal','Callback','axis(''normal'')');
    uimenu(AspectMenu,'Label','Square','Callback','axis(''square'')');
    uimenu(AspectMenu,'Label','Image','Callback','axis(''image'')');
    uimenu(AspectMenu,'Label','Equal','Callback','axis(''equal'')');

    Xaxis = uimenu(AxMenu,'Label','X Opts');
    Yaxis = uimenu(AxMenu,'Label','Y Opts');
    Zaxis = uimenu(AxMenu,'Label','Z Opts');

    if(strcmp(get(gca,'XScale'),'linear'))
        uimenu(Xaxis,'Label','Log','Callback','axiscall(1,''X'')');
    else
        uimenu(Xaxis,'Label','Linear','Callback','axiscall(1,''X'')');
    end
    uimenu(Xaxis,'Label','Auto Min','Callback','axiscall(2,''X'')');
    uimenu(Xaxis,'Label','Auto Max','Callback','axiscall(3,''X'')');
    if(strcmp(get(gca,'YScale'),'linear'))
        uimenu(Yaxis,'Label','Log','Callback','axiscall(1,''Y'')');
    else
        uimenu(Yaxis,'Label','Linear','Callback','axiscall(1,''Y'')');
    end
    uimenu(Yaxis,'Label','Auto Min','Callback','axiscall(2,''Y'')');
    uimenu(Yaxis,'Label','Auto Max','Callback','axiscall(3,''Y'')');
    if(strcmp(get(gca,'ZScale'),'linear'))
        uimenu(Zaxis,'Label','Log','Callback','axiscall(1,''Z'')');
    else
        uimenu(Zaxis,'Label','Linear','Callback','axiscall(1,''Z'')');
    end
    uimenu(Zaxis,'Label','Auto Min','Callback','axiscall(2,''Z'')');
    uimenu(Zaxis,'Label','Auto Max','Callback','axiscall(3,''Z'')');

    if(ishold)
        uimenu(AxMenu,'Label','Hold off','Callback','axiscall(4,0)');
    else
        uimenu(AxMenu,'Label','Hold on','Callback','axiscall(4,0)');
    end

    uimenu(AxMenu,'Label','Axes Vis','Callback','axiscall(12,[])');

    %  Zoom Menu

    %uimenu(AxMenu,'Label','Zoom Box','Callback','subzoom');

    uimenu(AxMenu,'Label','Zoom ON','Callback','zoomrb on','Checked','off');
    uimenu(AxMenu,'Label','Zoom OFF','Callback','zoomrb off','Checked','off');

    uimenu(AxMenu,'Label','X-Y Sliders','Callback','axiscall(9,[])',...
        'Checked','off');

    uimenu(AxMenu,'Label','3-D Viewer','Callback','viewer','Separator','on');
    %New Zoom InteractiveMouse
    %uimenu(AxMenu,'Label','Mouse Zoom','Separator','on','Callback','interactivemouse');

    Zoom3d = uimenu(AxMenu,'Label','3-D Zoom');
    maglevel = 2;
    set(Zoom3d,'UserData',[1;1;1;maglevel]);
    uimenu(Zoom3d,'Label','Zoom in','Callback','zoom3d(1)');
    uimenu(Zoom3d,'Label','Zoom out','Callback','zoom3d(2)');
    uimenu(Zoom3d,'Label','X Axis','Checked','on','Callback',...
        'axiscall(6,''X'')','Separator','on');
    uimenu(Zoom3d,'Label','Y Axis','Checked','on','Callback',...
        'axiscall(6,''Y'')');
    uimenu(Zoom3d,'Label','Z Axis','Checked','on','Callback',...
        'axiscall(6,''Z'')');
    uimenu(Zoom3d,'Label',['Mag Level -' num2str(maglevel) '-'],'Callback',...
        'axiscall(7,0)','Separator','on');
    set(gcf,'WindowButtonDownFcn','tsel');


function [newVOI]=dicomrt_closevoi(VOI)
% dicomrt_closevoi(VOI)
%
% Close VOI contours.
%
% VOI contains VOI contours.
%
% See also: dicomrt_close3dvoi
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Check case and set-up some parameters and variables
[voi_temp,type,label]=dicomrt_checkinput(VOI);
VOI=dicomrt_varfilter(voi_temp);

newVOI=VOI;
for k=1:size(VOI,1) % loop through the number of VOIs
    for j=1:size(VOI{k,2},1) % loop through the number of slices/VOI
        temp=VOI{k,2}{j};
        isequalx=isequal(temp(1,1,1),temp(end,1,1));
        isequaly=isequal(temp(1,2,1),temp(end,2,1));
        if or(~isequalx,~isequaly)
            temp(end+1,:)=temp(1,:);
            newVOI{k,2}{j}=temp;
        end
    end
end

[newVOI]=dicomrt_restorevarformat(voi_temp,newVOI);