unit erg_code;

interface

uses erg_base, erg_types, classes;

const codegenVersion='1.2.2';

var outputLanguage: String;

procedure generateCode(inputfile, outputfile: String; coloredElements:Boolean=true);

implementation

uses sysUtils;

var LinesRead: Integer;
    LinesRead_sub: Integer;
    InputBuffer: Array[1..65536] of char;

function makeLonger(s: String; minLength: Integer):String;
//will append blanks until desired length is reached
var s1: String;
begin
  s1:=s;
  while length(s1)<minLength do
    s1:=s1+' ';
  result:=s1;
end;

procedure convertToOutputLanguage(var s: String);
begin
  if lowercase(outputLanguage)='matlab' then   //convert from Fortran to Matlab
  begin
    s:=StringReplace(s,'**','^',[rfReplaceAll,rfIgnoreCase]);
  end;
end;

procedure SplitConditions(s: String; var lhs: TStringList; var rhs: TStringList);
var s1, s2: String;
    p: Integer;
begin
  s1:=s;
  lhs.Clear; rhs.Clear;
  while s1<>'' do
  begin
    p:=pos(';',s1);
    if p<1 then p:=length(s1)+1;
    s2:=copy(s1,1,p-1);
    s1:=copy(s1,p+1,length(s1));
    p:=pos('=',s2);
    if p>0 then
    begin
      lhs.Add(trim(copy(s2,1,p)));
      rhs.Add(trim(copy(s2,p+1,length(s2))));
    end
    else
    begin
      lhs.Add(trim(s2));
      rhs.Add('');
    end;
  end;
end;

function appendDot(s: String):String;
//will append ".0" if no dot is in the string, if possible before an "E"
begin
  if pos('.',s)<1 then
    if pos('E',s)<1 then
      if pos('e',s)<1 then
        result:=s+'.0'
      else
        result:=copy(s,1,pos('e',s)-1)+'.0'+copy(s,pos('e',s),length(s))
    else
      result:=copy(s,1,pos('E',s)-1)+'.0'+copy(s,pos('E',s),length(s))
  else
    result:=s;
end;

//******************* TimeTendencies ************************************//

