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

% GUIEDITTEXTUALFILEHEADER M-file for GUIEditTextualFileHeader.fig
%      GUIEDITTEXTUALFILEHEADER, by itself, creates a new GUIEDITTEXTUALFILEHEADER or raises the existing
%      singleton*.
%
%      H = GUIEDITTEXTUALFILEHEADER returns the handle to a new GUIEDITTEXTUALFILEHEADER or the handle to
%      the existing singleton*.
%
%      GUIEDITTEXTUALFILEHEADER('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in GUIEDITTEXTUALFILEHEADER.M with the given input arguments.
%
%      GUIEDITTEXTUALFILEHEADER('Property','Value',...) creates a new GUIEDITTEXTUALFILEHEADER or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before GUIEditTextualFileHeader_OpeningFunction gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to GUIEditTextualFileHeader_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help GUIEditTextualFileHeader

% Last Modified by GUIDE v2.5 05-Jan-2009 17:27:47

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @GUIEditTextualFileHeader_OpeningFcn, ...
                   'gui_OutputFcn',  @GUIEditTextualFileHeader_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);

if nargin & isstr(varargin{1})
  gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end

% End initialization code - DO NOT EDIT

% --- Executes just before GUIEditTextualFileHeader is made visible.
function GUIEditTextualFileHeader_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to GUIEditTextualFileHeader (see VARARGIN)

% Choose default command line output for GUIEditTextualFileHeader
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% Load TextualFileHeader if given
if length(varargin)>0
  if nargin & isstruct(varargin{1})
    if isfield(varargin{1},'TextualFileHeader')
    data=guidata(gcf);
    data.SegyHeader=varargin{1};
    data.SegyHeaderOrg=data.SegyHeader;

    if isempty(find(data.SegyHeader.TextualFileHeader>200))
        HeaderType=2; % ASCII
    else
        HeaderType=1; % EBCDIC
    end
    set(handles.popType,'Value',HeaderType);

    guidata(gcf, data);
    UpdateText(hObject, handles)
    end
  end
end

% UIWAIT makes GUIEditTextualFileHeader wait for user response (see UIRESUME)
uiwait(handles.figTextualHeader);

% --- Outputs from this function are returned to the command line.
function varargout = GUIEditTextualFileHeader_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
data=guidata(gcf);
varargout{1} =     data.SegyHeader;
close(handles.figTextualHeader);

