function     [position,janelas,messages]= twavef_detector(heasig,samp,time,position,w,intervalo,messages)
% script which identifies T wave, as well as its onset and offset
% implementation of changes to improve FN and reduce mean error utilizando a escala 5
%
%
%Input Parameters:
%   heasig: struct vector with header information
%   samp: samples included in the current excerpt (borders excluded)
%   time:  QRS times in inedexes refering the interval included in the current excerpt (borders excluded)
%   position: struct vector with the detected points
%   w: matrix with WT scales 1 to 5
%   intervalo: numeration of the beats processed in this segment
%
%Output Parameters:
%   janelas: T wave seach windows
%   actualized parameters: position
%
% Rute Almeida
% based on twave.m by Juan Pablo Mart�nez Cort�s
% Last update: Rute Almeida  07FEB2012
%
% 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
%
% Designed for MATLAB Version R12; tested with MATLAB Version R13
%
%%%%%% Constants and Thresholds  !!!!!!!!!!!!!!!!!!!!!!!!!
if ~isfield(messages.setup.wavedet,'Kton')
    messages.setup.wavedet.Kton = 4;      % 2 4!
end
if ~isfield(messages.setup.wavedet,'Ktoff')
    messages.setup.wavedet.Ktoff = 2.5;% 3.5 3!
end
if ~isfield(messages.setup.wavedet,'umbraldetT')
    messages.setup.wavedet.umbraldetT = 0.25;  % We use umbraldet*sqrt(mean(w(time(i):time(i+1),4).^2))
end
if ~isfield(messages.setup.wavedet,'umbralsig')
    messages.setup.wavedet.umbralsig  =  1/8;    % To decide if there are really 2, 1 or no peak
    %threshold to decide if there is or not a significative
    % T wave.3!
end
if ~isfield(messages.setup.wavedet,'rrant_mintol')
    messages.setup.wavedet.rrant_mintol  =  0.5;    % minimum time in sec admited for current RR to be used in T wave delineation or in Exponentially averaged RR
end
if ~isfield(messages.setup.wavedet,'rrant_maxtol')
    messages.setup.wavedet.rrant_maxtol  =  1.5;    % max time in sec admited for current RR to be used in Exponentially averaged RR
end
if ~isfield(messages.setup.wavedet,'rrant_average')
    messages.setup.wavedet.rrant_average  =  [0.8 0.2];    % Exponentially averaged RR weights
end
if ~isfield(messages.setup.wavedet,'inivent_tol')
    messages.setup.wavedet.inivent_tol  =  0.1;
end
if ~isfield(messages.setup.wavedet,'inivent_tol_S')
    messages.setup.wavedet.inivent_tol_S=0.05; % sec
end
if ~isfield(messages.setup.wavedet,'finvent_tol')
    messages.setup.wavedet.finvent_tol=0.240;% sec % it would be nioce to make it depend on round(rrmed/2)
end
if ~isfield(messages.setup.wavedet,'finvent_max')
    messages.setup.wavedet.finvent_max=0.6;% sec
end
if ~isfield(messages.setup.wavedet,'scale')
    messages.setup.wavedet.scale=4;
end
if ~isfield(messages.setup.wavedet,'scale2')
    messages.setup.wavedet.scale2=5;
end
if ~isfield(messages.setup.wavedet,'scalezerocros')
    messages.setup.wavedet.scalezerocros=3;
end
if ~isfield(messages.setup.wavedet,'scalezerocros2') %for twavetask5
    messages.setup.wavedet.scalezerocros2=4;
end

if ~isfield(messages.setup.wavedet,'min_vent')
    messages.setup.wavedet.min_vent=0.1;%sec
end
if ~isfield(messages.setup.wavedet,'Tmax_Tmin_time_min')
    messages.setup.wavedet.Tmax_Tmin_time_min=0.2;%0.15;%sec %10 ENE2012: 150 ms es poco
end
if ~isfield(messages.setup.wavedet,'Tmax_Tmin_bifasic')
    messages.setup.wavedet.Tmax_Tmin_bifasic=2.5;
end
if ~isfield(messages.setup.wavedet,'T_bound_tol')
    messages.setup.wavedet.T_bound_tol=0.12;%sec
end
Kton=messages.setup.wavedet.Kton;
Ktoff=messages.setup.wavedet.Ktoff;
umbraldetT = messages.setup.wavedet.umbraldetT;
umbralsig = messages.setup.wavedet.umbralsig;
rrant_mintol=messages.setup.wavedet.rrant_mintol;
rrant_maxtol=messages.setup.wavedet.rrant_maxtol;
rrant_average=messages.setup.wavedet.rrant_average;
inivent_tol= messages.setup.wavedet.inivent_tol;
inivent_tol_S=messages.setup.wavedet.inivent_tol_S;
finvent_tol=messages.setup.wavedet.finvent_tol;
finvent_max= messages.setup.wavedet.finvent_max;
scale=messages.setup.wavedet.scale;
scale2=messages.setup.wavedet.scale2;
scalezerocros=messages.setup.wavedet.scalezerocros;
scalezerocros2=messages.setup.wavedet.scalezerocros2; %#ok<NASGU>
min_vent=messages.setup.wavedet.min_vent;%sec
Tmax_Tmin_time_min= messages.setup.wavedet.Tmax_Tmin_time_min;%sec
Tmax_Tmin_bifasic=messages.setup.wavedet.Tmax_Tmin_bifasic;% extra criteria
T_bound_tol=messages.setup.wavedet.T_bound_tol;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isempty(heasig)

end
% janelas=[];
% endwinT=[];

% Initialization of auxiliary variables
if length(time)>1
    rrant = time(2)-time(1);
else
    aux=find(position.qrs~=0);
    if length(aux)>1
        rrant= diff(position.qrs(aux(end-1):aux(end))); %18MAR09
    else
        rrant=messages.setup.wavedet.freq;
    end
end

%18MAR09 % for the case in which the first RR is very wrong AGO2011
if rrant<rrant_mintol*messages.setup.wavedet.freq || rrant>rrant_maxtol*messages.setup.wavedet.freq;
    messages.warnings=[messages.warnings {['RR lower than' rrant_mintol*messages.setup.wavedet.freq ' ms or higher than' rrant_maxtol*messages.setup.wavedet.freq ' found in twavef']}];
    rrant=rrant_mintol*messages.setup.wavedet.freq;%Jul2011
end

T = []; Tprima=[]; picon=[]; picoff=[]; Ton=[]; Toff=[]; tipoT=[];
% minapos = []; minppos=[]; maxapos=[]; maxppos=[]; mina=[]; minp=[]; maxa=[];
% maxp=[];
picon_keep=[];%multileadchange
picoff_keep=[];%multileadchange
lead_keep=[];%multileadchange

