function varargout = daqwaterfall(varargin)
% daqwaterfall
%
%  Displays a waterfall plot with data streamed in from the Data
%  Acquisition Toolbox.  This demo was created using Guide in MATLAB 6.5.
%  This demo uses a lot of the code from demoai_fft.
%
%  The input source is hardcoded to channel one of the system soundcard.
%  You should be able to change this if necessary by modifying the
%  parameter in the I N P U T S section of the file daqwaterfall.m
%
% Last Modified by GUIDE v2.5 06-Jan-2003 23:34:34
% Author: Daniel Lee (dlee@mathworks.com)

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @daqwaterfall_OpeningFcn, ...
                   'gui_OutputFcn',  @daqwaterfall_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 daqwaterfall is made visible.
function daqwaterfall_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 daqwaterfall (see VARARGIN)

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

%%%%%%%%%%%%%%%%%%%%%%%
% ++++ I N P U T S ++++
%%%%%%%%%%%%%%%%%%%%%%%
% Change these settings to select a different source.
handles.adaptor = 'winsound';
handles.id      = 0;
handles.chan    = 1;

handles.samplesPerTrigger = 1024;
handles.sampleRate = 44100;
handles.numTraces = 10;   % number of traces to show in the waterfall.
handles.cycleTime = .9;   % Proportional to the amount of time spent per
                          %  visualization on CycleAll setting.
%%%%%%%%%%%%%%%%%%%%%%%
% ---- I N P U T S ----
%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ S E T U P   T H E   F I G U R E ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*%%%%%%
set(handles.figure1,'Color',get(handles.tTitle,'BackgroundColor'));