function RateCondition(p: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if ((lhs[j]='isflat=') or (lhs[j]='vertloc=')) and (processes[p].vertLoc<>StrToIntVertLoc(rhs[j])) then isOk:=false;
    if ((lhs[j]='isflat/=') or (lhs[j]='vertloc/=')) and (processes[p].vertLoc=StrToIntVertLoc(rhs[j])) then isOk:=false;
  end;
  result:=isOk;
end;

procedure RateLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer);
// execute the loop <rates> </rates> over all processes that impose creation or destruction rates on the tracer t.
const epsilon='0.00000000001';
var conditions: String;
    i, j, k, tt: Integer;
    tfactor: String;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    ratestring: String;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),17,length(trim(s))-17));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,16))='</timetendencies') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;         //find the beginning of the rate loop
  end;
  //output loop
  for i:=0 to length(processes)-1 do
  begin
   if processes[i].isActive=1 then
   begin
    if RateCondition(i,conditions) then    //check if conditions for the rates apply
    begin
      reset(Q);
      for j:=1 to LinesRead_sub do readln(Q);  //now, scrolled to the beginning of the rate loop
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
        else
        begin
          for j:=0 to length(processes[i].output)-1 do   //all products of this process are checked
          begin
            if tracers[t].isCombined=0 then              //standard tracer, the default case
            begin
              if processes[i].output[j].myTracerNum=t then   //the process i produces tracer t
              begin
                if (processes[i].output[j].amount='1') or (processes[i].output[j].amount='1.0') then
                  ratestring:='+ '+processes[i].name
                else
                  ratestring:='+ ('+processes[i].name+')*('+processes[i].output[j].amount+')';
                //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                  ratestring:=ratestring+'*'+cellheightTimesDensity;
                convertToOutputLanguage(ratestring);
                s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                writeln(Z,s);
              end;
            end
            else  //combined tracer, that means, it contains several (active or inactive) other tracers, but no elements
            begin
              for k:=0 to length(tracers[t].contents)-1 do
              begin
                tt:=tracers[t].contents[k].myElementNum;   //the real tracer is tt
                tfactor:=tracers[t].contents[k].amount;    //scaling factor to convert between t concentration and tt concentration
                if processes[i].output[j].myTracerNum=tt then  //the process i procuces tracer tt
                begin
                  if (processes[i].output[j].amount='1') or (processes[i].output[j].amount='1.0') then
                    ratestring:='+ ('+tfactor+')*('+processes[i].name+')'
                  else
                    ratestring:='+ ('+tfactor+')*('+processes[i].name+')*('+processes[i].output[j].amount+')';
                  //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                  if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                    ratestring:=ratestring+'/'+cellheightTimesDensity;
                  if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                    ratestring:=ratestring+'/'+cellheightTimesDensity;
                  if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                    ratestring:=ratestring+'*'+cellheightTimesDensity;
                  convertToOutputLanguage(ratestring);
                  s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<description>',processes[i].description+' (produces '+tracers[tt].name+')',[rfReplaceAll, rfIgnoreCase]);
                  writeln(Z,s);
                end;
              end;
            end;
          end;
        end;
      end;
    end;
   end;
  end;
  //input loop
  for i:=0 to length(processes)-1 do
  begin
   if processes[i].isActive=1 then
   begin
    if RateCondition(i,conditions) then
    begin
      reset(Q);
      for j:=1 to LinesRead_sub do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
        else
        begin
          if tracers[t].myChildOf<0 then                 //the tracer is not such a one that was automatically generated by splitting the colored elements
          begin
            if tracers[t].isCombined=0 then              //standard tracer, the default case
            begin
              for j:=0 to length(processes[i].input)-1 do
              begin
                if processes[i].input[j].myTracerNum=t then
                begin
                  if (processes[i].input[j].amount='1') or (processes[i].input[j].amount='1.0') then
                    ratestring:='- '+processes[i].name
                  else
                    ratestring:='- ('+processes[i].name+')*('+processes[i].input[j].amount+')';
                  //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                  if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                    ratestring:=ratestring+'/'+cellheightTimesDensity;
                  if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                    ratestring:=ratestring+'/'+cellheightTimesDensity;
                  if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                    ratestring:=ratestring+'*'+cellheightTimesDensity;
                  convertToOutputLanguage(ratestring);
                  s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                  writeln(Z,s);
                end;
              end;
            end
            else
            begin
              for k:=0 to length(tracers[t].contents)-1 do
              begin
                tt:=tracers[t].contents[k].myElementNum;   //the real tracer is tt
                tfactor:=tracers[t].contents[k].amount;    //scaling factor to convert between t concentration and tt concentration
                for j:=0 to length(processes[i].input)-1 do
                begin
                  if processes[i].input[j].myTracerNum=tt then //the process i consumes tracer tt
                  begin
                    if (processes[i].input[j].amount='1') or (processes[i].input[j].amount='1.0') then
                      ratestring:='- ('+tfactor+')*('+processes[i].name+')'
                    else
                      ratestring:='- ('+tfactor+')*('+processes[i].name+')*('+processes[i].input[j].amount+')';
                    //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                    if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                      ratestring:=ratestring+'/'+cellheightTimesDensity;
                    if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                      ratestring:=ratestring+'/'+cellheightTimesDensity;
                    if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                      ratestring:=ratestring+'*'+cellheightTimesDensity;
                    convertToOutputLanguage(ratestring);
                    s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                    s:=StringReplace(s,'<description>',processes[i].description+' (consumes '+tracers[tt].name+')',[rfReplaceAll, rfIgnoreCase]);
                    writeln(Z,s);
                  end;
                end;
              end;
            end;
          end
          else //This tracer was automatically generated during splitting of
               //So, only its mother is used as process precursor.
               //Still, it is also destroyed when the mother tracer is taken up by some process.
          begin
            for j:=0 to length(processes[i].input)-1 do
            begin
              if processes[i].input[j].myTracerNum=tracers[t].myChildOf then
              begin
                if (processes[i].input[j].amount='1') or (processes[i].input[j].amount='1.0') then
                  ratestring:='- '+processes[i].name
                else
                  ratestring:='- ('+processes[i].name+')*('+processes[i].input[j].amount+')';
                //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                  ratestring:=ratestring+'*'+cellheightTimesDensity;
                convertToOutputLanguage(ratestring);
                ratestring:=ratestring+'*max(0.0,min(1.0,'+tracers[t].name+'/max('+epsilon+','
                                            +tracers[tracers[t].myChildOf].name+')))';
                  //This factor is to make sure that the colored tracer is taken up with the same relative rate as the mother tracer.
                s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                writeln(Z,s);
              end;
            end;
            //still, this tracer may occur as "input" in one case: The repainting of "none element = color"
            for j:=0 to length(processes[i].input)-1 do
            begin
              if processes[i].input[j].myTracerNum=t then
              begin
                if (processes[i].input[j].amount='1') or (processes[i].input[j].amount='1.0') then
                  ratestring:='- '+processes[i].name
                else
                  ratestring:='- ('+processes[i].name+')*('+processes[i].input[j].amount+')';
                //flat processes have rates in [mmol/m**2/day)], non-flat tracers have unit [mmol/m**3]
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=1) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=0) and (processes[i].vertLoc=2) then
                  ratestring:=ratestring+'/'+cellheightTimesDensity;
                if (tracers[t].vertLoc=3) and (processes[i].vertLoc=0) then
                  ratestring:=ratestring+'*'+cellheightTimesDensity;
                convertToOutputLanguage(ratestring);
                s:=StringReplace(s,'<timeTendency>',makeLonger(ratestring,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                writeln(Z,s);
              end;
            end;
          end;
        end;
      end;
    end;
   end;
  end;
  LinesRead_sub:=LinesRead_sub+MyLinesRead;
end;


//******************* children *************************************//

function ChildCondition(p: Integer; c: String):Boolean;
begin
  result:=true;
end;

procedure ChildLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer);
var conditions: String;
    i, j: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    ratestring: String;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),8,length(trim(s))-8));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,10))='</children') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  //output loop
  for i:=0 to length(tracers)-1 do
   if tracers[i].isActive=1 then
   if tracers[i].myChildOf=t then
   begin
    if RateCondition(i,conditions) then
    begin
      reset(Q);
      for j:=1 to LinesRead_sub do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,10))='</children' then LoopFinished:=true
        else
        begin
              s:=StringReplace(s,'<childName>',tracers[i].name,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<name>',tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
              writeln(Z,s);
        end;
      end;
    end;
   end;
  LinesRead_sub:=LinesRead_sub+MyLinesRead;
end;

//******************* limitations ********************************************//

procedure LimitationLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer);
// execute the loop <limitations> </limitations> over all limitation functions that depend on the tracer t.
var
  i,j: Integer;
  loopFinished: Boolean;
  s1: String;
  myLinesRead: Integer;
  myFormula: String;