% --- Executes during object creation, after setting all properties.
function eTextHeader_CreateFcn(hObject, eventdata, handles)
% hObject    handle to eTextHeader (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

function eTextHeader_Callback(hObject, eventdata, handles)
% hObject    handle to eTextHeader (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of eTextHeader as text
%        str2double(get(hObject,'String')) returns contents of
%        eTextHeader as a double
%eTextHeader_Callback(handles.eTextHeader, eventdata, handles)
  HS=get(hObject,'String');
  VAL=get(hObject,'Value');

  data=guidata(hObject);
  data.HS=HS;
  data.VAL=VAL;
  %disp(sprintf('LINE=''%s''',HS(VAL,:)))
  guidata(hObject, data);

  % UPDATE TEXT STRING
  set(handles.eEditLine,'String',HS(VAL,:))

% --- Executes during object creation, after setting all properties.
function popType_CreateFcn(hObject, eventdata, handles)
% hObject    handle to popType (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: popupmenu controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

% --- Executes on selection change in popType.
function popType_Callback(hObject, eventdata, handles)
% hObject    handle to popType (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: contents = get(hObject,'String') returns popType contents as cell array
%        contents{get(hObject,'Value')} returns selected item from
%        popType
  UpdateText(hObject,handles)

function UpdateText(hObject, handles)
  data=guidata(hObject);

  HeaderType=get(handles.popType,'Value');

  if HeaderType==1,
    txt=char(ebcdic2ascii(data.SegyHeader.TextualFileHeader));
  else
    txt=char(data.SegyHeader.TextualFileHeader');
  end
  %disp('--')
  %reshape(txt,80,40)'
  %disp('--')
  %disp(size(txt))
  try
    s=[];
    for i=1:40;
      s=[s,txt((i-1)*80+1:(i-1)*80+80)];
      try
          if i~=40, s=[s,'|']; end
      catch
          keyboard
      end
    end
  catch
    if (HeaderType==1)
      warndlg(['The Textual Header is most probably NOT EBCDIC formatted'],'Textual Header Info')
    else
      warndlg(['The Textual Header is most probably NOT ASCII formatted'],'Textual Header Info')
    end
  end

  try
    set(handles.eTextHeader,'String',s)
  catch
    disp('WHUUPS')
  end

% --- Executes during object creation, after setting all properties.
function eEditLine_CreateFcn(hObject, eventdata, handles)
% hObject    handle to eEditLine (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc
    set(hObject,'BackgroundColor','white');
else
    set(hObject,'BackgroundColor',get(0,'defaultUicontrolBackgroundColor'));
end

function eEditLine_Callback(hObject, eventdata, handles)
% hObject    handle to eEditLine (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of eEditLine as text
%        str2double(get(hObject,'String')) returns contents of eEditLine as a double

data=guidata(hObject);
if ~isfield(data,'HS')
    eTextHeader_Callback(handles.eTextHeader, eventdata, handles)
    data=guidata(hObject);
end

try

    NEWSTR=get(hObject,'String');
    n=length(NEWSTR);
    if n<80
        NEWSTR(n+1:80)=' ';
    end

    n=length(NEWSTR);
    if n>=80
        data.HS(data.VAL,:)=NEWSTR(1:80);
    else
        data.HS(data.VAL,1:n)=NEWSTR(1:n);
    end

    HStransp=data.HS';

    if get(handles.popType,'Value')==1;
        % EBCDIC
        data.SegyHeader.TextualFileHeader=double(ascii2ebcdic(HStransp(:)));
    else
        % ASCII
        data.SegyHeader.TextualFileHeader=double(HStransp(:));
    end
    guidata(gcf, data);

    UpdateText(hObject, handles)

    eTextHeader_Callback(handles.eTextHeader, eventdata, handles)
catch
    keyboard

end

% --- Executes on button press in pbClose.
function pbClose_Callback(hObject, eventdata, handles)
% hObject    handle to pbClose (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
uiresume;

% --- Executes on button press in pbReset.
function pbReset_Callback(hObject, eventdata, handles)
% hObject    handle to pbReset (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
data=guidata(gcf);
data.SegyHeader=data.SegyHeaderOrg;
guidata(gcf, data);
UpdateText(hObject, handles)

% --- Executes on button press in pbFont.
function pbFont_Callback(hObject, eventdata, handles)
% hObject    handle to pbFont (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
d = uisetfont(handles.eTextHeader);
% Apply those settings to c2
set(handles.eEditLine,d);


function recastMrImage = recast_as_MrImage(this)
% recasts an MrImageSpm4D as a 4D MrImage, changes available methods etc.
%
%   Y = MrImageSpm4D()
%   YasMrImage = Y.recast_as_MrImage()
%
% This is a method of class MrImageSpm4D.
%
% IN
%
% OUT
%
% EXAMPLE
%   recast_as_MrImage
%
%   See also MrImageSpm4D

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

recastMrImage = MrImage();
recastMrImage.update_properties_from(this);

% house keeping: rename, if default name was used before, add info
% about recast
if strcmp(recastMrImage.name, 'MrImageSpm4D')
    recastMrImage.name = 'MrImage';
end
recastMrImage.info{end+1,1} = 'recast_as_MrImage';


function ppm = getPPM(MRSIStruct)
    ppm = MRSIStruct.ppm;
end


function varargout = guipas(varargin)
% GUIPAS Application M-file for guifastr.fig
%    FIG = GUIPAS launch guifastr GUI.
%    GUIPAS('callback_name', ...) invoke the named callback.

% Last Modified by GUIDE v2.5 14-Dec-2004 16:11:40

if nargin == 1  % LAUNCH GUI

	pasguifig = openfig(mfilename,'new');

	% Generate a structure of handles to pass to callbacks, and store it.
	handles = guihandles(pasguifig);
    handles.param.method='';
    handles.param.qrsevent='';
    handles.param.npc=get(handles.npc,'string');
    handles.opstatus=0;
    guidata(pasguifig, handles);

    % Intialize pop-up menu
    guipas('qrs_popup_Callback',handles.qrs_popup,[],handles,varargin{1})
    % ---------------------

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

	if nargout > 0
		varargout{1} = pasguifig;
	end

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

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

end

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

% --- Executes on selection change in qrs_popup.
function varargout = qrs_popup_Callback(h, eventdata, handles, varargin)
if nargin > 3
    set(h,'string',varargin{1});
    handles.param.qrsevent=get(h,'value');
    guidata(h, handles);
else
    handles.param.qrsevent=get(h,'value');
    guidata(h, handles);
end

% --- Executes on button press in rapco_method_check.
function varargout = rapco_method_check_Callback(h, eventdata, handles, varargin)
set(handles.gmean_method_check,'value',0);
set(handles.median_method_check,'value',0);
set(handles.mean_method_check,'value',0);
set(handles.npc_text,'ForeGroundColor',[0 0 0.55]);
set(handles.npc,'Enable','on');
handles.param.method='obs';
handles.param.npc=get(handles.npc,'string');
guidata(h, handles);

% --- Executes on editing npc textbox
function varargout = npc_Callback(h, eventdata, handles, varargin)
handles.param.npc=get(handles.npc,'string');
guidata(h, handles);

% --- Executes on button press in mean_method_check.
function varargout = mean_method_check_Callback(h, eventdata, handles, varargin)
set(handles.gmean_method_check,'value',0);
set(handles.median_method_check,'value',0);
set(handles.rapco_method_check,'value',0);
set(handles.npc_text,'ForeGroundColor',[0.55 0.55 0.55]);
set(handles.npc,'Enable','off');
handles.param.method='mean';
guidata(h, handles);

% --- Executes on button press in gmean_method_check.
function varargout = gmean_method_check_Callback(h, eventdata, handles, varargin)
set(handles.mean_method_check,'value',0);
set(handles.median_method_check,'value',0);
set(handles.rapco_method_check,'value',0);
set(handles.npc_text,'ForeGroundColor',[0.55 0.55 0.55]);
set(handles.npc,'Enable','off');
handles.param.method='gmean';
guidata(h, handles);

% --- Executes on button press in median_method_check.
function varargout = median_method_check_Callback(h, eventdata, handles, varargin)
set(handles.mean_method_check,'value',0);
set(handles.gmean_method_check,'value',0);
set(handles.rapco_method_check,'value',0);
set(handles.npc_text,'ForeGroundColor',[0.55 0.55 0.55]);
set(handles.npc,'Enable','off');
handles.param.method='median';
guidata(h, handles);

% --- Executes on button press in ok_button.
function varargout = ok_button_Callback(h, eventdata, handles, varargin)
handles.param.qrsevent=get(handles.qrs_popup,'value');
if isempty(handles.param.method)
    errordlg('Please select artifact construction method.','fmrib_pas error');
    return;
end
handles.opstatus=1;
guidata(h,handles);
uiresume(handles.pasgui_fig);

% --- Executes on button press in help_button.
function varargout = help_button_Callback(h, eventdata, handles, varargin)
pophelp('pop_fmrib_pas.m');

% --- Executes on button press in cancel_button.
function varargout = cancel_button_Callback(h, eventdata, handles, varargin)
handles.opstatus=0;
guidata(h,handles);
uiresume(handles.pasgui_fig);

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


function kdtree_compile()
% clc
localpath = fileparts(which('kdtree_compile'));
fprintf(1,'Compiling kdtree library [%s]...\n', localpath);

err = 0;
err = err | mex('-outdir',localpath, [localpath,'/kdtree_build.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_delete.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_k_nearest_neighbors.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_ball_query.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_nearest_neighbor.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_range_query.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_io_from_mat.cpp']);
err = err | mex('-outdir',localpath, [localpath,'/kdtree_io_to_mat.cpp']);

if err ~= 0,
   error('compile failed!');
else
   fprintf(1,'\bDone!\n');
end


function [streams,fileheader] = load_xdf(filename,varargin)
% Import an XDF file.
% [Streams,FileHeader] = load_xdf(Filename, Options...)
%
% This is a MATLAB importer for mult-stream XDF (Extensible Data Format) recordings. All
% information covered by the XDF 1.0 specification is imported, plus any additional meta-data
% associated with streams or with the container file itself.
%
% See https://github.com/sccn/xdf/ for more information on XDF.
%
% The function supports several further features, such as compressed XDF archives, robust
% time synchronization, support for breaks in the data, as well as some other defects.
%
% In:
%   Filename : name of the file to import (*.xdf or *.xdfz)
%
%   Options... : A list of optional name-value arguments for special use cases. The allowed names
%                are listed in the following:
%
%                Parameters that control various processing features:
%
%                'Verbose' : Whether to print verbose diagnostics. (default: false)
%
%                'HandleClockSynchronization' : Whether to enable clock synchronization based on
%                                               ClockOffset chunks. (default: true)
%
%                'HandleJitterRemoval' : Whether to perform jitter removal for regularly sampled
%                                        streams. (default: true)
%
%                'OnChunk' : Function that is called for each chunk of data as it
%                            is being retrieved from the file; the function is allowed to modify the
%                            data (for example, sub-sample it). The four input arguments are 1) the
%                            matrix of [#channels x #samples] values (either numeric or 2d cell
%                            array of strings), 2) the vector of unprocessed local time stamps (one
%                            per sample), 3) the info struct for the stream (same as the .info field
%                            in the final output, buth without the .effective_srate sub-field), and
%                            4) the scalar stream number (1-based integers). The three return values
%                            are 1) the (optionally modified) data, 2) the (optionally modified)
%                            time stamps, and 3) the (optionally modified) header (default: []).
%
%                'DisableVendorSpecifics' : Whether to perform certain vendor or system specific
%                                           operations. One example is the "BrainVision RDA" data
%                                           where it is necessary to pass the time stamps of a newly
%                                           introduced marker identifier channel on to the actual markers
%                                           in order to keep them perfectly in sync with the EEG data.
%                                           'DisableVendorSpecifics' takes the base names of certain
%                                           streams derived from the same source as a cell array of
%                                           strings (e.g. 'BrainVision RDA'). It is also possible to
%                                           disable vendor specifics altogether by providing the
%                                           value 'all'.
%
%                Parameters for advanced failure recovery in clock synchronization:
%
%                'HandleClockResets' : Whether the importer should check for potential resets of the
%                                      clock of a stream (e.g. computer restart during recording, or
%                                      hot-swap). Only useful if the recording system supports
%                                      recording under such circumstances. (default: true)
%
%                'ClockResetThresholdStds' : A clock reset must be accompanied by a ClockOffset
%                                            chunk being delayed by at least this many standard
%                                            deviations from the distribution. (default: 5)
%
%                'ClockResetThresholdSeconds' : A clock reset must be accompanied by a ClockOffset
%                                               chunk being delayed by at least this many seconds.
%                                               (default: 5)
%
%                'ClockResetThresholdOffsetStds' : A clock reset must be accompanied by a
%                                                  ClockOffset difference that lies at least this many
%                                                  standard deviations from the distribution. (default: 10)
%
%                'ClockResetThresholdOffsetSeconds' : A clock reset must be accompanied by a
%                                                     ClockOffset difference that is at least this
%                                                     many seconds away from the median. (default: 1)
%
%                'ClockResetMaxJitter' : Maximum tolerable jitter (in seconds of error) for clock
%                                        reset handling. (default: 5)
%
%                'CorrectStreamLags' : Apply lag correction described by timing spec sheets in the file.
%                                      (default: true)
%
%                Parameters for jitter removal in the presence of data breaks:
%
%                'JitterBreakThresholdSeconds' : An interruption in a regularly-sampled stream of at least this
%                                                many seconds will be considered as a potential break (if also
%                                                the BreakThresholdSamples is crossed) and multiple segments
%                                                will be returned. Default: 1
%
%                'JitterBreakThresholdSamples' : An interruption in a regularly-sampled stream of at least this
%                                                many samples will be considered as a potential break (if also
%                                                the BreakThresholdSeconds is crossed) and multiple segments
%                                                will be returned. Default: 500
%
%                Parameters for streams that can drop samples:
%
%                'FrameRateAccuracy' : How accurate the nominal frame rate is.
%                                      Used for can_drop_samples == true frames to determine
%                                      the maximum possible number of dropped frames. Default: 0.05%
%
% Out:
%   Streams : cell array of structs, one for each stream; the structs have the following content:
%             .time_series field: contains the stream's time series [#Channels x #Samples]
%                                 this matrix is of the type declared in .info.channel_format
%             .time_stamps field: contains the time stamps for each sample (synced across streams)
%
%             .info field: contains the meta-data of the stream (all values are strings)
%               .name: name of the stream
%               .type: content-type of the stream ('EEG','Events', ...)
%               .channel_format: value format ('int8','int16','int32','int64','float32','double64','string')
%               .nominal_srate: nominal sampling rate of the stream (as declared by the device);
%                               zero for streams with irregular sampling rate
%               .effective_srate: effective (measured) sampling rate of the stream, if regular
%                                 (otherwise omitted)
%               .desc: struct with any domain-specific meta-data declared for the stream; see
%                      www.xdf.org for the declared specifications
%
%             .segments field: struct array containing segment ranges for regularly sampled
%                              time series with breaks (not present if the stream is irregular)
%               .index_range: 1st and last index of the segment within the .time_series/.time_stamps
%                             arrays
%               .t_begin: time of the 1st sample in the segment, in seconds
%               .t_end: time of the last sample in the segment, in seconds
%               .duration: duration of the segment, in seconds
%               .num_samples: number of samples in the segment
%               .effective_srate: effective (i.e. measured) sampling rate within the segment
%
%   FileHeader : struct with file header contents in the .info field
%
% Examples:
%   % load the streams contained in a given XDF file
%   streams = load_xdf('C:\Recordings\myrecording.xdf')
%
% License:
%     This file is covered by the BSD license.
%
%     Copyright (c) 2012, Christian Kothe
%     Portions Copyright (c) 2010, Wouter Falkena
%     All rights reserved.
%
%     Redistribution and use in source and binary forms, with or without
%     modification, are permitted provided that the following conditions are
%     met:
%
%         * Redistributions of source code must retain the above copyright
%           notice, this list of conditions and the following disclaimer.
%         * Redistributions in binary form must reproduce the above copyright
%           notice, this list of conditions and the following disclaimer in
%           the documentation and/or other materials provided with the distribution
%
%     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
%     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
%     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
%     ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
%     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
%     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
%     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
%     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
%     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
%     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%     POSSIBILITY OF SUCH DAMAGE.
%
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2012-04-22
%
%                                Contains portions of xml2struct Copyright (c) 2010, Wouter Falkena,
%                                ASTI, TUDelft, 21-08-2010
%
%                                Contains frame snapping routine to handle dropped video frames by
%                                Matthew Grivich.
%
%                                version 1.13
LIBVERSION = '1.13';
% check inputs
opts = cell2struct(varargin(2:2:end),varargin(1:2:end),2);
if ~isfield(opts,'OnChunk')
    opts.OnChunk = []; end
if ~isfield(opts,'Verbose')
    opts.Verbose = false; end
if ~isfield(opts,'HandleClockSynchronization')
    opts.HandleClockSynchronization = true; end
if ~isfield(opts,'HandleClockResets')
    opts.HandleClockResets = true; end
if ~isfield(opts,'HandleJitterRemoval')
    opts.HandleJitterRemoval = true; end
if ~isfield(opts,'JitterBreakThresholdSeconds')
    opts.JitterBreakThresholdSeconds = 1; end
if ~isfield(opts,'JitterBreakThresholdSamples')
    opts.JitterBreakThresholdSamples = 500; end
if ~isfield(opts,'ClockResetThresholdSeconds')
    opts.ClockResetThresholdSeconds = 5; end
if ~isfield(opts,'ClockResetThresholdStds')
    opts.ClockResetThresholdStds = 5; end
if ~isfield(opts,'ClockResetThresholdOffsetSeconds')
    opts.ClockResetThresholdOffsetSeconds = 1; end
if ~isfield(opts,'ClockResetThresholdOffsetStds')
    opts.ClockResetThresholdOffsetStds = 10; end
if ~isfield(opts,'WinsorThreshold')
    opts.WinsorThreshold = 0.0001; end
if ~isfield(opts,'ClockResetMaxJitter')
    opts.ClockResetMaxJitter = 5; end
if ~isfield(opts,'DisableVendorSpecifics')
    opts.DisableVendorSpecifics = {}; end
if ~isfield(opts,'CorrectStreamLags')
    opts.CorrectStreamLags = true; end
if ~isfield(opts,'FrameRateAccuracy')
    opts.FrameRateAccuracy = .05; end
if ~exist(filename,'file')
    error(['The file "' filename '" does not exist.']); end

% uncompress if necessary (note: "bonus" feature, not part of the XDF 1.0 spec)
[p,n,x] = fileparts(filename);
if strcmp(x,'.xdfz')
    % idea for this type of approach by Michael Kleder
    import com.mathworks.mlwidgets.io.InterruptibleStreamCopier
    src = java.io.FileInputStream(filename);
    flt = java.util.zip.InflaterInputStream(src);
    filename = [p filesep n '_temp_uncompressed' x];
    dst = java.io.FileOutputStream(filename);
    copier = InterruptibleStreamCopier.getInterruptibleStreamCopier;
    copier.copyStream(flt,dst);
    dst.close();
    src.close();
end

streams = {};                                   % cell array of returned streams (in the order of appearance in the file)
idmap = sparse(2^31-1,1);                       % remaps stream id's onto indices in streams
temp = struct();                                % struct array of temporary per-stream information
fileheader = struct();                          % the file header
f = fopen(filename,'r','ieee-le.l64');          % file handle
closer = onCleanup(@()close_file(f,filename));  % object that closes the file when the function exits

% there is a fast C mex file for the inner loop, but it's
% not necessarily available for every platform
have_mex = exist('load_xdf_innerloop','file');
if ~have_mex
    if opts.Verbose
        disp(['NOTE: apparently you are missing a compiled binary version of the inner loop code.',...
            ' Attempting to download...']);
    end

    fname = ['load_xdf_innerloop.' mexext];
    mex_url = ['https://github.com/sccn/xdf/releases/download/v',...
        LIBVERSION, '/', fname];
    [this_path, this_name, this_ext] = fileparts(mfilename('fullpath'));
    try
        have_mex = true;
        websave(fullfile(this_path, fname), mex_url);
    catch ME
        if opts.Verbose
            disp(['Unable to download the compiled binary version for your platform.',...
                ' Using the slow MATLAB code instead.']);
        end
        have_mex = false;
        %rethrow(ME);
    end
end

% ======================
% === parse the file ===
% ======================

% read [MagicCode]
if ~strcmp(fread(f,4,'*char')','XDF:')
    error(['This is not a valid XDF file (' filename ').']); end

% for each chunk...

if opts.Verbose; fprintf('Now reading from %s ...', filename); end;
while 1
    % read [NumLengthBytes], [Length]
    len = double(read_varlen_int(f));
    if ~len
        break; end
    % read [Tag]
    switch fread(f,1,'uint16')
        case 3 % read [Samples] chunk
            try
                % read [StreamId]
                id = idmap(fread(f,1,'uint32'));
                if have_mex
                    % read the chunk data at once
                    data = fread(f,len-6,'*uint8');
                    % run the mex kernel
                    [values,timestamps] = load_xdf_innerloop(data, temp(id).chns, temp(id).readfmt, temp(id).sampling_interval, temp(id).last_timestamp);
                    temp(id).last_timestamp = timestamps(end);
                else % fallback MATLAB implementation
                    % read [NumSampleBytes], [NumSamples]
                    num = read_varlen_int(f);
                    % allocate space
                    timestamps = zeros(1,num);
                    if strcmp(temp(id).readfmt,'*string')
                        values = cell(temp(id).chns,num);
                    else
                        values = zeros(temp(id).chns,num);
                    end
                    % for each sample...
                    for s=1:num
                        % read or deduce time stamp
                        if fread(f,1,'*uint8')
                            timestamps(s) = fread(f,1,'double');
                        else
                            timestamps(s) = temp(id).last_timestamp + temp(id).sampling_interval;
                        end
                        % read the values
                        if strcmp(temp(id).readfmt,'*string')
                            for v = 1:size(values,1)
                                values{v,s} = fread(f,double(read_varlen_int(f)),'*char')'; end
                        else
                            values(:,s) = fread(f,size(values,1),temp(id).readfmt);
                        end
                        temp(id).last_timestamp = timestamps(s);
                    end
                end
                % optionally send through the OnChunk function
                if ~isempty(opts.OnChunk)
                    [values,timestamps,streams{id}] = opts.OnChunk(values,timestamps,streams{id},id); end %#ok<*AGROW>
                % append to the time series...
                temp(id).time_series{end+1} = values;
                temp(id).time_stamps{end+1} = timestamps;
            catch e
                % an error occurred (perhaps a chopped-off file): emit a warning
                % and return the file up to this point
                warning(e.identifier,e.message);
                break;
            end
        case 2 % read [StreamHeader] chunk
            % read [StreamId]
            streamid = fread(f,1,'uint32');
            id = length(streams)+1;
            idmap(streamid) = id; %#ok<SPRIX>
            % read [Content]
            header = parse_xml_struct(fread(f,len-6,'*char')');
            streams{id} = header;
            if opts.Verbose
                fprintf(['  found stream ' header.info.name '\n']); end
            % generate a few temporary fields
            temp(id).chns = str2num(header.info.channel_count); %#ok<*ST2NM>
            temp(id).srate = str2num(header.info.nominal_srate);
            temp(id).last_timestamp = 0;
            temp(id).time_series = {};
            temp(id).time_stamps = {};
            temp(id).clock_times = [];
            temp(id).clock_values = [];
            if temp(id).srate > 0
                temp(id).sampling_interval = 1/temp(id).srate;
            else
                temp(id).sampling_interval = 0;
            end
            % fread parsing format for data values
            temp(id).readfmt = ['*' header.info.channel_format];
            if strcmp(temp(id).readfmt,'*double64') && ~have_mex
                temp(id).readfmt = '*double'; end % for fread()
        case 6 % read [StreamFooter] chunk
            % read [StreamId]
            id = idmap(fread(f,1,'uint32'));
            % read [Content]
            footer = parse_xml_struct(fread(f,len-6,'*char')');
            streams{id} = hlp_superimposedata(footer,streams{id});
        case 1 % read [FileHeader] chunk
            fileheader = parse_xml_struct(fread(f,len-2,'*char')');
        case 4 % read [ClockOffset] chunk
            % read [StreamId]
            id = idmap(fread(f,1,'uint32'));
            % read [CollectionTime]
            temp(id).clock_times(end+1) = fread(f,1,'double');
            % read [OffsetValue]
            temp(id).clock_values(end+1) = fread(f,1,'double');
        otherwise
            % skip other chunk types (Boundary, ...)
            fread(f,len-2,'*uint8');
    end
end

% concatenate the signal across chunks
for k=1:length(temp)
    try
        temp(k).time_series = [temp(k).time_series{:}];
        temp(k).time_stamps = [temp(k).time_stamps{:}];
    catch e
        disp(['Could not concatenate time series for stream ' streams{k}.info.name '; skipping.']);
        disp(['Reason: ' e.message]);
        temp(k).time_series = [];
        temp(k).time_stamps = [];
    end
end

% ===================================================================
% === perform (fault-tolerant) clock synchronization if requested ===
% ===================================================================

if opts.HandleClockSynchronization
    if opts.Verbose
        disp('  performing clock synchronization...'); end
    for k=1:length(temp)
        if ~isempty(temp(k).time_stamps)
            try
                clock_times = temp(k).clock_times;
                clock_values = temp(k).clock_values;
                if isempty(clock_times)
                    error('No clock offset values present.'); end
            catch
                disp(['No clock offsets were available for stream "' streams{k}.info.name '"']);
                continue;
            end

            % detect clock resets (e.g., computer restarts during recording) if requested
            % this is only for cases where "everything goes wrong" during recording
            % note that this is a fancy feature that is not needed for normal XDF compliance
            if opts.HandleClockResets
                % first detect potential breaks in the synchronization data; this is only necessary when the
                % importer should be able to deal with recordings where the computer that served a stream
                % was restarted or hot-swapped during an ongoing recording, or the clock was reset otherwise
                time_diff = diff(clock_times);
                value_diff = abs(diff(clock_values));
                % points where a glitch in the timing of successive clock measurements happened
                time_glitch = (time_diff < 0 | (((time_diff - median(time_diff)) ./ median(abs(time_diff-median(time_diff)))) > opts.ClockResetThresholdStds & ...
                    ((time_diff - median(time_diff)) > opts.ClockResetThresholdSeconds)));
                % points where a glitch in successive clock value estimates happened
                value_glitch = (value_diff - median(value_diff)) ./ median(abs(value_diff-median(value_diff))) > opts.ClockResetThresholdOffsetStds & ...
                    (value_diff - median(value_diff)) > opts.ClockResetThresholdOffsetSeconds;
                % points where both a time glitch and a value glitch co-occur are treated as resets
                resets_at = time_glitch & value_glitch;
                % determine the [begin,end] index ranges between resets
                if any(resets_at)
                    tmp = find(resets_at)';
                    tmp = [tmp tmp+1]';
                    tmp = [1 tmp(:)' length(resets_at)];
                    ranges = num2cell(reshape(tmp,2,[])',2);
                    if opts.Verbose
                        disp(['  found ' num2str(nnz(resets_at)) ' clock resets in stream ' streams{k}.info.name '.']); end
                else
                    ranges = {[1,length(clock_times)]};
                end
            else
                % otherwise we just assume that there are no clock resets
                ranges = {[1,length(clock_times)]};
            end

            % calculate clock offset mappings for each data range
            mappings = {};
            for r=1:length(ranges)
                idx = ranges{r};
                if idx(1) ~= idx(2)
                    % to accomodate the Winsorizing threshold (in seconds) we rescale the data (robust_fit sets it to 1 unit)
                    mappings{r} = robust_fit([ones(idx(2)-idx(1)+1,1) clock_times(idx(1):idx(2))']/opts.WinsorThreshold, clock_values(idx(1):idx(2))'/opts.WinsorThreshold);
                else
                    mappings{r} = [clock_values(idx(1)) 0]; % just one measurement
                end
            end

            if length(ranges) == 1
                % apply the correction to all time stamps
                temp(k).time_stamps = temp(k).time_stamps + (mappings{1}(1) + mappings{1}(2)*temp(k).time_stamps);
            else
                % if there are data segments measured with different clocks we need to
                % determine, for any time stamp lying between two segments, to which of the segments it belongs
                clock_segments = zeros(size(temp(k).time_stamps));  % the segment index to which each stamp belongs
                begin_of_segment = 1;                               % first index into time stamps that belongs to the current segment
                end_of_segment = NaN; %#ok<NASGU>                   % last index into time stamps that belongs to the current segment
                for r=1:length(ranges)-1
                    cur_end_time = clock_times(ranges{r}(2));       % time at which the current segment ends
                    next_begin_time = clock_times(ranges{r+1}(1));  % time at which the next segment begins
                    % get the data that is not yet processed
                    remaining_indices = begin_of_segment:length(temp(k).time_stamps);
                    if isempty(remaining_indices)
                        break; end
                    remaining_data = temp(k).time_stamps(remaining_indices);
                    if next_begin_time > cur_end_time
                        % clock jumps forward: the end of the segment is where the data time stamps
                        % lie closer to the next segment than the current in time
                        end_of_segment = remaining_indices(min(find([abs(remaining_data-cur_end_time) > abs(remaining_data-next_begin_time),true],1)-1,length(remaining_indices)));
                    else
                        % clock jumps backward: the end of the segment is where the data time stamps
                        % jump back by more than the max conceivable jitter (as any negative delta is jitter)
                        end_of_segment = remaining_indices(min(find([diff(remaining_data) < -opts.ClockResetMaxJitter,true],1),length(remaining_indices)));
                    end
                    % assign the segment of data points to the current range
                    % go to next segment
                    clock_segments(begin_of_segment:end_of_segment) = r;
                    begin_of_segment = end_of_segment+1;
                end
                % assign all remaining time stamps to the last segment
                clock_segments(begin_of_segment:end) = length(ranges);
                % apply corrections on a per-segment basis
                for r=1:length(ranges)
                    temp(k).time_stamps(clock_segments==r) = temp(k).time_stamps(clock_segments==r) + (mappings{r}(1) + mappings{r}(2)*temp(k).time_stamps(clock_segments==r)); end
            end
        end
    end
end

% ===========================================
% === perform jitter removal if requested ===
% ===========================================
if opts.HandleJitterRemoval
    % jitter removal is a bonus feature that yields linearly increasing timestamps from data
    % where samples had been time stamped with some jitter (e.g., due to operating system
    % delays)
    if opts.Verbose
        disp('  performing jitter removal...'); end

    for k=1:length(temp)

        if ~isempty(temp(k).time_stamps) && temp(k).srate

            if isfield(streams{k}.info.desc, 'synchronization') && ...
                isfield(streams{k}.info.desc.synchronization, 'can_drop_samples') && ...
                strcmp(streams{k}.info.desc.synchronization.can_drop_samples, 'true')
                temp(k).time_stamps = droppedFramesCorrection(temp(k).time_stamps,temp(k).srate, opts.FrameRateAccuracy);
           else

                % identify breaks in the data
                diffs = diff(temp(k).time_stamps);
                breaks_at = diffs > max(opts.JitterBreakThresholdSeconds,opts.JitterBreakThresholdSamples*temp(k).sampling_interval);
                if any(breaks_at)
                    % turn the break mask into a cell array of [begin,end] index ranges
                    tmp = find(breaks_at)';
                    tmp = [tmp tmp+1]';
                    tmp = [1 tmp(:)' length(breaks_at)];
                    ranges = num2cell(reshape(tmp,2,[])',2);
                    if opts.Verbose
                        disp(['  found ' num2str(nnz(breaks_at)) ' data breaks in stream ' streams{k}.info.name '.']); end
                else
                    ranges = {[1,length(temp(k).time_stamps)]};
                end

                % process each segment separately
                segments = repmat(struct(),1,length(ranges));
                for r=1:length(ranges)
                    range = ranges{r};
                    segments(r).num_samples = range(2)-range(1)+1;
                    segments(r).index_range = range;
                    if segments(r).num_samples > 0
                        indices = segments(r).index_range(1):segments(r).index_range(2);
                        % regress out the jitter
                        mapping = temp(k).time_stamps(indices) / [ones(1,length(indices)); indices];
                        temp(k).time_stamps(indices) = mapping(1) + mapping(2) * indices;
                    end
                    % calculate some other meta-data about the segments
                    segments(r).t_begin = temp(k).time_stamps(range(1));
                    segments(r).t_end = temp(k).time_stamps(range(2));
                    segments(r).duration = segments(r).t_end - segments(r).t_begin;
                    segments(r).effective_srate = (segments(r).num_samples - 1) / segments(r).duration;
                end

                % calculate the weighted mean sampling rate over all segments
                temp(k).effective_rate = sum(bsxfun(@times,[segments.effective_srate],[segments.num_samples]/sum([segments.num_samples])));

                % transfer the information into the output structs
                streams{k}.info.effective_srate = temp(k).effective_rate;
                streams{k}.segments = segments;
            end
        end
    end
else
    % calculate effective sampling rate
    for k=1:length(temp)
        temp(k).effective_srate = (length(temp(k).time_stamps) - 1) / (temp(k).time_stamps(end) - temp(k).time_stamps(1)); end
end

% copy the information into the output
for k=1:length(temp)
    offset_mean = 0;
    if opts.CorrectStreamLags && ...
        isfield(streams{k}.info.desc, 'synchronization') && ...
        isfield(streams{k}.info.desc.synchronization, 'offset_mean')
                offset_mean = str2num(streams{k}.info.desc.synchronization.offset_mean);
    end
    streams{k}.time_series = temp(k).time_series;
    streams{k}.time_stamps = temp(k).time_stamps - offset_mean;
end

% =========================================
% === peform vendor specific operations ===
% =========================================

if ~any(strcmp('all',opts.DisableVendorSpecifics))

    % BrainVision RDA
    targetName = 'BrainVision RDA';
    if ~any(strcmp(opts.DisableVendorSpecifics,targetName))

        % find a target EEG stream...
        for k=1:length(streams)

            if strcmp(streams{k}.info.name,targetName) % Is a BrainVision RDA stream?
                mkChan = [];
                for iChan = 1:length( streams{ k }.info.desc.channels.channel ) % Find marker index channel (any channel, not necessary last)
                    if strcmp( streams{ k }.info.desc.channels.channel{ iChan }.label, 'MkIdx' ) && strcmp( streams{ k }.info.desc.channels.channel{ iChan }.type, 'Marker' ) && strcmp( streams{ k }.info.desc.channels.channel{ iChan }.unit, 'Counts (decimal)' )
                        mkChan = iChan;
                        break % Only one marker channel expected
                    end
                end
                if ~isempty( mkChan ) % Has a marker index channel?
                    for m = 1:length( streams ) % find a corresponding indexed marker stream...
                        if strcmp( streams{ m }.info.name, [ targetName ' Markers' ] ) && strcmp( streams{ m }.info.hostname, streams{ k }.info.hostname ) && strncmp( streams{ m }.info.source_id, streams{ k }.info.source_id, length( streams{ k }.info.source_id ) )
                            if opts.Verbose
                                disp( [ '  performing ', targetName, ' specific tasks for stream ', num2str( k ), '...' ] );
                            end
                            streams = ProcessBVRDAindexedMarkers( streams, k, m, mkChan );
                        end
                    end

					% Remove marker index channel
					streams{ k }.time_series( mkChan, : ) = [];
					streams{ k }.info.desc.channels.channel( mkChan ) = [];

					% Decrement channel count by 1
					streams{ k }.info.channel_count = num2str( str2num( streams{ k }.info.channel_count ) - 1 );

                end

            end

        end

    end

end

end

% ========================
% === helper functions ===
% ========================

% read a variable-length integer
function num = read_varlen_int(f)
try
    switch fread(f,1,'*uint8')
        case 1
            num = fread(f,1,'*uint8');
        case 4
            num = fread(f,1,'*uint32');
        case 8
            num = fread(f,1,'*uint64');
        otherwise
            error('Invalid variable-length integer encountered.');
    end
catch %#ok<*CTCH>
    num = 0;
end
end

% close the file and delete temporary data
function close_file(f,filename)
fclose(f);
if strfind(filename,'_temp_uncompressed.xdf')
    delete(filename); end
end

% parse a simplified (attribute-free) subset of XML into a MATLAB struct
function result = parse_xml_struct(str)
import org.xml.sax.InputSource
import javax.xml.parsers.*
import java.io.*
tmp = InputSource();
tmp.setCharacterStream(StringReader(str));
result = parseChildNodes(xmlread(tmp));

% this is part of xml2struct (slightly simplified)
    function [children,ptext] = parseChildNodes(theNode)
        % Recurse over node children.
        children = struct;
        ptext = [];
        if theNode.hasChildNodes
            childNodes = theNode.getChildNodes;
            numChildNodes = childNodes.getLength;
            for count = 1:numChildNodes
                theChild = childNodes.item(count-1);
                [text,name,childs] = getNodeData(theChild);
                if (~strcmp(name,'#text') && ~strcmp(name,'#comment'))
                    if (isfield(children,name))
                        if (~iscell(children.(name)))
                            children.(name) = {children.(name)}; end
                        index = length(children.(name))+1;
                        children.(name){index} = childs;
                        if(~isempty(text))
                            children.(name){index} = text; end
                    else
                        children.(name) = childs;
                        if(~isempty(text))
                            children.(name) = text; end
                    end
                elseif (strcmp(name,'#text'))
                    if (~isempty(regexprep(text,'[\s]*','')))
                        if (isempty(ptext))
                            ptext = text;
                        else
                            ptext = [ptext text];
                        end
                    end
                end
            end
        end
    end

% this is part of xml2struct (slightly simplified)
    function [text,name,childs] = getNodeData(theNode)
        % Create structure of node info.
        name = char(theNode.getNodeName);
        if ~isvarname(name)
            name = regexprep(name,'[-]','_dash_');
            name = regexprep(name,'[:]','_colon_');
            name = regexprep(name,'[.]','_dot_');
        end
        [childs,text] = parseChildNodes(theNode);
        if (isempty(fieldnames(childs)))
            try
                text = char(theNode.getData);
            catch
            end
        end
    end
end

function x = robust_fit(A,y,rho,iters)
% Perform a robust linear regression using the Huber loss function.
% x = robust_fit(A,y,rho,iters)
%
% Input:
%   A : design matrix
%   y : target variable
%   rho : augmented Lagrangian variable (default: 1)
%   iters : number of iterations to perform (default: 1000)
%
% Output:
%   x : solution for x
%
% Notes:
%   solves the following problem via ADMM for x:
%     minimize 1/2*sum(huber(A*x - y))
%
% Based on the ADMM Matlab codes also found at:
%   http://www.stanford.edu/~boyd/papers/distr_opt_stat_learning_admm.html
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2013-03-04

if ~exist('rho','var')
    rho = 1; end
if ~exist('iters','var')
    iters = 1000; end
Aty = A'*y;
L = sparse(chol(A'*A,'lower')); U = L';
z = zeros(size(y)); u = z;
for k = 1:iters
    x = U \ (L \ (Aty + A'*(z - u)));
    d = A*x - y + u;
    z = rho/(1+rho)*d + 1/(1+rho)*max(0,(1-(1+1/rho)./abs(d))).*d;
    u = d - z;
end
end

function res = hlp_superimposedata(varargin)
% Merge multiple partially populated data structures into one fully populated one.
% Result = hlp_superimposedata(Data1, Data2, Data3, ...)
%
% The function is applicable when you have cell arrays or structs/struct arrays with non-overlapping
% patterns of non-empty entries, where all entries should be merged into a single data structure
% which retains their original positions. If entries exist in multiple data structures at the same
% location, entries of later items will be ignored (i.e. earlier data structures take precedence).
%
% In:
%   DataK : a data structure that should be super-imposed with the others to form a single data
%           structure
%
% Out:
%   Result : the resulting data structure
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2011-08-19

% first, compactify the data by removing the empty items
compact = varargin(~cellfun('isempty',varargin));
% start with the last data structure, then merge the remaining data structures into it (in reverse
% order as this avoids having to grow arrays incrementally in typical cases)
res = compact{end};
for k=length(compact)-1:-1:1
    res = merge(res,compact{k}); end
end

function A = merge(A,B)
% merge data structures A and B
if iscell(A) && iscell(B)
    % make sure that both have the same number of dimensions
    if ndims(A) > ndims(B)
        B = grow_cell(B,size(A));
    elseif ndims(A) < ndims(B)
        A = grow_cell(A,size(B));
    end
    % make sure that both have the same size
    if all(size(B)==size(A))
        % we're fine
    elseif all(size(B)>=size(A))
        % A is a minor of B: grow A
        A = grow_cell(A,size(B));
    elseif all(size(A)>=size(B))
        % B is a minor of A: grow B
        B = grow_cell(B,size(A));
    else
        % A and B have mixed sizes... grow both as necessary
        M = max(size(A),size(B));
        A = grow_cell(A,M);
        B = grow_cell(B,M);
    end
    % find all non-empty elements in B
    idx = find(~cellfun(@(x)isequal(x,[]),B));
    if ~isempty(idx)
        % check if any of these is occupied in A
        clean = cellfun('isempty',A(idx));
        if ~all(clean)
            % merge all conflicting items recursively
            conflicts = idx(~clean);
            for k=conflicts(:)'
                A{k} = merge(A{k},B{k}); end
            % and transfer the rest
            if any(clean)
                A(idx(clean)) = B(idx(clean)); end
        else
            % transfer all to A
            A(idx) = B(idx);
        end
    end
elseif isstruct(A) && isstruct(B)
    % first make sure that both have the same fields
    fnA = fieldnames(A);
    fnB = fieldnames(B);
    if isequal(fnA,fnB)
        % we're fine
    elseif isequal(sort(fnA),sort(fnB))
        % order doesn't match -- impose A's order on B
        B = orderfields(B,fnA);
    elseif isempty(setdiff(fnA,fnB))
        % B has a superset of A's fields: add the remaining fields to A, and order them according to B
        remaining = setdiff(fnB,fnA);
        for fn = remaining'
            A(1).(fn{1}) = []; end
        A = orderfields(A,fnB);
    elseif isempty(setdiff(fnB,fnA))
        % A has a superset of B's fields: add the remaining fields to B, and order them according to A
        remaining = setdiff(fnA,fnB);
        for fn = remaining'
            B(1).(fn{1}) = []; end
        B = orderfields(B,fnA);
    else
        % A and B have incommensurable fields; add B's fields to A's fields, add A's fields to B's
        % and order according to A's fields
        remainingB = setdiff(fnB,fnA);
        for fn = remainingB'
            A(1).(fn{1}) = []; end
        remainingA = setdiff(fnA,fnB);
        for fn = remainingA'
            B(1).(fn{1}) = []; end
        B = orderfields(B,A);
    end
    % that being established, convert them to cell arrays, merge their cell arrays, and convert back to structs
    merged = merge(struct2cell(A),struct2cell(B));
    A = cell2struct(merged,fieldnames(A),1);
elseif isstruct(A) && ~isstruct(B)
    if ~isempty(B)
        error('One of the sub-items is a struct, and the other one is of a non-struct type.');
    else
        % we retain A
    end
elseif isstruct(B) && ~isstruct(A)
    if ~isempty(A)
        error('One of the sub-items is a struct, and the other one is of a non-struct type.');
    else
        % we retain B
        A = B;
    end
elseif iscell(A) && ~iscell(B)
    if ~isempty(B)
        error('One of the sub-items is a cell array, and the other one is of a non-cell type.');
    else
        % we retain A
    end
elseif iscell(B) && ~iscell(A)
    if ~isempty(A)
        error('One of the sub-items is a cell array, and the other one is of a non-cell type.');
    else
        % we retain B
        A = B;
    end
elseif isempty(A) && ~isempty(B)
    % we retain B
    A = B;
elseif isempty(B) && ~isempty(A)
    % we retain A
elseif ~isequal(A,B)
    % we retain A and warn about dropping B
    disp('Two non-empty (and non-identical) sub-elements occupied the same index; one was dropped. This warning will only be displayed once.');
end
end

function C = grow_cell(C,idx)
% grow a cell array to accomodate a particular index
% (assuming that this index is not contained in the cell array yet)
tmp = sprintf('%i,',idx);
eval(['C{' tmp(1:end-1) '} = [];']);
end

%function written by Matthew Grivich to handle case where frame rate is
%consistent but with dropped events, like a video camera.
function frameTimesModeled = droppedFramesCorrection(frameTimes, nominalFrameRate, frameRateAccuracy)

  %  figure
  %  plot(frameTimes(1:end-1), frameTimes(2:end)-frameTimes(1:end-1));
  %  xlabel('Frame Time (s)');
  %  ylabel('Frame Interval (s)');

    nFrames = length(frameTimes); %nFrames shown, does not included dropped.

    %calculates the maximum conceivable number of dropped frames, given the
    %nominal frame rate and the expected frame rate accuracy.
    maxDropped = round((frameTimes(end)-frameTimes(1))*nominalFrameRate*(1+frameRateAccuracy) - nFrames);
    stds = zeros(0, 1); %initialize array of zero length
    for iteration = 1:maxDropped
        interval = (frameTimes(end)-frameTimes(1))/(nFrames-1+iteration-1);
   %     fprintf('frequency: %1.20f\n',1/interval);
        frameNumbers = zeros(1,length(frameTimes));

        for i=1:length(frameNumbers)

            frameNumbers(i) =  round((frameTimes(i)-frameTimes(1))/interval)+1;

        end
        %remove zero frame intervals
        for i=length(frameNumbers)-1:-1:1
            if(frameNumbers(i) == frameNumbers(i+1))
                frameNumbers(i) = frameNumbers(i) -1;
            end
        end

        pf = polyfit(frameNumbers, frameTimes,1);
        %text(0,.0004, sprintf('interval: %1.20f\n', interval));
    %    fprintf('interval after fit: %1.20f\n',pf(1));

        frameTimesModeled = polyval(pf,frameNumbers);
  %     plot(stds)
      %  plot(frameTimesModeled, frameTimesModeled - frameTimes);
  %      text(0,.02, sprintf('dropped: %d\n', iteration-1));
  %      xlabel('Frame Time (s)')
  %      ylabel('Frame Time Modeled - Frame Time (s)');
  %     pause(0.001);

        stds(iteration) = std(frameTimesModeled-frameTimes);
        %if (mean(stds) - min(stds))/std(stds) > 10
        %    break;
        %end

    end

 %   figure
 %   plot(stds);
    dropped = find(stds==min(stds)) - 1;

    interval = (frameTimes(end)-frameTimes(1))/(nFrames-1+dropped);
   % fprintf('interval: %1.20f\n',interval);
    frameNumbers = zeros(1,length(frameTimes));

    %Find closest frame.
    for i=1:length(frameNumbers)
        frameNumbers(i) =  round((frameTimes(i)-frameTimes(1))/interval)+1;
    end
    %remove zero frame intervals
    for i=length(frameNumbers)-1:-1:1
        if(frameNumbers(i) >= frameNumbers(i+1))
            frameNumbers(i) = frameNumbers(i+1) -1;
        end
    end

    pf = polyfit(frameNumbers, frameTimes,1);
    %text(0,.0004, sprintf('interval: %1.20f\n', interval));
%    fprintf('interval after fit: %1.20f\n',pf(1));

    frameTimesModeled = polyval(pf,frameNumbers);
%figure
%        plot(frameTimesModeled, smooth(frameTimesModeled - frameTimes,21));
%        xlabel('Frame Time (s)')
%        ylabel('Frame Time Modeled - Frame Time (s)');
 %      pause(0.5);

%    fprintf('Interval: %1.20f\n',pf(1));
%    fprintf('Dropped Frames: %d\n', dropped);

   % figure
   % plot(frameTimes(1:end-1), pf(1)*(frameNumbers(2:end)-frameNumbers(1:end-1)));
   % xlabel('Frame Time (s)');
   % ylabel('Frame Interval (s)');

end

function streams = ProcessBVRDAindexedMarkers( streams, dataStream, mkStream, mkChan )

clearMarkers = [];

for iMrk = 1:length( streams{ mkStream }.time_series )

    % Decode marker
    MrkInfo = regexp( streams{ mkStream }.time_series{ iMrk }, 'mk(?<idx>\d+)=(?<str>.*)', 'names' );

    if ~isempty( MrkInfo.idx )

        % Find corresponding sample in marker index channel
        lat = find( streams{ dataStream }.time_series( mkChan, : ) == str2double( MrkInfo.idx ) );
        offset = streams{ mkStream }.time_stamps( iMrk ) - streams{ dataStream }.time_stamps( lat );

        % Is the index unique (overflow)?
        if length( lat ) > 1
            [ minOffset, minOffsetIdx ] = min( abs( offset ) ); %#ok<ASGLU>
            lat = lat( minOffsetIdx );
            offset = offset( minOffsetIdx );
        end

        % Sanity check
        if offset > 10
            warning( 'Time stamp difference between indexed marker %s (%.3f s) and corresponding sample in marker channel (%.3f s) exceeding threshold.\n', MrkInfo.idx, streams{ mkStream }.time_stamps( iMrk ), streams{ dataStream }.time_stamps( lat ) )
        end

        % Copy time stamp and rewrite marker
        if ~isempty( lat )
            streams{ mkStream }.time_stamps( iMrk ) = streams{ dataStream }.time_stamps( lat );
            streams{ mkStream }.time_series{ iMrk } = MrkInfo.str;
        else
            warning( 'No corresponding sample found in marker channel for indexed marker %s. Removing...', MrkInfo.idx )
            clearMarkers = [ clearMarkers iMrk ]; %#ok<AGROW>
        end

    end

end

% Remove markers without corresponding marker channel sample
streams{ mkStream }.time_stamps( clearMarkers ) = [];
streams{ mkStream }.time_series( clearMarkers ) = [];

end


% OtSkimCubeDelete Delete a matrix from a skim cube.
%
% Usage:
%    OtSkimCubeDelete(hscube, pmturi)
% where:
%    hscube skim cube handle as created by OtMatrixCubeNew()
%    pmturi is a PMTURI struct as created by OtPmturi()
%

% OtMatlab: Matlab interface to the Omnitrans transport planning software

% Copyright (c) 2008, VORtech Computing
% Copyright (c) 2010, Omnitrans International

% All rights reserved.

% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:

%     * Redistributions of source code must retain the above copyright
%       notice, this list of conditions and the following disclaimer.
%     * Redistributions in binary form must reproduce the above copyright
%       notice, this list of conditions and the following disclaimer in
%       the documentation and/or other materials provided with the distribution
%     * Neither the name of VORtech Computing and Omnitrans International nor the names
%       of its contributors may be used to endorse or promote products derived
%       from this software without specific prior written permission.

% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.

% 20006-04-24 Created (Johan Meijdam, VORtech)

function iret = OtSkimCubeDelete(hscube, pmturi)
	iret = Ot('skimcube.delete', hscube, pmturi);
end


function get_sofea()
    disp( [' The restrictive policy of the mathworks File Exchange prevents me from posting the actual source'])
    disp( [' Please visit:'])
    disp( ['     hogwarts.ucsd.edu/~pkrysl/sofea']);
    disp( [' Petr Krysl'])
end


function [trl, event] = ft_trialfun_artinis(cfg)

% FT_TRIALFUN_ARTINIS is adjoining the result of ft_trialfun_general and
% those events found by FT_TRIALFUN_GENERAL.
%
% The trialdef structure can contain the following specifications
%   cfg.trialdef.eventtype  = 'string'
%   cfg.trialdef.eventvalue = number, string or list with numbers or strings
%   cfg.trialdef.oxyproj    = 'string', indicating an oxyproj-file, in
%                             which information about the events for this
%                             oxy3-file are stored
%   cfg.trialdef.prestim    = latency in seconds (optional)
%   cfg.trialdef.poststim   = latency in seconds (optional)
%
% If you want to read all data from a continuous file in segments, you can specify
%    cfg.trialdef.triallength = duration in seconds (can be Inf)
%    cfg.trialdef.ntrials     = number of trials
%
% If you specify
%   cfg.trialdef.eventtype  = '?'
% a list with the events in your datafile will be displayed on screen.
%
% If you specify
%   cfg.trialdef.eventtype = 'gui'
% a graphical user interface will allow you to select events of interest.
%
% See also FT_TRIALFUN_GENERAL, FT_DEFINETRIAL, FT_PREPROCESSING

% You are using the FieldTrip NIRS toolbox developed and maintained by
% Artinis Medical Systems (http://www.artinis.com). For more information
% on FieldTrip, see http://www.fieldtriptoolbox.org
%
% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
% International License. To view a copy of this license, visit
% http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to
% Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
%
% Creative Commons Attribution-ShareAlike 4.0 International License:
% -----------------------------------
% You are free to:
%
%     Share - copy and redistribute the material in any medium or format
%     Adapt - remix, transform, and build upon the material
%     for any purpose, even commercially.
%
%     The licensor cannot revoke these freedoms as long as you follow the
%     license terms.
%
% Under the following terms:
%
%     Attribution - You must give appropriate credit, provide a link to
%                    the license, and indicate if changes were made. You
%                    may do so in any reasonable manner, but not in any way
%                    that suggests the licensor endorses you or your use.
%
%     ShareAlike - If you remix, transform, or build upon the material,
%                   you must distribute your contributions under the same
%                   license as the original.
%
%     No additional restrictions - You may not apply legal terms or
%                                   technological measures that legally
%                                   restrict others from doing anything the
%                                   license permits.
%
% -----------------------------------
%
% This toolbox is not to be used for medical or clinical purposes.
%
% Copyright (c) 2016 by Artinis Medical Systems.
% Contact: askforinfo@artinis.com
%
% Main programmer:
% Jörn M. Horschig, Artinis Medical Systems BV, http://www.artinis.com
% $Id$

if ~isfield(cfg.trialdef, 'oxyproj') || ~strcmp(cfg.trialdef.oxyproj(end-7:end), '.oxyproj') || ~exist(cfg.trialdef.oxyproj, 'file')
  error('You need to provide an existing oxyproj-file as input to use this function (cfg.trialdef.oxyproj)')
end

cfg.eventformat = ft_getopt(cfg, 'eventformat');
cfg.headerformat = ft_getopt(cfg, 'headerformat');
cfg.dataformat = ft_getopt(cfg, 'dataformat');

fprintf('reading the events from ''%s''\n', cfg.headerfile);
event = ft_read_event(cfg.headerfile, 'headerformat', cfg.headerformat, 'eventformat', cfg.eventformat, 'dataformat', cfg.dataformat);

fprintf('reading the events from ''%s''\n', cfg.trialdef.oxyproj);
measurement = parseoxyproj(cfg.trialdef.oxyproj, cfg.datafile);

if numel(measurement)>1
  warning('More than one measurement found in the oxyproj file - removing all but the first one\n');
  measurement(2:end) = [];
end

% read the header for obtaining the sample rate
hdr = ft_read_header(cfg.headerfile, 'headerformat', cfg.headerformat);

% create the trl and event-structure with the new information
trl = [];
for i=1:numel(measurement.events.names)
  add2trl = strcmp(cfg.trialdef.eventtype, 'oxyproj') && strcmp(cfg.trialdef.eventvalue, measurement.events.names{i});
  for e=1:numel(measurement.events.onsets{i})
    % add event
    event(end+1).type      = 'oxyproj';
    event(end).sample    = measurement.events.onsets{i}(e);
    event(end).value     = measurement.events.names{i};
    event(end).offset    = 0;
    event(end).duration  = measurement.events.durations{i}(e);
    if add2trl
      trloff = round(-cfg.trialdef.prestim * hdr.Fs);
      trlbeg = event(end).sample+trloff;
      % this will not work if prestim was not defined, the code will then crash
      trldur = round((cfg.trialdef.poststim+cfg.trialdef.prestim)*hdr.Fs) - 1;
      trlend = trlbeg + trldur;
      trl(end+1, :) = [trlbeg trlend trloff];
    end
  end
end

% create trials
cfg.event = event;
[trl, event] = ft_trialfun_general(cfg);


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

%%
clear; close all; clc;

%% Syntax
% |[V]=selectBox(varargin);|

%% 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 struc2xls(filename,s,varargin)
%STRUC2XLS Saves a data structure to an Excel file
% STRUC2XLS (FILE,S) writes the data structure S to an Excel workbook named
% FILE in the current directory. FILE can also be entered using path
% notation to specify a different output directory. If the file does not
% exist, it will be created in the current directory. If it does exists,
% **it will be overwritten or modified without warning!** If the output
% file exists, it cannot be open in Excel. The name of the structure can
% include indexing values to export only a subset of the structure.
%
% Optional output parameters: (...,'Sheet',SHEETNAME) specifies the name of
% the worksheet. SHEETNAME mustbe a character string. If it does not exist
% in the Excel workbook it will be added. Default is 'Sheet1'.
%
% (...,'Col',C) and (...,'Row',R) specifiy the starting column and row of
% in Excel, respectively. R must be a positive integer, and C must be a
% capital letter index from 'A' to 'IV' using the Excel's column indexing
% format. See Excel if clarification is needed. Default is column A, row 1
% (cell A1 in Excel).
%
% (...,'Orientataion','O') specifies orientation of output. O is entered as
% 'V' for vertical or 'H' for horizontal. When V is entered, field names
% appear as column headers beginning in row R beginning at column C, and
% the contents of the structure are written in the rows below them. When H
% is entered, field names appear in column C beginning at row R and the
% contents of the structure are written in the columns to the right.
% Default is V.
%
% EXAMPLES:
%   Create a sample structure 's' with two fields:
%	s=%	struct('Name',{'Jon','Jonathan','Johnny'},'Score',[85,100,75]);
%
%   Basic output to file 'demo.xls' in current directory:
%	struc2xls('demo',s)
%
%   Output beginning in cell D4 of Excel file:
%	struc2xls('demo',s,'Col','D','Row',4)
%
%   Output to worksheet 'Students':
%	struc2xls('demo',s,'Sheet','Students')
%
%   Output horizontal data (header row, data in columns):
%   struc2xls('demo',s,'Orientation','H')
%
%   Output to alternate directory
%   structxls('C:\Users\Johnny\Desktop\demo',s)
%
%
%   Modified from struct2xls.m by Francisco de Castro:
%   http://www.mathworks.com/matlabcentral/fileexchange/18530
%
%   Jeff Evans Dec-22-2008

%Defaults
sheet= 'Sheet1';
col= 'A';
fstrow= 1;
orient= 'V';

%Optional arguments
if ~isempty(varargin)
    for j= 1:2:length(varargin)
        switch varargin{j}
            case 'Sheet'
                sheet= varargin{j+1};
            case 'Col'
                col= varargin{j+1};
            case 'Row'
                fstrow= varargin{j+1};
            case 'Orientation'
                orient= varargin{j+1};
            otherwise
                error ('Unrecognized argument name');
        end
    end
end

%Output range
rangeOut=strcat(col,num2str(fstrow));

%Transform to cell
c= struct2cell(s);

%Field names
names= fieldnames(s);

%Concatenate field names and data. Create output dataset in vertical or
%horizontal orientation.
if strmatch(orient,'V')==1
    out=[names';c'];
elseif strmatch(orient,'H')==1
    out=[names, c];
end
%write
xlswrite(filename,out,sheet,rangeOut);


function Snirf2Nirs(rootdir, options)
if ~exist('rootdir','var')
    rootdir = pwd;
end
if ~exist('options','var')
    options = 'sort';
end
rootdir = filesepStandard(rootdir);
files = DataFilesClass(rootdir, 'snirf');
files = files.files;
for ii = 1:length(files)
    if files(ii).IsDir()
        continue
    end
    fname = filesepStandard([files(ii).rootdir, files(ii).name]);
    s = SnirfClass(fname);
    n = NirsClass(s);
    if optionExists(options, 'sort')
        n.SortData();
    end
    [pname, fname] = fileparts(fname);
    fnameNew = [filesepStandard(pname), fname, '.nirs'];
    fprintf('Converting %s to %s\n', fname, fnameNew)
    n.Save(fnameNew);
end


addpath matlab;
vl_setupnn;


classdef NoOptimizationVariablesValidator < AbstractLaunchVehicleDataValidator
    %NoOptimizationVariablesValidator Summary of this class goes here
    %   Detailed explanation goes here

    properties
        lvdData LvdData
    end

    methods
        function obj = NoOptimizationVariablesValidator(lvdData)
            obj.lvdData = lvdData;
        end

        function [errors, warnings] = validate(obj)
            errors = LaunchVehicleDataValidationError.empty(0,1);
            warnings = LaunchVehicleDataValidationWarning.empty(0,1);

            varSet = obj.lvdData.optimizer.vars;
            numVars = length(varSet.getTotalScaledXVector());
            if(numVars == 0)
                str = sprintf('No optimization variables enabled on script.');
                warnings(end+1) = LaunchVehicleDataValidationWarning(str);
            end
        end
    end
end


% Yagiz Aksoy
% 2017

tbloc = fileparts(mfilename('fullpath'));
addpath(fullfile(tbloc, 'affinity'));
addpath(fullfile(tbloc, 'trimming'));
addpath(fullfile(tbloc, 'common'));
clear tbloc;


function sessions = selectSessions(studyDir,nSessions);
%
% sessions = selectSessions(studyDir,[nSessions]);
%
% Select multiple sessions. studyDir is the path
% to the "study", i.e., the path where most sessions
% are kept. Defaults to the parent of pwd. Also provides
% the option to manually select different sessions, if
% they're located all over the place, using uigetmanydirs.
%
% Returns a cell containing full path to each session.
%
% I wrote this on top of uigetmanydirs b/c I found that
% interface to be pretty slow and annoying (the default
% uigetdir for windows is always low and cut-off on my
% machine), plus I usually keep my session folders in one
% place anyway.
%
% nSessions: optional argument specifying # of
% sessions to select. If omitted or 0, takes
% all selected. Otherwise, takes first nSessions.
%
% ras 03/05
if notDefined('studyDir')
    studyDir = fileparts(pwd);
end

if notDefined('nSessions')
    nSessions = 0;
end

sessions = [];

% get names of directories
w = dir(studyDir);
names = {w.name};
dirCheck = [w.isdir];
names = names(dirCheck==1); % only dirs
names = setdiff(names,{'.' '..'});

% first option will be to manually locate sesions
% outside the guessed study dir
names = [{'(Find Other Session)'} names];

[whichSessions, ok] = listdlg('PromptString','Use coords from which sessions?',...
                        'ListSize',[400 600],...
                        'ListString',names,...
                        'InitialValue',1,...
                        'OKString','OK');
if ~ok  return;  end

% if subject requested to find other session, allow to
% add that to list:
if ismember(whichSessions,1)
    if nSessions==1
        while 1
            sess = uigetdir(pwd, 'Select a session to import');
            if exist(fullfile(sess, 'Gray', 'coords.mat'), 'file')
                sessions = {sess};
                break;
            else
                h = warndlg(['Selected session doesn''t have a ' ...
                           'Gray/coords.mat file. Please choose again.']);
                set(h, 'CloseRequestFcn', 'uiresume; closereq; ');
                uiwait
            end
        end
    else
        sessions = uigetmanydirs('Gray/coords.mat');
    end
end

% also get any selected from the listdlg, and add the
% parent dir to get a full path:
sessInds = whichSessions(whichSessions>1);
tmpSessions = names(sessInds);
for i = 1:length(tmpSessions)
    tmpSessions{i} = fullfile(studyDir,tmpSessions{i});
end
sessions = [sessions tmpSessions];

if nSessions > 0
    nSessions = min(length(sessions),nSessions);
    sessions = sessions(1:nSessions);
end

return


function [s, vw] = openGrayWindow
%
%   [s, vw] = openGrayWindow
%
% Calls openRawVolumeWindow to set up the VOLUME data structure,
% opens and initializes the volume window.  Then calls switch2Gray.
%
% djh, 2/99
% Junjie 2004.02.14 let function return s

mrGlobals

s = openRawVolumeWindow;
VOLUME{s} = switch2Gray(VOLUME{s});

selectView(VOLUME{s});

% If user requested a view output, give it to them:
if nargout > 1,	vw = VOLUME{s}; end

return;


function obj = compile(obj, export_path, varargin)
    % export the symbolic expressions of the system dynamics matrices and
    % vectors and compile as MEX files.
    %
    % Parameters:
    %  export_path: the path to export the file @type char
    %  varargin: variable input parameters @type varargin
    %   StackVariable: whether to stack variables into one @type logical
    %   File: the (full) file name of exported file @type char
    %   ForceExport: force the export @type logical
    %   BuildMex: flag whether to MEX the exported file @type logical
    %   Namespace: the namespace of the function @type char

    input_categories = {'Control','ConstraintWrench','External'};
    for k=1:3
        category = input_categories{k};
        % export the input map
        gmap_funcs = fieldnames(obj.Gmap.(category));
        if ~isempty(gmap_funcs)
            for i=1:length(gmap_funcs)
                fun = gmap_funcs{i};
                if ~isempty(obj.Gmap.(category).(fun))
                    export(obj.Gmap.(category).(fun),export_path,varargin{:});
                end
            end
        end

        % export the input vector fields
        gvec_funcs = fieldnames(obj.Gvec.(category));
        if ~isempty(gvec_funcs)
            for i=1:length(gvec_funcs)
                fun = gvec_funcs{i};
                if ~isempty(obj.Gvec.(category).(fun))
                    export(obj.Gvec.(category).(fun),export_path,varargin{:});
                end
            end
        end
    end

end


function assign (varargin)
% assign - make a list of assignments (matlab 5 or higher)
%
%	ASSIGN('VAR1', VAL1, 'VAR2', VAL2, ...) makes the assignments
%	VAR1 = VAL1; VAR2 = VAL2; ... in the caller's workspace.
%
%	This is most useful when passing in an option list to a
%	function.  Thus in the function which starts:
%		function foo(x,y,varargin)
%		z = 0;
%		assign(varargin{:});
%	the variable z can be given a non-default value by calling the
%	function like so: foo(x,y,'z',4);

vars = {varargin{1:2:end}};
vals = {varargin{2:2:end}};

% use length(vals) not length(vars) in case of odd number of arguments
for i = 1:length(vals), % changed 1 to a 2
  assignin('caller', vars{i}, vals{i});
end


function [varargout] = rdivide(varargin)

% RDIVIDE compile the missing mex file on the fly

% Copyright (C) 2012, Donders Centre for Cognitive Neuroimaging, Nijmegen, NL
%
% 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$

% remember the original working directory
pwdir = pwd;

% determine the name and full path of this function
funname = mfilename('fullpath');
mexsrc  = [funname '.c'];
[mexdir, mexname] = fileparts(funname);

try
  % try to compile the mex file on the fly
  warning('trying to compile MEX file from %s', mexsrc);
  cd(mexdir);
  mex(mexsrc);
  cd(pwdir);
  success = true;

catch
  % compilation failed
  disp(lasterr);
  error('could not locate MEX file for %s', mexname);
  cd(pwdir);
  success = false;
end

if success
  % execute the mex file that was just created
  funname   = mfilename;
  funhandle = str2func(funname);
  [varargout{1:nargout}] = funhandle(varargin{:});
end


%Kinematics_String_Replace

r_P1_O_CHAR_X = strrep(r_P1_O_CHAR_X,IN,OUT);
v_P1_O_CHAR_X = strrep(v_P1_O_CHAR_X,IN,OUT);
a_P1_O_CHAR_X = strrep(a_P1_O_CHAR_X,IN,OUT);

r_P2_O_CHAR_X = strrep(r_P2_O_CHAR_X,IN,OUT);
v_P2_O_CHAR_X = strrep(v_P2_O_CHAR_X,IN,OUT);
a_P2_O_CHAR_X = strrep(a_P2_O_CHAR_X,IN,OUT);

r_P1_O_CHAR_Y = strrep(r_P1_O_CHAR_Y,IN,OUT);
v_P1_O_CHAR_Y = strrep(v_P1_O_CHAR_Y,IN,OUT);
a_P1_O_CHAR_Y = strrep(a_P1_O_CHAR_Y,IN,OUT);

r_P2_O_CHAR_Y = strrep(r_P2_O_CHAR_Y,IN,OUT);
v_P2_O_CHAR_Y = strrep(v_P2_O_CHAR_Y,IN,OUT);
a_P2_O_CHAR_Y = strrep(a_P2_O_CHAR_Y,IN,OUT);


% The COBRAToolbox: testLegalRxnFormula.m
%
% Purpose:
%     - testReactionEq tests the functionality of LegalRxnFormula in the
%     rBioNet extension
%
% Authors:
%     - Stefania Magnusdottir April 2017
%

global CBTDIR

% save the current path
currentDir = pwd;

% initialize the test
fileDir = fileparts(which('testReactionEq'));
cd(fileDir);

% load E. coli model
model = getDistributedModel('ecoli_core_model.mat');

% check validity of reaction formulas
formula = printRxnFormula(model, model.rxns(1));
result = LegalRxnFormula(formula, model.rxns(1));

% test output
assert(result)

% change the directory
cd(currentDir)


function [handleImageArray, nameImageArray] = get_all_image_objects(this, type)
% Returns cell of handles & names for all/selected image objects in MrSeries
%
%   Y = MrSeries()
%  [handleImageArray, nameImageArray] = Y.get_all_image_objects(inputs)
%
% This is a method of class MrSeries.
%
% IN
%
% OUT
%   handleImageArray    cell(nImages, 1) of handles to MrImages
%   nameImageArray      cell(nImages, 1) of names of properties of MrSeries
%                       which are MrImages
%
% EXAMPLE
%   get_all_image_objects
%
%   See also MrSeries

% Author:   Saskia Klein & Lars Kasper
% Created:  2014-07-09
% Copyright (C) 2014 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public Licence (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

%handleImageArray = findall(this, 'MrImage');

if nargin < 2
    type = 'all';
end

nameStatImageArray = {'mean'; 'sd'; 'snr'; 'coeffVar'; 'diffLastFirst'; 'diffOddEven'};
nameDataImageArray = {'data'};
nameAnatomy = {'anatomy'};

switch lower(type)
    case 'data'
        nameImageArray = nameDataImageArray;
    case {'stat', 'stats', 'statistical'}
        nameImageArray = nameStatImageArray;
    case 'anatomy'
        nameImageArray = nameAnatomy;
    case 'all'
        nameImageArray = [nameDataImageArray; nameStatImageArray; ...
            nameAnatomy];
end

nImages = numel(nameImageArray);

for iImage = 1:nImages
    handleImageArray{iImage} = this.(nameImageArray{iImage});
end


function [whichScans,ok] = er_selectScans(vw, title)
% [whichScans, ok] = er_selectScans(vw, title);
%
% Like selectScans, provides a prompt to select a subset
% of scans for the current view/data Type, returning an
% array of the scan numbers selected.
%
% I like this a litte better than select scans, b/c it
% gives the annotation for each scan, and makes multi select
% easier.
%
%
% 04/05/04 ras.
global dataTYPES;

if ~exist('title','var')    title = 'Choose scans:';    end

whichSeries = vw.curDataType;
nscans = viewGet(vw, 'numScans');
scanList    = cell(1, nscans);
for i = 1:nscans
    scanList{i} = dataTYPES(whichSeries).scanParams(i).annotation;
    if isempty(scanList{i}), scanList{i} = sprintf('Scan%i',i); end
end

[whichScans, ok] = listdlg('PromptString',title,...
    'ListSize',[300 400],...
    'ListString',scanList,'InitialValue',1 ,'OKString','OK');

% Maybe we should check the return and announce of there is a problem?

return


function [x,fs,txt] = sapisynth(t,m)
%SAPISYNTH  synthesize text string or matrix [X,FS,TXT]=(T,M)
%
%  Usage:         sapisynth('Hello world');          % Speak text
%                 sapisynth([1 2+3i; -1i 4],'j');    % speak a matrix using 'j' for sqrt(-1)
%          [x,fs]=sapisynth('Hello world','k11');    % save waveform at 11kHz
%                 sapisynth('Hello world','fc');     % use a female child voice if available
%
%  Inputs: t  is either a text string or a matrix
%          m  is a mode string containing one or more of the
%             following options (# denotes an integer):
%
%             'l'   x=a cell array containing a list of talkers
%             'l#'  specify talker # (in the range 1:nvoices)
%             'r#'  speaking rate -10(slow) to +10(fast) [0]
%             'k#'  target sample rate in kHz [22]
%             'o'   audio output [default if no output arguments]
%             'j'   use 'j' rather than 'i' for complex numbers
%             'm','f' 'c','t','a','s' = Male Female Child, Teen, Adult, Senior
%                       specify any combination in order of priority
%             'v'   autoscale volumne to a peak value of +-1
%             'v#'  set volume (0 to 100) [100]
%             'p#'  set pitch -10 to +10 [0]
%
% Outputs: x    is the output waveform unless the 'l' option is chosen in
%               which case x is a cell array with one row per available
%               voice containing {'Name' 'Gender' 'Age'} where
%               Gender={Male,Female} and Age={Unknown,Child,Teen,Adult,Senior}
%          fs   is the actual sample frequency
%         txt   gives the actual text sring sent to the synthesiser
%
% The input text string can contain embedded command which are described
% in full at http://msdn.microsoft.com/en-us/library/ms717077(v=vs.85).aspx
% and summarised here:
%
% '... <bookmark mark="xyz"/> ...'               insert a bookmark
% '... <context id="date_mdy"> 03/04/01 </context> ...' specify order of dates
% '... <emph> ... </emph> ...'                   emphasise
% '... <volume level="50"> ... </volume> ...'    change volume level to 50% of full
% '... <partofsp part="noun"> XXX </partofsp> ...'      specify part of speech of XXX: unkown, noun, verb modifier, function, interjection
% '... <pitch absmiddle="-5"> ... </pitch> ...'  change pitch to -5 [0 is default pitch]
% '... <pitch middle="5"> ... </pitch> ...'      add 5 onto the pitch
% '... <pron sym="h eh 1 l ow "/> ...'           insert phoneme string
% '... <rate absspeed="-5"> ... </rate> ...'     change rate to -5 [0 is default rate]
% '... <rate speed="5"> ... </rate> ...'         add 5 onto the rate
% '... <silence msec="500"/> ...'                insert 500 ms of silence
% '... <spell> ... </spell> ...'                 spell out the words
% '... <voice required="Gender=Female;Age!=Child"> ...' specify target voice attributes to be Female non-child
%                                                         Age={Child, Teen, Adult, Senior}, Gender={Male, Female}
%
% Acknowledgement: This function was originally based on tts.m written by Siyi Deng

% Bugs/Suggestions:
%  (1) Allow the speaking of structures and cells
%  (2) Allow a blocking call to sound output and/or a callback procedure and/or a status call
%  (3) Have pitch and/or volume change to emphasise the first entry in a matrix row.
%  (4) extract true frequency from output stream

%      Copyright (C) Mike Brookes 2011
%      Version: $Id: sapisynth.m,v 1.8 2011/02/02 08:13:00 dmb Exp $
%
%   VOICEBOX is a MATLAB toolbox for speech processing.
%   Home page: http://www.ee.ic.ac.uk/hp/staff/dmb/voicebox/voicebox.html
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   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 2 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 can obtain a copy of the GNU General Public License from
%   http://www.gnu.org/copyleft/gpl.html or by writing to
%   Free Software Foundation, Inc.,675 Mass Ave, Cambridge, MA 02139, USA.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
persistent vv vvi vvj

% Check that we are on a PC

if ~ispc, error('only works on a PC'); end

% decode the options

if nargin<2
    m='';
end
opts=zeros(52,3); % [exists+number specified, value]
lmode=length(m);
i=1;
while i<=lmode
    if i<lmode  % read a following integer if it exists
        [v,nv,e,ni]=sscanf(m(i+1:end),'%d',1);
    else
        nv=0;
        ni=1;
    end
    k=1+double(lower(m(i)))-'a'+26*(m(i)<'a');
    if k>=1 && k<=52
        opts(k,1)=1+nv;
        if nv
            opts(k,2)=v;
        end
        opts(k,3)=i;  % save position in mode string
    end
    i=i+ni;
end

S=actxserver('SAPI.SpVoice');
V=invoke(S,'GetVoices');  % get a list of voices from the registry
nv=V.Count;
if isempty(vv) || size(vvi,1)~=nv
    vv=cell(nv,3);
    vvi=zeros(nv,6);
    ages={'Senior' 'Adult' 'Teen' 'Child'};
    for i=1:nv
        VI=V.Item(i-1);
        vv{i,1}=VI.GetDescription;
        vv{i,2}=VI.GetAttribute('Gender');
        vvi(i,1)=MatchesAttributes(VI,'Gender=Male');
        vvi(i,2)=MatchesAttributes(VI,'Gender=Female');
        vv{i,3}='Unknown';
        for j=1:length(ages)
            if MatchesAttributes(VI,['Age=' ages{j}])
                vv{i,3}=ages{j};
                vvi(i,2+j)=1;
                break
            end
        end
    end
    vvj=vvi;
    % in the matrix below, the rows and columns are in the order Senior,Adult,Teen,Child.
    % Thus the first row gives the cost of selecting a voice with the wrong age when 'Senior'
    % was requested by the user. A voice of unkown age always scores 0 so entries with negative
    % values are preferred over 'unknown' while those with positive values are not.
    % Diagonal elements of the matrix are ignored (hence set to 0) since correct matches are
    % handled earlier with higher priority.
    vvj(:,3:6)=vvj(:,3:6)*[0 0 1 2; 1 0 2 3; 1 0 0 -1; 1 0 -1 0]'; % fuzzy voice attribute matching
end

% deal with the voice selection options

optv=opts([13 6 19 1 20 3],[3 1 2]);
if opts(12)   % if 'l' option specified - we need to get the voices
    if opts(12)>1
        S.Voice = V.Item(mod(opts(12,2)-1,nv));
    else
        x=vv;
        return
    end
elseif any(optv(:,2))
    optv(:,3)=(1:6)';
    optv=sortrows(optv(optv(:,2)>0,:));  % sort in order of occurrence in mode string
    no=size(optv,1);
    optp=zeros(nv,2*no+1);
    optp(:,end)=(1:nv)'; % lowest priority condition is original rank
    optp(:,1:no)=-vvi(:,optv(:,3));
    optp(:,no+1:2*no)=vvj(:,optv(:,3));
    optp=sortrows(optp);
    S.Voice = V.Item(optp(1,end)-1);
end

% deal with the 'r' option

if opts(18)>1  % 'r' option is specified with a number
    S.Rate=min(max(opts(18,2),-10),10);
end

% deal with the 'v' option

if opts(22)>1  % 'r' option is specified with a number
    S.Volume=min(max(opts(22,2),0),100);
end

% deal with the 'k' option

ff=[11025 12000 16000 22050 24000 32000 44100 48000]; % valid frequencies
if opts(11)>1  % 'k' option is specified with a number
    [v,jf]=min(abs(ff/1000-opts(11,2)));
else
    jf=4;  % default is 16kHz
end
fs=ff(jf);

M=actxserver('SAPI.SpMemoryStream');
M.Format.Type = sprintf('SAFT%dkHz16BitMono',fix(fs/1000));
S.AudioOutputStream = M;
if ischar(t)
    txt=t;
else
    txt='';
    if numel(t)
        sgns={' minus ', '', ' plus '};
        sz=size(t);
        w=permute(t,[2 1 3:numel(sz)]);
        sz(1:2)=sz(1)+sz(2)-sz(1:2); % Permute the first two dimensions for reading
        szp=cumprod(sz);
        imch='i'+(opts(10)>0);
        vsep='';
        for i=1:numel(w)
            wr=real(w(i));
            wi=imag(w(i));
            switch((wr~=0)+2*(wi~=0))+4*(abs(wi)==1)
                case {0,1}
                    txt=[txt sprintf('%s%g',vsep,wr)];
                case 2
                    txt=[txt sprintf('%s%g%c,',vsep,wi,imch)];
                case 3
                    txt=[txt sprintf('%s%g%s%g%c,',vsep,wr,sgns{2+sign(wi)},abs(wi),imch)];
                case 6
                    if wi>0
                        txt=[txt vsep imch ','];
                    else
                        txt=[txt vsep 'minus ' imch ','];
                    end
                case 7
                    txt=[txt sprintf('%s%g%s%c,',vsep,wr,sgns{2+sign(wi)},imch)];
            end
            % could use a <silence msec="???"/> command here
            vsep=[repmat('; ',1,find([0 mod(i,szp)]==0,1,'last')-1) ' '];
        end
    end
end

% deal with the 'p' option

if opts(16)>1  % 'r' option is specified with a number
    txt=[sprintf('<pitch absmiddle="%d"> ',min(max(opts(16,2),-10),10)) txt];
end

invoke(S,'Speak',txt);
x = mod(32768+reshape(double(invoke(M,'GetData')),2,[])'*[1; 256],65536)/32768-1;
delete(M);      % delete output stream
delete(S);      % delete all interfaces

if opts(22)==1 % 'v' option with no argument
    x=x*(1/max(abs(x))); % autoscale
end
if opts(15)>0 || ~nargout % 'o' option for audio output
    sound(x,fs);
end


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

%%
clear; close all; clc;

%% Syntax
% |[pathNames]=getSubPaths(pathName);|

%% 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/>.