axes(handles.axes1);
handles.hLine1 = plot(zeros(1,handles.samplesPerTrigger)');
set(handles.hLine1,'Color', [.1 .1 0.5]);
set(handles.axes1,'Color',[235/255 255/255 235/255])
set(handles.axes1,'XGrid','on','YGrid','on')
t=title('Time Domain Signal','Color',[.05 .05 .25],'FontWeight','Bold','FontSize',9);
xlabel('Time (s)','FontSize',8);
ylabel('Voltage (V)','FontSize',8);

axes(handles.axes2);
handles.hLine2 = plot(zeros(1,handles.samplesPerTrigger/2)');
set(handles.hLine2,'Color', [.1 0.5 .1]);
set(handles.axes2,'Color',[235/255 255/255 235/255])
set(handles.axes2,'XGrid','on','YGrid','on')
t=title('Frequency Domain Signal','Color',[.05 0.25 .05],'FontWeight','Bold','FontSize',9);
xlabel('Frequency (Hz)','FontSize',8);
ylabel('Magnitude (dB)','FontSize',8);

axes(handles.axes3);
set(handles.axes3,'View',[103 10]);
set(handles.axes3,'Color',[234/255 234/255 255/255]);
grid(handles.axes3,'on');
h = get(handles.axes3,'title');
set(h,'string','Waterfall Plot','FontWeight','Bold','Color',[.25 .05 .05],'FontSize',9);
h = get(handles.axes3,'ylabel');
set(h,'string','Frequency (Hz)','FontSize',8);
h = get(handles.axes3,'zlabel');
set(h,'string','Magnitude (dB)','FontSize',8);

set(hObject,'RendererMode','Manual')  %  If you don't do this, the surface plot
set(hObject,'Renderer','OpenGL')      %    will draw VERY slowly.

set(handles.tSource,'String',sprintf('%s:%d',handles.adaptor,handles.id));
set(handles.tChannel,'String',num2str(handles.chan));
set(handles.poSampleRate,'String',[{'44100'},{'22000'},{'8000'}]);
set(handles.poPlotType,'String',[{'CycleAll'},{'Classic'},{'Classic(Top)'},{'Mosaic'},{'Waterfall'},{'Rotate'}]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- S E T U P   T H E   F I G U R E ----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ D R A W  T H E  L O G O ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
L = 40*membrane(1,25);
axes(handles.axes4);
set(handles.axes4,...
    'CameraPosition', [-193.4013 -265.1546  220.4819],...
    'XLim',[1 51], ...
    'YLim',[1 51], ...
    'Visible','off', ...
    'ZLim',[-13 40]);

s = surface(L, ...
    'EdgeColor','none', ...
    'FaceColor',[0.9 0.2 0.2], ...
    'FaceLighting','phong', ...
    'AmbientStrength',0.3, ...
    'DiffuseStrength',0.6, ...
    'Clipping','off',...
    'BackFaceLighting','lit', ...
    'SpecularStrength',1.1, ...
    'SpecularColorReflectance',1, ...
    'SpecularExponent',7);
l1 = light('Position',[40 100 20], ...
    'Style','local', ...
    'Color',[0 0.8 0.8]);
l2 = light('Position',[.5 -1 .4], ...
    'Color',[0.8 0.8 0]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- D R A W  T H E  L O G O ----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

ai=localSetupAI(handles);
handles.ai = ai;

% Update handles structure
guidata(hObject, handles);

localStartAI(ai);

% UIWAIT makes daqwaterfall wait for user response (see UIRESUME)
% uiwait(handles.figure1);

function localStartAI(ai)
%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ S T A R T  A I ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%
start(ai);
trigger(ai);
%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- S T A R T  A I ----
%%%%%%%%%%%%%%%%%%%%%%%%%%

function localStopAI(ai)
%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ S T O P  A I ++++
%%%%%%%%%%%%%%%%%%%%%%%%
stop(ai);
delete(ai);
%%%%%%%%%%%%%%%%%%%%%%%%
% ---- S T O P  A I ----
%%%%%%%%%%%%%%%%%%%%%%%%

function ai=localSetupAI(handles)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ S E T U P   T H E   A N A L O G   I N P U T ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Object Configuration.
% Create an analog input object with one channel.
ai = analoginput(handles.adaptor, handles.id);
addchannel(ai, handles.chan);

% Configure the callback to update the display.
set(ai, 'TimerFcn', @localfftShowData);

% Configure the analog input object.
set(ai, 'SampleRate', handles.sampleRate);

% Configure the analog input object to trigger manually twice.
%  We do this because we are using peekdata to acquire the data in
%   a timer callback function.
%  The first trigger will fill the buffer with handles.samplesPerTrigger
%   number of samples.  We'll know we have enough samples to start
%   processing data when the analog input object's SamplesAvailable property
%   is equal to handles.samplesPerTrigger.
%  The analog input object will then wait for
%   another manual trigger, and while it is waiting the object will still be
%   in its running state, which means the timer event will run. To keep the
%   object in the running state, we need only never manually trigger this
%   second trigger.
%  Had we set the TriggerRepeat to 0, the analog input object would stop
%   after the first trigger and the timer functions would stop running.
%
set(ai, 'SamplesPerTrigger', handles.samplesPerTrigger);
set(ai, 'TriggerRepeat', 1);
set(ai, 'TriggerType', 'manual');

% Initialize callback parameters.  The TimerAction is initialized
% after figure has been created.
set(ai, 'TimerPeriod', 0.01);
set(ai, 'BufferingConfig',[handles.samplesPerTrigger*2,20]);

% Initialize time and frequency plots with lines of y=0
d=zeros(1,handles.samplesPerTrigger);
time = 1:handles.samplesPerTrigger;
f=1:handles.samplesPerTrigger/2;
mag=zeros(1,handles.samplesPerTrigger/2);

% Store state information in the analog input objects UserData area.
data.storedFFTsIndex = 1;
data.plotSurf        = 0;
data.ai              = ai;
data.getdata         = [d time];
data.daqfft          = [f mag];
data.handle          = [];
data.figureHandles   = handles;
data.view            = [103 10];
data.rotateStep      = 4;
data.counter         = 0;

% Set the object's UserData to data.
set(data.ai, 'UserData', data);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- S E T U P   T H E   A N A L O G   I N P U T ----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Outputs from this function are returned to the command line.
function varargout = daqwaterfall_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
varargout{1} = handles.output;

% --- Executes on button press in pbExit or when you press
%     the figure close 'X' button (I set this function to
%     the figures CloseRequestFcn in GUIDE).
function pbExit_Callback(hObject, eventdata, handles)
% hObject    handle to pbExit (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
localStopAI(handles.ai);
closereq;

% --- Executes during object creation, after setting all properties.
function poSampleRate_CreateFcn(hObject, eventdata, handles)
% hObject    handle to poSampleRate (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

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ C H A N G E   T H E   S A M P L E   R A T E  ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% --- Executes on selection change in poSampleRate.
function poSampleRate_Callback(hObject, eventdata, handles)

% hObject    handle to poSampleRate (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 poSampleRate contents as cell array
%        contents{get(hObject,'Value')} returns selected item from poSampleRate

% First, stop and delete the current analog input object
localStopAI(handles.ai);

% Extract the new samplerate.
v=get(handles.poSampleRate,'Value');
s=get(handles.poSampleRate,'String');
handles.sampleRate = str2num(s{v});

% Create a new analog input with the new sample rate.
handles.ai = localSetupAI(handles);

% Update handles structure
guidata(hObject, handles);

% Restart the analog input
localStartAI(handles.ai);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- C H A N G E   T H E   S A M P L E   R A T E  ----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ***********************************************************************
% Calculate the fft of the data.  (Copied from demoai_fft.m)
function [f, mag] = localDaqfft(data,Fs,blockSize)

% Calculate the fft of the data.
xFFT = fft(data);
xfft = abs(xFFT);

% Avoid taking the log of 0.
index = find(xfft == 0);
xfft(index) = 1e-17;

mag = 20*log10(xfft);
mag = mag(1:blockSize/2);

f = (0:length(mag)-1)*Fs/blockSize;
f = f(:);

% ***********************************************************************
% Update the plot. This routine is a Timer callback, it is called
%  automatically at a preset time interval. See line 184 for where
%  this routine is assigned as a callback
function localfftShowData(obj,event)

if (get(obj,'SamplesAvailable') >= obj.SamplesPerTrigger)

	% Get the handles.
	data = obj.UserData;

	handles = data.figureHandles;

	% Execute a peekdata.
	x = peekdata(obj, obj.SamplesPerTrigger);

	% FFT calculation.
	Fs = obj.SampleRate;
	blockSize = obj.SamplesPerTrigger;
	[f,mag] = localDaqfft(x,Fs,blockSize);

	% Dynamically modify Analog axis as we go.
	maxX=max(x);
	minX=min(x);
	yax1=get(handles.axes1,'YLim');
	yax1(1)=minX - .0001; % need to subtract a value to make sure yax(1) never equals yax(2)
	yax1(2)=maxX + .0001;
	set(handles.axes1,'YLim',yax1)
	set(handles.axes1,'XLim',[0 (obj.SamplesPerTrigger-1)/obj.SampleRate])

	% Dynamically modify Frequency axis as we go.
	maxF=max(f);
	minF=min(f);
	xax=get(handles.axes2,'XLim');
    xax(1)=minF;
    xax(2)=maxF;
	set(handles.axes2,'XLim',xax)

	% Dynamically modify Magnitude axis as we go.
	maxM=max(mag);
	minM=min(mag);
	yax2=get(handles.axes2,'YLim');
	yax2(1)=minM - .0001;
	yax2(2)=maxM + .0001;
	set(handles.axes2,'YLim',yax2)

	% Update the line plots.
	set(handles.hLine1, 'XData', [0:(obj.SamplesPerTrigger-1)]/obj.SampleRate, 'YData', x(:,1));
	set(handles.hLine2, 'XData', f(:,1), 'YData', mag(:,1));

    % Find the frequency at which the max signal strength is at.
    [ymax,maxindex] = max(mag);
    set(handles.tFreq,'String',sprintf('%4.1d Hz',f(maxindex)));

    % Store the current FFT into the array of FFTs used for the waterfall.
	data.storedFFTs(data.storedFFTsIndex,:) = mag';

    % This circular shift is used so that when we display the 3D plot, the
    %  newest FFT will appear in 'front' and the oldest in 'back'.
    % To understand this, note how the plotting routines are using this fftOrder
    %  array to reorder the FFTs stored in data.storedFFTs and also note
    %  how data.storedFFTsIndex is used to store FFTs in data.storedFFTs.
    %
	fftOrder = 1:handles.numTraces;
	fftOrder = circshift(fftOrder,[ 1 -data.storedFFTsIndex ]);

	data.storedFFTsIndex = data.storedFFTsIndex + 1;
	if (data.storedFFTsIndex > handles.numTraces)
        data.storedFFTsIndex = 1;
        data.plotSurf        = 1; % Indicates a full history is stored.
	end

	% Update the surface plot if we have a full history.
	if (data.plotSurf)
        cla(handles.axes3);

        v=get(handles.poPlotType,'Value');
        s=get(handles.poPlotType,'String');
        switch s{v}
            case 'Classic'
                data.view  = [103 30];
                data=localClassic(handles,data,f,fftOrder);
            case 'Classic(Top)'
                data.view  = [90 -90];
                data=localClassic(handles,data,f,fftOrder);
            case 'Mosaic'
                data.view  = [90 -90];
                data=localMosaic(handles,data,f,fftOrder);
            case 'Waterfall'
                data.view = [103 30];
                data=localWaterfall(handles,data,f,fftOrder,yax2);
            case 'Rotate'
                data=localRotate(handles,data,f,fftOrder);
            case 'CycleAll'
                data=localCycleAll(handles,data,f,fftOrder);
        end

    end

	set(data.ai, 'UserData', data);

	drawnow;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ++++ V I S U A L I Z A T I O N ++++
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function data=localClassic(handles,data,f,fftOrder)
    [X,Y] = meshgrid(1:handles.numTraces,f(1:end));
    surf(X,Y,data.storedFFTs(fftOrder,:)','parent',handles.axes3);
  	set(handles.axes3,'XLim',[1 handles.numTraces],'YLim',[0 f(end)])
    shading(handles.axes3,'interp');
    set(handles.axes3,'View',data.view)

function data=localMosaic(handles,data,f,fftOrder)
    [X,Y] = meshgrid(1:handles.numTraces,f(1:10:end));
    surf(X,Y,data.storedFFTs(fftOrder,(1:10:end))','parent',handles.axes3);
  	set(handles.axes3,'XLim',[1 handles.numTraces],'YLim',[0 f(end)])
    set(handles.axes3,'View',data.view)

function data=localWaterfall(handles,data,f,fftOrder,yax2)
    [X,Y] = meshgrid(1:handles.numTraces,f(1:end));
    p=plot3(X,Y,data.storedFFTs(fftOrder,:)','parent',handles.axes3);

    % rotate the color map of the lines in the plot3
    map  = linspace(0,1,handles.numTraces);
    map2 = linspace(1,0,handles.numTraces);
    rotatemap  = map(fftOrder);
    rotatemap2 = map2(fftOrder);
    for k=1:handles.numTraces;
        set(p(k),'Color',[rotatemap(k) .1 rotatemap2(k)]);
    end

  	set(handles.axes3,'XLim',[1 handles.numTraces],'YLim',[0 f(end)]);
    shading(handles.axes3,'interp');
    set(handles.axes3,'View',data.view)

function data=localRotate(handles,data,f,fftOrder)
    [X,Y] = meshgrid(1:handles.numTraces,f(1:8:end));
    surf(X,Y,data.storedFFTs(fftOrder,(1:8:end))','parent',handles.axes3);
  	set(handles.axes3,'XLim',[1 handles.numTraces],'YLim',[0 f(end)])
    set(handles.axes3,'View',data.view)

    % Rotate the view point.
    data.view(1) = 90;
    if data.view(2) >= 90-data.rotateStep
        data.rotateStep = -4;
    elseif data.view(2) <= -90-data.rotateStep
        data.rotateStep = 4;
    end
    data.view(2) = data.view(2)+data.rotateStep;

function data=localCycleAll(handles,data,f,fftOrder)
    data.counter = data.counter + get(data.ai,'TimerPeriod');
    if data.counter > 5*handles.cycleTime
        data.counter = 0;
    elseif data.counter > 4*handles.cycleTime
        data=localRotate(handles,data,f,fftOrder);
    elseif data.counter > 3*handles.cycleTime
        data.view  = [103 30];
        data=localWaterfall(handles,data,f,fftOrder);
    elseif data.counter > 2*handles.cycleTime
        data.view  = [90 -90];
        data=localMosaic(handles,data,f,fftOrder);
    elseif data.counter > 1*handles.cycleTime
        data.view  = [90 -90];
        data=localClassic(handles,data,f,fftOrder);
    else
        data.view  = [103 30];
        data=localClassic(handles,data,f,fftOrder);
    end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% ---- V I S U A L I Z A T I O N ----
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% --- Executes during object creation, after setting all properties.
function poPlotType_CreateFcn(hObject, eventdata, handles)
% hObject    handle to poPlotType (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 poPlotType.
function poPlotType_Callback(hObject, eventdata, handles)
% hObject    handle to poPlotType (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 poPlotType contents as cell array
%        contents{get(hObject,'Value')} returns selected item from poPlotType


function [outputFile] = reportPDF(model, microbeID, biomassReaction, inputDataFolder, reportFolder, ncbiID)
% Runs all test functions and writes a report in a .tex file. Writes a PDF
% using pdf2latex.
% Requires a LaTex installation, e.g. MiKTex (https://miktex.org/download)
% and pdftex (https://ctan.org/pkg/pdftex).
%
% INPUT
% model             COBRA model structure
% microbeID         Microbe ID in carbon source data file
% biomassReaction   Model biomass reaction
% inputDataFolder   Folder with experimental data and database files
%                   to load
% reportFolder      Path to folder where report documents should be written
%                   (e.g., 'C:\Reports')
%
% OPTIONAL INPUT
% ncbiID            Organism NCBI ID
%
% OUTPUT
% outputFile        Name of the output file with report
%
% Stefania Magnusdottir, Nov 2017
% Almut Heinken, Sep 2018-adapted nomenclature

% load database
database=loadVMHDatabase;

% switch directory to create the LaTex file
currentDir = pwd;
cd(reportFolder)

% Open file to write report (using LaTeX)
% fileNameTex = [reportFolder filesep datestr(datetime('now'), 'yyyymmdd'), '_', microbeID, '.tex'];
fileNameTex = [reportFolder filesep  microbeID, '.tex'];
fid = fopen(fileNameTex, 'w');
cd(currentDir);

% Print preamble to file
fprintf(fid, '\\documentclass[11pt,a4paper]{article}\n');
fprintf(fid, '\\title{%s}\n', strrep(microbeID, '_', '\_'));
fprintf(fid, '\\date{\\today}\n');
fprintf(fid, '\\begin{document}\n');
fprintf(fid, '\\maketitle\n');
fprintf(fid, '\n');

% Taxonomic lineage
fprintf(fid, '\\section{Taxonomic lineage}\n');
if ~isempty(ncbiID)
    taxonomy = parseNCBItaxonomy(ncbiID);
    taxFields = fieldnames(taxonomy);
    if ~isempty(taxFields)
        fprintf(fid, '\\begin{tabular}{rl}\n');
        for i = 1:length(taxFields)
            fprintf(fid, '%s: & %s \\\\\n', taxFields{i}, strrep(taxonomy.(taxFields{i}), '_', '\_'));
        end
        fprintf(fid, '\\end{tabular}\n');
    end
    fprintf(fid, '\n');
end

% Reconstruction properties
fprintf(fid, '\\section{Reconstruction properties}\n');
fprintf(fid, '\\begin{tabular}{rl}\n');
fprintf(fid, 'Reactions: & %d \\\\\n', length(model.rxns));
fprintf(fid, 'Metabolites: & %d \\\\\n', length(model.mets));
fprintf(fid, 'Genes: & %d \\\\\n', length(model.genes));
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\n');

% Mass and charge balance
fprintf(fid, '\\section{Mass and charge balance}\n');
[massImbalancedRxns, chargeImbalancedRxns, ~, metsMissingFormulas] = ...
    testModelMassChargeBalance(model, true, biomassReaction);
if size(massImbalancedRxns, 1) > 1
    fprintf(fid, '\\subsection{Mass imbalanced reactions}\n');
    fprintf(fid, '\\begin{tabular}{lc}\n');
    fprintf(fid, 'Reaction & Element imbalances\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    for i = 2:size(massImbalancedRxns, 1)
        fprintf(fid, '%s & %s \\\\\n', strrep(massImbalancedRxns{i, 1}, '_', '\_'), ...
                massImbalancedRxns{i, 3});
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
end
if size(chargeImbalancedRxns, 1) > 1
    fprintf(fid, '\\subsection{Charge imbalanced reactions}\n');
    fprintf(fid, '\\begin{tabular}{lc}\n');
    fprintf(fid, 'Reaction & Charge imbalance\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    for i = 2:size(chargeImbalancedRxns, 1)
        fprintf(fid, '%s & %s \\\\\n', strrep(chargeImbalancedRxns{i, 1}, '_', '\_'), ...
                chargeImbalancedRxns{i, 3});
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
end
if ~isempty(metsMissingFormulas)
    fprintf(fid, '\\subsection{Metabolites missing formulas}\n');
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, 'Metabolite & Name \\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    for i = 1:size(metsMissingFormulas, 1)
        fprintf(fid, '%s & %s\\\\\n', strrep(metsMissingFormulas{i, 1}, '_', '\_'), ...
                strrep(metsMissingFormulas{i, 2}, '_', '\_'));
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
end

% Leak test
fprintf(fid, '\\section{Leak test}\n');
leakingMets = testLeakingMetabolites(model);
if ~isempty(leakingMets)
    fprintf(fid, '\\begin{tabular}{l}\n');
    for i = 1:length(leakingMets)
        fprintf(fid, '%s\\\\\n', strrep(leakingMets{i, 1}, '_', '\_'));
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No leaking metabolites.\n');
end

% ATP production
fprintf(fid, '\\section{ATP production}\n');
[atpFluxAerobic, atpFluxAnaerobic] = testATP(model);
fprintf(fid, '\\begin{tabular}{ll}\n');
fprintf(fid, 'ATP demand (aerobic): & %4.4f mmol/gDW/h\\\\\n', atpFluxAerobic);
fprintf(fid, 'ATP demand (anaerobic): & %4.4f mmol/gDW/h\\\\\n', atpFluxAnaerobic);
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\n');

% Growth
fprintf(fid, '\\section{Growth}\n');
[AerobicGrowth, AnaerobicGrowth] = testGrowth(model, biomassReaction);
fprintf(fid, '\\begin{tabular}{ll}\n');
fprintf(fid, 'Unlimited growth (aerobic): & %4.4f mmol/gDW/h\\\\\n', AerobicGrowth(1, 1));
fprintf(fid, 'Unlimited growth (anaerobic): & %4.4f mmol/gDW/h\\\\\n', AnaerobicGrowth(1, 1));
fprintf(fid, 'C omplex medium growth (aerobic): & %4.4f mmol/gDW/h\\\\\n', AerobicGrowth(1, 2));
fprintf(fid, 'Complex medium growth (anaerobic): & %4.4f mmol/gDW/h\\\\\n', AnaerobicGrowth(1, 2));
fprintf(fid, '\\end{tabular}\n');
fprintf(fid, '\n');

% Carbon sources
fprintf(fid, '\\section{Carbon sources}\n');
[TruePositives, FalseNegatives] = testCarbonSources(model, microbeID, biomassReaction, database, inputDataFolder);
if ~isempty(FalseNegatives) || ~isempty(TruePositives)
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, '\\textit{In vitro} carbon source & Taken up by model\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    if ~isempty(FalseNegatives)
        for i = 1:length(FalseNegatives)
            fprintf(fid, '%s & %s\\\\\n', FalseNegatives{i}, 'FALSE');
        end
    end
    if ~isempty(TruePositives)
        for i = 1:length(TruePositives)
            fprintf(fid, '%s & %s\\\\\n', TruePositives{i}, 'TRUE');
        end
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No carbon sources reported for organism.\n');
end

% Fermentation products
fprintf(fid, '\\section{Fermentation products}\n');
[TruePositives, FalseNegatives] = testFermentationProducts(model, microbeID, biomassReaction, database, inputDataFolder);
if ~isempty(FalseNegatives) || ~isempty(TruePositives)
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, '\\textit{In vitro} fermentation product & Secreted by model\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    if ~isempty(FalseNegatives)
        for i = 1:length(FalseNegatives)
            fprintf(fid, '%s & %s\\\\\n', FalseNegatives{i}, 'FALSE');
        end
    end
    if ~isempty(TruePositives)
        for i = 1:length(TruePositives)
            fprintf(fid, '%s & %s\\\\\n', TruePositives{i}, 'TRUE');
        end
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No fermentation products reported for organism.\n');
end

% Uptake of known consumed metabolites
fprintf(fid, '\\section{Known consumed metabolites}\n');
[TruePositives, FalseNegatives] = testMetaboliteUptake(model, microbeID, biomassReaction, database, inputDataFolder);
if ~isempty(FalseNegatives) || ~isempty(TruePositives)
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, 'Consumed metabolites in model\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    if ~isempty(FalseNegatives)
        for i = 1:length(FalseNegatives)
            fprintf(fid, '%s & %s\\\\\n', FalseNegatives{i}, 'FALSE');
        end
    end
    if ~isempty(TruePositives)
        for i = 1:length(TruePositives)
            fprintf(fid, '%s & %s\\\\\n', TruePositives{i}, 'TRUE');
        end
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No known consumed metabolites reported for organism.\n');
end

% Production of known secretion products
fprintf(fid, '\\section{Known secretion products}\n');
[TruePositives, FalseNegatives] = testSecretionProducts(model, microbeID, biomassReaction, database, inputDataFolder);
if ~isempty(FalseNegatives) || ~isempty(TruePositives)
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, 'Secretion products in model\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    if ~isempty(FalseNegatives)
        for i = 1:length(FalseNegatives)
            fprintf(fid, '%s & %s\\\\\n', FalseNegatives{i}, 'FALSE');
        end
    end
    if ~isempty(TruePositives)
        for i = 1:length(TruePositives)
            fprintf(fid, '%s & %s\\\\\n', TruePositives{i}, 'TRUE');
        end
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No known secretion products reported for organism.\n');
end

% Growth on defined media
[growsOnDefinedMedium,constrainedModel,growthOnKnownCarbonSources] = testGrowthOnDefinedMedia(model, microbeID, biomassReaction, inputDataFolder);
fprintf(fid, '\\section{Growth on defined medium}\n');
if ~strcmp(growsOnDefinedMedium,'NA')
    fprintf(fid, '\\begin{tabular}{ll}\n');
    if growsOnDefinedMedium==1
        fprintf(fid, 'The model grows on defined medium with at least one carbon source.\n');
    elseif growsOnDefinedMedium==0
        fprintf(fid, 'The model cannot on defined medium reported for the organism.\n');
    end
        fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
%     fprintf(fid, '\\subsection{Growth rates on defined medium}\n');
%     fprintf(fid, '\\begin{tabular}{ll}\n');
%     fprintf(fid, 'Carbon source & Aerobic & Anaerobic\\\\\n');
%     fprintf(fid, '\\hline\\\\\n');
%     for i=1:size(growthOnKnownCarbonSources,1)
%         fprintf(fid, '%s & %s\\\\\n', growthOnKnownCarbonSources{i,1}, str2double(growthOnKnownCarbonSources{i,2}), str2double(growthOnKnownCarbonSources{i,3}));
%     end
%     fprintf(fid, '\\end{tabular}\n');
%     fprintf(fid, '\n');
else
    fprintf(fid, 'No growth media reported for organism.\n');
end

% Bile acid biosynthesis
fprintf(fid, '\\section{Bile acid biosynthesis}\n');
[TruePositives, FalseNegatives] = testBileAcidBiosynthesis(model, microbeID, biomassReaction, database);
if ~isempty(FalseNegatives) || ~isempty(TruePositives)
    fprintf(fid, '\\begin{tabular}{ll}\n');
    fprintf(fid, 'Bile acid biosynthesis in model\\\\\n');
    fprintf(fid, '\\hline\\\\\n');
    if ~isempty(FalseNegatives)
        for i = 1:length(FalseNegatives)
            fprintf(fid, '%s & %s\\\\\n', FalseNegatives{i}, 'FALSE');
        end
    end
    if ~isempty(TruePositives)
        for i = 1:length(TruePositives)
            fprintf(fid, '%s & %s\\\\\n', TruePositives{i}, 'TRUE');
        end
    end
    fprintf(fid, '\\end{tabular}\n');
    fprintf(fid, '\n');
else
    fprintf(fid, 'No bile acid biosynthesis reported for organism.\n');
end

% Close document
fprintf(fid, '\\end{document}\n');
fclose(fid);

% write PDF
cd(reportFolder)
system(['pdflatex ' fileNameTex]);
cd(currentDir);

outputFile = strcat(microbeID, '.pdf');
end


classdef(Abstract) AbstractSecondOrderIntegrator < AbstractIntegrator
    %AbstractSecondOrderIntegrator Summary of this class goes here
    %   Detailed explanation goes here

    properties

    end

    methods
        [t,y,yp,te,ye,ype,ie] = integrate(obj, odefun, tspan, y0, yp0, evtsFunc, odeOutputFun)
    end
end


function aedat = TrimTimeStampReset(aedat)

%{
If timestamp reset events occur, timestamps become non-monotonic.
Resolve this by searching for non-monotonicites in the timestamps for each
data type and trimming away everything before the last non-monotonicity.
A better technique would look at the packetTimestamps, at least in aedat3
imports.
%}

dbstop if error

% Special

if ~isfield(aedat, 'data')
    disp('No data found')
    return
end
if isfield(aedat.data, 'special')
    ts = int64(aedat.data.special.timeStamp);
    lastTsReset = find(ts(2 : end) - ts(1 : end - 1) < 0, 1, 'last');
    if ~isempty(lastTsReset)
        keepLogical = true(aedat.data.special.numEvents, 1);
        keepLogical(1 : lastTsReset) = false;
        aedat.data.special.timeStamp = aedat.data.special.timeStamp (keepLogical);
        aedat.data.special.address       = aedat.data.special.address  (keepLogical);
        aedat.data.special.numEvents = nnz(keepLogical);
        if isfield(aedat.data.special, 'valid')
            aedat.data.special.valid    = aedat.data.special.valid (keepLogical);
        end
    end
end
% Polarity

if isfield(aedat.data, 'polarity')
    ts = int64(aedat.data.polarity.timeStamp);
    lastTsReset = find(ts(2 : end) - ts(1 : end - 1) < 0, 1, 'last');
    if ~isempty(lastTsReset)
        keepLogical = true(aedat.data.polarity.numEvents, 1);
        keepLogical(1 : lastTsReset) = false;
        aedat.data.polarity.timeStamp   = aedat.data.polarity.timeStamp (keepLogical);
        aedat.data.polarity.x           = aedat.data.polarity.x         (keepLogical);
        aedat.data.polarity.y           = aedat.data.polarity.y         (keepLogical);
        aedat.data.polarity.polarity    = aedat.data.polarity.polarity  (keepLogical);
        aedat.data.polarity.numEvents   = nnz(keepLogical);
        if isfield(aedat.data.polarity, 'valid')
            aedat.data.polarity.valid    = aedat.data.polarity.valid (keepLogical);
        end
    end
end

% Frames

% This assumes that timestamps have been simplified to aedat2 standard, if
% they came from aedat3 file
if isfield(aedat.data, 'frame')
    ts = int64(aedat.data.frame.timeStampStart);
    lastTsReset = find(ts(2 : end) - ts(1 : end - 1) < 0, 1, 'last');
    if ~isempty(lastTsReset)
        keepLogical = true(aedat.data.frame.numEvents, 1);
        keepLogical(1 : lastTsReset) = false;
        aedat.data.frame.timeStampStart = aedat.data.frame.timeStampStart (keepLogical);
        aedat.data.frame.timeStampEnd = aedat.data.frame.timeStampEnd (keepLogical);
        aedat.data.frame.samples           = aedat.data.frame.samples         (keepLogical);
        aedat.data.frame.xLength           = aedat.data.frame.xLength         (keepLogical);
        aedat.data.frame.yLength           = aedat.data.frame.yLength         (keepLogical);
        aedat.data.frame.xPosition         = aedat.data.frame.xPosition       (keepLogical);
        aedat.data.frame.yPosition         = aedat.data.frame.yPosition       (keepLogical);
        aedat.data.frame.numEvents         = nnz(keepLogical);
        if isfield(aedat.data.frame, 'valid')
            aedat.data.frame.valid    = aedat.data.frame.valid (keepLogical);
        end
    end
end
% Imu6

if isfield(aedat.data, 'imu6')
    ts = int64(aedat.data.imu6.timeStamp);
    lastTsReset = find(ts(2 : end) - ts(1 : end - 1) < 0, 1, 'last');
    if ~isempty(lastTsReset)
        keepLogical = true(aedat.data.imu6.numEvents, 1);
        keepLogical(1 : lastTsReset) = false;
        aedat.data.imu6.timeStamp = aedat.data.imu6.timeStamp (keepLogical);
        aedat.data.imu6.accelX       = aedat.data.imu6.accelX (keepLogical);
        aedat.data.imu6.accelY       = aedat.data.imu6.accelY (keepLogical);
        aedat.data.imu6.accelZ       = aedat.data.imu6.accelZ (keepLogical);
        aedat.data.imu6.gyroX        = aedat.data.imu6.gyroX (keepLogical);
        aedat.data.imu6.gyroY        = aedat.data.imu6.gyroY (keepLogical);
        aedat.data.imu6.gyroZ        = aedat.data.imu6.gyroZ (keepLogical);
        aedat.data.imu6.temperature  = aedat.data.imu6.temperature (keepLogical);
        aedat.data.imu6.numEvents    = nnz(keepLogical);
        if isfield(aedat.data.imu6, 'valid')
            aedat.data.imu6.valid    = aedat.data.imu6.valid (keepLogical);
        end
    end
end

% To do: handle other event types

% To do - correct first and last timestamp in info, also numPackets, ...
% startTime, endTime, startPacket, endPacket, startEvent, endEvent


function varargout=lpcrf2lo(varargin)
%
% For calling details please see v_lpcrf2lo.m
%
% This dummy routine is included for backward compatibility only
% and will be removed in a future release of voicebox. Please use
% v_lpcrf2lo.m in future and/or update with v_voicebox_update.m
%
%      Copyright (C) Mike Brookes 2018
%      Version: $Id: lpcrf2lo.m 10863 2018-09-21 15:39:23Z dmb $
%
if nargout
    varargout=cell(1,nargout);
    [varargout{:}]=v_lpcrf2lo(varargin{:});
else
    v_lpcrf2lo(varargin{:});
end


function tf = areTheseToolboxesInstalled(requiredToolboxes)
%ARETHESETOOLBOXESINSTALLED takes a cell array of toolbox names and checks whether they are currently installed
% SYNOPSIS tf = areTheseToolboxesInstalled(requiredToolboxes)
%
% INPUT requiredToolboxes: cell array with toolbox names to test for. Eg.
%        {'MATLAB','Image Processing Toolbox'}
%
% OUTPUT tf: true or false if the required toolboxes are installed or not
%--------------------------------------------------------------------------
% http://stackoverflow.com/a/2061075/1485872
%--------------------------------------------------------------------------
%--------------------------------------------------------------------------
% This file is part of the TIGRE Toolbox
%
% Copyright (c) 2015, University of Bath and
%                     CERN-European Organization for Nuclear Research
%                     All rights reserved.
%
% License:            Open Source under BSD.
%                     See the full license at
%                     https://github.com/CERN/TIGRE/blob/master/LICENSE
%
% Contact:            tigre.toolbox@gmail.com
% Codes:              https://github.com/CERN/TIGRE/
% Coded by:           Ander Biguri
%--------------------------------------------------------------------------

% get all installed toolbox names
v = ver;
% collect the names in a cell array
[installedToolboxes{1:length(v)}] = deal(v.Name);

% check
tf = all(ismember(requiredToolboxes,installedToolboxes));


% Assigns the given value to the named property of an object or structure.
% This function can deal with nested properties.
%
% Input arguments:
% obj:
%    the structure, handle or value object the value should be assigned to
% name:
%    a property name with dot (.) separating property names at
%    different hierarchy levels
% value:
%    the value to assign to the property at the deepest hierarchy
%    level
%
% Output arguments:
% obj:
%    the updated object or structure, optional for handle objects
%
% Example:
%    obj = struct('surface', struct('nested', 10));
%    obj = nestedassign(obj, 'surface.nested', 23);
%    disp(obj.surface.nested);  % prints 23
%
% See also: nestedfetch

% Copyright 2010 Levente Hunyadi
function obj = nestedassign(obj, name, value)
    if ~iscell(name)
        nameparts = strsplit(name, '.');
    else
        nameparts = name;
    end
    obj = nestedassign_recurse(obj, nameparts, value);
end

function obj = nestedassign_recurse(obj, name, value)
% Assigns the given value to the named property of an object.
%
% Input arguments:
% obj:
%    the handle or value object the value should be assigned to
% name:
%    a cell array of the composite property name
% value:
%    the value to assign to the property at the deepest hierarchy
%    level
    if numel(name) > 1
        obj.(name{1}) = nestedassign_recurse(obj.(name{1}), name(2:end), value);
    else
        obj.(name{1}) = value;
    end
end


% SPHERE_READ Read a NIST SPHERE audio file
%
% Usages
%    [y, fs] = SPHERE_READ(filename, N)
%
%    sz = SPHERE_READ(filename, 'size')
%
% Input
%    filename (char): The name of the file to read.
%    N (int, optional): The number of samples to read.
%
% Output
%    y (numeric): The audio data in the file.
%    fs (numeric): The sample rate of the data.
%    sz (int): The number of samples in the file.
%
% Description
%    Analogous in use to the WAVREAD function, SPHERE_READ processes NIST
%    SPHERE files, such as those found in TIMIT. For more information, see
%    WAVREAD.
%
% See also
%    WAVREAD

function [y, fs] = sphere_read(filename, N)
	if nargin < 2
		N = [];
	end

	fid = fopen(filename);

	if fid == -1
		error('File not found.');
	end

	line = fgets(fid);

	if (isnumeric(line) && line == -1) || isempty(strfind(line, 'NIST_1A'))
		error('Invalid NIST SPHERE file (header not present).');
	end

	line = fgets(fid);

	header_length = str2num(line);

	if isempty(header_length)
		error('Invalid NIST SPHERE file (header length not present).');
	end

	header_length = round(header_length);

	fclose(fid);

	fid = fopen(filename,'rt','ieee-le');

	[data, count] = fread(fid, header_length, 'uchar');

	fclose(fid);

	if count ~= header_length
		error('Invalid NIST SPHERE file (unable to read header).');
	end

	header = char(data');

	lines = regexp(header,char(10),'split');

	fs = [];
	byte_format = [];
	count = [];

	for k = 1:length(lines)
		if strfind(lines{k}, 'sample_coding') == 1
			if ~isempty(strfind(lines{k}, 'shorten'))
				error('Unsupported coding scheme (''shorten'').');
			end
		elseif strfind(lines{k}, 'sample_rate') == 1
			fs = strread(lines{k}, 'sample_rate -i %d');
		elseif strfind(lines{k}, 'sample_count') == 1
			count = strread(lines{k}, 'sample_count -i %d');
		elseif strfind(lines{k}, 'channel_count') == 1
			if strread(lines{k}, 'channel_count -i %d') > 1
				error('Unsupported number of channels (>1).');
			end
		elseif strfind(lines{k}, 'sample_n_bytes') == 1
			if strread(lines{k}, 'sample_n_bytes -i %d') ~= 2
				error('Unsupported bit depth (not 16 bits).');
			end
		elseif strfind(lines{k}, 'sample_byte_format') == 1
			byte_format = strread(lines{k}, 'sample_byte_format -s2 %s');
		end
	end

	if ischar(N) && strcmp(N,'size')
		y = count;
		return;
	end

	if ~isempty(N) && isnumeric(N)
		count = N;
	end

	if isempty(byte_format)
		warning('''sample_byte_format'' not specified, assuming little-endian.');
		byte_format = '01';
	end

	if strcmp(byte_format, '01')
		endianness = 'le';
	elseif strcmp(byte_format, '10')
		endianness = 'be';
	else
		error('Unknown ''sample_byte_format''.');
	end

	fid = fopen(filename, 'r', ['ieee-' endianness]);

	temp = fread(fid, header_length, 'char');

	if isempty(count)
		count = Inf;
	end

	y = fread(fid, count, 'short');

	fclose(fid);
end


function S= bbciutil_transformProc2FcnParam(S)
%TRANSFORMPROC2FCNPARAM - Transform 'proc' field to fcn/param fields
%
%This function just transforms the way a sequence of processing steps
%is given. This is best exmplained by an example:
% S.proc=  {{'proc_baseline', [-200 0]}, ...
%           {'proc_jumpingMeans', 5, 10}};
% T.fcn= {'proc_baseline', 'proc_jumpingMeans'}
% T.param= {{[-200 0]}, {5, 10}};
%The 'proc' representation is more intuitive for definition, but the
%fcn/param representation is better for performance. There the representation
%is transform initially in bbci_apply.
%
%Synopsis:
%  T= bbciutil_transformProc2FcnParam(S)
%
%Arguments:
%  S - Structure which has the proc
%
%Output:
%  T - Updated structure

% 02-2011 Benjamin Blankertz

if ~isfield(S, 'proc'),
  for k= 1:length(S),
    if (~isfield(S,'param') || isempty(S(k).param)) && ...
          isfield(S(k), 'fcn') && ~isempty(S(k).fcn),
      S(k).param= repmat({{}}, [1 length(S(k).fcn)]);
    end
  end
  return;
end

for k= 1:length(S),
  if isfield(S(k),'fcn') && ~isempty(S(k).fcn),
%    warning('struct has already nonempty field fcn');
    continue;
  end
  nProcs= length(S(k).proc);
  S(k).fcn= cell(1, nProcs);
  S(k).param= cell(1, nProcs);
  for p= 1:nProcs,
    [S(k).fcn{p}, S(k).param{p}]= misc_getFuncParam(S(k).proc{p});
  end
end
S= rmfield(S, 'proc');


% std_rmalldatafields - remove all data fields from STUDY (before saving
%                       it for instance.
%
% Usage:
%   STUDY = std_rmalldatafields(STUDY, type);
%
% Input:
%   STUDY   - EEGLAB study structure
%
% Optional input:
%   type    - ['chan'|'clust'|'both'] remove from changrp channel location
%             structure, cluster structure or both. Default is 'both'.
%
% Ouput:
%   STUDY   - updated EEGLAB study structure
%
% Author: Arnaud Delorme, CERCO/CNRS, UCSD, 2010-

% Copyright (C) Arnaud Delorme, arno@salk.edu
%
% 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 should have received a copy of the GNU General Public License
% along with this program; if not, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function STUDY = std_rmalldatafields(STUDY, chanorcomp);

if nargin < 1
    help std_rmalldatafields;
    return;
end;
if nargin < 2
    chanorcomp = 'both';
end;

fields = { 'erpdata'  'erptimes'  'erpdatatrials'  'erptrialinfo' ...
               'specdata' 'specfreqs' 'specdatatrials' 'spectrialinfo' ...
               'erspdata' 'erspbase'  'erspfreqs'      'ersptimes' 'erspdatatrials' 'ersptrialinfo'  ...
               'topo' 'topox' 'topoy' 'topoall' 'topopol' ...
               'itcdata'  'itcfreqs'  'itctimes'       'itcdatatrials' 'itctrialinfo'  ...
               'erpimdata' 'erpimtrials' 'erpimevents' 'erpimtimes' 'erspsubjinds' 'itcsubjinds' 'dipoles' ...
               'data' 'datatimes' 'datasortvals' 'datacontinds' 'centroid' };
for ff = 1:length(fields)
    if strcmpi(chanorcomp, 'data') || strcmpi(chanorcomp, 'both')
        if isfield(STUDY.changrp, fields{ff})
            STUDY.changrp = rmfield(STUDY.changrp, fields{ff} );
        end;
    end;
    if strcmpi(chanorcomp, 'clust') || strcmpi(chanorcomp, 'both')
        if isfield(STUDY.cluster, fields{ff})
            STUDY.cluster = rmfield(STUDY.cluster, fields{ff} );
        end;
    end;
end;


function obj=coord_flip(obj)
% coord_flip Flips plot axes
%
% Using coord_flip flips the plot axes, i.e. x data will be on the vertical axis and y data on
% the horizontal axis. This is mainly useful to draw stat_ visualisations horizontally,
% such as boxplots, violin plots, etc. Coord flip sets the following axes properties
% 'CameraUpVector',[1 0 0],'XDir','reverse','YAxisLocation','Right' and
% sets things up for correct resizing of the figure. You can further modify
% these axe properties with axe_property() in order to get the desired axes
% orientation and location

    %CameraUpVector takes in accound 'XDir': when using 'reverse' the
    %CameraUpVector doesn't need to be negative X [-1 0 0]
    obj.axe_property('CameraUpVector',[1 0 0],'XDir','reverse','YAxisLocation','Right');
    for obj_ind=1:numel(obj)
        obj(obj_ind).is_flipped=true;
    end
end


classdef ECGtask_ECG_delineation < ECGtask

% ECGtask for ECGwrapper (for Matlab)
% ---------------------------------
%
% Description:
%
% Abstract class for defining ECGtask interface
%
% Adding user-defined QRS detectors:
% A QRS detector that has the following interface can be added to the task:
%
%     [positions_single_lead, position_multilead] = your_ECG_delineator( ECG_matrix, ECG_header, progress_handle, payload_in);
%
% where the arguments are:
%    + ECG_matrix, is a matrix size [ECG_header.nsamp ECG_header.nsig]
%    + ECG_header, is a struct with info about the ECG signal, such as:
%         .freq, the sampling frequency
%         .desc, description about the signals.
%    + progress_handle, is a handle to a waitbar object, that can be used
%          to track the progress within your function. See the
%          documentation about this class in this kit.
%    + payload_in, is a user data variable allowed to be sent each call to
%          your function. It is sent, via the payload property of this
%          class, for example:
%
%         this_ECG_wrappers.ECGtaskHandle.payload = your_variable;
%         this_ECG_wrappers.ECGtaskHandle.payload = {your_var1 your_var2};
%         this_ECG_wrappers.ECGtaskHandle.payload = load(cached_filenames);
%
%          In the context of delineation, it is thought to be a user
%          corrected, or "gold quality" QRS location, in order to
%          improve the wave delineation quality. If "payload_in" is a
%          struct, this function will automatically filter and time-shift
%          all QRS detection fields started with the string "corrected_".
%          For this purpose, QRScorrector task, automatically appends this
%          string to eachm anually reviewed QRS location series.
%
% the output of your function must be:
%    + positions_single_lead, a cell array size ECG_header.nsig with the
%          QRS sample locations found in each lead.
%    + position_multilead, a numeric vector with the QRS locations
%          calculated using multilead rules.
%
%
% Author: Mariano Llamedo Soria (llamedom at frba.utn.edu.ar)
% Version: 0.1 beta
% Birthdate  : 18/2/2013
% Last update: 18/2/2013

    properties(GetAccess = public, Constant)
        name = 'ECG_delineation';
        target_units = 'ADCu';
        doPayload = true;
        cAnnotationFields = { 'Pon' 'P' 'Poff' 'QRSon' 'qrs' 'Q' 'R' 'S' 'QRSoff' 'Ton' 'T' 'Toff' };

    end

    properties( GetAccess = public, SetAccess = private)
        % if user = memory;
        % memory_constant is the fraction respect to user.MaxPossibleArrayBytes
        % which determines the maximum input data size.
        memory_constant = 0.3;

        started = false;

    end

    properties( Access = private, Constant)

        cQRSdelineators = {'all-delineators' 'wavedet' };

    end

    properties( Access = private )

        delineators2do
        bWFDBdelineators
        tmp_path_local
        command_sep
        WFDB_bin_path
        lead_idx = [];
        lead_names

    end

    properties
        progress_handle
        only_ECG_leads = false;
        delineators = 'all-delineators';
        wavedet_config
        payload
        tmp_path
    end

    methods

        function obj = ECGtask_ECG_delineation(obj)

            % wavedet configuration structure
            obj.wavedet_config.setup.wavedet.QRS_detection_only = false;

        end

        function Start(obj, ECG_header, ECG_annotations)

            if( obj.only_ECG_leads )
                obj.lead_idx = get_ECG_idx_from_header(ECG_header);
            else
%                 'all-leads'
                obj.lead_idx = 1:ECG_header.nsig;
            end

            % lead names desambiguation
            str_aux = regexprep(cellstr(ECG_header.desc), '\W*(\w+)\W*', '$1');
            obj.lead_names = regexprep(str_aux, '\W', '_');

            [str_aux2, ~ , aux_idx] = unique( obj.lead_names );
            aux_val = length(str_aux2);

            if( aux_val ~= ECG_header.nsig )
                for ii = 1:aux_val
                    bAux = aux_idx==ii;
                    aux_matches = sum(bAux);
                    if( sum(bAux) > 1 )
                        obj.lead_names(bAux) = strcat( obj.lead_names(bAux), repmat({'v'}, aux_matches,1), cellstr(num2str((1:aux_matches)')) );
                    end
                end
            end

            obj.lead_names = regexprep(obj.lead_names, '\W*(\w+)\W*', '$1');
            obj.lead_names = regexprep(obj.lead_names, '\W', '_');

            if( strcmpi('all-delineators', obj.delineators) )
                obj.delineators2do = obj.cQRSdelineators(2:end);
            else
                if( ischar(obj.delineators) )
                    obj.delineators2do = {obj.delineators};
                else
                    obj.delineators2do = obj.delineators;
                end
            end

            % local path required to avoid network bottlenecks in distributed filesystems
            if( isunix() && exist('/scratch/', 'dir') )
                str_username = getenv('USER');
                obj.tmp_path_local = ['/scratch/' str_username filesep];
                if( ~exist(obj.tmp_path_local, 'dir') )
                    if(~mkdir(obj.tmp_path_local))
                        obj.tmp_path_local = '/scratch/';
                    end
                end
                obj.tmp_path = tempdir;
            else
                if( isempty(obj.tmp_path) )
                    obj.tmp_path = tempdir;
                    obj.tmp_path_local = tempdir;
                end
            end

            obj.started = true;

        end

        function payload_out = Process(obj, ECG, ECG_start_offset, ECG_sample_start_end_idx, ECG_header, ECG_annotations, ECG_annotations_start_end_idx )

            payload_out = [];

            if( ~obj.started )
                obj.Start(ECG_header);
                if( ~obj.started )
                    cprintf('*[1,0.5,0]', 'Task %s unable to be started for %s.\n', obj.name, ECG_header.recname);
                    return
                end
            end

            % payload property is used in this task to input an external QRS
            % detector, or manually corrected detections.
            aux_struct = get_QRS_from_payload(obj.payload, ECG_start_offset, ECG_sample_start_end_idx);

            cant_QRSdelineators = length(obj.delineators2do);

            for ii = 1:cant_QRSdelineators

                this_delineator = obj.delineators2do{ii};

                [this_delineator, this_delineator_name] = strtok(this_delineator, ':');
                if( isempty(this_delineator_name) )
                    this_delineator_name = this_delineator;
                else
                    this_delineator_name = this_delineator_name(2:end);
                end

                cprintf( 'Blue', [ 'Processing ECG delineator ' this_delineator_name '\n' ] );

                %% perform ECG delineation

                switch( this_delineator )

                    case 'wavedet'
                    %% Wavedet delineation

                        obj.progress_handle.checkpoint(['Processing ' this_delineator])

                        try

                            [position_multilead, positions_single_lead] = wavedet_interface(ECG, ECG_header, aux_struct, obj.lead_idx, obj.wavedet_config, ECG_sample_start_end_idx, ECG_start_offset, obj.progress_handle);

                            for jj = 1:length(obj.lead_idx)
                                payload_out.wavedet.(obj.lead_names{obj.lead_idx(jj)}) = positions_single_lead(jj);
                            end

                            payload_out.wavedet.multilead = position_multilead;

                        catch aux_ME

                            strAux = sprintf('Wavedet failed in recording %s\n', ECG_header.recname);
                            strAux = sprintf('%s\n', strAux);

                            report = getReport(aux_ME);
                            fprintf(2, '%s\nError report:\n%s', strAux, report);

                        end

                    case 'user'
                        %% user-defined delineator

                        obj.progress_handle.checkpoint(['Processing ' this_delineator_name])

                        try

                            if( exist(this_delineator_name) == 2 )

%                                 ud_func_pointer = eval(['@' this_delineator_name]);
                                ud_func_pointer = str2func(this_delineator_name);

                                obj.progress_handle.checkpoint([ 'User defined function: ' this_delineator_name])

                                ECG_header_aux = trim_ECG_header(ECG_header, obj.lead_idx);

                                [positions_single_lead, position_multilead] = ud_func_pointer( double(ECG(:,obj.lead_idx)), ECG_header_aux, obj.progress_handle, aux_struct);

                                % filter and offset delineation
                                for jj = 1:length(obj.lead_idx)

                                    % filter heartbeats within range
                                    bAux = positions_single_lead(jj).qrs >= ECG_sample_start_end_idx(1) & positions_single_lead(jj).qrs <= ECG_sample_start_end_idx(2);

                                    aux_struct = [];
                                    for fn = rowvec(fieldnames(positions_single_lead(jj)))
                                        aux_val = positions_single_lead(jj).(fn{1});
                                        if( any(strcmpi(obj.cAnnotationFields, fn{1})) )
                                            aux_struct.(fn{1}) = aux_val(bAux) + ECG_start_offset - 1;
                                        else
                                            aux_struct.(fn{1}) = aux_val;
                                        end
                                    end

                                    payload_out.(this_delineator_name).(obj.lead_names{obj.lead_idx(jj)}) = aux_struct;

                                end

                                if( ~isempty(position_multilead) )

                                    % filter heartbeats within range
                                    bAux = position_multilead.qrs >= ECG_sample_start_end_idx(1) & position_multilead.qrs <= ECG_sample_start_end_idx(2);

                                    aux_struct = [];
                                    for fn = rowvec(fieldnames(position_multilead))
                                        aux_val = position_multilead.(fn{1});
                                        if( any(strcmpi(obj.cAnnotationFields, fn{1})) )
                                            aux_struct.(fn{1}) = aux_val(bAux) + ECG_start_offset - 1;
                                        else
                                            aux_struct.(fn{1}) = aux_val;
                                        end
                                    end

                                    position_multilead = aux_struct;

                                    payload_out.(this_delineator_name).multilead = position_multilead;

                                end

                            else
                                disp_string_framed(2, sprintf('Function "%s" is not reachable in path.', this_delineator_name));
                                fprintf(1, 'Make sure that exist(%s) == 2\n',this_delineator_name);
                            end

                        catch aux_ME

                            disp_string_framed(2, sprintf('Delineator "%s" failed in recording %s lead %s', this_delineator_name, ECG_header.recname, ECG_header.desc(jj,:) ) );

                            report = getReport(aux_ME);
                            fprintf(2, 'Error report:\n%s', report);

                        end

                end

            end

        end

        function payload = Finish(obj, payload, ECG_header)

        end

        function payload = Concatenate(obj, plA, plB)

            if( isempty(plA) )

                payload = plB;

            else
                for this_ECG_delineator = rowvec(fieldnames(plA))

                    this_ECG_delineator = this_ECG_delineator{1};

                    if( isfield(plB, this_ECG_delineator)  )

                        for this_lead = rowvec(fieldnames(plA.(this_ECG_delineator)))

                            this_lead = this_lead{1};

                            if( isfield(plB.(this_ECG_delineator), this_lead)  )

                                for fn = rowvec(fieldnames(plA.(this_ECG_delineator).(this_lead) ))

                                    if( isfield(plB.(this_ECG_delineator).(this_lead), fn{1}) )
                                        payload.( this_ECG_delineator).(this_lead).(fn{1}) = [ colvec(plA.( this_ECG_delineator).(this_lead).(fn{1}) ); colvec( plB.(this_ECG_delineator).(this_lead).(fn{1}) ) ];
                                    else
                                        error('ECGtask_ECG_delineation:BadTMPfiles', ['Results from ' this_ECG_delineator '.' this_lead  '.' fn{1} ' not found. Skipping concatenation.'])
                                    end

                                end
                            else
                                error('ECGtask_ECG_delineation:BadTMPfiles', ['Results from ' this_ECG_delineator '.' this_lead ' not found. Skipping concatenation.'] )
                            end
                        end

                    else
                        error('ECGtask_ECG_delineation:BadTMPfiles', ['Results from ' this_ECG_delineator ' not found. Skipping concatenation.'] )
                    end
                end
            end

        end

        %% property restriction functions

        function set.delineators(obj,x)
            if( (ischar(x) || iscellstr(x)) )
                x = cellstr(x);
                aux_val = colvec(intersect(obj.cQRSdelineators, x));
                aux_idx = find(cellfun(@(a)(~isempty(strfind(a, 'user:'))), x));
                obj.delineators = [aux_val; colvec(x(aux_idx)) ];
            else
                warning('ECGtask_ECG_delineation:BadArg', 'Invalid delineators.');
            end
        end

        function set.tmp_path_local(obj,x)
            if( ischar(x) )
                if(exist(x, 'dir'))
                    obj.tmp_path_local = x;
                else
                    if(mkdir(x))
                        obj.tmp_path_local = x;
                    else
                        warning('ECGtask_ECG_delineation:BadArg', ['Could not create ' x ]);
                    end
                end

            else
                warning('ECGtask_ECG_delineation:BadArg', 'tmp_path_local must be a string.');
            end
        end

        function set.tmp_path(obj,x)
            if( ischar(x) )
                if(exist(x, 'dir'))
                    obj.tmp_path = x;
                else
                    if(mkdir(x))
                        obj.tmp_path = x;
                    else
                        warning('ECGtask_ECG_delineation:BadArg', ['Could not create ' x ]);
                    end
                end

            else
                warning('ECGtask_ECG_delineation:BadArg', 'tmp_path_local must be a string.');
            end
        end

    end

    methods ( Access = private )

    end

end


% ----------------------------------------------------------------------------
% function hfssExportToFileFC(fid, Name, PTS, Solution, Freq, [Phase = 0])
%
% Description :
% -------------
% Evaluates the top stack element at a set of points specified in an
% external file (PTS) and exports the data to a file (Name).
%
% Parameters :
% ------------
% fid      - file identifier of the HFSS script file.
% Name     - name of the field where the stack element will be exported.
% PTS      - name of the PTS file containing the grid (with extension).
% Solution - solution being resolved.
% Freq     - frequency of the solution.
% Phase    - phase of the field in degrees.
%
% Note :
% ------
% 1) This function is bad documented in scripting.pdf. It only indicates
%    two arguments while in reallity it requires more.
% 2) A grid can be generated by the function hfssCreatePTS.
%
% Example :
% ---------
% hfssCreatePTS(fid, 'MyGrid', [0 0 0], [1 1 1], [.1 .1 .1], 'mm');
% hfssEnterQty(fid, 'E');
% hfssExportToFileTC(fid, 'EField', 'MyGrid.pts', 'Solution', Freq);
%
% ----------------------------------------------------------------------------

% ----------------------------------------------------------------------------
% CHANGELOG
%
% 06-Oct-2012: *Initial release.
% ----------------------------------------------------------------------------

% ----------------------------------------------------------------------------
% Written by Daniel R. Prado
% danysan@gmail.com / drprado@tsc.uniovi.es
% 06 October 2012
% ----------------------------------------------------------------------------
function hfssExportToFileFC(fid, Name, PTS, Solution, Freq, Phase)

% Arguments processor.
if (nargin < 5)
	error('Insufficient # of arguments !');
elseif (nargin < 6)
    Phase = [];
end

% Setup default arguments
if isempty(Phase)
    Phase = 0;
end

% Preamble
fprintf(fid, '\n');
fprintf(fid, 'Set oModule = oDesign.GetModule("FieldsReporter")\n');

% Command
fprintf(fid, 'oModule.ExportToFile "%s.fld", _\n', Name);
fprintf(fid, '"%s", _\n', PTS);
fprintf(fid, '"%s : LastAdaptive", _\n', Solution);
fprintf(fid, 'Array("Freq:=", "%fGHz", _\n', Freq);
fprintf(fid, '"Phase:=", "%fdeg"), true\n', Phase);


classdef TestFastFeatureDetector
    %TestFastFeatureDetector

    properties (Constant)
        im = fullfile(mexopencv.root(),'test','tsukuba_l.png');
        kfields = {'pt', 'size', 'angle', 'response', 'octave', 'class_id'};
    end

    methods (Static)
        function test_detect_img
            img = imread(TestFastFeatureDetector.im);
            obj = cv.FastFeatureDetector('Threshold',10, 'NonmaxSuppression',true);
            assert(obj.Threshold == 10);
            assert(obj.NonmaxSuppression == true);
            typename = obj.typeid();

            kpts = obj.detect(img);
            validateattributes(kpts, {'struct'}, {'vector'});
            assert(all(ismember(TestFastFeatureDetector.kfields, fieldnames(kpts))));
        end

        function test_detect_imgset
            img = imread(TestFastFeatureDetector.im);
            imgs = {img, img};
            obj = cv.FastFeatureDetector();

            kpts = obj.detect(imgs);
            validateattributes(kpts, {'cell'}, {'vector', 'numel',numel(imgs)});
            cellfun(@(kpt) validateattributes(kpt, {'struct'}, {'vector'}), kpts);
            cellfun(@(kpt) assert(all(ismember(TestFastFeatureDetector.kfields, fieldnames(kpt)))), kpts);
        end

        function test_detect_mask
            img = imread(TestFastFeatureDetector.im);
            mask = zeros(size(img), 'uint8');
            mask(:,1:end/2) = 255;  % only search left half of the image
            obj = cv.FastFeatureDetector();
            kpts = obj.detect(img, 'Mask',mask);
            validateattributes(kpts, {'struct'}, {'vector'});
            assert(all(ismember(TestFastFeatureDetector.kfields, fieldnames(kpts))));
            xy = cat(1, kpts.pt);
            assert(all(xy(:,1) <= ceil(size(img,2)/2)));
        end

        function test_detect_types
            types = {'TYPE_5_8', 'TYPE_7_12', 'TYPE_9_16'};
            img = imread(TestFastFeatureDetector.im);
            obj = cv.FastFeatureDetector();
            for i=1:numel(types)
                obj.Type = types{i};
                kpts = obj.detect(img);
                validateattributes(kpts, {'struct'}, {'vector'});
            end
        end

        function test_error_1
            try
                cv.FastFeatureDetector('foobar');
                throw('UnitTest:Fail');
            catch e
                assert(strcmp(e.identifier,'mexopencv:error'));
            end
        end
    end

end


function [wordCounts] = updateCounts(wordCounts, data)
  wordCounts.word = wordCounts.word + data.numWords;
  if isfield(data, 'numChars') && data.numChars > 0
    wordCounts.char = wordCounts.char + data.numChars;
  end
end


function clearex(varargin)
%% This function clear all workspace
% except for one or more selected variable
%
% File created on Feb 15th 2008
%
% Last modified: Feb 15th 2008
%
% Author: Arnaud Laurent
%
%   Inputs: name of variables to keep (e.g. 'a','b','c')
%   Note: It is possible to use wildcard (e.g. 'a*')

a = evalin('base','who');
var = cell(size(varargin));
for i=1:nargin
    var{i}=varargin{i};
end
assignin('base','ClEaRGsJioU',var);
var = evalin('base','who(ClEaRGsJioU{:})');
clearvar = a(~ismember(a,var));
assignin('base','ClEaRGsJioU',clearvar);
evalin('base','clear(ClEaRGsJioU{:},''ClEaRGsJioU'')')


%%train network config
param.caffe_path=fullfile(fileparts(pwd),'matcaffe');
param.solver_netfile=fullfile(fileparts(pwd),'model','solver_bn.prototxt');
param.fintune_model=fullfile(fileparts(pwd),'pretrain_model','googlenet_bn.caffemodel');
param.test_net_file=fullfile(fileparts(pwd),'model','test_bn.prototxt');
param.test_batch_size=32;
param.result_save_file='LPW.txt';
param.save_model_file='LPW';
param.save_model_name='LPW_iter';
param.test_interval=500;
param.train_maxiter=50000;
param.data_path=fullfile(fileparts(pwd),'generate_data');
param.traindata_filename='train_data_LPW';
param.testdata_filename='test_data_LPW';
param.use_data_split_index=1;
param.data_split_num=1;
param.test_person_num=1975;
param.use_gpu=1;
param.gpu_id=0;
%%
for split_index=param.use_data_split_index:param.data_split_num
    if ~exist(strcat(param.save_model_file,num2str(split_index)),'file')
        mkdir(strcat(param.save_model_file,num2str(split_index)));
    end
    load(strcat(param.data_path,param.traindata_filename,num2str(split_index),'/train_data.mat'));
    %% find caffe
    cur_path=pwd;
    addpath(genpath(param.caffe_path))
    cd(param.caffe_path);
    caffe.reset_all;
    caffe.init_log(fullfile(cur_path,'log'));
    addpath(param.caffe_path);
    if param.use_gpu
        caffe.set_mode_gpu;
    else
        caffe.set_mode_cpu;
    end
    cd(cur_path);
    caffe_solver=caffe.get_solver(param.solver_netfile,param.gpu_id);
	net=caffe.get_net(param.test_net_file,'test');
    caffe_solver.use_caffemodel(param.fintune_model);
    input_data_shape=caffe_solver.nets{1}.blobs('data').shape;
    batch_size=input_data_shape(4);
    batch_data=zeros(input_data_shape,'single');
    batch_label=zeros(1,1,1,batch_size,'single');
    train_x_axis=[];
    train_y_axis=[];
    label_train=[label_train_cam2_view1;label_train_cam2_view2;label_train_cam2_view3;label_train_cam2_view4; ...
        label_train_cam3_view1;label_train_cam3_view2;label_train_cam3_view3;label_train_cam3_view4];
    train_image_name=[train_image_name_cam2_view1,train_image_name_cam2_view2,train_image_name_cam2_view3,train_image_name_cam2_view4, ...
        train_image_name_cam3_view1,train_image_name_cam3_view2,train_image_name_cam3_view3,train_image_name_cam3_view4];
    iter=0;
    while iter<param.train_maxiter
        [batch_data,batch_label]=get_train_minibatch_for_LPW(batch_data,batch_label,batch_size, ...
            label_train,train_image_name);
        caffe_solver.nets{1}.blobs('data').set_data(batch_data);
        caffe_solver.nets{1}.blobs('label').set_data(batch_label);
        caffe_solver.step(1);
        iter = caffe_solver.iter;
        if mod(iter,10)==0
            soft_loss=caffe_solver.nets{1}.blobs('loss3/loss').get_data;
            acc=caffe_solver.nets{1}.blobs('loss3/top-1').get_data;
            loss1=caffe_solver.nets{1}.blobs('loss1/loss').get_data;
            loss2=caffe_solver.nets{1}.blobs('loss2/loss').get_data;
            train_x_axis=[train_x_axis,iter];
            train_y_axis=[train_y_axis,soft_loss];
            plot(train_x_axis,train_y_axis);
            drawnow;
            fprintf('iter= %d ,softmaxloss= %f ,loss1=%f  loss2=%f\n',iter,soft_loss,loss1,loss2);
        end
        if mod(iter,param.test_interval)==0
            caffe_solver.nets{1}.save(strcat(param.save_model_file,num2str(split_index),'/',param.save_model_name,'_',num2str(iter),'.caffemodel'));
            net.copy_from(strcat(param.save_model_file,num2str(split_index),'/',param.save_model_name,'_',num2str(iter),'.caffemodel'));
            net.blobs('data').reshape([224 224 3 param.test_batch_size]);
            load(strcat(param.data_path,param.testdata_filename,num2str(split_index),'/test_data.mat'));
            test_script;
        end
        if iter>10000 && mod(iter,200)==0
            caffe_solver.nets{1}.save(strcat(param.save_model_file,num2str(split_index),'/',param.save_model_name,'_',num2str(iter),'.caffemodel'));
        end
    end
    %test_network_for_LPW;
end


function [inputImages, inputSignals, valid, imageSizes, imgFeatures] = filterImages(inputImages, inputSignals, varargin)
	% Filters large and small objects in an set of images, returns filtered matricies along with vector with decisions and sizes.
	% Biafra Ahanonu
	% 2013.10.31
	% based on SpikeE code
	% inputs
		%
	% outputs
		%

	[inputImages, inputSignals, valid, imageSizes, imgFeatures] = ciapkg.image.filterImages(inputImages, inputSignals,'passArgs', varargin);
end


classdef SensorEnum < matlab.mixin.SetGet
    %SensorEnum Summary of this class goes here
    %   Detailed explanation goes here

    enumeration
        ConicalSensor('Conical Sensor');
        RectangularSensor('Rectangular Sensor');
    end

    properties
        name(1,:) char
    end

    methods
        function obj = SensorEnum(name)
            obj.name = name;
        end
    end

    methods(Static)
        function listBoxStr = getListBoxStr()
            m = enumeration('SensorEnum');
            [~,I] = sort({m.name});
            listBoxStr = {m(I).name};
        end

        function [ind, enum] = getIndForName(name)
            m = enumeration('SensorEnum');
            [~,I] = sort({m.name});
            m = m(I);
            ind = find(ismember({m.name},name),1,'first');
            enum = m(ind);
        end

        function [enum, ind] = getEnumForListboxStr(nameStr)
            m = enumeration('SensorEnum');
            ind = find(ismember({m.name},nameStr),1,'first');
            enum = m(ind);
        end
    end
end


function menu = MakeNumberedMenu(names) % e.g. name = {Cluster.name} (note {})
menu = [{'(choose)'},names];
for j=2:length(menu),menu(j)={[num2str(j-1) ': ' menu{j}]};end
end


function sT = rotate(sT, rot)

sT.vertices = rotate(sT.vertices,rot);
sT.edges = rotate(sT.edges,rot);

end


function z = uplus( x )

%   Disciplined convex/geometric programming information for UPLUS (+):
%      Unary plus may be used in DCPs and DGPs without restrictions.

z = x;

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


% Generates test fixtures for whole cell simulation.
%
% Author: Jonathan Karr, jkarr@stanford.edu
% Author: Jared Jacobs, jmjacobs@stanford.edu
% Affilitation: Covert Lab, Department of Bioengineering, Stanford University
% Last updated: 5/31/2011
function generateTestFixtures()
import edu.stanford.covert.cell.sim.runners.SimulationRunner;

%create runner which helps do the tasks below
runner = SimulationRunner(...
    'useCachedKb', false, ...
    'useCachedSim', false, ...
    'cacheKb', true, ...
    'cacheSimulation', true);

%construction KB and simulation
sim = runner.constructKbAndSimulation();

% verify initial growth rate distribution
if ~runner.isSimulationFitted(sim)
    throw(MException('Simulation:notFitted', 'Simulation not properly fitted'));
end

%save simulation parameters in JSON format
runner.cacheSimulationParametersJson(sim);

%generate simulation test fixtures
runner.cacheSimulationTestFixtures(sim);


function mtrFigureDOCCSagProj(machineDir, imageDir)

subjVec = {'ss040804','mho040625','bg040719','md040714'};
paramNames = {'kLength','kSmooth','kMidSD'};

threshVec = [1000 500];
params = [-2 18 0.175];
pathsRootFile = ['paths_100k_5k_kSmooth_' num2str(params(2)) '_kLength_' num2str(params(1)) '_kMidSD_' num2str(params(3))];
pathsFile = [pathsRootFile '.dat'];

for ss = 1:length(subjVec)
    subjDir = fullfile(machineDir,subjVec{ss});

    combSTTFile = 'sttDOCCComb.nii.gz';
    fgDir = fullfile(subjDir, 'fibers');
    disp(['cd ' fgDir]);
    cd(fgDir);
    f = dir('paths_STT_LDOCC*_fd_image.nii.gz');
    if isempty(f)
        disp('Density file does not exist, creating it ...');
        mtrComputeManyFiberDensities(subjDir,'paths_STT_LDOCC.dat',3000);
    end
    sttDensityImage = dir('paths_STT_LDOCC*_fd_image.nii.gz');
    niL = niftiRead(sttDensityImage(1).name);

    f = dir('paths_STT_RDOCC*_fd_image.nii.gz');
    if isempty(f)
        disp('Density file does not exist, creating it ...');
        mtrComputeManyFiberDensities(subjDir,'paths_STT_RDOCC.dat',3000);
    end
    sttDensityImage = dir('paths_STT_RDOCC*_fd_image.nii.gz');
    niR = niftiRead(sttDensityImage(1).name);

    imgComb = double(niL.data>0) + double(niR.data>0)*2;
    disp(['Writing ' fullfile(subjDir,imageDir,combSTTFile) ' ...']);
    dtiWriteNiftiWrapper(imgComb,niL.qto_xyz,fullfile(subjDir,imageDir,combSTTFile));

    for tt = threshVec
        densityFile = [pathsRootFile '_thresh_' num2str(tt) '_fd_image.nii.gz'];
        combFile = ['ctDOCCComb_kLength_' num2str(params(1)) '_thresh_' num2str(tt) '.nii.gz'];

        fgDir = fullfile(subjDir, 'conTrack/resamp_LDOCC');
        disp(['cd ' fgDir]);
        cd(fgDir);
        % See if density file exists
        f = dir(densityFile);
        if isempty(f)
            disp('Density file does not exist, creating it ...');
            mtrComputeManyFiberDensities(subjDir,pathsFile,threshVec);
        end
        niL = niftiRead(densityFile);

        fgDir = fullfile(subjDir,'conTrack/resamp_RDOCC');
        disp(['cd ' fgDir]);
        cd(fgDir);
        % See if density file exists
        f = dir(densityFile);
        if isempty(f)
            mtrComputeManyFiberDensities(subjDir,pathsFile,threshVec);
        end
        niR = niftiRead(densityFile);

        imgComb = double(niL.data>0) + double(niR.data>0)*2;
        disp(['Writing ' fullfile(subjDir,imageDir,combFile) ' ...']);
        dtiWriteNiftiWrapper(imgComb,niL.qto_xyz,fullfile(subjDir,imageDir,combFile));
    end
end


function x = colvec(x)
% Reshape the input into a column vector
% This function is useful when a function returns a row vector or matrix
% and you want to immediately reshape it in a functional way. Suppose
% f(a,b,c) returns a row vector, matlab will not let you write f(a,b,c)(:)
% - you would have to first store the result. With this function you can
% write colvec(f(a,b,c)) and be assured that the result is a column vector.

% This file is from pmtk3.googlecode.com

x = x(:);
end


function varargout = dirod(varargin)
% Date sorted folder listing
%
% USAGE:
%   dirod
%   dirod FILENAMEMASK
%   dirod('FILENAMEMASK')
%   LIST = dirod('FILENAMEMASK')
%
% INPUTS:
%   FILENAMEMASK - A file name mask, e.g. *, *.m or d*.m
%
% OUTPUTS:
%   LIST         - A directory structure ordered by date
%
% COMMENTS:
%   Similar to the windows command dir /od

% Copyright: Kevin Sheppard
% kevin.sheppard@economics.ox.ac.uk
% Revision: 1    Date: 6/7/2010

if isempty(varargin)
    arg = '*';
elseif length(varargin)==1
    arg = varargin{1};
else
    error('Too many input arguments.  0 or 1 input only');
end

dirData = dir(arg);
[~,index]=sort([dirData.datenum]);
dirData = dirData(index);

fileNameLen = length(dirData(1).name);
for i=2:length(dirData)
    fileNameLen = max(fileNameLen,length(dirData(i).name));
end

if nargout==0
    disp(' ');
    for i=1:length(dirData)
        disp([dirData(i).name repmat(' ',1,fileNameLen-length(dirData(i).name)+2) dirData(i).date]);
    end
    disp(' ');
else
    varargout{1} = dirData;
end


%{
    Copyright (C) Cambridge Electronic Design Limited 2014
    Author: James Thompson
    Web: www.ced.co.uk email: james@ced.co.uk, softhelp@ced.co.uk

    This file is part of CEDS64ML, a MATLAB interface to the SON64 library.

    CEDS64ML 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.

    CEDS64ML 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 CEDS64ML.  If not, see <http://www.gnu.org/licenses/>.
%}

function [ i64MaxTime ] = CEDS64MaxTime( fhand )
%CEDS64MAXTIME Returns the maximum time in ticks of all channels for a SON file
%   [ i64MaxTime ] = CEDS64MaxTime( fhand )
%   Inputs
%   fhand - Integer file handle
%   Outputs
%   i64MaxTime - the maximum time in ticks of all channels in this file,
%   otherwise a negative error code.

if (nargin ~= 1)
    i64MaxTime = -22;
    return;
end

i64MaxTime = calllib('ceds64int', 'S64MaxTime', fhand);
end


function data = boxesData( varargin )
% Get ground truth data for object proposal bounding box evaluation.
%
% Used to get dataset information for evaluation of bounding box object
% proposals using boxesEval.m. This requires separate download of the
% appropriate dataset first. Currently only set up for PASCAL VOC 2007 but
% it would be fairly simple to extend to other datasets.
%
% As a first step, it is necessary to download the PASCAL VOC 2007 dataset.
% Go to http://pascallin.ecs.soton.ac.uk/challenges/VOC/voc2007/ and get:
%  VOCdevkit_08-Jun-2007, VOCtrainval_06-Nov-2007, VOCtest_06-Nov-2007
% After downloading these files extract them to dataDir='boxes/VOCdevkit'.
% Once complete this function will process the data and return information
% in a convenient format for boxesEval.m.
%
% USAGE
%  data = boxesData( opts )
%
% INPUTS
%  opts       - parameters (struct or name/value pairs)
%   .resDir     - ['boxes/'] location for results and evaluation
%   .dataDir    - ['boxes/VOCdevkit/'] dir containing PASCAL VOC 2007
%   .split      - ['val'] data split for evaluation
%
% OUTPUTS
%  data       - dataset split information
%   .split      - data split for evaluation
%   .n          - number of images
%   .ids        - list of string ids
%   .imgs       - list of image filenames
%   .gt         - ground truth for each image
%
% EXAMPLE
%
% See also edgeBoxesDemo, edgeBoxes, boxesEval
%
% Structured Edge Detection Toolbox      Version 3.0
% Copyright 2014 P. Dollar and L. Zitnick.  [pdollar-at-microsoft.com]
% Please email me if you find bugs, or have suggestions or questions!
% Licensed under the MSR-LA Full Rights License [see license.txt]

% get default parameters (unimportant parameters are undocumented)
dfs={ 'resDir','boxes/', 'dataDir','boxes/VOCdevkit/', 'split','val' };
o=getPrmDflt(varargin,dfs,1);

% locations of PASCAL VOC dataset
if(~exist(o.dataDir,'dir')), error('dataset not found, see help'); end
imDir=[o.dataDir 'VOC2007/JPEGImages/'];
gtDir=[o.dataDir 'VOC2007/Annotations/'];
imList=[o.dataDir 'VOC2007/ImageSets/Main/' o.split '.txt'];
addpath(genpath([o.dataDir 'VOCcode']));

% check if data already exists, if yes load and return
dataNm=[o.resDir '/GroundTruth' '-' o.split '.mat'];
if(exist(dataNm,'file')), data=load(dataNm); data=data.data; return; end

% get list of image ids
if(~exist(imList,'file')), error('ids file not found'); end
f=fopen(imList); ids=textscan(f,'%s %*s'); ids=ids{1}; fclose(f);

% generate list of image and gt filenames then load gt
n=length(ids); imgs=cell(n,1); gt=cell(n,1);
for i=1:n, imgs{i}=[imDir ids{i} '.jpg']; end
for i=1:n, gt{i}=[gtDir ids{i} '.xml']; end
if(~exist(imgs{1},'file')), error('images not found'); end
if(~exist(gt{1},'file')), error('annotations not found'); end
parfor i=1:n, [~,gt{i}]=bbGt('bbLoad',gt{i},'format',1); end

% create output structure and cache to disk
data=struct('split',o.split,'n',n,'ids',{ids},'imgs',{imgs},'gt',{gt});
if(~exist(o.resDir,'dir')), mkdir(resDir); end; save(dataNm,'data');

end


function [netSecretionFluxes, netUptakeFluxes, Y, modelStats, summary, statistics, modelsOK] = mgPipe(modPath, abunFilePath, computeProfiles, resPath, dietFilePath, infoFilePath, biomasses, hostPath, hostBiomassRxn, hostBiomassRxnFlux, figForm, numWorkers, rDiet, pDiet, lowerBMBound, upperBMBound, includeHumanMets, adaptMedium, pruneModels)
% mgPipe is a MATLAB based pipeline to integrate microbial abundances
% (coming from metagenomic data) with constraint based modeling, creating
% individuals' personalized models.
% The pipeline is divided in 3 parts:
% [PART 1] Analysis of individuals' specific microbes abundances are computed.
% [PART 2]: 1 Constructing a global metabolic model (setup) containing all the
% microbes listed in the study. 2 Building individuals' specific models
% integrating abundance data retrieved from metagenomics. For each organism,
% reactions are coupled to the objective function.
% [PART 3] Simulations under different diet regimes.
%
% USAGE:
%       [netSecretionFluxes, netUptakeFluxes, Y, modelStats, summary, statistics, modelsOK] = mgPipe(modPath, abunFilePath, computeProfiles, resPath, dietFilePath, infoFilePath, biomasses, hostPath, hostBiomassRxn, hostBiomassRxnFlux, figForm, numWorkers, rDiet, pDiet, lowerBMBound, upperBMBound, includeHumanMets, adaptMedium, pruneModels)
%
% INPUTS:
%    modPath:                char with path of directory where models are stored
%    abunFilePath:           char with path and name of file from which to retrieve abundance information
%    computeProfiles:        boolean defining whether flux variability analysis to
%                            compute the metabolic profiles should be performed.
%    resPath:                char with path of directory where results are saved
%    dietFilePath:           char with path of directory where the diet is saved
%    infoFilePath:           char with path to stratification criteria if available
%    biomasses:              Cell array containing names of biomass objective functions
%                            of models to join. Needs to be the same length as
%                            the length of models in the abundance file.
%    hostPath:               char with path to host model, e.g., Recon3D (default: empty)
%    hostBiomassRxn:         char with name of biomass reaction in host (default: empty)
%    hostBiomassRxnFlux:     double with the desired flux through the host
%                            biomass reaction (default: zero)
%    figForm:                format to use for saving figures
%    numWorkers:             integer indicating the number of cores to use for parallelization
%    rDiet:                  boolean indicating if to enable also rich diet simulations (default: 'false')
%    pDiet:                  boolean indicating if to enable also personalized diet simulations (default: 'false')
%    lowerBMBound:           lower bound on community biomass (default=0.4)
%    upperBMBound:           upper bound on community biomass (default=1)
%    includeHumanMets:       boolean indicating if human-derived metabolites
%                            present in the gut should be provided to the models (default: true)
%    adaptMedium:            boolean indicating if the medium should be adapted through the
%                            adaptVMHDietToAGORA function or used as is (default=true)
%    pruneModels:            boolean indicating whether reactions that do not carry flux on the
%                            input diet should be removed from the microbe models.
%                            Recommended for large datasets (default: false)
%
% OUTPUTS:
%    init:                   status of initialization
%    netSecretionFluxes:     Net secretion fluxes by microbiome community models
%    netUptakeFluxes:        Net uptake fluxes by microbiome community models
%    Y:                      Classical multidimensional scaling
%    modelStats:             Reaction and metabolite numbers for each model
%    summary:                Table with average, median, minimal, and maximal
%                            reactions and metabolites
%    statistics:             If info file with stratification is provided, will
%                            determine if there is a significant difference.
%    modelsOK:               Boolean indicating if the created microbiome models
%                            passed verifyModel. If true, all models passed.
%
% AUTHORS:
%   - Federico Baldini, 2017-2018
%   - Almut Heinken, 07/20: converted to function
%   - Almut Heinken, 01/21: adapted inputs

%% PIPELINE: [PART 1]
% If PART1 was already
% computed: if the associated file is already present in the results folder its
% execution is skipped else its execution starts

[mapP]=detectOutput(resPath,'mapInfo.mat');

if ~isempty(mapP)
    s= 'mapping file found: loading from resPath and skipping [PART1] analysis';
    disp(s)
    load(strcat(resPath,'mapInfo.mat'))
end

[mapP]=detectOutput(resPath,'mapInfo.mat');
if isempty(mapP)
    % check if AGORA models are in path
    if ~exist('modPath', 'var') || ~exist(modPath, 'dir')
        error('modPath is not defined. Please set the path of the model directory.');
    else
        if ~exist(modPath, 'dir')
            error(['modPath (' modPath ') does not exist.']);
        end
    end

    abundance = table2cell(readtable(abunFilePath));

    % The number of microbeNames, their names, the number of samples and their identifiers
    % are automatically detected from the input file.
    [sampNames,microbeNames,exMets]=getIndividualSizeName(abunFilePath,modPath);

    % remove rows of organisms that are not present in any sample
    if contains(version,'(R202') % for Matlab R2020a and newer
        microbeNames(sum(cell2mat(abundance(:,2:end)),2)<0.0000001,:)=[];
        abundance(sum(cell2mat(abundance(:,2:end)),2)<0.0000001,:)=[];
    else
        microbeNames(sum(str2double(abundance(:,2:end)),2)<0.0000001,:)=[];
        abundance(sum(str2double(abundance(:,2:end)),2)<0.0000001,:)=[];
    end

    % Extracellular spaces simulating the lumen are built and stored for
    % each microbe.
    [activeExMets,couplingMatrix]=buildModelStorage(microbeNames, modPath, dietFilePath, adaptMedium, includeHumanMets, numWorkers, pruneModels, biomasses);

    % Computing reaction abundance and reaction presence
    [ReactionAbundance,ReactionPresence] = fastCalculateReactionAbundance(abunFilePath, modPath, {}, numWorkers);
    writetable(cell2table(ReactionAbundance'),[resPath filesep 'ReactionAbundance.csv'], 'WriteVariableNames', false);
    writetable(cell2table(ReactionPresence'),[resPath filesep 'ReactionPresence.csv'], 'WriteVariableNames', false);

    % Computing subsystem abundance
    subsystemAbundance = calculateSubsystemAbundance([resPath filesep 'ReactionAbundance.csv']);
    writetable(cell2table(subsystemAbundance),[resPath filesep 'SubsystemAbundance.csv'], 'WriteVariableNames', false);

    % plot subsystem abundance
    data=cell2mat(subsystemAbundance(2:end,2:end));
    xlabels=subsystemAbundance(1,2:end);
    ylabels=subsystemAbundance(2:end,1);
    % remove low abundance subsystems
    ylabels(find(sum(data,2)<1),:)=[];
    data(find(sum(data,2)<1),:)=[];
    figure;
    imagesc(data)
    colormap('hot')
    colorbar
    if length(xlabels)<30
        set(gca,'xtick',1:length(xlabels));
        xticklabels(xlabels);
        xtickangle(90)
    end

    if length(xlabels)<30
        set(gca,'xtick',1:length(xlabels));
        xticklabels(xlabels);
        xtickangle(90)
    end
    set(gca,'ytick',1:length(ylabels));
    yticklabels(ylabels);
    ax=gca;

    if length(ylabels)<50
        ax.YAxis.FontSize = 8;
    else
        ax.YAxis.FontSize = 6;
    end

    set(gca,'TickLabelInterpreter', 'none');
    title('Relative reaction abundances summarized by subsystem')
    print(strcat(resPath, 'Subsystem_abundances'), figForm)

    % save mapping info
    save([resPath filesep 'mapInfo.mat'], 'mapP', 'exMets', 'activeExMets', 'sampNames', 'microbeNames', 'couplingMatrix', 'abundance','-v7.3')
end

%end of trigger for Autoload
%% PIPELINE: [PART 2.1]

% Now we detect from the content of the results folder If PART2 was already
% computed: if the associated file is already present in the results folder its
% execution is skipped else its execution starts

% If desired, a model of the host (e.g., Recon3D) can also be joined with
% the microbiome models.
if ~isempty(hostPath)
    % workaround for models that give an error in readCbModel
    try
        host = readCbModel(hostPath);
    catch
        warning('Host model could not be read through readCbModel. Consider running verifyModel.')
        modelStruct=load(hostPath);
        getfn=fieldnames(modelStruct);
        host=modelStruct.(getfn{1});
    end
else
    host = {};
end

%end of trigger for Autoload

% A  model joining all the reconstructions contained in the study
% will be created in this section. This model will be later used, integrating
% abundances coming from the metagenomic sequencing, to derive the different microbiota
% models. The result of this section will be automatically saved in the results
% folder.

%% PIPELINE: [PART 2]
% Now we will create the different microbiota models integrating the given abundances.
% Coupling constraints and personalized "cumulative biomass" objective functions
% are also added. Models that are already existent will not be recreated, and
% new microbiota models will be saved in the results folder.

% set parallel pool
if numWorkers > 1
    poolobj = gcp('nocreate');
    if isempty(poolobj)
        parpool(numWorkers)
    end
end

% create a separate setup model for each sample
% define what counts as zero abundance
tol=0.0000001;

clear('microbeNames','activeExMets','abundance')

if length(sampNames)>50
    steps=50;
else
    steps=length(sampNames);
end
% proceed in batches for improved effiency
cnt=1;
modelsWithErrors={};

for j=1:steps:length(sampNames)
    if length(sampNames)-j>=steps-1
        endPnt=steps-1;
    else
        endPnt=length(sampNames)-j;
    end
    getErrors={};

    parfor i=j:j+endPnt
        % Each personalized model will be created separately.
        % get the list of models for each sample and remove the ones not in
        % this sample

        % check if model already exists
        if ~isempty(host)
            mId = strcat('host_microbiota_model_samp_', sampNames{i,1}, '.mat');
        else
            mId = strcat('microbiota_model_samp_', sampNames{i,1}, '.mat');
        end
        if ~isfile(mId)
            mappingData=load([resPath filesep 'mapInfo.mat'])
            microbeNamesSample = mappingData.microbeNames;
            couplingMatrixSample = mappingData.couplingMatrix;
            abunRed=mappingData.abundance(:,i+1);
            abunRed=[mappingData.abundance(:,1),abunRed];

            microbeNamesSample(cell2mat(abunRed(:,2)) < tol,:)=[];
            couplingMatrixSample(cell2mat(abunRed(:,2)) < tol,:)=[];
            abunRed(cell2mat(abunRed(:,2)) < tol,:)=[];
            setupModel = fastSetupCreator(exMets, microbeNamesSample, host);

            % create personalized models for the batch
            createdModel=createPersonalizedModel(abunRed,resPath,setupModel,sampNames(i,1),microbeNamesSample,couplingMatrixSample,host,hostBiomassRxn);
            results=verifyModel(createdModel{1});
            getErrors{i} = results;
        end
    end
    for i=j:j+endPnt
        if length(getErrors) >= i
            if ~isempty(getErrors{i})
                modelsWithErrors{cnt,1} = sampNames{i,1};
                cnt=cnt+1;
            end
        end
    end
end

if isempty(modelsWithErrors)
    modelsOK = true;
else
    modelsOK = false;
end

%% PIPELINE: [PART 3]
%
% In this phase, for each microbiota model, a diet, in the form of set constraints
% to the exchanges reactions of the diet compartment, is integrated. Flux Variability
% analysis for all the exchange reactions of the diet and fecal compartment is
% also computed and saved in a file called "simRes".

load([resPath filesep 'mapInfo.mat'])
[exchanges, netProduction, netUptake, presolve, infeasModels] = microbiotaModelSimulator(resPath, exMets, sampNames, dietFilePath, hostPath, hostBiomassRxn, hostBiomassRxnFlux, numWorkers, rDiet, pDiet, computeProfiles, lowerBMBound, upperBMBound, includeHumanMets, adaptMedium);
% Finally, NMPCs (net maximal production capability) are computed in a metabolite
% resolved manner and saved in a comma delimited file in the results folder. NMPCs
% indicate the maximal production of each metabolite and are computing summing
% the maximal secretion flux with the maximal uptake flux. Similarity of metabolic
% profiles (using the different NMPCs as features) between individuals are also
% evaluated with classical multidimensional scaling.

if computeProfiles
    [netSecretionFluxes, netUptakeFluxes, Y] = mgSimResCollect(resPath, sampNames, exchanges, rDiet, pDiet, infoFilePath, netProduction, netUptake, figForm);
else
    netSecretionFluxes={};
    netUptakeFluxes={};
    Y=[];
    delete('simRes.mat','intRes.mat')
end

% get stats on microbiome models-number of reactions and metabolites
for i=1:length(sampNames)
    modelNames{i}=['microbiota_model_samp_' sampNames{i}];
end

close all

if ~isempty(infoFilePath)
    [modelStats,summary,statistics]=retrieveModelStats(resPath, modelNames, abunFilePath, numWorkers, infoFilePath);
else
    [modelStats,summary,statistics]=retrieveModelStats(resPath, modelNames, abunFilePath, numWorkers);
end
writetable(cell2table(modelStats),[resPath filesep 'ModelStatistics.csv'], 'WriteVariableNames', false);
writetable(cell2table(summary),[resPath filesep 'ModelStatsSummary.csv'], 'WriteVariableNames', false);
if ~isempty(statistics)
    writetable(cell2table(statistics),[resPath filesep 'ModelStatsStratification.csv'], 'WriteVariableNames', false);
end

end