%figure
%%%plot(messages.lixo)
% hold on
janelas=NaN*ones(length(time),3);
endwinT=NaN*ones(length(time),1);
for i = 1:length(time)   % For each beat

    if (i>1),
        if (rrant_maxtol*rrant>(time(i)-time(i-1)))&&(time(i)-time(i-1)>rrant_mintol*rrant),
            rrmed = rrant_average(1)*rrant + rrant_average(2)*(time(i)-time(i-1));% Exponentially averaged RR new value
        else
            rrmed = rrant;  % Exponentially averaged RR old value
        end
    else                  % Only for the first in each segment of the ECG
        rrmed = rrant;
    end
    rrant = rrmed;        % For next segment
    inivent = round(inivent_tol*messages.setup.wavedet.freq);   % Begining of window

    %Rute multilead 03.Dec.04

    %if ~isempty(pos.S(i)),              % If there is an S wave
    %         inivent = max(inivent, pos.S(i)-pos.qrs(i)+round(0.05*messages.setup.wavedet.freq));
    %     end

    if ~isempty(position.S(i+intervalo(1)-1));
        inivent = max(inivent, position.S(i+intervalo(1)-1)-samp(1)+1-time(i)+round(inivent_tol_S*messages.setup.wavedet.freq));
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% changed 13/06/02 Rute
    %if rrmed >= messages.setup.wavedet.freq,  %
    %if rrmed <= messages.setup.wavedet.freq,  %   21AGO09 % for rrmed < 1 seg finiven=finvent_max seg
    if rrmed <= messages.setup.wavedet.freq && i~=length(time) && (rrant_maxtol*rrant>(time(i+1)-time(i)))&&(time(i+1)-time(i)>rrant_mintol*rrant) %02FEB2011
        finvent = round(finvent_max*messages.setup.wavedet.freq);     % End of window
    else
        finvent = round(rrmed*finvent_max);
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %% changed 13/06/02 Rute
    if i~=length(time),                 % For last beat in the segment
        %finvent = min(finvent,time(i+1)-time(i)-round(finvent_tol*messages.setup.wavedet.freq));
        %finvent = min(finvent,time(i+1)-time(i)-min(round((time(i+1)-time(i))*1/3),round(finvent_tol*messages.setup.wavedet.freq)));
        finvent = min(finvent,time(i+1)-time(i)-min(round((time(i+1)-time(i))*0.4),round(finvent_tol*messages.setup.wavedet.freq)));
    elseif i~=1%Rute 18MAR09
        finvent = min(finvent,time(i)-time(i-1)-round(finvent_tol*messages.setup.wavedet.freq));
    elseif length(aux)>1
        finvent = min(finvent,diff(position.qrs(aux(end-1):aux(end)))-round(finvent_tol*messages.setup.wavedet.freq));%Rute 18MAR09
    end

    % We work at scale 4 (in general).
    if isempty(position.QRSoff(i+intervalo(1)-1)),   % Should never happen, but...
        begwin = min(inivent + time(i),length(w)); %Rute 4Jul2011
    else
        begwin = min(max(inivent + time(i), position.QRSoff(i+intervalo(1)-1)-samp(1)+1+1),length(w));%Rute 4Jul2011
        % if QRSoff is bad anoated it can miss T wave...
    end

    endwin = max(1,min(finvent + time(i),length(w))); %% Rute 20/05/02
    janelas(i,:)=[i begwin endwin];
    endwinT(i)=endwin;
    %%%%plot([begwin endwin ], messages.lixo([begwin endwin]),'*g')

    % Positive maxima and negative minima in the window
    maxpos = begwin + modmax(w(begwin+1:endwin,scale),2,0,+1);
    minpos = begwin + modmax(w(begwin+1:endwin,scale),2,0,-1);
    [maxim ind] = max(w(maxpos,scale));   % The biggest of the positive
    maxpos = maxpos(ind);
    [minim ind] = min(w(minpos,scale));   % The biggest of the negative
    minpos = minpos(ind);

    if isempty(maxpos),               % If no local positive maximum
        % The maximum will be the first
        % or the last sample
        if (w(begwin,scale)>=w(endwin,scale)) && w(begwin,scale)>0,
            maxpos = begwin; maxim = w(maxpos,scale);
        elseif (w(endwin,scale)>=w(begwin,scale)) && w(endwin,scale)>0,
            maxpos = endwin; maxim = w(maxpos,scale);
        end
    end
    if isempty(minpos),               % if no local negative minimum
        % the minimum will be the first
        % or the last sample
        if (w(begwin,scale)<=w(endwin,scale)) && w(begwin,scale)<0,
            minpos = begwin; minim = w(minpos,scale);
        elseif (w(endwin,scale)<=w(begwin,scale)) && w(endwin,scale)<0,
            minpos = endwin; minim = w(minpos,scale);
        end
    end

    absmax = abs(maxim);
    absmin = abs(minim);

    if i<length(time),
        veficaz = sqrt(mean(w(time(i):time(i+1),scale).^2));
    else                        % if last beat of the segment
        veficaz = sqrt(mean(w(time(i):end,scale).^2));
    end

    hay_onda = ((absmax>umbraldetT*veficaz)|(absmin>umbraldetT*veficaz));
    if isempty(hay_onda)
        hay_onda = 0;
    end
    % Rute 18/06/02

    if endwin-begwin<(min_vent*messages.setup.wavedet.freq) || isnan(hay_onda)% se a janela tem amplitude menor do que min_vent seg enato nao existe onda T
        hay_onda =0;
    end

    % Is there a wave?
    if hay_onda,
        if absmax >= absmin,    % the greatest modulus maximum is the maximum
            % Now we search the two minima nearest to maxpos, one before and one after
            minapos = max(modmax(w(begwin+1:maxpos-1,scale),2,0,-1));
            minapos = begwin + minapos; % Position of the negative minimum before the maximum
            if isempty(minapos) && (maxpos ~= begwin) && (w(begwin,scale)<0),
                minapos = begwin;% If no local minimum before the maximum, take the first sample
            end
            minppos = min(modmax(w(maxpos+1:endwin,scale),2,0,-1));
            minppos = maxpos + minppos; % Position of the positive maximum after the minimum
            if isempty(minppos) && (maxpos ~= endwin) && (w(endwin,scale)<0),
                minppos = endwin;        % If no local minimum after the maximum, take the last sample
            end

            mina = abs(w(minapos,scale));     % Amplitude of minimum before maximum
            minp = abs(w(minppos,scale));     % Amplitude of minimum after maximum

            if (mina < umbralsig*absmax), % If mina is not big enough
                mina =[];                  % forget it

            elseif (maxpos-minapos>Tmax_Tmin_time_min*messages.setup.wavedet.freq), %or if ther are more than Tmax_Tmin_time_min sec to maxpos
                mina = [];
            end
            if (minp < umbralsig*absmax), % If minp is not big enough
                minp =[];                  % forget it
            elseif (minppos-maxpos>Tmax_Tmin_time_min*messages.setup.wavedet.freq), % and also if there are more than Tmax_Tmin_time_min sec to maxpos
                minp =[];
            end

            if ~isnan(mina)&~isnan(minp), %#ok<AND2>     %%% NUEVO JP
                if (mina >= minp)&&(minp < umbralsig*absmax*Tmax_Tmin_bifasic),
                    minp = [];
                elseif (minp> mina)&&(mina < umbralsig*absmax*Tmax_Tmin_bifasic),
                    mina = [];
                end
            end

            % Test which modulus maxima are significative and find zero crossings
            if isempty(mina),
                if isempty(minp),
                    tipoT = 2;      % only upwards T wave
                    if maxpos - minapos > 2,         % if not !!!!!!!!!!!
                        ind = zerocros(flipud(w(minapos:maxpos,scalezerocros)));   %Scale scalezerocros !!!
                        T = maxpos - ind +1;                           % Zero crossing = T wave position
                        picoff = maxpos;                               %wavelet  peak to detect offset
                    elseif isempty(minapos);  % If there were no minimum, there is no zero crossing
                        T = picant (w(begwin:maxpos,scale),maxpos);       % Take the minimum at scale scale
                        if ~isempty(T) %%%%%%%%%%%% 14/06/02 Rute
                            picoff = maxpos;
                        else
                            picoff = []; % if did not exist a peak in scale scale there is no T
                        end %%%%%%%%%%%% 14/06/02 Rute
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave' }];
                    end
                else      % minp exists (is significative) but mina not
                    tipoT = 0;  		%normal T wave
                    if minppos -maxpos >2,       % if not!!!!!!???
                        ind = zerocros(w(maxpos:minppos,scalezerocros));  %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(maxpos:minppos,scale));
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = maxpos + ind -1;
                        picon = maxpos;		% For determining onset and offset
                        picoff = minppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            else
                if isempty(minp),   %  mina exists (is significative) but minp not
                    tipoT = 1;  	%inverted T wave
                    if maxpos -minapos >2,  % if not !!!!!!!!!
                        ind = zerocros(w(minapos:maxpos,scalezerocros));  % wavelet zero crossing is T wave peak  %% 07/06/02 Rute
                        if isempty(ind)%%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(minapos:maxpos,scale));  % wavelet zero crossing is T wave peak  %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minapos + ind -1;
                        picon = minapos;
                        picoff = maxpos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else    % both mina and minp are significative.  Biphasic wave.
                    tipoT = 5;	% biphasic neg-pos T wave
                    if maxpos - minapos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(minapos:maxpos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute

                        end%%%%%%%%%%%%%%Rute 03/09/02
                        T = maxpos - ind +1;
                        picon = minapos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                    if minppos - maxpos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(maxpos:minppos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind)%%%%%%%%%%%%%%Rute 03/09/02
                            %ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute %18.11.05 DUVIDA
                            %DUVIDA 18Nov05
                            ind = zerocros(flipud(w(maxpos:minppos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        %Tprima = maxpos + ind -1;
                        %DUVIDA 18Nov05
                        Tprima = minppos- ind +1;
                        picoff = minppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            end
        else        % If the greatest modulus maximum is the minimum
            % Search two maxima, one before and one after the minimum
            maxapos = max(modmax(w(begwin+1:minpos-1,scale),2,0,1));
            maxapos = begwin + maxapos;
            if isempty(maxapos) && (minpos ~= begwin) && (w(begwin,scale)>0),
                maxapos = begwin;
            end
            maxppos = min(modmax(w(minpos+1:endwin,scale),2,0,1));
            maxppos = minpos + maxppos;
            if isempty(maxppos) && (minpos ~= endwin) && (w(endwin,scale)>0),
                maxppos = endwin;
            end
            maxa = abs(w(maxapos,scale));
            maxp = abs(w(maxppos,scale)) ;                                   % See if they are significative
            if (maxa < umbralsig*absmin)
                maxa =[];
            elseif (minpos-maxapos>Tmax_Tmin_time_min*messages.setup.wavedet.freq),
                maxa = [];
            end
            if (maxp < umbralsig*absmin),
                maxp =[];
            elseif (maxppos-minpos>Tmax_Tmin_time_min*messages.setup.wavedet.freq),
                maxp = [];
            end
            if ~isnan(maxa)&~isnan(maxp), %#ok<AND2>     %%% NUEVO JP
                if (maxa >= maxp)&&(maxp < umbralsig*absmin*Tmax_Tmin_bifasic),
                    maxp = [];
                elseif (maxp> maxa)&&(maxa < umbralsig*absmin*Tmax_Tmin_bifasic),
                    maxa = [];
                end
            end
            % Test which modulus maxima are significative and find zero crossings
            if isempty(maxa),
                if isempty(maxp),
                    tipoT = 3;      % only downwards T wave
                    if minpos - maxapos > 2, %!!!!!!!!
                        ind = zerocros(flipud(w(maxapos:minpos,scalezerocros)));   %Scale scalezerocros
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(flipud(w(maxapos:minpos,scale)));
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minpos - ind +1;
                        picoff = minpos;
                    elseif isempty(maxapos);  % If there were no maximum, there is no zero crossing.
                        T = picant (w(begwin:minpos,scale),minpos);  % minimo en escala scale.
                        if ~isempty(T) %%%%%%%%%%%% 14/06/02 Rute
                            picoff = minpos;
                        else
                            picoff = []; % if did not exist a peak in scale scale there is no T
                        end %%%%%%%%%%%% 14/06/02 Rute
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else      % maxp is signficative, but not maxa
                    tipoT = 1;  %inverted T wave
                    if maxppos -minpos >2,  % !!!!!!
                        ind = zerocros(w(minpos:maxppos,scalezerocros));  %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%Rute 03/09/02
                            ind = zerocros(w(minpos:maxppos,scale));  %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%Rute 03/09/02
                        T = minpos + ind -1;
                        picon = minpos;		% For calculating onset and offset
                        picoff = maxppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            else
                if isempty(maxp),   % maxa is significative, but not maxp
                    tipoT = 0;       %normal T wave
                    if minpos -maxapos >2,
                        ind = zerocros(w(maxapos:minpos,scalezerocros)); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            ind = zerocros(w(maxapos:minpos,scale)); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        T = maxapos + ind -1;
                        picon = maxapos;
                        picoff = minpos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                else    % both maxa and maxp are significative.  Biphasic wave.
                    tipoT = 4;	% biphasic pos-neg T wave
                    if minpos - maxapos > 2,  %!!!!!!!!!!!
                        ind = zerocros(flipud(w(maxapos:minpos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            ind = zerocros(flipud(w(maxapos:minpos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        T = minpos - ind +1;
                        picon = maxapos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                    if maxppos - minpos > 2,    %!!!!!!!!!!!!
                        ind = zerocros(flipud(w(minpos:maxppos,scalezerocros))); %% 07/06/02 Rute
                        if isempty(ind) %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                            %ind = zerocros(flipud(w(minapos:maxpos,scale))); %% 03/09/02 Rute %18.11.05 DUVIDA
                            %DUVIDA 18Nov05
                            ind = zerocros(flipud(w(minpos:maxppos,scale))); %% 03/09/02 Rute
                        end %%%%%%%%%%%%%%%%%%%% 03/09/02 Rute
                        %Tprima = minpos + ind -1; %18.11.05 DUVIDA
                        %DUVIDA 18Nov05
                        Tprima = maxppos- ind +1;
                        picoff = maxppos;
                    else %%%%%%%%%%%%%%%%%%%%% nao faz nada!!!!!!! % 12/06/02  Rute
                        messages.warnings=[messages.warnings {'unknown case: unable to classify T wave'}];
                    end
                end
            end
        end

        % T wave onset and offset detection
        %if isempty(T), picon=[]; picoff=[]; end

        picon_keep=[picon_keep picon]; %#ok<AGROW> %multileadchange
        picoff_keep=[picoff_keep picoff];  %#ok<AGROW> %multileadchange
        lead_keep=[lead_keep scale];  %#ok<AGROW> %multileadchange

        if ~isempty(picon),
            Ton = searchon (picon, w(max(begwin,picon-round(T_bound_tol*messages.setup.wavedet.freq)):picon,scale), Kton);
        end
        if ~isempty(picoff),
            Toff=searchoff(picoff, w(picoff:min([size(w,1) picoff+round(T_bound_tol*messages.setup.wavedet.freq) ]) ,scale) , Ktoff);
            if (Toff > endwin),
                Toff = endwin;
            end
        end
    else
        %% using scale 5 07/06/02 Rute %%%%%%%%%%%%%%%%%%%%%%%%%%%
        %% Neste momento usa-se a mesma janela e os mesmos limiares que para a escala scale

        maxpos = begwin + modmax(w(begwin+1:endwin,scale2),2,0,+1);
        minpos = begwin + modmax(w(begwin+1:endwin,scale2),2,0,-1);
        [maxim ind] = max(w(maxpos,scale2));   % The biggest of the positive
        maxpos = maxpos(ind);
        [minim ind] = min(w(minpos,scale2));   % The biggest of the negative
        minpos = minpos(ind);
        if isempty(maxpos),               % If no local positive maximum
            % The maximum will be the first
            % or the last sample
            if (w(begwin,scale2)>=w(endwin,scale2)) && w(begwin,scale2)>0,
                maxpos = begwin; maxim = w(maxpos,scale2);
            elseif (w(endwin,scale2)>=w(begwin,scale2)) && w(endwin,scale2)>0,
                maxpos = endwin; maxim = w(maxpos,scale2);
            end
        end
        if isempty(minpos),               % if no local negative minimum
            % the minimum will be the first
            % or the last sample
            if (w(begwin,scale2)<=w(endwin,scale2)) && w(begwin,scale2)<0,
                minpos = begwin; minim = w(minpos,scale2);
            elseif (w(endwin,scale2)<=w(begwin,scale2)) && w(endwin,scale2)<0,
                minpos = endwin; minim = w(minpos,scale2);
            end
        end

        absmax = abs(maxim);
        absmin = abs(minim);
        if i<length(time),
            veficaz = sqrt(mean(w(time(i):time(i+1),scale2).^2));
        else                        % if last beat of the segment
            veficaz = sqrt(mean(w(time(i):end,scale2).^2));
        end
        hay_ondascale2 = ((absmax>umbraldetT*veficaz)|(absmin>umbraldetT*veficaz));

        if endwin-begwin<(min_vent*messages.setup.wavedet.freq) % se a janela tem amplitude menor do que min_vent seg enato nao existe onda T
            hay_ondascale2 =0;
        end
        if hay_ondascale2,
            t5; % procurar na escala scale2 10/07/02 Rute
            picon_keep=[picon_keep picon];%#ok<AGROW> %multileadchange
            picoff_keep=[picoff_keep picoff];%#ok<AGROW>%multileadchange
            lead_keep=[lead_keep scale2];%#ok<AGROW>%multileadchange
        end
    end
    % Is there a wave
    %% utilizar a escala scale2 07/06/02 Rute %%%%%%%%%%%%%%%%%%%%%%%%%%%

    % Filling the structure with positions
    if isempty(Ton), Ton = NaN; end;
    if isempty(Toff), Toff = NaN;  end;
    if isempty(T), T=NaN; end;
    if isnan(T),
        picon_keep=[picon_keep NaN]; %#ok<AGROW>%multileadchange
        picoff_keep=[picoff_keep NaN]; %#ok<AGROW>%multileadchange
        lead_keep=[lead_keep NaN]; %#ok<AGROW>%multileadchange
    end
    if isempty(Tprima), Tprima=NaN; end;
    if isempty(tipoT), tipoT=NaN; end;
    pos.Tscale(i)=scale;
    pos.Ton(i) = Ton;
    pos.Toff(i) = Toff;
    pos.T(i) = T;
    pos.Tprima(i) = Tprima;
    pos.Ttipo(i) = tipoT;
    %%%plot([Toff(~isnan(Toff))], messages.lixo([Toff(~isnan(Toff))]),'*r')
    T = []; Tprima=[]; picon=[]; picoff=[]; Ton=[]; Toff=[]; tipoT=[];
%     minapos = []; minppos=[]; maxapos=[]; maxppos=[]; mina=[]; minp=[]; maxa=[];
%     maxp=[];

end
position.Tscale(intervalo(1):intervalo(2)) = pos.Tscale;
position.Ton(intervalo(1):intervalo(2)) = pos.Ton+samp(1)-1;
position.Toff(intervalo(1):intervalo(2))= pos.Toff+samp(1)-1;
position.T(intervalo(1):intervalo(2)) = pos.T+samp(1)-1;
position.Tprima(intervalo(1):intervalo(2)) = pos.Tprima+samp(1)-1;
position.Ttipo(intervalo(1):intervalo(2)) = pos.Ttipo;


function [qrs,jpoints] = wqrsm_fast(data,fs,PWfreq,TmDEF,jflag)
% The swqrsm detector rewrited from swqrsm.c
% rewrited by Giulia Da Poian, August 7, 2018
% use struct swqrsm instead of global varaibles
% giulia.da.poian@emory.com
%
% input:
%   data:   ECG data, the swqrsm.c used the data in raw adus, if the input
%           data is in physical units, when the function found the peak-peak
%           value < 10, it will multiply the value by WFDB_DEFGAIN (200)
%   fs:     sampling frequency (default: 125)
%   PWfreq: power line (mains) frequency, in Hz (default: 60)
%   TmDEF:  minimum threshold value (default: 100)
%   jflag:  annotate J-points (ends of QRS complexes) (default: 0)
% output:
%   qrs:    QRS fiducial mark in samples
%   jpoints:J-points annotation, if jflag==1
%
% Matlab code based on:
%  file: swqrsm.c		Wei Zong      23 October 1998
% 			Last revised:  9 April 2010 (by G. Moody)
% -----------------------------------------------------------------------------
% swqrsm: Single-lead QRS detector based on length transform
% Copyright (C) 1998-2010 Wei Zong
%
% 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.
%
% You may contact the author by e-mail (wzong@mit.edu) or postal mail
% (MIT Room E25-505, Cambridge, MA 02139, USA).  For updates to this software,
% please visit PhysioNet (http://www.physionet.org/).
% ------------------------------------------------------------------------------
%
% This program analyzes an ECG signal, detecting QRS onsets and J-points, using
% a nonlinearly-scaled ECG curve length feature.  This version has been optimized
% for ECGs sampled at 125 Hz, but it can analyze ECGs sampled at any frequency
% using on-the-fly resampling provided by the WFDB library.
%
% `swqrsm' can process records containing any number of signals, but it uses only
% one signal for QRS detection (signal 0 by default;  this can be changed using
% the `-s' option, see below).  'swqrsm' has been optimized for adult human ECGs.
% For other ECGs, it may be necessary to experiment with the input sampling
% frequency and the time constants.
% ----------------------------------------------------------------------
% This matlab version has updates to deal with a variable sampling
% frequency but has not been exhanstively tested.
% ----- Add notes here:
%
%

if nargin<5
jflag = 0;
end
if nargin<4
TmDEF = 100;
end
if nargin<3
PWfreq = 60;
end
if nargin<2
fs = 125;
end

swqrsm.BUFLN = 16384; %	/* must be a power of 2, see ltsamp() */
EYE_CLS = 0.25; %   /* eye-closing period is set to 0.25 sec (250 ms) */
MaxQRSw = 0.13; %    /* maximum QRS width (130ms) */
NDP	= 2.5; %    /* adjust threshold if no QRS found in NDP seconds */
PWFreqDEF = PWfreq; %    /* power line (mains) frequency, in Hz (default) */
% TmDEF = 100; %	/* minimum threshold value (default) */
WFDB_DEFGAIN = 200.0; %  /* default value for gain (adu/physical unit) */

timer_d=0;
gain=WFDB_DEFGAIN;
swqrsm.lfsc = fix(1.25*gain*gain/fs);	% /* length function scale constant */
PWFreq = PWFreqDEF;	% /* power line (mains) frequency, in Hz */

% test data is physical units (mV) or raw adus units
datatest=data(1:fix(length(data)/fs)*fs);
if length(datatest)>fs
datatest=reshape(datatest,fs,[]);
end
test_ap=median(max(datatest)-min(datatest));
if test_ap<10 % peak-peak < 10 mV, may physical units
data=data*gain;
end

swqrsm.data = data;
swqrsm.lbuf = zeros(swqrsm.BUFLN,1);
swqrsm.ebuf = zeros(swqrsm.BUFLN,1);
swqrsm.ebuf(1:end)=fix(sqrt(swqrsm.lfsc));
swqrsm.lt_tt = 0;
swqrsm.aet = 0;
swqrsm.Yn = 0;
swqrsm.Yn1 = 0;
swqrsm.Yn2 = 0;

qrs = [];
jpoints = [];

Tm = fix(TmDEF / 5.0);
%     spm = 60 * fs;
%     next_minute = from + spm;
swqrsm.LPn = fix(fs/PWFreq); %		/* The LP filter will have a notch at the power line (mains) frequency */
if (swqrsm.LPn > 8)
swqrsm.LPn = 8;	% /* avoid filtering too agressively */
end
swqrsm.LP2n = 2 * swqrsm.LPn;
EyeClosing = fix(fs * EYE_CLS); % /* set eye-closing period */
ExpectPeriod = fix(fs * NDP);   % /* maximum expected RR interval */
swqrsm.LTwindow = fix(fs * MaxQRSw);   % /* length transform window size */

%     for i=1:2000
%         ltdata(i)=ltsamp(i);
%     end
%     /* Average the first 8 seconds of the length-transformed samples
%        to determine the initial thresholds Ta and T0. The number of samples
%        in the average is limited to half of the ltsamp buffer if the sampling
%        frequency exceeds about 2 KHz. */

t1 = fs*8;
if t1> fix(swqrsm.BUFLN*0.9)
    t1=swqrsm.BUFLN/2;
end

T0=0;
for t=1:t1
    swqrsm = ltsamp(t,swqrsm);
    T0 = T0 + swqrsm.lt_data;
end

T0 = T0 / t1;
Ta = 3 * T0;

% /* Main loop */
t=1;
learning=1;
while t<length(data)
    if (learning==1)
        if (t > t1)
            learning = 0;
            T1 = T0;
            t = 1;	% /* start over */
        else
            T1 = 2*T0;
        end
    end

    % /* Compare a length-transformed sample against T1. */
    swqrsm = ltsamp(t,swqrsm);
    if ( swqrsm.lt_data > T1) %	/* found a possible QRS near t */
        timer_d = 0; % /* used for counting the time after previous QRS */
        maxd = swqrsm.lt_data;
        mind = maxd;
        for tt = t+1:t + fix(EyeClosing/2)
            swqrsm = ltsamp(tt,swqrsm);
            if (swqrsm.lt_data > maxd)
                maxd = swqrsm.lt_data;
            end
        end
        for tt = t-1:-1:t - fix(EyeClosing/2)
            swqrsm = ltsamp(tt,swqrsm);
            if (swqrsm.lt_data < mind)
                mind = swqrsm.lt_data;
            end
        end
        if (maxd > mind+10) % /* There is a QRS near tt */
        % /* Find the QRS onset (PQ junction) */
            onset = fix(maxd/100) + 2;
            tpq = t - 5;
            for tt = t:-1:t - fix(EyeClosing/2)
                swqrsm = ltsamp(tt,swqrsm);
                swqrsm_1 = ltsamp(tt-1,swqrsm);
                swqrsm_2 = ltsamp(tt-2,swqrsm_1);
                swqrsm_3 = ltsamp(tt-3,swqrsm_2);
                swqrsm_4 = ltsamp(tt-4,swqrsm_3);
                if (swqrsm.lt_data - swqrsm_1.lt_data < onset && swqrsm_1.lt_data - swqrsm_2.lt_data < onset && swqrsm_2.lt_data - swqrsm_3.lt_data < onset && swqrsm_3.lt_data - swqrsm_4.lt_data < onset)
                    swqrsm = swqrsm_4;
                    tpq = tt - swqrsm.LP2n; %	/* account for phase shift */
                    break;
                end
            end

            if (learning~=1)
                % /* Check that we haven't reached the end of the record. */
                if tpq>length(data)
                    break;
                end
                % /* Record an annotation at the QRS onset */
                qrs = [qrs tpq];

                % J-points processing
                if (jflag)
                    tj = t+5;
                    for tt=t:t + fix(EyeClosing/2)
                        swqrsm = ltsamp(tt, swqrsm);
                        if swqrsm.lt_data > maxd - fix(maxd/10)
                            tj = tt;
                            break;
                        end
                    end
                    if tj>length(data)
                        break;
                    end
                    % Record an annotation at the J-point
                    jpoints = [jpoints tj];
                end
            end
            % /* Adjust thresholds */
            Ta = Ta + (maxd - Ta)/10;
            T1 = Ta / 3;

            % /* Lock out further detections during the eye-closing period */
            t = t + EyeClosing;
        end

    elseif (learning~=1)
    % 	    /* Once past the learning period, decrease threshold if no QRS
    % 	       was detected recently. */
        timer_d = timer_d + 1;
        if timer_d > ExpectPeriod && Ta > Tm
            Ta = Ta -1;
            T1 = Ta / 3;
        end
    end
    t = t+1;
end

end

function swqrsm = ltsamp(t,swqrsm)
    % /* ltsamp() returns a sample of the length transform of the input at time t.
    %    Since this program analyzes only one signal, ltsamp() does not have an
    %    input argument for specifying a signal number; rather, it always filters
    %    and returns samples from the signal designated by the global variable
    %    'sig'.  The caller must never "rewind" by more than swqrsm.BUFLN samples (the
    %    length of ltsamp()'s buffers). */
    while (t > swqrsm.lt_tt)
        swqrsm.Yn2 = swqrsm.Yn1;
        swqrsm.Yn1 = swqrsm.Yn;
        v0=swqrsm.data(1);
        v1=swqrsm.data(1);
        v2=swqrsm.data(1);
        if swqrsm.lt_tt>0 && swqrsm.lt_tt<=length(swqrsm.data)
            v0 = swqrsm.data(swqrsm.lt_tt);
        end
        if swqrsm.lt_tt-swqrsm.LPn>0 && (swqrsm.lt_tt-swqrsm.LPn)<=length(swqrsm.data)
            v1 = swqrsm.data(swqrsm.lt_tt-swqrsm.LPn);
        end
        if swqrsm.lt_tt-swqrsm.LP2n>0 && (swqrsm.lt_tt-swqrsm.LP2n)<=length(swqrsm.data)
            v2 = swqrsm.data(swqrsm.lt_tt-swqrsm.LP2n);
        end
        if v0~=-32768 && v1~=-32768 && v2~=-32768
            swqrsm.Yn = 2*swqrsm.Yn1 - swqrsm.Yn2 + v0 - 2*v1 + v2;
        end
        dy = fix((swqrsm.Yn - swqrsm.Yn1) / swqrsm.LP2n);	%	/* lowpass derivative of input */
        swqrsm.lt_tt=swqrsm.lt_tt+1;
        swqrsm.et = fix(sqrt(swqrsm.lfsc +dy*dy)); % /* length transform */
        id = mod(swqrsm.lt_tt,swqrsm.BUFLN);
        if id == 0
            id = swqrsm.BUFLN;
        end
        swqrsm.ebuf(id) = swqrsm.et;
        id2 = mod(swqrsm.lt_tt-swqrsm.LTwindow,swqrsm.BUFLN);
        if id2 == 0
            id2 = swqrsm.BUFLN;
        end
        swqrsm.aet = swqrsm.aet + (swqrsm.et - swqrsm.ebuf(id2));
        swqrsm.lbuf(id) = swqrsm.aet;
    % 	/* swqrsm.lbuf contains the average of the length-transformed samples over
    % 	   the interval from tt-swqrsm.LTwindow+1 to tt */
    end

    id3 = mod(t,swqrsm.BUFLN);
    if id3 == 0
        id3 = swqrsm.BUFLN;
    end
    swqrsm.lt_data = swqrsm.lbuf(id3);
end


%sim_lcmrawbasis.m
%Jamie Near, McGill University 2014.
%Modified (greatly simplified) for new FID-A spin system definitions, 2018.
%
% USAGE:
% [RF,out]=sim_lcmrawbasis(n,sw,Bfield,linewidth,metab,tau1,tau2,addref,makeraw,seq)
%
% DESCRIPTION:
% Generate an LCModel .RAW file to be used as an individual metabolite basis
% spectrum in an LCModel basis set.  The relevant characteristics of the
% acquisition can be specified (pulse sequence, number of points, spectral
% width, etc)
%
% INPUTS:
% n         = number of points in fid/spectrum
% sw        = desired spectral width in [Hz]
% Bfield    = main magnetic field strength in [T]
% linewidth = linewidth in [Hz]
% tau1      = first echo time in [ms] (if seq='st' or 'l', tau1 = TE)
% tau2      = second echo time in [ms].  (Used in Press, but not used in SE or LASER.
%             If seq='st', tau2=TM).
% addref    = add reference at 0ppm (for use in LCModel makebasis) ['y' or 'n']
% makeraw   = make output file for lcmodel ['y' or 'n']
% seq       = pulse sequence ['se' for Spin Echo, 'p' for Press, 'st' for Steam, or 'l' for LASER]
% metab     = one of the following choices
%   'H2O'    = Water
%   'Ala'    = Alanine
%   'Asp'    = Aspartate
%   'PCh'    = PhosphoCholine
%   'Cr'     = Creatine
%   'PCr'    = PhosphoCreatine
%   'GABA'   = Gamma-aminobutyric acid (kaiser)
%   'Gln'    = Glutamine
%   'Glu'    = Glutamate
%   'GSH'    = Glutathione
%   'Gly'    = Glycine
%   'Ins'    = Myo-inositol
%   'Lac'    = Lactate
%   'NAA'    = N-acetyl aspartate
%   'Scyllo' = Scyllo-inositol
%   'Tau'    = Taurine
%   'Asc'    = Ascorbate (Vitamin C)
%   'bHB'    = beta-Hydroxybutyrate
%   'bHG'    = beta-Hydroxyglutarate
%   'Glc'    = Glucose
%   'NAAG'   = N-acetyl aspartyl glutamate
%   'GPC'    = Glycero-phosphocholine
%   'PE'     = Phosphoryl ethanolamine
%   'Ser'    = Serine
%
% OUTPUTS:
% RF        = not used.
% out       = Simulated basis spectrum in FID-A structure format.

function [RF,out]=sim_lcmrawbasis(n,sw,Bfield,linewidth,metab,tau1,tau2,addref,makeraw,seq)

load('spinSystems.mat');

eval(['sys=sys' metab ';']);
spins=0;
for k=1:length(sys)
    spins=spins+length(sys(k).shifts);
end
disp(['simulating metabolite ' metab ' with ' num2str(spins) ' spins...  Please Wait...']);
switch seq
    case 'se'
        out = sim_spinecho(n,sw,Bfield,linewidth,sys,tau1);
    case 'p'
        out = sim_press(n,sw,Bfield,linewidth,sys,tau1,tau2);
    case 'st'
        out = sim_steam(n,sw,Bfield,linewidth,sys,tau1,tau2);
    case 'l'
        out = sim_laser(n,sw,Bfield,linewidth,sys,tau1);
    otherwise
        disp(['ERROR:  Sequence ' seq 'not recognized!!!']);
end

if addref=='y'||addref=='Y'
    addref=1;
elseif addref=='n'||addref=='N'
    addref=0;
end

if addref
    sysRef.J=0;
    sysRef.shifts=0;
    sysRef.name='Ref_0ppm';
    sysRef.scaleFactor=1;
    switch seq
        case 'se'
            ref = sim_spinecho(n,sw,Bfield,linewidth,sysRef,tau1);
        case 'p'
            ref = sim_press(n,sw,Bfield,linewidth,sysRef,tau1,tau2);
        case 'st'
            ref = sim_steam(n,sw,Bfield,linewidth,sysRef,tau1,tau2);
        case 'l'
            ref = sim_laser(n,sw,Bfield,linewidth,sysRef,tau1);
        otherwise
            disp(['ERROR:  Sequence ' seq 'not recognized!!!']);
    end
    out=op_addScans(out,ref);
end

if makeraw=='y'||makeraw=='Y'
    makeraw=1;
elseif makeraw=='n'||makeraw=='N'
    makeraw=0;
end

if makeraw
    RF=io_writelcmraw(out,[metab '.RAW'],metab);
else
    RF=[];
end


%% Example: Carbon Ion Treatment Plan
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Copyright 2017 the matRad development team.
%
% This file is part of the matRad project. It is subject to the license
% terms in the LICENSE file found in the top-level directory of this
% distribution and at https://github.com/e0404/matRad/LICENSES.txt. No part
% of the matRad project, including this file, may be copied, modified,
% propagated, or distributed except according to the terms contained in the
% LICENSE file.
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%
% In this example we will show
% (i) how to load patient data into matRad
% (ii) how to setup a carbon ion dose calculation plan including variable RBE optimization
% (iii) how to inversely optimize the pencil beam intensities based on the
% RBE-weighted dose
% (iv) how to inversely optimize the pencil beam intensities based on the
% biological effect
% (v) how to change the tissues' radiobiological characteristics
% (vi) how to recalculated the dose considering the previously optimized pencil beam intensities
% (vii) how to compare the two results

%% Patient Data Import
% Let's begin with a clear Matlab environment and import the liver
% patient into your workspace.

matRad_rc; %If this throws an error, run it from the parent directory first to set the paths

load('LIVER.mat');

%% Treatment Plan
% The next step is to define your treatment plan labeled as 'pln'. This
% structure requires input from the treatment planner and defines the most
% important cornerstones of your treatment plan.
%%
% First of all, we need to define what kind of radiation modality we would
% like to use. Possible values are photons, protons or carbon. In this
% example we would like to use carbon ions for treatment planning. Next, we
% need to define a treatment machine to correctly load the corresponding
% base data. matRad features generic base data in the file
% 'carbon_Generic.mat'; consequently the machine has to be set accordingly
pln.radiationMode = 'carbon';
pln.machine       = 'Generic';

%%
% Define the flavor of biological optimization for treatment planning along
% with the quantity that should be used for optimization. Possible values
% are (none: physical optimization; const_RBExD: constant RBE of 1.1;
% LEMIV_effect: effect-based optimization; LEMIV_RBExD: optimization of
% RBE-weighted dose. As we use carbon ions, we decide to use base data from
% the local effect model IV and want to optimize the RBE-weighted dose.
% Therefore we set bioOptimization to LEMIV_RBExD
pln.propOpt.bioOptimization = 'LEMIV_RBExD';

%%
% The remaining plan parameters are set like in the previous example files
pln.numOfFractions        = 30;
pln.propStf.gantryAngles  = 315;
pln.propStf.couchAngles   = 0;
pln.propStf.bixelWidth    = 3;
pln.propStf.numOfBeams    = numel(pln.propStf.gantryAngles);
pln.propStf.isoCenter     = ones(pln.propStf.numOfBeams,1) * matRad_getIsoCenter(cst,ct,0);
pln.propOpt.runDAO        = 0;
pln.propOpt.runSequencing = 0;

% dose calculation settings
pln.propDoseCalc.doseGrid.resolution.x = 3; % [mm]
pln.propDoseCalc.doseGrid.resolution.y = 3; % [mm]
pln.propDoseCalc.doseGrid.resolution.z = 3; % [mm]

%% Generate Beam Geometry STF
stf = matRad_generateStf(ct,cst,pln);

%%
% Let's have a closer look on the stf.ray sub-structure which contains the
% actual  beam/ray geometry information. For illustration purposes we want
% to show the last ray. Besides geometrical  information about the position
% and orientation of the ray, we can also find pencil beam information. If
% the ray coincides with the target, pencil beams were defined along the
% ray from target entry to target exit.
display(stf.ray(end));

%%
% Here are the energies selected on the last ray:
display(stf.ray(end).energy);

%% Dose Calculation
dij = matRad_calcParticleDose(ct,stf,pln,cst);

%% Inverse Optimization  for IMPT based on RBE-weighted dose
% The goal of the fluence optimization is to find a set of bixel/spot
% weights which yield the best possible dose distribution according to the
% clinical objectives and constraints underlying the radiation treatment.
resultGUI = matRad_fluenceOptimization(dij,cst,pln);

%% Plot the Resulting Dose Slice
% Let's plot the transversal iso-center dose slice
slice = round(pln.propStf.isoCenter(3)./ct.resolution.z);
figure,
imagesc(resultGUI.RBExDose (:,:,slice)),colorbar, colormap(jet);

%% Inverse Optimization  for IMPT based on biological effect
% To perform a dose optimization for carbon ions we can also use the
% biological effect instead of the RBE-weighted dose. Therefore we have to
% change the optimization mode and restart the optimization
pln.propOpt.bioOptimization = 'LEMIV_effect';
resultGUI_effect = matRad_fluenceOptimization(dij,cst,pln);

%% Visualize differences
% Through optimzation based on the biological effect we obtain a slightly
% different dose distribution as visualized by the following dose
% difference map
figure;
imagesc(resultGUI.RBExDose (:,:,slice)-resultGUI_effect.RBExDose(:,:,slice));
colorbar;
colormap(jet);

%% Change Radiosensitivity
% The previous treatment plan was optimized using an photon alpha-beta
% ratio of 2 for all tissues. Now, Let's change the radiosensitivity by
% adapting alphaX. This will change the photon alpha-beta ratio
% from 2 to 10.
for i = 1:size(cst,1)
    cst{i,5}.alphaX      = 0.5;
    cst{i,5}.TissueClass = 2;
end

%% Recalculate Plan
% Let's use the existing optimized pencil beam weights and recalculate the RBE weighted dose
resultGUI_tissue = matRad_calcDoseDirect(ct,stf,pln,cst,resultGUI.w);

%% Result Comparison
% Let's compare the new recalculation against the optimization result.
plane = 3;
doseWindow = [0 max([resultGUI_effect.RBExDose(:); resultGUI_tissue.RBExDose(:)])];

figure,
matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_effect.RBExDose,plane,slice,[],[],colorcube,[],doseWindow,[]);
title('original plan')
figure,
matRad_plotSliceWrapper(gca,ct,cst,1,resultGUI_tissue.RBExDose,plane,slice,[],[],colorcube,[],doseWindow,[]);
title('manipulated plan')
%%
% At this point we would like to see the absolute difference of the original optimization and the
% recalculation.
absDiffCube = resultGUI_effect.RBExDose-resultGUI_tissue.RBExDose;
figure,
matRad_plotSliceWrapper(gca,ct,cst,1,absDiffCube,plane,slice,[],[],colorcube);
title('absolute difference')
%%
% Plot both doses with absolute difference and gamma analysis
[gammaCube,gammaPassRate,hfigure]=matRad_compareDose(resultGUI_effect.RBExDose, resultGUI.RBExDose, ct, cst,[1 1 1],'on');


function [sourcemodel, cfg] = ft_prepare_leadfield(cfg, data)

% FT_PREPARE_LEADFIELD computes the forward model for many dipole locations
% on a regular 2D or 3D sourcemodel and stores it for efficient inverse modelling
%
% Use as
%   [sourcemodel] = ft_prepare_leadfield(cfg, data)
%
% It is necessary to input the data on which you want to perform the
% inverse computations, since that data generally contain the gradiometer
% information and information about the channels that should be included in
% the forward model computation. The data structure can be either obtained
% from FT_PREPROCESSING, FT_FREQANALYSIS or FT_TIMELOCKANALYSIS. If the data is empty,
% all channels will be included in the forward model.
%
% The configuration should contain
%   cfg.channel            = Nx1 cell-array with selection of channels (default = 'all'),
%                            see FT_CHANNELSELECTION for details
%
% The positions of the sources can be specified as a regular 3-D
% sourcemodel that is aligned with the axes of the head coordinate system
%   cfg.xgrid      = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.ygrid      = vector (e.g. -20:1:20) or 'auto' (default = 'auto')
%   cfg.zgrid      = vector (e.g.   0:1:20) or 'auto' (default = 'auto')
%   cfg.resolution = number (e.g. 1 cm) for automatic sourcemodel generation
% Alternatively the position of a few sources at locations of interest can
% be specified, for example obtained from an anatomical or functional MRI
%   cfg.sourcemodel.pos        = N*3 matrix with position of each source
%   cfg.sourcemodel.inside     = N*1 vector with boolean value whether sourcemodel point is inside brain (optional)
%   cfg.sourcemodel.dim        = [Nx Ny Nz] vector with dimensions in case of 3-D sourcemodel (optional)
%
% The volume conduction model of the head should be specified as
%   cfg.headmodel     = structure with volume conduction model, see FT_PREPARE_HEADMODEL
%
% The EEG or MEG sensor positions can be present in the data or can be specified as
%   cfg.elec          = structure with electrode positions or filename, see FT_READ_SENS
%   cfg.grad          = structure with gradiometer definition or filename, see FT_READ_SENS
%
% Optionally, you can modify the leadfields by reducing the rank (i.e.
% remove the weakest orientation), or by normalizing each column.
%   cfg.reducerank      = 'no', or number (default = 3 for EEG, 2 for MEG)
%   cfg.normalize       = 'yes' or 'no' (default = 'no')
%   cfg.normalizeparam  = depth normalization parameter (default = 0.5)
%   cfg.backproject     = 'yes' or 'no' (default = 'yes') determines when reducerank is applied
%                         whether the lower rank leadfield is projected back onto the original
%                         linear subspace, or not.
%
% Depending on the type of headmodel, some additional options may be
% specified.
%
% For OPENMEEG based headmodels:
%   cfg.openmeeg.batchsize    = scalar (default 100e3), number of dipoles
%                               for which the leadfield is computed in a
%                               single call to the low-level code. Trades off
%                               memory efficiency for speed.
%   cfg.openmeeg.dsm          = 'no'/'yes', reuse existing DSM if provided
%   cfg.openmeeg.keepdsm      = 'no'/'yes', option to retain DSM (no by default)
%   cfg.openmeeg.nonadaptive  = 'no'/'yes'
%
% For SINGLESHELL based headmodels:
%   cfg.singleshell.batchsize = scalar or 'all' (default 1), number of dipoles
%                               for which the leadfield is computed in a
%                               single call to the low-level code. Trades off
%                               memory efficiency for speed.
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
% If you specify this option the input data will be read from a *.mat
% file on disk. This mat files should contain only a single variable named 'data',
% corresponding to the input structure.
%
% See also FT_SOURCEANALYSIS, FT_DIPOLEFITTING, FT_PREPARE_HEADMODEL, FT_PREPARE_SOURCEMODEL

% Undocumented local options:
% cfg.feedback
% cfg.sel50p      = 'no' (default) or 'yes'
% cfg.lbex        = 'no' (default) or a number that corresponds with the radius
% cfg.mollify     = 'no' (default) or a number that corresponds with the FWHM

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

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar data
ft_preamble provenance data
ft_preamble trackconfig

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% the data can be passed as input arguments or can be read from disk
hasdata = exist('data', 'var');

if ~hasdata
  % the data variable will be passed to the prepare_headmodel function below
  % where it would be used for channel selection
  data = [];
else
  % check if the input data is valid for this function
  data = ft_checkdata(data);
end

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed', {'hdmfile', 'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'vol',     'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'grid',    'sourcemodel'});
cfg = ft_checkconfig(cfg, 'renamed', {'om',      'openmeeg'});
cfg = ft_checkconfig(cfg, 'renamed', {'elecfile', 'elec'});
cfg = ft_checkconfig(cfg, 'renamed', {'gradfile', 'grad'});
cfg = ft_checkconfig(cfg, 'renamed', {'optofile', 'opto'});

% set the defaults
cfg.normalize      = ft_getopt(cfg, 'normalize',      'no');
cfg.normalizeparam = ft_getopt(cfg, 'normalizeparam', 0.5);
cfg.lbex           = ft_getopt(cfg, 'lbex',           'no');
cfg.sel50p         = ft_getopt(cfg, 'sel50p',         'no');
cfg.feedback       = ft_getopt(cfg, 'feedback',       'text');
cfg.mollify        = ft_getopt(cfg, 'mollify',        'no');
cfg.patchsvd       = ft_getopt(cfg, 'patchsvd',       'no');
cfg.backproject    = ft_getopt(cfg, 'backproject',    'yes'); % determines whether after rank reduction the subspace projected leadfield is backprojected onto the original space
% cfg.reducerank   = ft_getopt(cfg, 'reducerank', 'no');      % the default for this depends on EEG/MEG and is set below

cfg = ft_checkconfig(cfg, 'renamed', {'tightgrid', 'tight'});  % this is moved to cfg.sourcemodel.tight by the subsequent createsubcfg
cfg = ft_checkconfig(cfg, 'renamed', {'sourceunits', 'unit'}); % this is moved to cfg.sourcemodel.unit by the subsequent createsubcfg

% put the low-level options pertaining to the sourcemodel in their own field
cfg = ft_checkconfig(cfg, 'createsubcfg', {'sourcemodel'});
% move some fields from cfg.sourcemodel back to the top-level configuration
cfg = ft_checkconfig(cfg, 'createtopcfg', {'sourcemodel'});

% this code expects the inside to be represented as a logical array
cfg.sourcemodel = ft_checkconfig(cfg.sourcemodel, 'renamed',  {'pnt' 'pos'});
cfg = ft_checkconfig(cfg, 'inside2logical', 'yes');

if strcmp(cfg.sel50p, 'yes') && strcmp(cfg.lbex, 'yes')
  ft_error('subspace projection with either lbex or sel50p is mutually exclusive');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% collect and preprocess the electrodes/gradiometer and head model
[headmodel, sens, cfg] = prepare_headmodel(cfg, data);

% set the default for reducing the rank of the leadfields
if ft_senstype(sens, 'eeg')
  cfg.reducerank = ft_getopt(cfg, 'reducerank', 3);
else
  cfg.reducerank = ft_getopt(cfg, 'reducerank', 2);
end

% construct the sourcemodel for which the leadfield will be computed
tmpcfg           = keepfields(cfg, {'sourcemodel', 'mri', 'headshape', 'symmetry', 'smooth', 'threshold', 'spheremesh', 'inwardshift', 'xgrid' 'ygrid', 'zgrid', 'resolution', 'tight', 'warpmni', 'template', 'showcallinfo'});
tmpcfg.headmodel = headmodel;
if ft_senstype(sens, 'eeg')
  tmpcfg.elec = sens;
elseif ft_senstype(sens, 'meg')
  tmpcfg.grad = sens;
end
sourcemodel = ft_prepare_sourcemodel(tmpcfg);

% check whether units are equal (NOTE: this was previously not required,
% this check can be removed if the underlying bug is resolved. See
% http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387
if ~isfield(headmodel, 'unit') || ~isfield(sourcemodel, 'unit') || ~isfield(sens, 'unit')
  ft_warning('cannot determine the units of all geometric objects required for leadfield computation (headmodel, sourcemodel, sensor configuration). THIS CAN LEAD TO WRONG RESULTS! (refer to http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387)');
else
  if ~strcmp(headmodel.unit, sourcemodel.unit) || ~strcmp(sourcemodel.unit, sens.unit)
    ft_error('geometric objects (headmodel, sourcemodel, sensor configuration) are not expressed in the same units (this used to be allowed, and will be again in the future, but for now there is a bug which prevents a correct leadfield from being computed; see http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2387)');
  end
end

% find the indices of all sourcemodel points that are inside the brain
insideindx = find(sourcemodel.inside);

if ft_headmodeltype(headmodel, 'openmeeg')

  ft_hastoolbox('openmeeg', 1);  % add to path (if not yet on path)

  % repeated system calls to the openmeeg executable makes it rather slow
  % calling it once is much more efficient
  fprintf('calculating leadfield for all positions at once, this may take a while...\n');

  if(~isfield(cfg,'openmeeg'))
    cfg.openmeeg = [];
  end
  batchsize   = ft_getopt(cfg.openmeeg, 'batchsize',100e3); % number of voxels per DSM batch; set to e.g. 1000 if not much RAM available
  dsm         = ft_getopt(cfg.openmeeg, 'dsm');             % reuse existing DSM if provided
  keepdsm     = ft_getopt(cfg.openmeeg, 'keepdsm', 'no');   % retain DSM
  nonadaptive = ft_getopt(cfg.openmeeg, 'nonadaptive', 'no');

  ndip       = length(insideindx);
  numchunks  = ceil(ndip/batchsize);
  if(numchunks > 1)
    if istrue(keepdsm)
      ft_warning('Keeping DSM output not supported when the computation is split into batches')
    end
    keepdsm = false;
  end

  try
    % DSM computation is computationally intensive:
    % As it can be reused with same voxel sourcemodel (i.e. if voxels are defined in
    % MRI coordinates rather than MEG coordinates), optionally save result.
    % Dense voxel grids may require several gigabytes of RAM, so optionally
    % split into smaller batches

    [h2sens,ds2sens] = ft_sensinterp_openmeeg(sourcemodel.pos(insideindx,:), headmodel, sens);

    % use pre-existing DSM if present
    if(~isempty(dsm))
      lf = ds2sens + h2sens*headmodel.mat*dsm;
    else
      lf = zeros(size(ds2sens)); % pre-allocate Msensors x Nvoxels

      for ii = 1:numchunks
        % select sourcemodel positions for this batch
        diprange = (((ii-1)*batchsize + 1):(min((ii)*batchsize,ndip)));
        % remap with 3 orientations per position
        diprangeori = [((ii-1)*3*batchsize + 1):(min((ii)*3*batchsize,3*ndip))];
        dsm = ft_sysmat_openmeeg(sourcemodel.pos(insideindx(diprange),:), headmodel, sens, nonadaptive);
        lf(:,diprangeori) = ds2sens(:,diprangeori) + h2sens*headmodel.mat*dsm;

        if istrue(keepdsm)
          % retain DSM in cfg if desired
          cfg.openmeeg.dsm = dsm;
        end

        dipindx = insideindx(diprange);
      end
    end
  catch
    me = lasterror;
    rethrow(me);
  end

  % apply montage, if applicable
  if isfield(sens, 'tra')
    lf = sens.tra * lf;
  end

  % lead field computation already done, but pass to ft_compute_leadfield so that
  % any post-computation options can be applied (e.g., normalization, etc.)
  lf = ft_compute_leadfield(sourcemodel.pos(diprange,:), sens, headmodel, 'lf', lf, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);

  % reshape result into sourcemodel.leadfield cell-array
  for i=1:ndip
    sourcemodel.leadfield{insideindx(i)} = lf(:,3*(i-1) + [1:3]);
  end
  clear lf

elseif ft_headmodeltype(headmodel, 'singleshell')
  cfg.singleshell = ft_getopt(cfg, 'singleshell', []);
  batchsize       = ft_getopt(cfg.singleshell, 'batchsize', 1);
  if ischar(batchsize) && strcmp(batchsize, 'all')
    batchsize = length(insideindx);
  end

  dippos     = sourcemodel.pos(insideindx,:);
  ndip       = length(insideindx);
  numchunks  = ceil(ndip/batchsize);

  ft_progress('init', cfg.feedback, 'computing leadfield');
  for k = 1:numchunks
    ft_progress(k/numchunks, 'computing leadfield %d/%d\n', k, numchunks);
    diprange = (((k-1)*batchsize + 1):(min(k*batchsize,ndip)));
    tmp      = ft_compute_leadfield(dippos(diprange,:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);
    for i=1:length(diprange)
      thisindx = insideindx(diprange(i));
      if istrue(cfg.backproject)
        sourcemodel.leadfield{thisindx} = tmp(:,(i-1)*3+(1:3));
      else
        sourcemodel.leadfield{thisindx} = tmp(:,(i-1)*cfg.reducerank+(1:cfg.reducerank));
      end

      if isfield(cfg, 'sourcemodel') && isfield(cfg.sourcemodel, 'mom')
        % multiply with the normalized dipole moment to get the leadfield in the desired orientation
        sourcemodel.leadfield{thisindx} = sourcemodel.leadfield{thisindx} * sourcemodel.mom(:,thisindx);
      end
    end
  end
  ft_progress('close');

else
  ft_progress('init', cfg.feedback, 'computing leadfield');
  for i=1:length(insideindx)
    % compute the leadfield on all sourcemodel positions inside the brain
    ft_progress(i/length(insideindx), 'computing leadfield %d/%d\n', i, length(insideindx));
    thisindx = insideindx(i);
    sourcemodel.leadfield{thisindx} = ft_compute_leadfield(sourcemodel.pos(thisindx,:), sens, headmodel, 'reducerank', cfg.reducerank, 'normalize', cfg.normalize, 'normalizeparam', cfg.normalizeparam, 'backproject', cfg.backproject);

    if isfield(cfg, 'sourcemodel') && isfield(cfg.sourcemodel, 'mom')
      % multiply with the normalized dipole moment to get the leadfield in the desired orientation
      sourcemodel.leadfield{thisindx} = sourcemodel.leadfield{thisindx} * sourcemodel.mom(:,thisindx);
    end
  end % for all sourcemodel locations inside the brain
  ft_progress('close');
end

% represent the leadfield for positions outside the brain as empty array
sourcemodel.leadfield(~sourcemodel.inside) = {[]};

% add the label of the channels
sourcemodel.label           = sens.label;
sourcemodel.leadfielddimord = '{pos}_chan_ori';

% mollify the leadfields
if ~strcmp(cfg.mollify, 'no')
  sourcemodel = mollify(cfg, sourcemodel);
end

% combine leadfields in patches and do an SVD on them
if ~strcmp(cfg.patchsvd, 'no')
  sourcemodel = patchsvd(cfg, sourcemodel);
end

% compute the 50 percent channel selection subspace projection
if ~strcmp(cfg.sel50p, 'no')
  sourcemodel = sel50p(cfg, sourcemodel, sens);
end

% compute the local basis function expansion (LBEX) subspace projection
if ~strcmp(cfg.lbex, 'no')
  sourcemodel = lbex(cfg, sourcemodel);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble trackconfig
ft_postamble previous   data
ft_postamble provenance sourcemodel
ft_postamble history    sourcemodel
ft_postamble savevar    sourcemodel


function [im_th,TOAref,trgt,ijdim_ref,ul,bbox,zen,azi,zc,B1Satu,B2Satu,B3Satu,resolu]=nd2toarbt(path_top,filename)
% convert DNs to TOA ref and BT
% Revisions:
% Add Landsat 9 (Shi 2/17/2022)
% Cannot use the GRIDobj to read our band, because it may result in Nan data.
% (Shi 2/26/2018)
% Added a target image based on blue band, which will be helpfull to
% resample and reproject the auxiliary data.  (Shi 2/16/2018)
% Use REF vs. DN instead of RAD vs. DN (Zhe 06/20/2013)
% Combined the Earth-Sun distance table into the function (Zhe 04/09/2013)
% Process Landsat 8 DN values (Zhe 04/04/2013)
% Proces the new metadata for Landsat TM/ETM+ images (Zhe 09/28/2012)
% Fixed bugs caused by earth-sun distance table (Zhe 01/15/2012)
%
% [im_th,TOAref,ijdim_ref,ul,zen,azi,zc,B1Satu,B2Satu,B3Satu,resolu]=nd2toarbt(filename)
% Where:
% Inputs:
% filename='L*MTL.txt';
% Outputs:
% 1) im_th = Brightness Temperature (BT)
% 2) TOAref = Top Of Atmoshpere (TOA) reflectance
% 3) trgt = Target Grid Object
% 4) ijdim = [nrows,ncols]; % dimension of optical bands
% 5) ul = [upperleft_mapx upperleft_mapy];
% 6) zen = solar zenith angle (degrees);
% 7) azi = solar azimuth angle (degrees);
% 8) zc = Zone Number
% 9,10,11) Saturation (true) in the Visible bands
% 12) resolution of Fmask results

% % % fprintf('Read in header information & TIF images\n');
[Lmax,Lmin,Qcalmax,Qcalmin,Refmax,Refmin,ijdim_ref,ijdim_thm,reso_ref,...
    reso_thm,ul,bbox,zen,azi,zc,Lnum,doy]=lndhdrread(path_top,filename);

% earth-sun distance see G. Chander et al. RSE 113 (2009) 893-903
Tab_ES_Dist = [
    1 0.98331
    2 0.98330
    3 0.98330
    4 0.98330
    5 0.98330
    6 0.98332
    7 0.98333
    8 0.98335
    9 0.98338
    10 0.98341
    11 0.98345
    12 0.98349
    13 0.98354
    14 0.98359
    15 0.98365
    16 0.98371
    17 0.98378
    18 0.98385
    19 0.98393
    20 0.98401
    21 0.98410
    22 0.98419
    23 0.98428
    24 0.98439
    25 0.98449
    26 0.98460
    27 0.98472
    28 0.98484
    29 0.98496
    30 0.98509
    31 0.98523
    32 0.98536
    33 0.98551
    34 0.98565
    35 0.98580
    36 0.98596
    37 0.98612
    38 0.98628
    39 0.98645
    40 0.98662
    41 0.98680
    42 0.98698
    43 0.98717
    44 0.98735
    45 0.98755
    46 0.98774
    47 0.98794
    48 0.98814
    49 0.98835
    50 0.98856
    51 0.98877
    52 0.98899
    53 0.98921
    54 0.98944
    55 0.98966
    56 0.98989
    57 0.99012
    58 0.99036
    59 0.99060
    60 0.99084
    61 0.99108
    62 0.99133
    63 0.99158
    64 0.99183
    65 0.99208
    66 0.99234
    67 0.99260
    68 0.99286
    69 0.99312
    70 0.99339
    71 0.99365
    72 0.99392
    73 0.99419
    74 0.99446
    75 0.99474
    76 0.99501
    77 0.99529
    78 0.99556
    79 0.99584
    80 0.99612
    81 0.99640
    82 0.99669
    83 0.99697
    84 0.99725
    85 0.99754
    86 0.99782
    87 0.99811
    88 0.99840
    89 0.99868
    90 0.99897
    91 0.99926
    92 0.99954
    93 0.99983
    94 1.00012
    95 1.00041
    96 1.00069
    97 1.00098
    98 1.00127
    99 1.00155
    100 1.00184
    101 1.00212
    102 1.00240
    103 1.00269
    104 1.00297
    105 1.00325
    106 1.00353
    107 1.00381
    108 1.00409
    109 1.00437
    110 1.00464
    111 1.00492
    112 1.00519
    113 1.00546
    114 1.00573
    115 1.00600
    116 1.00626
    117 1.00653
    118 1.00679
    119 1.00705
    120 1.00731
    121 1.00756
    122 1.00781
    123 1.00806
    124 1.00831
    125 1.00856
    126 1.00880
    127 1.00904
    128 1.00928
    129 1.00952
    130 1.00975
    131 1.00998
    132 1.01020
    133 1.01043
    134 1.01065
    135 1.01087
    136 1.01108
    137 1.01129
    138 1.01150
    139 1.01170
    140 1.01191
    141 1.01210
    142 1.01230
    143 1.01249
    144 1.01267
    145 1.01286
    146 1.01304
    147 1.01321
    148 1.01338
    149 1.01355
    150 1.01371
    151 1.01387
    152 1.01403
    153 1.01418
    154 1.01433
    155 1.01447
    156 1.01461
    157 1.01475
    158 1.01488
    159 1.01500
    160 1.01513
    161 1.01524
    162 1.01536
    163 1.01547
    164 1.01557
    165 1.01567
    166 1.01577
    167 1.01586
    168 1.01595
    169 1.01603
    170 1.01610
    171 1.01618
    172 1.01625
    173 1.01631
    174 1.01637
    175 1.01642
    176 1.01647
    177 1.01652
    178 1.01656
    179 1.01659
    180 1.01662
    181 1.01665
    182 1.01667
    183 1.01668
    184 1.01670
    185 1.01670
    186 1.01670
    187 1.01670
    188 1.01669
    189 1.01668
    190 1.01666
    191 1.01664
    192 1.01661
    193 1.01658
    194 1.01655
    195 1.01650
    196 1.01646
    197 1.01641
    198 1.01635
    199 1.01629
    200 1.01623
    201 1.01616
    202 1.01609
    203 1.01601
    204 1.01592
    205 1.01584
    206 1.01575
    207 1.01565
    208 1.01555
    209 1.01544
    210 1.01533
    211 1.01522
    212 1.01510
    213 1.01497
    214 1.01485
    215 1.01471
    216 1.01458
    217 1.01444
    218 1.01429
    219 1.01414
    220 1.01399
    221 1.01383
    222 1.01367
    223 1.01351
    224 1.01334
    225 1.01317
    226 1.01299
    227 1.01281
    228 1.01263
    229 1.01244
    230 1.01225
    231 1.01205
    232 1.01186
    233 1.01165
    234 1.01145
    235 1.01124
    236 1.01103
    237 1.01081
    238 1.01060
    239 1.01037
    240 1.01015
    241 1.00992
    242 1.00969
    243 1.00946
    244 1.00922
    245 1.00898
    246 1.00874
    247 1.00850
    248 1.00825
    249 1.00800
    250 1.00775
    251 1.00750
    252 1.00724
    253 1.00698
    254 1.00672
    255 1.00646
    256 1.00620
    257 1.00593
    258 1.00566
    259 1.00539
    260 1.00512
    261 1.00485
    262 1.00457
    263 1.00430
    264 1.00402
    265 1.00374
    266 1.00346
    267 1.00318
    268 1.00290
    269 1.00262
    270 1.00234
    271 1.00205
    272 1.00177
    273 1.00148
    274 1.00119
    275 1.00091
    276 1.00062
    277 1.00033
    278 1.00005
    279 0.99976
    280 0.99947
    281 0.99918
    282 0.99890
    283 0.99861
    284 0.99832
    285 0.99804
    286 0.99775
    287 0.99747
    288 0.99718
    289 0.99690
    290 0.99662
    291 0.99634
    292 0.99605
    293 0.99577
    294 0.99550
    295 0.99522
    296 0.99494
    297 0.99467
    298 0.99440
    299 0.99412
    300 0.99385
    301 0.99359
    302 0.99332
    303 0.99306
    304 0.99279
    305 0.99253
    306 0.99228
    307 0.99202
    308 0.99177
    309 0.99152
    310 0.99127
    311 0.99102
    312 0.99078
    313 0.99054
    314 0.99030
    315 0.99007
    316 0.98983
    317 0.98961
    318 0.98938
    319 0.98916
    320 0.98894
    321 0.98872
    322 0.98851
    323 0.98830
    324 0.98809
    325 0.98789
    326 0.98769
    327 0.98750
    328 0.98731
    329 0.98712
    330 0.98694
    331 0.98676
    332 0.98658
    333 0.98641
    334 0.98624
    335 0.98608
    336 0.98592
    337 0.98577
    338 0.98562
    339 0.98547
    340 0.98533
    341 0.98519
    342 0.98506
    343 0.98493
    344 0.98481
    345 0.98469
    346 0.98457
    347 0.98446
    348 0.98436
    349 0.98426
    350 0.98416
    351 0.98407
    352 0.98399
    353 0.98391
    354 0.98383
    355 0.98376
    356 0.98370
    357 0.98363
    358 0.98358
    359 0.98353
    360 0.98348
    361 0.98344
    362 0.98340
    363 0.98337
    364 0.98335
    365 0.98333
    366 0.98331];

if Lnum >= 4 && Lnum <= 7
    % LPGS Upper lef corner alignment (see Landsat handbook for detail)
    ul(1)=ul(1)-15;
    ul(2)=ul(2)+15;
    resolu=[reso_ref,reso_ref];
    % Read in all bands
    % Band1
    n_B1=dir(fullfile(path_top,'L*B1*'));
    if isempty(n_B1)
        n_B1=dir(fullfile(path_top,'L*b1*'));
    end
    im_B1=single(imread(fullfile(path_top,n_B1.name)));
    % Band2
    % Also used to be target or referenced image when resampling and reprojecting to the image extent and resolution.
    n_B2=dir(fullfile(path_top,'L*B2*'));
    if isempty(n_B2)
        n_B2=dir(fullfile(path_top,'L*b2*'));
    end
    im_B2=single(imread(fullfile(path_top,n_B2.name)));
    trgt = GRIDobj(fullfile(path_top,n_B2.name));
%     im_B2=single(trgt.Z);
    trgt.Z=[];% remove non-used values to save memory.

    % Band3
    n_B3=dir(fullfile(path_top,'L*B3*'));
    if isempty(n_B3)
        n_B3=dir(fullfile(path_top,'L*b3*'));
    end
    im_B3=single(imread(fullfile(path_top,n_B3.name)));
    % Band4
    n_B4=dir(fullfile(path_top,'L*B4*'));
    if isempty(n_B4)
        n_B4=dir(fullfile(path_top,'L*b4*'));
    end
    im_B4=single(imread(fullfile(path_top,n_B4.name)));
    % Band5
    n_B5=dir(fullfile(path_top,'L*B5*'));
    if isempty(n_B5)
        n_B5=dir(fullfile(path_top,'L*b5*'));
    end
    im_B5=single(imread(fullfile(path_top,n_B5.name)));
    % Band6
    if Lnum==7
        n_B6=dir(fullfile(path_top,'L*B6*1*'));
        if isempty(n_B6)
            n_B6=dir(fullfile(path_top,'L*b6*1*'));
        end
    else
        n_B6=dir(fullfile(path_top,'L*B6*'));
        if isempty(n_B6)
            n_B6=dir(fullfile(path_top,'L*b6*'));
        end
    end
    im_th=single(imread(fullfile(path_top,n_B6.name)));
    % check to see whether need to resample thermal band
    if reso_ref~=reso_thm
        % resmaple thermal band
        im_th=pixel2pixv([ul(2),ul(1)],[ul(2),ul(1)],...
            resolu,[reso_thm,reso_thm],...
            im_th,[ijdim_ref(2),ijdim_ref(1)],[ijdim_thm(2),ijdim_thm(1)]);
    end
    % Band7
    n_B7=dir(fullfile(path_top,'L*B7*'));
    if isempty(n_B7)
        n_B7=dir(fullfile(path_top,'L*b7*'));
    end
    im_B7=single(imread(fullfile(path_top,n_B7.name)));
    % only processing pixesl where all bands have values (id_mssing)
    id_missing=im_B1==0|im_B2==0|im_B3==0|im_B4==0|im_B5==0|im_th==0|im_B7==0;
    % find pixels that are saturated in the visible bands
    B1Satu=im_B1==255;
    B2Satu=im_B2==255;
    B3Satu=im_B3==255;

    % ND to radiance first
% % %     fprintf('From DNs to TOA ref & BT\n');
    im_B1=((Lmax(1)-Lmin(1))/(Qcalmax(1)-Qcalmin(1)))*(im_B1-Qcalmin(1))+Lmin(1);
    im_B2=((Lmax(2)-Lmin(2))/(Qcalmax(2)-Qcalmin(2)))*(im_B2-Qcalmin(2))+Lmin(2);
    im_B3=((Lmax(3)-Lmin(3))/(Qcalmax(3)-Qcalmin(3)))*(im_B3-Qcalmin(3))+Lmin(3);
    im_B4=((Lmax(4)-Lmin(4))/(Qcalmax(4)-Qcalmin(4)))*(im_B4-Qcalmin(4))+Lmin(4);
    im_B5=((Lmax(5)-Lmin(5))/(Qcalmax(5)-Qcalmin(5)))*(im_B5-Qcalmin(5))+Lmin(5);
    im_th=((Lmax(6)-Lmin(6))/(Qcalmax(6)-Qcalmin(6)))*(im_th-Qcalmin(6))+Lmin(6);
    im_B7=((Lmax(7)-Lmin(7))/(Qcalmax(7)-Qcalmin(7)))*(im_B7-Qcalmin(7))+Lmin(7);

    % Landsat 4, 5, and 7 solar spectral irradiance
    % see G. Chander et al. RSE 113 (2009) 893-903
    esun_L7=[1997.000, 1812.000, 1533.000, 1039.000, 230.800, -1.0, 84.90];
    esun_L5=[1983.0, 1796.0, 1536.0, 1031.0, 220.0, -1.0, 83.44];
    esun_L4=[1983.0, 1795.0, 1539.0, 1028.0, 219.8, -1.0, 83.49];

    if Lnum==7
        ESUN=esun_L7;
    elseif Lnum==5
        ESUN=esun_L5;
    elseif Lnum==4
        ESUN=esun_L4;
    end

    % earth-sun distance see G. Chander et al. RSE 113 (2009) 893-903
    dsun_doy = Tab_ES_Dist(doy,2);

    % compute TOA reflectances
    % converted from degrees to radiance
    s_zen=deg2rad(zen);
    im_B1=10^4*pi*im_B1*dsun_doy^2/(ESUN(1)*cos(s_zen));
    im_B2=10^4*pi*im_B2*dsun_doy^2/(ESUN(2)*cos(s_zen));
    im_B3=10^4*pi*im_B3*dsun_doy^2/(ESUN(3)*cos(s_zen));
    im_B4=10^4*pi*im_B4*dsun_doy^2/(ESUN(4)*cos(s_zen));
    im_B5=10^4*pi*im_B5*dsun_doy^2/(ESUN(5)*cos(s_zen));
    im_B7=10^4*pi*im_B7*dsun_doy^2/(ESUN(7)*cos(s_zen));

    % convert Band6 from radiance to BT
    % fprintf('From Band 6 Radiance to Brightness Temperature\n');
    % see G. Chander et al. RSE 113 (2009) 893-903
    K1_L4=  671.62;
    K2_L4= 1284.30;
    K1_L5=  607.76;
    K2_L5= 1260.56;
    K1_L7 =  666.09;
    K2_L7 = 1282.71;

    if Lnum==7
        K1=K1_L7;
        K2=K2_L7;
    elseif Lnum==5
        K1=K1_L5;
        K2=K2_L5;
    elseif Lnum==4
        K1=K1_L4;
        K2=K2_L4;
    end

    im_th=K2./log((K1./im_th)+1);
    % convert from Kelvin to Celcius with 0.01 scale_facor
    im_th=100*(im_th-273.15);
    % get data ready for Fmask
    im_B1(id_missing)=-9999;
    im_B2(id_missing)=-9999;
    im_B3(id_missing)=-9999;
    im_B4(id_missing)=-9999;
    im_B5(id_missing)=-9999;
    im_th(id_missing)=-9999;
    im_B7(id_missing)=-9999;
    clear id_missing;
    TOAref=zeros(ijdim_ref(1),ijdim_ref(2),6,'single');% Band1,2,3,4,5,&7
    TOAref(:,:,1)=im_B1;clear im_B1;
    TOAref(:,:,2)=im_B2;clear im_B2;
    TOAref(:,:,3)=im_B3;clear im_B3;
    TOAref(:,:,4)=im_B4;clear im_B4;
    TOAref(:,:,5)=im_B5;clear im_B5;
    TOAref(:,:,6)=im_B7;clear im_B7;

elseif Lnum == 8 || Lnum == 9
    % LPGS Upper lef corner alignment (see Landsat handbook for detail)
    ul(1)=ul(1)-15;
    ul(2)=ul(2)+15;
    resolu=[reso_ref,reso_ref];
    % Read in all bands
    % Band2
    n_B2=dir(fullfile(path_top,'L*B2*'));
    if isempty(n_B2)
        n_B2=dir(fullfile(path_top,'L*b2*'));
    end
    im_B2=single(imread(fullfile(path_top,n_B2(1).name))); clear n_B2;
    % Band3
    % Also used to be target or referenced image when resampling and reprojecting to the image extent and resolution.
    n_B3=dir(fullfile(path_top,'L*B3*'));
    if isempty(n_B3)
        n_B3=dir(fullfile(path_top,'L*b3*'));
    end
    im_B3=single(imread(fullfile(path_top,n_B3(1).name)));
    trgt = GRIDobj(fullfile(path_top,n_B3.name)); clear n_B3;
%     im_B3=single(trgt.Z);
    trgt.Z=[];% remove non-used values to save memory.

    % Band4
    n_B4=dir(fullfile(path_top,'L*B4*'));
    if isempty(n_B4)
        n_B4=dir(fullfile(path_top,'L*b4*'));
    end
    im_B4=single(imread(fullfile(path_top,n_B4(1).name))); clear n_B4;
    % Band5
    n_B5=dir(fullfile(path_top,'L*B5*'));
    if isempty(n_B5)
        n_B5=dir(fullfile(path_top,'L*b5*'));
    end
    im_B5=single(imread(fullfile(path_top,n_B5(1).name))); clear n_B5;
    % Band6
    n_B6=dir(fullfile(path_top,'L*B6*'));
    if isempty(n_B6)
        n_B6=dir(fullfile(path_top,'L*b6*'));
    end
    im_B6=single(imread(fullfile(path_top,n_B6(1).name))); clear n_B6;
    % Band7
    n_B7=dir(fullfile(path_top,'L*B7*'));
    if isempty(n_B7)
        n_B7=dir(fullfile(path_top,'L*b7*'));
    end
    im_B7=single(imread(fullfile(path_top,n_B7(1).name))); clear n_B7;
    % Band9
    n_B9=dir(fullfile(path_top,'L*B9.*'));
    if isempty(n_B9)
        n_B9=dir(fullfile(path_top,'L*b9.*'));
    end
    im_B9=single(imread(fullfile(path_top,n_B9(1).name))); clear n_B9;
    % Band10
    n_B10=dir(fullfile(path_top,'L*B10*'));
    if isempty(n_B10)
        n_B10=dir(fullfile(path_top,'L*b10*'));
    end
    im_B10=single(imread(fullfile(path_top,n_B10(1).name))); clear n_B10 path_top;

    % check to see whether need to resample thermal band
    if reso_ref~=reso_thm
        % resmaple thermal band
        im_B10=pixel2pixv([ul(2),ul(1)],[ul(2),ul(1)],...
            resolu,[reso_thm,reso_thm],...
            im_B10,[ijdim_ref(2),ijdim_ref(1)],[ijdim_thm(2),ijdim_thm(1)]);
    end

    % only processing pixesl where all bands have values (id_mssing)
    id_missing=im_B2==0|im_B3==0|im_B4==0|im_B5==0|im_B6==0|im_B7==0|im_B9==0|im_B10==0;
    % find pixels that are saturated in the visible bands
    B1Satu=im_B2==65535;
    B2Satu=im_B3==65535;
    B3Satu=im_B4==65535;

    % ND to TOA reflectance with 0.0001 scale_facor
% % %     fprintf('From DNs to TOA ref & BT\n');
    im_B2=((Refmax(1)-Refmin(1))/(Qcalmax(1)-Qcalmin(1)))*(im_B2-Qcalmin(1))+Refmin(1);
    im_B3=((Refmax(2)-Refmin(2))/(Qcalmax(2)-Qcalmin(2)))*(im_B3-Qcalmin(2))+Refmin(2);
    im_B4=((Refmax(3)-Refmin(3))/(Qcalmax(3)-Qcalmin(3)))*(im_B4-Qcalmin(3))+Refmin(3);
    im_B5=((Refmax(4)-Refmin(4))/(Qcalmax(4)-Qcalmin(4)))*(im_B5-Qcalmin(4))+Refmin(4);
    im_B6=((Refmax(5)-Refmin(5))/(Qcalmax(5)-Qcalmin(5)))*(im_B6-Qcalmin(5))+Refmin(5);
    im_B7=((Refmax(6)-Refmin(6))/(Qcalmax(6)-Qcalmin(6)))*(im_B7-Qcalmin(6))+Refmin(6);
    im_B9=((Refmax(7)-Refmin(7))/(Qcalmax(7)-Qcalmin(7)))*(im_B9-Qcalmin(7))+Refmin(7);
    im_B10=((Lmax(8)-Lmin(8))/(Qcalmax(8)-Qcalmin(8)))*(im_B10-Qcalmin(8))+Lmin(8);

    % compute TOA reflectances
    % with a correction for the sun angle
    s_zen=deg2rad(zen);
    im_B2=10^4*im_B2/cos(s_zen);
    im_B3=10^4*im_B3/cos(s_zen);
    im_B4=10^4*im_B4/cos(s_zen);
    im_B5=10^4*im_B5/cos(s_zen);
    im_B6=10^4*im_B6/cos(s_zen);
    im_B7=10^4*im_B7/cos(s_zen);
    im_B9=10^4*im_B9/cos(s_zen);

    % convert Band6 from radiance to BT
    % fprintf('From Band 6 Radiance to Brightness Temperature\n');
    K1_B10 =  774.89;
    K2_B10 = 1321.08;

    im_B10=K2_B10./log((K1_B10./im_B10)+1);

    % convert from Kelvin to Celcius with 0.01 scale_facor
    im_B10=100*(im_B10-273.15);

    % get data ready for Fmask
    im_B2(id_missing)=-9999;
    im_B3(id_missing)=-9999;
    im_B4(id_missing)=-9999;
    im_B5(id_missing)=-9999;
    im_B6(id_missing)=-9999;
    im_B7(id_missing)=-9999;
    im_B9(id_missing)=-9999;
    im_B10(id_missing) = -9999;
    clear id_missing; % empty memory

    TOAref=zeros(ijdim_ref(1),ijdim_ref(2),7,'single');% Band 2,3,4,5,6,7,& 9
    TOAref(:,:,1)=im_B2; clear im_B2;
    TOAref(:,:,2)=im_B3; clear im_B3;
    TOAref(:,:,3)=im_B4; clear im_B4;
    TOAref(:,:,4)=im_B5; clear im_B5;
    TOAref(:,:,5)=im_B6; clear im_B6;
    TOAref(:,:,6)=im_B7; clear im_B7;
    TOAref(:,:,7)=im_B9; clear im_B9;

    im_th=zeros(ijdim_ref(1),ijdim_ref(2),'single');% Band 10
    im_th(:,:) = im_B10; clear im_B10;
else
    fprintf('This sensor is not Landsat 4, 5, 7, or 8!\n');
    return;
end

end


function [growthOnMinimalMedium,essentialExchanges,modelPruned] = predictGrowthRequirements(model)
% Predicts growth requirements for a genome-scale reconstruction.
% The list of predicted essential exchanges as well as a model
% constrained with these exchanges are returned.
%
% INPUT
% model                  COBRA model structure
% biomassReaction        String listing the biomass reaction
%
% OUTPUT
% growthOnMinimalMedium  Bool if growth on defined medium yes or no
% essentialExchanges     Exchanges that need to be open to enable growth
% constrainedModel       Model constrained with predicted essential
%                        exchanges
%
% Almut Heinken, November 2020

tol=1e-6;

% set "unlimited" constraints
model = changeRxnBounds(model, model.rxns(strncmp('EX_', model.rxns, 3)), -1000, 'l');
model = changeRxnBounds(model, model.rxns(strncmp('EX_', model.rxns, 3)), 1000, 'u');

% remove positive values on lower bounds
model.lb(find(model.lb>0))=0;
%
% % remove compounds that should not be essential for any organism
% unlikelyRequirements={'EX_alaasp(e)';'EX_alagln(e)';'EX_alaglu(e)';'EX_alagly(e)';'EX_alahis(e)';'EX_alaleu(e)';'EX_alathr(e)';'EX_glygln(e)';'EX_glyglu(e)';'EX_glyleu(e)';'EX_glymet(e)';'EX_glyphe(e)';'EX_glypro(e)';'EX_glytyr(e)';'EX_metala(e)';'EX_glyasn(e)';'EX_glyasp(e)';'EX_dpcoa(e)';'EX_coa(e)';'EX_cmp(e)';'EX_dtmp(e)';'EX_ump(e)';'EX_gmp(e)';'EX_dgtp(e)';'EX_nadp(e)';'EX_nad(e)';'EX_thmmp(e)';'EX_malt(e)';'EX_stys(e)';'EX_raffin(e)';'EX_melib(e)';'EX_lcts(e)';'EX_gtp(e)';'EX_datp(e)';'EX_amp(e)';'EX_pppi(e)';'EX_r5p(e)';'EX_g1p(e)';'EX_glu_D(e)';'EX_agm(e)';'EX_glycys(e)';'EX_1hibup_S(e)';'EX_1hibupglu_S(e)';'EX_1hmdgluc(e)';'EX_1ohmdz(e)';'EX_2hatvacid(e)';'EX_2hatvacidgluc(e)';'EX_2hatvlac(e)';'EX_2hatvlacgluc(e)';'EX_2hibup_S(e)';'EX_2hibupglu_S(e)';'EX_2oh_cbz_glc(e)';'EX_2oh_cbz(e)';'EX_2oh_mtz_glc(e)';'EX_2oh_mtz(e)';'EX_34dhphe(e)';'EX_3hibup_S(e)';'EX_3hibupglu_S(e)';'EX_3meacmp(e)';'EX_3oh_cbz_glc(e)';'EX_3oh_cbz(e)';'EX_3oh_dlor_glc(e)';'EX_3oh_dlor(e)';'EX_3oh_mdea_glc(e)';'EX_3oh_mdea(e)';'EX_3oh_mxn_glc(e)';'EX_3oh_mxn(e)';'EX_4dh_tpno_1glc(e)';'EX_4dh_tpno(e)';'EX_4hmdgluc(e)';'EX_4hphac(e)';'EX_4oh_dcf_glc(e)';'EX_4oh_dcf(e)';'EX_4oh_kp_glc(e)';'EX_4oh_kp(e)';'EX_4oh_levole_glc(e)';'EX_4oh_levole(e)';'EX_4oh_meth_glc(e)';'EX_4oh_meth(e)';'EX_4oh_propl_glc(e)';'EX_4oh_propl(e)';'EX_4oh_trz_glc(e)';'EX_4oh_trz(e)';'EX_4oh_vcz_glc(e)';'EX_4oh_vcz(e)';'EX_4ohmdz(e)';'EX_5asa(e)';'EX_5fura(e)';'EX_5oh_sulfp_glc(e)';'EX_5oh_sulfp(e)';'EX_5ohfvs(e)';'EX_5ohfvsglu(e)';'EX_6bhglz(e)';'EX_6bhglzglc(e)';'EX_6ohfvs(e)';'EX_6ohfvsglu(e)';'EX_7a_czp(e)';'EX_7bhglz(e)';'EX_7bhglzglc(e)';'EX_7oh_efv_glc(e)';'EX_7oh_efv(e)';'EX_814dioh_efv_glc(e)';'EX_814dioh_efv(e)';'EX_8oh_efv_glc(e)';'EX_8oh_efv(e)';'EX_abz_ala_b(e)';'EX_ac_amn_b_gly_glc(e)';'EX_ac_amn_b_gly(e)';'EX_ac5asa(e)';'EX_acisnzd(e)';'EX_acmp_glc(e)';'EX_acmp(e)';'EX_acmpglu(e)';'EX_alpz_4oh_glc(e)';'EX_alpz_4oh(e)';'EX_alpz_aoh_glc(e)';'EX_alpz_aoh(e)';'EX_am14_glc(e)';'EX_am14(e)';'EX_am1ccs(e)';'EX_am1cglc(e)';'EX_am5_glc(e)';'EX_am5(e)';'EX_am6_glc(e)';'EX_am6(e)';'EX_amio_c_glc(e)';'EX_amio_c(e)';'EX_amio_glc(e)';'EX_amio(e)';'EX_amn_b_gly_glc(e)';'EX_amn_b_gly(e)';'EX_amntd_m6_glc(e)';'EX_amntd_m6(e)';'EX_anzp(e)';'EX_atvacid(e)';'EX_atvacylgluc(e)';'EX_atvethgluc(e)';'EX_atvlac(e)';'EX_atvlacgluc(e)';'EX_bhpm_glc(e)';'EX_bhpm(e)';'EX_bilr_355(e)';'EX_bilr_M10(e)';'EX_bilr_M12(e)';'EX_bilr_M14(e)';'EX_bilr_M15(e)';'EX_bilr_M16(e)';'EX_bilr_M7(e)';'EX_brv(e)';'EX_bsn_glc(e)';'EX_bsn(e)';'EX_bvu(e)';'EX_bz_glc(e)';'EX_bz(e)';'EX_bzd(e)';'EX_C02528(e)';'EX_caribup_s(e)';'EX_caribupglu_S(e)';'EX_cbz_glc(e)';'EX_cbz(e)';'EX_cd6168_glc(e)';'EX_cd6168(e)';'EX_chlphncl_glc(e)';'EX_chlphncl(e)';'EX_cholate(e)';'EX_clcxb_c_glc(e)';'EX_clcxb_c(e)';'EX_clcxb_glc(e)';'EX_clcxb(e)';'EX_clobi_c(e)';'EX_clobi_glc(e)';'EX_crvsm1(e)';'EX_crvsm23(e)';'EX_cvm1gluc(e)';'EX_cvm23gluc(e)';'EX_czp(e)';'EX_daa_glc(e)';'EX_daa(e)';'EX_dcf_glc(e)';'EX_dcf(e)';'EX_dchac(e)';'EX_ddea_glc(e)';'EX_ddea(e)';'EX_des_astzl_glc(e)';'EX_des_astzl(e)';'EX_dfdcytd(e)';'EX_dfduri(e)';'EX_dgchol(e)';'EX_dh5fura(e)';'EX_digitoxin(e)';'EX_digoxin_glc(e)';'EX_digoxin(e)';'EX_dihydro_digitoxin(e)';'EX_dihydro_digoxin(e)';'EX_dlb_glc(e)';'EX_dlb(e)';'EX_dnpz_5des(e)';'EX_dnpz_6des(e)';'EX_dnpz_m11(e)';'EX_dnpz_m12(e)';'EX_dnpz_m13(e)';'EX_dnpz_m14(e)';'EX_dnpz_m9(e)';'EX_doh_etr_glc(e)';'EX_doh_etr(e)';'EX_doh_vcz_glc(e)';'EX_doh_vcz(e)';'EX_dopa(e)';'EX_dxo_glc(e)';'EX_dxo(e)';'EX_efv_glc(e)';'EX_efv(e)';'EX_eltr_glc(e)';'EX_eltr_m3(e)';'EX_eltr_m4(e)';'EX_eltr(e)';'EX_estradiol(e)';'EX_estradiolglc(e)';'EX_estriol(e)';'EX_estriolglc(e)';'EX_estrone(e)';'EX_estroneglc(e)';'EX_eztmb_glc(e)';'EX_eztmb(e)';'EX_fcsn(e)';'EX_fvs(e)';'EX_fvsgluc(e)';'EX_fvstet(e)';'EX_fvstetglu(e)';'EX_gchola(e)';'EX_glc3meacp(e)';'EX_gltmn_glc(e)';'EX_gltmn(e)';'EX_gmfl_glc(e)';'EX_gmfl_mI_glc(e)';'EX_gmfl_mI(e)';'EX_gmfl_mII_glc(e)';'EX_gmfl_mII(e)';'EX_gmfl_mIII_glc(e)';'EX_gmfl_mIII(e)';'EX_gmfl(e)';'EX_gtacmp(e)';'EX_HC02191(e)';'EX_hst_3_glc(e)';'EX_hst_3_s(e)';'EX_hst_37_diglc(e)';'EX_hst_3glc_7s(e)';'EX_hst_7_glc(e)';'EX_hst_7_s(e)';'EX_hst_7glc_3s(e)';'EX_hst(e)';'EX_ibup_S(e)';'EX_ibupgluc(e)';'EX_imn_glc(e)';'EX_imn(e)';'EX_inv_m1(e)';'EX_inv(e)';'EX_isnzd(e)';'EX_isosorbide_5mn_glc(e)';'EX_isosorbide_5mn(e)';'EX_kprofen_glc(e)';'EX_kprofen(e)';'EX_lactl(e)';'EX_lst4exp(e)';'EX_lstn(e)';'EX_lstn1gluc(e)';'EX_lstnm4(e)';'EX_lstnm7(e)';'EX_mdz_glc(e)';'EX_mdz(e)';'EX_mdzglc(e)';'EX_miso_glc(e)';'EX_miso(e)';'EX_mrphn_3glc(e)';'EX_mrphn_6glc(e)';'EX_mrphn(e)';'EX_mtym(e)';'EX_mtz_glc(e)';'EX_mtz(e)';'EX_N_oh_phtn_glc(e)';'EX_N_oh_phtn(e)';'EX_nchlphncl(e)';'EX_neopront(e)';'EX_nsldp_m5_glc(e)';'EX_nsldp_m5(e)';'EX_nverp_glc(e)';'EX_nverp(e)';'EX_nzp(e)';'EX_odsm_egltmn_glc(e)';'EX_odsm_egltmn(e)';'EX_odsm_gltmn_glc(e)';'EX_odsm_gltmn(e)';'EX_oh_etr_glc(e)';'EX_oh_etr(e)';'EX_oh_pbl_glc(e)';'EX_oh_pbl(e)';'EX_olsa(e)';'EX_pcresol(e)';'EX_phppa_glc(e)';'EX_phppa(e)';'EX_phtn_glc(e)';'EX_prob_glc(e)';'EX_prob(e)';'EX_pront_glc(e)';'EX_pront(e)';'EX_propl_glc(e)';'EX_propl(e)';'EX_prx_mI_glc(e)';'EX_prx_mI(e)';'EX_prx_mII_glc(e)';'EX_prx_mII(e)';'EX_ptvst(e)';'EX_ptvstgluc(e)';'EX_pvs(e)';'EX_pvsgluc(e)';'EX_R_6oh_warf_glc(e)';'EX_R_6oh_warf(e)';'EX_R_7oh_warf_glc(e)';'EX_R_7oh_warf(e)';'EX_R_8oh_warf_glc(e)';'EX_R_8oh_warf(e)';'EX_r406_glc(e)';'EX_r406(e)';'EX_r529_glc(e)';'EX_r529(e)';'EX_r788(e)';'EX_regfnb_glc(e)';'EX_regfnb(e)';'EX_rep_glc(e)';'EX_rep(e)';'EX_rpn_104557_cb_glc(e)';'EX_rpn_104557(e)';'EX_rpn_96990_glc(e)';'EX_rpn_96990(e)';'EX_rpn_oh_glc(e)';'EX_rpn_oh(e)';'EX_rsv(e)';'EX_rsvgluc(e)';'EX_S_4oh_warf_glc(e)';'EX_S_4oh_warf(e)';'EX_S_6oh_warf_glc(e)';'EX_S_6oh_warf(e)';'EX_sanilamide(e)';'EX_sb_611855(e)';'EX_sb_y(e)';'EX_sch_488128(e)';'EX_sch_57871_glc(e)';'EX_sch_57871(e)';'EX_sfnd_1689_glc(e)';'EX_sfnd_1689(e)';'EX_sftz_glc(e)';'EX_sftz(e)';'EX_simvgluc(e)';'EX_smap_glc(e)';'EX_smap(e)';'EX_smvacid(e)';'EX_sn38(e)';'EX_sn38g(e)';'EX_spz_glc(e)';'EX_spz_sfn_glc(e)';'EX_spz_sfn(e)';'EX_spz(e)';'EX_srv(e)';'EX_ssz(e)';'EX_stg_m3(e)';'EX_stg_m4(e)';'EX_stg(e)';'EX_sulfp(e)';'EX_tab(e)';'EX_tat(e)';'EX_taur(e)';'EX_tchola(e)';'EX_tdchola(e)';'EX_tgz_glc(e)';'EX_tgz(e)';'EX_thsacmp(e)';'EX_tlf_a_1a(e)';'EX_tlf_a_1b(e)';'EX_tlf_a_1x(e)';'EX_tlf_a_2(e)';'EX_tlf_a_2a(e)';'EX_tlf_a_3(e)';'EX_tlf_a_4(e)';'EX_tlf_a_m1(e)';'EX_tlf_a_m2(e)';'EX_tlf_a_m3(e)';'EX_tlf_a_m4(e)';'EX_tlf_a_m4a(e)';'EX_tlf_a_m5(e)';'EX_tlf_a_m5a(e)';'EX_tlf_a_m5b(e)';'EX_tlf_a_m6(e)';'EX_tlf_a_m9(e)';'EX_tlf_a(e)';'EX_tlms_glc(e)';'EX_tlms(e)';'EX_tmacmp(e)';'EX_tolcp_ac_glc(e)';'EX_tolcp_ac(e)';'EX_tolcp_am_glc(e)';'EX_tolcp_am(e)';'EX_tolcp_glc(e)';'EX_tolcp(e)';'EX_tpno_1glc_4g(e)';'EX_tpno_4g(e)';'EX_tpno_4glc(e)';'EX_tpnoh(e)';'EX_tsacmgluc(e)';'EX_tym(e)'};
% model=changeRxnBounds(model,unlikelyRequirements,0,'l');

% set objective
biomassReaction=model.rxns{find(strncmp(model.rxns,'bio',3)),1};
model = changeObjective(model, biomassReaction);

[modelMin, modelPruned, essentialExchanges] = generateCompactExchModel(model,tol,biomassReaction,1,1);
modelMin = changeObjective(modelMin, biomassReaction);
FBA=optimizeCbModel(modelMin,'max');
growthOnMinimalMedium=FBA.f;

end


%%*****************************************************************
%% validate: validate data
%%*****************************************************************
%% SDPT3: version 4.0
%% Copyright (c) 1997 by
%% Kim-Chuan Toh, Michael J. Todd, Reha H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*****************************************************************

   function  [blk,At,C,b,dim,nnblk,parbarrier] = ...
              validate(blk,At,C,b,par,parbarrier);

   if (nargin >= 5)
      spdensity = par.spdensity;
   else
      spdensity = 0.4;
   end
%%
   if ~iscell(blk);
      error('validate: blk must be a cell array'); end;
   if (size(blk,2) < 2)
      error('validate: blk must be a cell array with at least 2 columns');
   end
   if ~iscell(At) | ~iscell(C);
      error('validate: At, C must be cell arrays');
   end
   if (size(At,1) ~= size(blk,1))
      if (size(At,2) == size(blk,1));
         At = At';
      else
         error('validate: size of At is not compatible with blk');
      end
   end
   if (size(C,1) ~= size(blk,1))
      if (size(C,2) == size(blk,1))
         C = C';
      else
         error('validate: size of C is not compatible with blk');
      end
   end
   if (min(size(b)) > 1); error('validate: b must be a vector'); end
   if (size(b,1) < size(b,2)); b = b'; end
   m = length(b);
%%
%%-----------------------------------------
%% validate blk, At, C
%%-----------------------------------------
%%
   for p = 1:size(blk,1)
      if (size(blk{p,2},1) > size(blk{p,2},2))
         blk{p,2} = blk{p,2}';
      end
      pblk = blk(p,:);
      n = sum(pblk{2});
      numblk = length(pblk{2});
      if strcmp(pblk{1},'s');
         m1 = size(At{p,1},2);
         n2 = sum(pblk{2}.*pblk{2});  n22 = sum(pblk{2}.*(pblk{2}+1))/2;
         ntotal(p) = n22;
         if ~all(size(C{p}) == n)
            error('validate: blk and C are not compatible');
         end
         if (norm(C{p}-C{p}',inf) > 1e-13*norm(C{p},inf));
            error('validate: C is not symmetric');
         end
         if (size(At{p,1}) == [m1, n22] & m1~=n22);
            At{p,1} = At{p,1}';
         end
         if (~isempty(At{p,1})) & (size(At{p,1},1) ~= n22)
            error('validate: blk and At not compatible');
         end
         if (nnz(At{p,1}) < spdensity*n22*m1)
            if ~issparse(At{p,1}); At{p,1} = sparse(At{p,1}); end
         end
         if (length(pblk) > 2) %% for low rank constraints
            len = sum(pblk{3});
            if (size(pblk{1,3},2) < size(pblk{1,3},1))
                blk{p,3} = blk{p,3}';
            end
            if any(size(At{p,2})- [n,len])
               error(' low rank structure specified in blk and At not compatible')
            end
            if (length(At(p,:)) > 2)  & ~isempty(At{p,3})
               if all(size(At{p,3},2)-[1,4])
                  error(' low rank structure in At{p,3} not specified correctly')
               end
               if (size(At{p,3},2) == 1)
                  if (size(At{p,3},1) < size(At{p,3},2)); At{p,3} = At{p,3}'; end
                  lenn = length(At{p,3});
                  constrnum = mexexpand(pblk{3},[1:length(pblk{3})]');
                  At{p,3} = [constrnum, [1:lenn]', [1:lenn]', At{p,3}];
               elseif (size(At{p,3},2) == 4)
                  dd = At{p,3};
                  [dummy,idxsort] = sort(dd(:,1));
                  dd = dd(idxsort,:);
                  lenn = size(dd,1);
                  idxD = [0; find(diff(dd(:,1))); lenn];
                  ii = zeros(lenn,1); jj = zeros(lenn,1);
                  ss = [0,cumsum(pblk{3})];
                  for k = 1:length(pblk{3})
                     idx = [idxD(k)+1 : idxD(k+1)];
                     ii(idx) = dd(idx,2)+ss(k); %% convert to cumulative indexing
                     jj(idx) = dd(idx,3)+ss(k);
                  end
                  At{p,3} = [dd(:,1),ii,jj,dd(:,4)];
               end
            else
               constrnum = mexexpand(pblk{3},[1:length(pblk{3})]');
               At{p,3} = [constrnum,  [1:len]', [1:len]', ones(len,1)];
            end
         end
         if (nnz(C{p}) < spdensity*n2) | (numblk > 1);
            if ~issparse(C{p}); C{p} = sparse(C{p}); end;
         else
            if issparse(C{p}); C{p} = full(C{p}); end;
         end
      elseif strcmp(pblk{1},'q') | strcmp(pblk{1},'l') | strcmp(pblk{1},'u');
         ntotal(p) = n;
         if (min(size(C{p})) ~= 1 | max(size(C{p})) ~= n);
            error(['validate: blk and C are not compatible']);
         end;
         if (size(C{p},1) < size(C{p},2)); C{p} = C{p}'; end
         if (size(At{p,1}) == [m, n] & m~=n);
            At{p,1} = At{p,1}';
         end
         if ~all(size(At{p,1}) == [n,m]);
            error('validate: blk and At not compatible');
         end
         if ~issparse(At{p,1});
            At{p,1} = sparse(At{p,1});
         end
         if (nnz(C{p}) < spdensity*n);
            if ~issparse(C{p}); C{p} = sparse(C{p}); end;
         else
            if issparse(C{p}); C{p} = full(C{p}); end;
         end;
      else
         error(' blk: some fields are not specified correctly');
      end
   end
   if (sum(ntotal) < m)
      error(' total dimension of C should be > length(b)');
   end
%%
%%-----------------------------------------
%% problem dimension
%%-----------------------------------------
%%
   dim = zeros(1,4);  nnblk = zeros(1,2);
   for p = 1:size(blk,1)
      pblk = blk(p,:);
      if strcmp(pblk{1},'s')
         dim(1) = dim(1) + sum(pblk{2});
         nnblk(1) = nnblk(1) + length(pblk{2});
         nn(p) = sum(pblk{2});
      elseif strcmp(pblk{1},'q')
         dim(2) = dim(2) + sum(pblk{2});
         nnblk(2) = nnblk(2) + length(pblk{2});
         nn(p) = length(pblk{2});
      elseif strcmp(pblk{1},'l')
         dim(3) = dim(3) + sum(pblk{2});
         nn(p) = sum(pblk{2});
      elseif strcmp(pblk{1},'u')
         dim(4) = dim(4) + sum(pblk{2});
         nn(p) = sum(pblk{2});
      end
   end
%%
%%-----------------------------------------
%% validate parbarrier
%%-----------------------------------------
%%
    if (nargin == 6)
       if ~iscell(parbarrier);
	  if (length(parbarrier) == size(blk,1))
	     tmp = parbarrier;
             clear parbarrier;
	     parbarrier = cell(size(blk,1),1);
	     for p = 1:size(blk,1)
		parbarrier{p} = tmp(p);
             end
          end
       end
       if (size(parbarrier,2) > size(parbarrier,1))
          parbarrier = parbarrier';
       end
       for p = 1:size(blk,1)
          pblk = blk(p,:);
          if (size(parbarrier{p},1) > size(parbarrier{p},2))
             parbarrier{p} = parbarrier{p}';
          end
          len = length(parbarrier{p});
          if strcmp(pblk{1},'s') | strcmp(pblk{1},'q')
             if (len == 1)
                parbarrier{p} = parbarrier{p}*ones(1,length(pblk{2}));
	     elseif (len == 0)
                parbarrier{p} = zeros(1,length(pblk{2}));
	     elseif (len ~= length(pblk{2}))
                error('blk and parbarrier not compatible');
             end
          elseif strcmp(pblk{1},'l')
             if (len == 1)
                parbarrier{p} = parbarrier{p}*ones(1,sum(pblk{2}));
	     elseif (len == 0)
		parbarrier{p} = zeros(1,sum(pblk{2}));
             elseif (len ~= sum(pblk{2}))
                error('blk and parbarrier not compatible');
             end
          elseif strcmp(pblk{1},'u')
             parbarrier{p}= zeros(1,sum(pblk{2}));
          end
       end
    end
%%***********************************************************************