begin
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,13))='</limitations') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;         //find the beginning of the limitation loop
  end;
  for i:=0 to length(limitations)-1 do
  begin
    if limitations[i].myTracerNum=t then
    begin
      reset(Q);
      for j:=1 to LinesRead_sub do readln(Q);  //now, scrolled to the beginning of the limitation loop
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,13))='</limitations' then LoopFinished:=true
        else
        begin
          if limitations[i].limitationType='HARD' then       // theta function
            myFormula:='theta('+limitations[i].tracer+'-'+limitations[i].value+')'
          else if limitations[i].limitationType='MM' then    // Michaelis-Menten
            myFormula:=limitations[i].tracer+'/('+limitations[i].tracer+'+'+limitations[i].value+')'
          else if limitations[i].limitationType='MMQ' then   // quadratic Michaelis-Menten
            myFormula:=limitations[i].tracer+'*'+limitations[i].tracer+'/('
                       +limitations[i].tracer+'*'+limitations[i].tracer+'+'+limitations[i].value+'*'+limitations[i].value+')'
          else if limitations[i].limitationType='IV' then    // Ivlev
            myFormula:='1.0-exp(-'+limitations[i].tracer+'/'+limitations[i].value+')'
          else if limitations[i].limitationType='IVQ' then   // quadratic Ivlev
            myFormula:='1.0-exp(-'+limitations[i].tracer+'**2/'+limitations[i].value+'**2)'
          else if limitations[i].limitationType='LIN' then   // linear
            myFormula:='min('+limitations[i].tracer+'/'+limitations[i].value+',1.0)'
          else if limitations[i].limitationType='TANH' then   // linear
            myFormula:='tanh('+limitations[i].tracer+'/'+limitations[i].value+')';
          s:=StringReplace(s,'<name>',makeLonger('lim_'+limitations[i].tracer+'_'+IntToStr(i),20),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<formula>',myFormula,[rfReplaceAll, rfIgnoreCase]);
          writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead_sub:=LinesRead_sub+MyLinesRead;
end;

//******************* containing tracers (of cElements) **********************//

function ctCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if ((lhs[j]='isflat=') or (lhs[j]='vertloc=')) and (tracers[i].vertLoc<>StrToIntVertLoc(rhs[j])) then isOk:=false;
    if ((lhs[j]='isflat/=') or (lhs[j]='vertloc/='))and (tracers[i].vertLoc=StrToIntVertLoc(rhs[j])) then isOk:=false;
    if (lhs[j]='vertspeed=') and (tracers[i].vertSpeed <> rhs[j]) then isOk:=false;
    if (lhs[j]='vertspeed/=') and (tracers[i].vertSpeed = rhs[j]) then isOk:=false;
  end;
  result:=isOk;
end;

procedure ctLoop(s: String; var Q: TextFile; var Z: TextFile; ce: Integer);
var conditions: String;
    i, j: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    ratestring: String;
    Current3dTracerNum, CurrentFlatTracerNum, CurrentMovingTracerNum: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),20,length(trim(s))-20));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,19))='</containingtracers') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  Current3dTracerNum:=0; CurrentFlatTracerNum:=0; CurrentMovingTracerNum:=0;
  for i:=0 to length(tracers)-1 do
  begin
   if tracers[i].vertLoc=0 then Current3dTracerNum:=Current3dTracerNum+1
                          else CurrentFlatTracerNum:=CurrentFlatTracerNum+1;
   if (tracers[i].vertLoc=0) and (tracers[i].vertSpeed<>'0') then
     CurrentMovingTracerNum:=CurrentMovingTracerNum+1;

   if pos('_with_'+cElements[ce].color+'_'+cElements[ce].element,tracers[i].name)>0 then
   begin
    if ctCondition(i,conditions) then
    begin
      reset(Q);
      for j:=1 to LinesRead_sub do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,19))='</containingtracers' then
          LoopFinished:=true
        else
        begin
              s:=StringReplace(s,'<ctNumFlat>',IntToStr(currentFlatTracerNum),[rfReplaceAll, rfIgnoreCase]);
              if tracers[i].vertLoc=0 then
                s:=StringReplace(s,'<ctIndexNum>',IntToStr(current3dTracerNum),[rfReplaceAll, rfIgnoreCase])
              else
                s:=StringReplace(s,'<ctIndexNum>',IntToStr(currentFlatTracerNum),[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ct>',tracers[i].name,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctAmount>',tracers[i].contents[0].amount,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctNumMoving>',IntToStr(CurrentMovingTracerNum),[rfReplaceAll, rfIgnoreCase]);
              writeln(Z,s);
        end;
      end;
    end;
   end;
  end;
  LinesRead_sub:=LinesRead_sub+MyLinesRead;
end;

//******************* Constants ************************************//

function ConstantCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if (lhs[j]='name=') and (lowercase(constants[i].name)<>rhs[j]) then isOk:=false;
    if (lhs[j]='name/=') and (lowercase(constants[i].name)=rhs[j]) then isOk:=false;
  end;
  result:=isOk;
end;

procedure ConstantLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, j: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    myValue: String;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,11))='</constants') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  for i:=0 to length(constants)-1 do
  begin
    if ConstantCondition(i,conditions) then
    begin
      myValue:=makeLonger(AppendDot(FloatToStr(constants[i].value)),8);
      convertToOutputLanguage(myValue);
      reset(Q);
      for j:=1 to LinesRead do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,11))='</constants' then LoopFinished:=true
        else
        begin
          s:=StringReplace(s,'<name>',makeLonger(constants[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<value>',myValue,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',constants[i].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<comment>',constants[i].comment,[rfReplaceAll, rfIgnoreCase]);
          writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
end;

//******************* Tracers ************************************//

function hasRates(t: Integer):Boolean;
var p, i, k, tt: Integer;
    found: Boolean;
begin
  found:=false;
  if tracers[t].myChildOf < 0 then
  begin
    if tracers[t].isCombined = 0 then
    begin
      for p:=0 to length(processes)-1 do
      begin
        for i:=0 to length(processes[p].input)-1 do
          if processes[p].input[i].myTracerNum=t then found:=true;
        for i:=0 to length(processes[p].output)-1 do
          if processes[p].output[i].myTracerNum=t then found:=true;
      end;
    end
    else //combined tracer
    begin
      for k:=0 to length(tracers[t].contents)-1 do
      begin
        tt:=tracers[t].contents[k].myElementNum;   //the real tracer is tt
        for p:=0 to length(processes)-1 do
        begin
          for i:=0 to length(processes[p].input)-1 do
            if processes[p].input[i].myTracerNum=tt then found:=true;
          for i:=0 to length(processes[p].output)-1 do
            if processes[p].output[i].myTracerNum=tt then found:=true;
        end;
      end;
    end;
  end
  else        //colored tracer, so only mother tracers are used as process input
    for p:=0 to length(processes)-1 do
    begin
      for i:=0 to length(processes[p].input)-1 do
        if processes[p].input[i].myTracerNum=tracers[t].myChildOf then found:=true;
      for i:=0 to length(processes[p].output)-1 do
        if processes[p].output[i].myTracerNum=t then found:=true;
    end;
  result:=found;
end;

function hasRatesFlat(t: Integer; sf: String):Boolean;
var p, i, k, tt: Integer;
    found: Boolean;
    f: Integer;
begin
  if lowercase(sf)='wat' then f:=0
  else if lowercase(sf)='sed' then f:=1
  else if lowercase(sf)='sur' then f:=2
  else if lowercase(sf)='fis' then f:=3
  else f:=StrToInt(sf);
  found:=false;
  if tracers[t].myChildOf < 0 then
  begin
    if tracers[t].isCombined = 0 then
    begin
      for p:=0 to length(processes)-1 do
      begin
        if processes[p].vertLoc=f then
        begin
          for i:=0 to length(processes[p].input)-1 do
            if processes[p].input[i].myTracerNum=t then found:=true;
          for i:=0 to length(processes[p].output)-1 do
            if processes[p].output[i].myTracerNum=t then found:=true;
        end;
      end;
    end
    else
    begin //combined tracer
      for k:=0 to length(tracers[t].contents)-1 do
      begin
        tt:=tracers[t].contents[k].myElementNum;   //the real tracer is tt
        for p:=0 to length(processes)-1 do
        begin
          if processes[p].vertLoc=f then
          begin
            for i:=0 to length(processes[p].input)-1 do
              if processes[p].input[i].myTracerNum=tt then found:=true;
            for i:=0 to length(processes[p].output)-1 do
              if processes[p].output[i].myTracerNum=tt then found:=true;
          end;
        end;
      end;
    end;
  end
  else  //colored tracer, so only mother tracers are used as process input
    for p:=0 to length(processes)-1 do
    begin
      if processes[p].vertLoc=f then
      begin
        for i:=0 to length(processes[p].input)-1 do
          if processes[p].input[i].myTracerNum=tracers[t].myChildOf then found:=true;
        for i:=0 to length(processes[p].output)-1 do
          if processes[p].output[i].myTracerNum=t then found:=true;
      end;
    end;
  result:=found;
end;

function TracerCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
    myHasCETotal, myHasCEAged: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  //check if this is a colored tracer which has a cElement with isTracer / isAging = 1
  myHasCETotal:=0; myHasCEAged:=0;
  for j:=0 to length(cElements)-1 do
    if pos('_with_'+cElements[j].color+'_'+cElements[j].element,tracers[i].name)>0 then
    begin
      if cElements[j].myIsTracer=1 then myHasCeTotal:=1;
      if cElements[j].myIsAging=1  then myHasCeAged:=1;
    end;

  for j:=0 to lhs.Count-1 do
  begin
    if (lhs[j]='name=') and (lowercase(tracers[i].name)<>rhs[j]) then isOk:=false;
    if (lhs[j]='name/=') and (lowercase(tracers[i].name)=rhs[j]) then isOk:=false;
    if (lhs[j]='vertspeed=') and (lowercase(tracers[i].vertSpeed)<>rhs[j]) then isOk:=false;
    if (lhs[j]='vertspeed/=') and (lowercase(tracers[i].vertSpeed)=rhs[j]) then isOk:=false;
    if (lhs[j]='opacity=') and (lowercase(tracers[i].opacity)<>rhs[j]) then isOk:=false;
    if (lhs[j]='opacity/=') and (lowercase(tracers[i].opacity)=rhs[j]) then isOk:=false;
    if ((lhs[j]='isflat=') or (lhs[j]='vertloc=')) and (tracers[i].vertLoc<>StrToIntVertLoc(rhs[j])) then isOk:=false;
    if ((lhs[j]='isflat/=') or (lhs[j]='vertloc/=')) and (tracers[i].vertLoc=StrToIntVertLoc(rhs[j])) then isOk:=false;
    if (lhs[j]='ispositive=') and (IntToStr(tracers[i].isPositive)<>rhs[j]) then isOk:=false;
    if (lhs[j]='ispositive/=') and (IntToStr(tracers[i].isPositive)=rhs[j]) then isOk:=false;
    if (lhs[j]='ismixed=') and (IntToStr(tracers[i].isMixed)<>rhs[j]) then isOk:=false;
    if (lhs[j]='ismixed/=') and (IntToStr(tracers[i].isMixed)=rhs[j]) then isOk:=false;
    if (lhs[j]='childof=') and (lowercase(tracers[i].childOf)<>rhs[j]) then isOk:=false;
    if (lhs[j]='childof/=') and (lowercase(tracers[i].childOf)=rhs[j]) then isOk:=false;
    if (lhs[j]='hastimetendencies') and not hasRates(i) then isOk:=false;
    if (lhs[j]='hastimetendenciesvertloc=') and not hasRatesFlat(i,rhs[j]) then isOk:=false;
    if (lhs[j]='hastimetendenciesvertloc/=') and hasRatesFlat(i,rhs[j]) then isOk:=false;
    if (lhs[j]='atmosdep=') and (IntToStr(tracers[i].atmosDep)<>rhs[j]) then isOk:=false;
    if (lhs[j]='atmosdep/=') and (IntToStr(tracers[i].atmosDep)=rhs[j]) then isOk:=false;
    if (lhs[j]='riverdep=') and (IntToStr(tracers[i].riverDep)<>rhs[j]) then isOk:=false;
    if (lhs[j]='riverdep/=') and (IntToStr(tracers[i].riverDep)=rhs[j]) then isOk:=false;
    if (lhs[j]='isoutput=') and ( ((modelinfos.debugMode=0) and (tracers[i].isOutput<>StrToInt(rhs[j])))
                                or((modelinfos.debugMode=1)  and (1<>StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='isoutput/=') and ( ((modelinfos.debugMode=0) and (tracers[i].isOutput=StrToInt(rhs[j])))
                                or ((modelinfos.debugMode=1) and (1=StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='useinitvalue=') and (IntToStr(tracers[i].useInitValue)<>rhs[j]) then isOk:=false;
    if (lhs[j]='useinitvalue/=') and (IntToStr(tracers[i].useInitValue)=rhs[j]) then isOk:=false;
    if (lhs[j]='initvalue=') and (tracers[i].initValue<>rhs[j]) then isOk:=false;
    if (lhs[j]='initvalue/=') and (tracers[i].initValue=rhs[j]) then isOk:=false;
    if (lhs[j]='calcbeforezintegral=') and (IntToStr(tracers[i].myCalcBeforeZIntegral)<>rhs[j]) then isOk:=false;
    if (lhs[j]='calcbeforezintegral/=') and (IntToStr(tracers[i].myCalcBeforeZIntegral)=rhs[j]) then isOk:=false;
    if (lhs[j]='hascetotal') and (myHasCeTotal<>1) then isOk:=false;
    if (lhs[j]='hasceaged') and (myHasCeAged<>1) then isOk:=false;
    if (lhs[j]='solubility=') and (lowercase(tracers[i].solubility)<>rhs[j]) then isOk:=false;
    if (lhs[j]='solubility/=') and (lowercase(tracers[i].solubility)=rhs[j]) then isOk:=false;
  end;
  result:=isOk;
end;

procedure TracerLoop(s: String; var Q: TextFile; var Z: TextFile; backward: Boolean=false);
var conditions: String;
    i, j, ii, n, ifs: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    myNumFlat: integer;
    myNum3D: integer;
    myNumMoving: Integer;
    myNumTracer: Integer;
    myCETotalIndex: String;
    myCEAgedIndex: String;
    myCETotalName: String;
    myCEAgedName: String;
    myCEAmount: String;
    myChildOfNumMoving, myChildOfNumMovingTemp: Integer;
    parentI: Integer;
begin
  DecimalSeparator:='.';
  if backward then
    conditions:=lowercase(copy(trim(s),18,length(trim(s))-18))
  else
    conditions:=lowercase(copy(trim(s),10,length(trim(s))-10));
  myLinesRead:=0;
  s1:='';
  while not ((lowercase(copy(trim(s1),1,9))='</tracers') or (lowercase(copy(trim(s1),1,17))='</backwardtracers')) and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  for ii:=0 to length(Tracers)-1 do
  begin
   if backward then i:=length(tracers)-1-ii
   else i:=ii;
   if tracers[i].isActive=1 then
   begin
    myNumFlat:=0;
    myNumMoving:=0;
    myNum3D:=0;
    myNumTracer:=0;
    for n:=0 to i do
    begin
     if tracers[n].isActive=1 then
     begin
      if tracers[n].vertLoc <> 0 then
        myNumFlat:=myNumFlat+1;
      if tracers[n].vertLoc = 0 then
        myNum3D:=myNum3D+1;
      if tracers[n].vertSpeed <> '0' then
        myNumMoving:=myNumMoving+1;
      myNumTracer:=myNumTracer+1;
     end;
    end;
    if TracerCondition(i,conditions) then
    begin
      myCETotalIndex:='-1';
      myCEAgedIndex:='-1';
      myCETotalName:='';
      myCEAgedName:='';
      myCEAmount:='0.0';
      for j:=0 to length(cElements)-1 do
        if pos('_with_'+cElements[j].color+'_'+cElements[j].element,tracers[i].name)>0 then
        begin
          if cElements[j].myIsTracer=1 then
          begin
            myCETotalIndex:='idx_total_'+cElements[j].color+'_'+cElements[j].element;
            myCETotalName:='total_'+cElements[j].color+'_'+cElements[j].element;
          end
          else
            myCETotalIndex:='-1';
          if cElements[j].myIsAging=1 then
          begin
            myCEAgedIndex:='idx_aged_'+cElements[j].color+'_'+cElements[j].element;
            myCEAgedName:='aged_'+cElements[j].color+'_'+cElements[j].element;
          end
          else
            myCETotalIndex:='-1';
          myCEAmount := tracers[i].contents[0].amount;
          myChildOfNumMovingTemp:=0;
          for parentI:=0 to length(tracers)-1 do
          begin
            if tracers[parentI].vertSpeed <> '0' then
              myChildOfNumMovingTemp:=myChildOfNumMovingTemp+1;
            if lowercase(tracers[parentI].name)=lowercase(tracers[i].childOf) then
              myChildOfNumMoving:=myChildOfNumMovingTemp;
          end;
        end;
      reset(Q);
      for j:=1 to LinesRead do readln(Q);
      LinesRead_sub:=LinesRead;
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        LinesRead_sub:=LinesRead_sub+1;
        if EOF(Q) then LoopFinished:=true;
        if (lowercase(copy(trim(s),1,9))='</tracers') or (lowercase(copy(trim(s),1,17))='</backwardtracers') then LoopFinished:=true
        else if lowercase(copy(trim(s),1,15))='<timetendencies' then
          RateLoop(s,Q,Z,i)
        else if lowercase(copy(trim(s),1,9))='<children' then
          childLoop(s,Q,Z,i)
        else if lowercase(copy(trim(s),1,12))='<limitations' then
          LimitationLoop(s,Q,Z,i)
        else if lowercase(copy(trim(s),1,3))='<if' then
        begin
          if tracerCondition(i,lowercase(copy(trim(s),5,length(trim(s))-5)))=false then
          begin //<if> condition is not fulfilled, so search for the corresponding </if>
            ifs:=1;
            while ifs>0 do
            begin
              readln(Q,s);
              LinesRead_sub:=LinesRead_sub+1;
              if lowercase(copy(trim(s),1,3))='<if' then
                ifs:=ifs+1
              else if lowercase(copy(trim(s),1,4))='</if' then
                ifs:=ifs-1;
            end;
          end;
        end
        else if lowercase(copy(trim(s),1,4))='</if' then
          //do nothing, ignore this line
        else
        begin
          s:=StringReplace(s,'<name>',makeLonger(Tracers[i].name,1),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',Tracers[i].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<comment>',Tracers[i].comment,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<vertSpeed>',Tracers[i].vertSpeed,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<vertDiff>',Tracers[i].vertDiff,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<vertSpeedValue>',AppendDot(FloatToStr(Tracers[i].myVertSpeedValue)),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<-vertSpeedValue>',AppendDot(FloatToStr(-Tracers[i].myVertSpeedValue)),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<opacity>',Tracers[i].opacity,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<childof>',Tracers[i].childOf,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<childOfNumMoving>',IntToStr(myChildOfNumMoving),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<solubility>',tracers[i].solubility,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<schmidtNumber>',tracers[i].schmidtNumber,[rfReplaceAll, rfIgnoreCase]);
          if tracers[i].gasName='' then
            s:=StringReplace(s,'<gasName>',tracers[i].name,[rfReplaceAll, rfIgnoreCase])
          else
            s:=StringReplace(s,'<gasName>',tracers[i].gasName,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<molarMass>',tracers[i].molarMass,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<initValue>',tracers[i].initValue,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat>',IntToStr(myNumFlat),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+1>',IntToStr(myNumFlat+1),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+2>',IntToStr(myNumFlat+2),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+3>',IntToStr(myNumFlat+3),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+4>',IntToStr(myNumFlat+4),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+5>',IntToStr(myNumFlat+5),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+6>',IntToStr(myNumFlat+6),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+7>',IntToStr(myNumFlat+7),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+8>',IntToStr(myNumFlat+8),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numFlat+9>',IntToStr(myNumFlat+9),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D>',IntToStr(myNum3D),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+1>',IntToStr(myNum3D+1),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+2>',IntToStr(myNum3D+2),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+3>',IntToStr(myNum3D+3),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+4>',IntToStr(myNum3D+4),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+5>',IntToStr(myNum3D+5),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+6>',IntToStr(myNum3D+6),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+7>',IntToStr(myNum3D+7),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+8>',IntToStr(myNum3D+8),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<num3D+9>',IntToStr(myNum3D+9),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving>',IntToStr(myNumMoving),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+1>',IntToStr(myNumMoving+1),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+2>',IntToStr(myNumMoving+2),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+3>',IntToStr(myNumMoving+3),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+4>',IntToStr(myNumMoving+4),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+5>',IntToStr(myNumMoving+5),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+6>',IntToStr(myNumMoving+6),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+7>',IntToStr(myNumMoving+7),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+8>',IntToStr(myNumMoving+8),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numMoving+9>',IntToStr(myNumMoving+9),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer>',IntToStr(myNumTracer),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+1>',IntToStr(myNumTracer+1),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+2>',IntToStr(myNumTracer+2),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+3>',IntToStr(myNumTracer+3),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+4>',IntToStr(myNumTracer+4),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+5>',IntToStr(myNumTracer+5),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+6>',IntToStr(myNumTracer+6),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+7>',IntToStr(myNumTracer+7),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+8>',IntToStr(myNumTracer+8),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numTracer+9>',IntToStr(myNumTracer+9),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<ceTotalIndex>',myCETotalIndex,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<ceAgedIndex>',myCEAgedIndex,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<ceTotalName>',myCETotalName,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<ceAgedName>',myCEAgedName,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<ceAmount>',myCEAmount,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<atmosDep>',IntToStr(Tracers[i].atmosDep),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<riverDep>',IntToStr(Tracers[i].riverDep),[rfReplaceAll, rfIgnoreCase]);
          writeln(Z,s);
        end;
      end;
    end;
   end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
end;

//******************* Auxiliaries ************************************//

function AuxiliaryCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if (lhs[j]='name=') and (lowercase(auxiliaries[i].name)<>rhs[j]) then isOk:=false;
    if (lhs[j]='name/=') and (lowercase(auxiliaries[i].name)=rhs[j]) then isOk:=false;
    if (lhs[j]='calcafterprocesses=') and (IntToStr(auxiliaries[i].calcAfterProcesses)<>rhs[j]) then isOk:=false;
    if (lhs[j]='calcafterprocesses/=') and (IntToStr(auxiliaries[i].calcAfterProcesses)=rhs[j]) then isOk:=false;
    if (lhs[j]='calcbeforezintegral=') and (IntToStr(auxiliaries[i].myCalcBeforeZIntegral)<>rhs[j]) then isOk:=false;
    if (lhs[j]='calcbeforezintegral/=') and (IntToStr(auxiliaries[i].myCalcBeforeZIntegral)=rhs[j]) then isOk:=false;
    if (lhs[j]='iterations=') and (IntToStr(auxiliaries[i].iterations)<>rhs[j]) then isOk:=false;
    if (lhs[j]='iterations/=') and (IntToStr(auxiliaries[i].iterations)=rhs[j]) then isOk:=false;
    if ((lhs[j]='isflat=') or (lhs[j]='vertloc=')) and (auxiliaries[i].vertLoc<>StrToIntVertLoc(rhs[j])) then isOk:=false;
    if ((lhs[j]='isflat/=') or (lhs[j]='vertloc/=')) and (auxiliaries[i].vertLoc=StrToIntVertLoc(rhs[j])) then isOk:=false;
    if (lhs[j]='isoutput=') and ( ((modelinfos.debugMode=0) and (auxiliaries[i].isOutput<>StrToInt(rhs[j])))
                                or((modelinfos.debugMode=1)  and (1<>StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='isoutput/=') and ( ((modelinfos.debugMode=0) and (auxiliaries[i].isOutput=StrToInt(rhs[j])))
                                or ((modelinfos.debugMode=1) and (1=StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='isusedelsewhere=') and (IntToStr(auxiliaries[i].isUsedElsewhere)<>rhs[j]) then isOk:=false;
    if (lhs[j]='isusedelsewhere/=') and (IntToStr(auxiliaries[i].isUsedElsewhere)=rhs[j]) then isOk:=false;
    if (lhs[j]='iszgradient=') and (IntToStr(auxiliaries[i].isZGradient)<>rhs[j]) then isOk:=false;
    if (lhs[j]='iszgradient/=') and (IntToStr(auxiliaries[i].isZGradient)=rhs[j]) then isOk:=false;
    if (lhs[j]='iszintegral=') and (IntToStr(auxiliaries[i].isZIntegral)<>rhs[j]) then isOk:=false;
    if (lhs[j]='iszintegral/=') and (IntToStr(auxiliaries[i].isZIntegral)=rhs[j]) then isOk:=false;

  end;
  result:=isOk;
end;

procedure AuxiliaryLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, j, k: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    myFormula: String;
    mytemp: Array[1..9] of String;
    deleteLine: Boolean;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),14,length(trim(s))-14));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,13))='</auxiliaries') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  for i:=0 to length(Auxiliaries)-1 do
  begin
    if AuxiliaryCondition(i,conditions) then
    begin
      myformula:=Auxiliaries[i].formula;
      convertToOutputLanguage(myformula);
      for j:=1 to 9 do
      begin
        mytemp[j]:=Auxiliaries[i].temp[j];
        convertToOutputLanguage(mytemp[j]);
      end;
      reset(Q);
      for j:=1 to LinesRead do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,13))='</auxiliaries' then LoopFinished:=true
        else
        begin
          deleteLine:=false;
          s:=StringReplace(s,'<name>',makeLonger(Auxiliaries[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          //if <tempX> appears in line, but tempX='', then delete this line
          for k:=1 to 9 do
            if (myTemp[k]='') and (pos('<temp'+IntToStr(k)+'>',lowercase(s))>0) then
              deleteLine:=true
            else
              s:=StringReplace(s,'<temp'+IntToStr(k)+'>',makeLonger(myTemp[k],30),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<formula>',makeLonger(myFormula,30),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',Auxiliaries[i].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<comment>',Auxiliaries[i].comment,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<iterations>',IntToStr(Auxiliaries[i].iterations),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<iterinit>',Auxiliaries[i].iterInit,[rfReplaceAll, rfIgnoreCase]);
          if deleteLine=false then writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
end;

//******************* Processes ************************************//

function ProcessCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if (lhs[j]='name=') and (lowercase(processes[i].name)<>rhs[j]) then isOk:=false;
    if (lhs[j]='name/=') and (lowercase(processes[i].name)=rhs[j]) then isOk:=false;
    if ((lhs[j]='isflat=') or (lhs[j]='vertloc=')) and (processes[i].vertLoc<>StrToIntVertLoc(rhs[j])) then isOk:=false;
    if ((lhs[j]='isflat/=') or (lhs[j]='vertloc/=')) and (processes[i].vertLoc=StrToIntVertLoc(rhs[j])) then isOk:=false;
    if (lhs[j]='isoutput=') and ( ((modelinfos.debugMode=0) and (processes[i].isOutput<>StrToInt(rhs[j])))
                                or((modelinfos.debugMode=1)  and (1<>StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='isoutput/=') and ( ((modelinfos.debugMode=0) and (processes[i].isOutput=StrToInt(rhs[j])))
                                or ((modelinfos.debugMode=1) and (1=StrToInt(rhs[j]))) ) then isOk:=false;
    if (lhs[j]='isstiff=') and (processes[i].myIsStiff<>StrToInt(rhs[j])) then isOk:=false;
    if (lhs[j]='isstiff/=') and (processes[i].myIsStiff=StrToInt(rhs[j])) then isOk:=false;
    if (lhs[j]='processtype=') and (lowercase(processes[i].processType)<>rhs[j]) then isOk:=false;
    if (lhs[j]='processtype/=') and (lowercase(processes[i].processType)=rhs[j]) then isOk:=false;
  end;
  result:=isOk;
end;

procedure ProcessLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, j: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    myRate, myStiffFactor, myStiffTracer: String;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,11))='</processes') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  for i:=0 to length(Processes)-1 do
  begin
   if processes[i].isActive=1 then
   begin
    if ProcessCondition(i,conditions) then
    begin
      myRate:=Processes[i].turnover;
      convertToOutputLanguage(myRate);
      myStiffFactor:=processes[i].myStiffFactor;
      convertToOutputLanguage(myStiffFactor);
      if processes[i].myStiffTracerNum>=0 then
        myStiffTracer:=tracers[processes[i].myStiffTracerNum].name
      else
        myStiffTracer:='';
      reset(Q);
      for j:=1 to LinesRead do readln(Q);
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,11))='</processes' then LoopFinished:=true
        else
        begin
          s:=StringReplace(s,'<name>',makeLonger(Processes[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',Processes[i].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<turnover>',makeLonger(myRate,30),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<comment>',Processes[i].comment,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<processtype>',Processes[i].processType,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<stifffactor>',myStiffFactor,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<stifftracer>',myStiffTracer,[rfReplaceAll, rfIgnoreCase]);
          writeln(Z,s);
        end;
      end;
    end;
   end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
end;

//******************* cElements ************************************//

function cElementCondition(i: Integer; c: String):Boolean;
var isOk: Boolean;
    lhs,rhs: TStringList;
    j: Integer;
begin
  lhs:=TStringList.Create; rhs:=TStringList.Create;
  isOk:=true;
  splitConditions(c,lhs,rhs);
  for j:=0 to lhs.Count-1 do
  begin
    if (lhs[j]='istracer=') and (IntToStr(cElements[i].myIsTracer)<>rhs[j]) then isOk:=false;
    if (lhs[j]='istracer/=') and (IntToStr(cElements[i].myIsTracer)=rhs[j]) then isOk:=false;
    if (lhs[j]='isaging=') and (IntToStr(cElements[i].myIsAging)<>rhs[j]) then isOk:=false;
    if (lhs[j]='isaging/=') and (IntToStr(cElements[i].myIsAging)=rhs[j]) then isOk:=false;
  end;
  result:=isOk;
end;

procedure cElementLoop(s: String; var Q: TextFile; var Z: TextFile; active: Boolean=true);
var conditions: String;
    i, j: Integer;
    loopFinished: Boolean;
    s1: String;
    myLinesRead: Integer;
    totalIndexNum, totalIndexTopNum, totalIndexBottomNum: Integer;
    agedIndexNum, agedIndexTopNum, agedIndexBottomNum: Integer;
    Current3dTracerNum, CurrentFlatTracerNum: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  while not (lowercase(copy(trim(s1),1,11))='</celements') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
  end;
  for i:=0 to length(cElements)-1 do
  begin
    if cElementCondition(i,conditions) and active then
    begin
      totalIndexNum:=0; totalIndexTopNum:=0; totalIndexBottomNum:=0;
      agedIndexNum:=0; agedIndexTopNum:=0; agedIndexBottomNum:=0;
      Current3dTracerNum:=0; CurrentFlatTracerNum:=0;
      for j:=0 to length(tracers)-1 do
      begin
        if tracers[j].isActive>0 then
          if tracers[j].vertLoc=0 then Current3dTracerNum:=Current3dTracerNum+1
                                 else CurrentFlatTracerNum:=CurrentFlatTracerNum+1;
        if tracers[j].name='total_'+cElements[i].color+'_'+cElements[i].element then
          totalIndexNum:=Current3dTracerNum;
        if tracers[j].name='total_'+cElements[i].color+'_'+cElements[i].element+'_at_top' then
          totalIndexTopNum:=CurrentFlatTracerNum;
        if tracers[j].name='total_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom' then
          totalIndexBottomNum:=CurrentFlatTracerNum;
        if tracers[j].name='aged_'+cElements[i].color+'_'+cElements[i].element then
          agedIndexNum:=Current3dTracerNum;
        if tracers[j].name='aged_'+cElements[i].color+'_'+cElements[i].element+'_at_top' then
          agedIndexTopNum:=CurrentFlatTracerNum;
        if tracers[j].name='aged_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom' then
          agedIndexBottomNum:=CurrentFlatTracerNum;
      end;
      reset(Q);
      for j:=1 to LinesRead do readln(Q);
      LinesRead_sub:=LinesRead;
      loopFinished:=false;
      while not LoopFinished do
      begin
        readln(Q,s);
        LinesRead_sub:=LinesRead_sub+1;
        if EOF(Q) then LoopFinished:=true;
        if lowercase(copy(trim(s),1,11))='</celements' then LoopFinished:=true
        else if lowercase(copy(trim(s),1,18))='<containingtracers' then
          ctLoop(s,Q,Z,i)
        else
        begin
          s:=StringReplace(s,'<total>',makeLonger('total_'+cElements[i].color+'_'+cElements[i].element,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalTop>',makeLonger('total_'+cElements[i].color+'_'+cElements[i].element+'_at_top',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalBottom>',makeLonger('total_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalBottomNumFlat>',IntToStr(totalIndexBottomNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<aged>',makeLonger('aged_'+cElements[i].color+'_'+cElements[i].element,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedTop>',makeLonger('aged_'+cElements[i].color+'_'+cElements[i].element+'_at_top',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedBottom>',makeLonger('aged_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedBottomNumFlat>',IntToStr(agedIndexBottomNum),[rfReplaceAll, rfIgnoreCase]);

          s:=StringReplace(s,'<totalIndex>',makeLonger('idx_total_'+cElements[i].color+'_'+cElements[i].element,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalIndexTop>',makeLonger('idx_flat_total_'+cElements[i].color+'_'+cElements[i].element+'_at_top',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalIndexBottom>',makeLonger('idx_flat_total_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndex>',makeLonger('idx_aged_'+cElements[i].color+'_'+cElements[i].element,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndexTop>',makeLonger('idx_flat_aged_'+cElements[i].color+'_'+cElements[i].element+'_at_top',15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndexBottom>',makeLonger('idx_flat_aged_'+cElements[i].color+'_'+cElements[i].element+'_at_bottom',15),[rfReplaceAll, rfIgnoreCase]);

          s:=StringReplace(s,'<totalIndexNum>',IntToStr(totalIndexNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalIndexTopNum>',IntToStr(totalIndexTopNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<totalIndexBottomNum>',IntToStr(totalIndexBottomNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndexNum>',IntToStr(agedIndexNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndexTopNum>',IntToStr(agedIndexTopNum),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<agedIndexBottomNum>',IntToStr(agedIndexBottomNum),[rfReplaceAll, rfIgnoreCase]);

          writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
end;

//*********************** Main routine *********************************//

procedure generateCode(inputfile, outputfile: String; coloredElements:Boolean=true);
var Q, Z: TextFile;
    s: String;
    nowstring: String;
    myNumFlatTracers: Integer;
    myNum3DTracers: Integer;
    myNumMovingTracers: Integer;
    i: Integer;
    found: boolean;
begin
  DateTimeToString(nowstring,'yyyy-mmm-dd hh":"mm',now);
  myNumFlatTracers:=0;
  myNum3DTracers:=0;
  myNumMovingTracers:=0;
  for i:=0 to length(tracers)-1 do
  begin
    if tracers[i].vertLoc<>0 then myNumFlatTracers:=myNumFlatTracers+1
                            else myNum3DTracers:=myNum3DTracers+1;
  end;
  for i:=0 to length(tracers)-1 do
  begin
    if tracers[i].vertSpeed<>'0' then myNumMovingTracers:=myNumMovingTracers+1;
  end;

//  ShowMessage('opening '+inputfile+' for read');
  AssignFile(Q,inputfile);
  SetTextBuf(Q,InputBuffer);
  reset(Q);
  LinesRead:=0;
//  ShowMessage('opening '+outputfile+' for write');
  AssignFile(Z,outputfile);
  rewrite(Z);

  while not EOF(Q) do
  begin
    readln(Q,s);
    LinesRead:=LinesRead+1;
    if lowercase(copy(trim(s),1,10))='<constants' then
      ConstantLoop(s,Q,Z)
    else if lowercase(copy(trim(s),1,8))='<tracers' then
      TracerLoop(s,Q,Z,false)
    else if lowercase(copy(trim(s),1,16))='<backwardtracers' then
      TracerLoop(s,Q,Z,true)
    else if lowercase(copy(trim(s),1,12))='<auxiliaries' then
      AuxiliaryLoop(s,Q,Z)
    else if lowercase(copy(trim(s),1,10))='<processes' then
      ProcessLoop(s,Q,Z)
    else if lowercase(copy(trim(s),1,10))='<celements' then
      cElementLoop(s,Q,Z,coloredElements)
    else
    begin
      s:=StringReplace(s,'<name>',ModelInfos.name,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<version>',ModelInfos.version,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<description>',ModelInfos.description,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<author>',ModelInfos.author,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<contact>',ModelInfos.contact,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<codegen_version>',codegenVersion,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<now>',nowstring,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers>',IntToStr(myNumFlatTracers),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+1>',IntToStr(myNumFlatTracers+1),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+2>',IntToStr(myNumFlatTracers+2),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+3>',IntToStr(myNumFlatTracers+3),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+4>',IntToStr(myNumFlatTracers+4),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+5>',IntToStr(myNumFlatTracers+5),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+6>',IntToStr(myNumFlatTracers+6),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+7>',IntToStr(myNumFlatTracers+7),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+8>',IntToStr(myNumFlatTracers+8),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numFlatTracers+9>',IntToStr(myNumFlatTracers+9),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers>',IntToStr(myNum3DTracers),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+1>',IntToStr(myNum3DTracers+1),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+2>',IntToStr(myNum3DTracers+2),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+3>',IntToStr(myNum3DTracers+3),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+4>',IntToStr(myNum3DTracers+4),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+5>',IntToStr(myNum3DTracers+5),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+6>',IntToStr(myNum3DTracers+6),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+7>',IntToStr(myNum3DTracers+7),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+8>',IntToStr(myNum3DTracers+8),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<num3DTracers+9>',IntToStr(myNum3DTracers+9),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers>',IntToStr(myNumMovingTracers),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+1>',IntToStr(myNumMovingTracers+1),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+2>',IntToStr(myNumMovingTracers+2),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+3>',IntToStr(myNumMovingTracers+3),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+4>',IntToStr(myNumMovingTracers+4),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+5>',IntToStr(myNumMovingTracers+5),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+6>',IntToStr(myNumMovingTracers+6),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+7>',IntToStr(myNumMovingTracers+7),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+8>',IntToStr(myNumMovingTracers+8),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<numMovingTracers+9>',IntToStr(myNumMovingTracers+9),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<maxIterations>',IntToStr(MaxIterations),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'</ifParam>','',[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'</ifNoParam>','',[rfReplaceAll, rfIgnoreCase]);
      writeln(Z,s);
    end;
  end;

  closefile(Q);
  closefile(Z);
end;

end.
