unit erg_code;

interface

uses erg_base, erg_types, classes;

{!
  \brief a global variable stating the program version
  }
  
const codegenVersion='1.4.2';

var outputLanguage: String;

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

implementation

uses sysUtils;

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

//! This is a dummy function
{!
  \private
  The pas2dox tool, which converts the *.pas files to *.cpp files (for doxygen),
  does not recognize the end of the var-definition section above. As a result,
  the first function is not properly converted by pas2dox and not included in
  the doxygen documentation.
}
procedure dummyFunForDoxygen();
var s: string;
begin
  // empty; we need some code in this function!!!
  s:='7';
end;

//! Adds spaces in the end of a string until it has a defined length
{!
  \private
  \param s input string to enlarge
  \param minLength minimum length of the output string (integer)
  \return s_enlarged string with minimum length of minLength and trim(s_enlarged)==trim(s)
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Enlarges a string.
}
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;

//! Convert a mathematical formula into the format for a chosen programming languate
{!
  \private
  \param s mathematical formula as string (is overwritten; =output)
  \param name optional prefix for temporary variables (string)
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Converts a formula for the use in a certain programming language. '^' is
  converted into '**' if we have a Fortran file. The language is determined
  from the variable outputLanguage.

  Reads global variables:
  - outputLanguage
}
procedure convertToOutputLanguage(var s: String; name: String='');
var
  i, pos1, pos2, pos3: Integer;
  paranthesisCount: Integer;
begin
  if lowercase(outputLanguage)='matlab' then   //convert from Fortran to Matlab
  begin
    s:=StringReplace(s,'**','^',[rfReplaceAll,rfIgnoreCase]);
  end;
  if lowercase(outputLanguage)='ferret' then   //convert from Fortran to Ferret
  begin
    s:=StringReplace(s,'**','^',[rfReplaceAll,rfIgnoreCase]);
    //remove '-' at beginning of expression
    s:=StringReplace(s,'(-','(0-',[rfReplaceAll,rfIgnoreCase]);
    s:=StringReplace(s,'( -','(0-',[rfReplaceAll,rfIgnoreCase]);
    if copy(s,1,1)='-' then s:='0'+s;
    //replace functions which do not exist in ferret, as it is complicated to define them
    while pos('sqrt(',lowercase(s))>0 do
    begin
      //replace sqrt function by ((...)^0.5)
      //seek the corresponding ')'
      i:=pos('sqrt(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('sqrt(',lowercase(s));
      s:=copy(s,1,pos1-1)+'(('+copy(s,pos1+5,pos3-(pos1+5))+')^0.5)'+copy(s,pos3+1,length(s));
    end;
    while pos('power(',lowercase(s))>0 do
    begin
      //replace power function by ((...)^(...))
      //seek the corresponding ',' and ')'
      i:=pos('power(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=',') then //this is the comma between base and exponent
          pos2:=i;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('power(',lowercase(s));
      s:=copy(s,1,pos1-1)+'(('+copy(s,pos1+6,pos2-(pos1+6))+')^('+copy(s,pos2+1,pos3-(pos2+1))+'))'+copy(s,pos3+1,length(s));
    end;
    while pos('theta(',lowercase(s))>0 do
    begin
      //replace theta function by ((...) gt 0)
      //seek the corresponding ')'
      i:=pos('theta(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('theta(',lowercase(s));
      s:=copy(s,1,pos1-1)+'(('+copy(s,pos1+6,pos3-(pos1+6))+') gt 0)'+copy(s,pos3+1,length(s));
    end;
    while pos('tanh(',lowercase(s))>0 do
    begin
      //replace tanh function by (sinh(...)/cosh(...))
      //seek the corresponding ')'
      i:=pos('tanh(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('tanh(',lowercase(s));
      s:=copy(s,1,pos1-1)+'(sinh('+copy(s,pos1+5,pos3-(pos1+5))+')/cosh('+copy(s,pos1+5,pos3-(pos1+5))+'))'+copy(s,pos3+1,length(s));
    end;
    while pos('sinh(',lowercase(s))>0 do
    begin
      //replace sinh function by ((exp(...)-exp(0-(...)))/2)
      //seek the corresponding ')'
      i:=pos('sinh(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('sinh(',lowercase(s));
      s:=copy(s,1,pos1-1)+'((exp('+copy(s,pos1+5,pos3-(pos1+5))+')-exp(0-('+copy(s,pos1+5,pos3-(pos1+5))+')))/2)'+copy(s,pos3+1,length(s));
    end;
    while pos('cosh(',lowercase(s))>0 do
    begin
      //replace cosh function by ((exp(...)+exp(0-(...)))/2)
      //seek the corresponding ')'
      i:=pos('cosh(',lowercase(s));
      paranthesisCount:=0;
      while i<length(s) do
      begin
        i:=i+1;
        if copy(s,i,1)='(' then
          paranthesisCount:=paranthesisCount+1;
        if (paranthesisCount=1) and (copy(s,i,1)=')') then //this is the closing paranthesis
        begin
          pos3:=i;
          break;
        end
        else if copy(s,i,1)=')' then paranthesisCount:=paranthesisCount-1;
      end;
      pos1:=pos('cosh(',lowercase(s));
      s:=copy(s,1,pos1-1)+'((exp('+copy(s,pos1+5,pos3-(pos1+5))+')+exp(0-('+copy(s,pos1+5,pos3-(pos1+5))+')))/2)'+copy(s,pos3+1,length(s));
    end;
    if name <> '' then  //for auxiliaries, 'temp1', ..., 'temp9' must be replaced by '<name>_temp1', ..., '<name>_temp9'
    begin
      s:=StringReplace(s,'temp1',name+'_temp1',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp2',name+'_temp2',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp3',name+'_temp3',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp4',name+'_temp4',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp5',name+'_temp5',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp6',name+'_temp6',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp7',name+'_temp7',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp8',name+'_temp8',[rfReplaceAll,rfIgnoreCase]);
      s:=StringReplace(s,'temp9',name+'_temp9',[rfReplaceAll,rfIgnoreCase]);
    end;
  end;
end;


//! Splits a condition string, which consists of several sub-condition strings joined by 'OR', into its sub-condition strings
{!
   \private
   \param s String: condition string of the format "condition_string1 OR condition_string2 OR ..."
   \param partsOR TStringList: the sub-conditions [condition_string1, condition_string2, ...]
   \author Daniel Neumann, daniel.neumann@io-warnemuende.de
   
   The procedure is not case sensitive with respect to 'OR' ('or' is also possible).
   
   Returns an empty TStringList as partsOR if s is '';
   Else, returns an one-element TStringList if s contains no 'OR' or 'or';
   Else, returns a n-element TStringList when s contains n-1 'OR' or 'or';
   }
procedure SplitOR(s: String; partsOR: TStringList);
var s1, s2: String;
    p: Integer;
begin
  s1:=s;
  partsOR.Clear;
  while s1<>'' do
  begin
    p:=pos(' or ',lowercase(s1));
    if p<1 then p:=length(s1)+1;
    s2:=copy(s1,1,p-1);
    s1:=copy(s1,p+4,length(s1));
    partsOR.Add(trim(s2));
  end;
end;


//! Join two condition strings (separator: ';') if both are not empty
{!
   \private
   \param cs1 condition string of the format "cond1=val1; cond2=val2; cond3/=val3; ..."
   \param cs2 condition string of the format "cond1=val1; cond2=val2; cond3/=val3; ..."
   \return csj joint condition string consisting of both conditions strings (if non-empty)
   \author Daniel Neumann, daniel.neumann@io-warnemuende.de

   Returns '' fi cs1 and cs2 are empty; <br>
   Else, returns cs1 if cs2 is empty; <br>
   Else, returns cs2 if cs1 is empty; <br>
   Else, returns 'cs1;cs2'
   }
function JoinConditions(cs1: String; cs2: String): String;
var csj: String;
begin
  csj := '';
  if ((cs1 = '') and (cs2 = '')) then
    csj := ''
  else if cs1 = '' then
    csj := cs2
  else if cs2 = '' then
    csj := cs1
  else
    csj := cs1+';'+cs2;
  
  result:=csj;
end;


//! Merge a condition string into another conditions string (which can consist of OR-separated sub-conditions)
{!
  \private
  \param condition string: input condition string; can consist of or-separated sub-conditions
  \param condition_prefix string: append it to each sub-condition in 'condition' (separated by 'OR')
  \return new condition string
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de    
  
  <b>Example</b>
  - condition: "vertloc=SUR OR verloc=SED"
  - condition_prefix: "childof=none"
  - output: "childof=none;vertloc=SUR OR childof=none;verloc=SED"
  
  <b>Calls</b>
  - SplitOR()  
  - JoinConditions()
  }
function MergeConditionPrefix(const condition: String; const condition_prefix: String): String;
var conditionOut: String;
    subC: TStringList; 
    i: Integer;
begin
  // case 1: input condition string is empty => return condition_prefix
  //  if condition_prefix is empty => we loose nothing
  if (condition='') then
    conditionOut:=condition_prefix
  else if (condition_prefix='') then
    conditionOut:=condition // condition<>'' but prefix='' => return condition
  else
  begin
    // here, condition<>'' and condition_prefix<>'' => real work!
    
    // split condition into subconditions
    subC:=TStringList.Create;
    SplitOR(condition,subC);
    
    // iterate subconditions and prepend condition_prefix
    conditionOut:='';
    for i:=0 to subC.Count-1 do
      conditionOut:=conditionOut+' OR '+JoinConditions(condition_prefix,subC[i]);
    
    conditionOut:=copy(conditionOut,5,999);
  end;
  
  result:=conditionOut;
end;


//! Replace a part of a condition by another condition; special case, when an OR is present, is handeled
{!
  \private
  \param condition string: input condition string; can consist of or-separated sub-conditions
  \param replace_condition string: replace this string in condition by insert_condition
  \param insert_condition string: insert this into condition were replace_condition was previously
  \return new condition string
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de    
  
  <b>Example</b> TODO
  - condition: "vertloc=SUR OR verloc=SED"
  - condition_prefix: "childof=none"
  - output: "childof=none;vertloc=SUR OR childof=none;verloc=SED"
  
  <b>Calls</b>
  - SplitOR()  
  - JoinConditions()
  }
function ReplaceSubCondition(const condition: String; const replace_condition: String; const insert_condition: String): String;
var conditionOut, conditionTmp: String;
    subC_old, subC_insert: TStringList; 
    i,j: Integer;
begin
  // case 1: input condition string is empty => return condition_prefix
  //  the value of replace_condition is not relevant => we loose nothing
  if (condition='') then
    conditionOut:=''
  else if ((replace_condition='') or (pos(lowercase(replace_condition),lowercase(condition))=0)) then
    conditionOut:=condition
    // a) condition<>'' but replace_condition='' => return condition
    // b) condition does not contain replace_condition
  else if ((insert_condition='') or (pos('or',lowercase(insert_condition))=0)) then
    conditionOut:=StringReplace(condition,replace_condition,insert_condition,[rfReplaceAll,rfIgnoreCase])
    // a) insert_condition='' => the user wants to remove some conditions => no special treatment
    // b) no 'or' in insert_conditions => we can just replace replace_condition (no problems with logical OR)
  else if (pos('or',lowercase(replace_condition))<>0) then
    conditionOut:=StringReplace(condition,replace_condition,insert_condition,[rfReplaceAll,rfIgnoreCase])
    // If OR is in replace_condition, we cannot generally guerantee that the 
    // resulting expression matches the wishes of the user. We trust him that 
    // he knows what he does and just replace replace_contion.
  else
  begin
    // here, condition<>'' and insert_condition<>'' and replace_condition<>''
    //       and replace_condition contains NO OR
    //       but insert_condition contains one or more ORs
    //        => real work!
    
    // split condition into subconditions
    subC_old:=TStringList.Create;
    subC_insert:=TStringList.Create;
    SplitOR(condition,subC_old);
    SplitOR(insert_condition,subC_insert);
    
    // iterate subconditions and prepend condition_prefix
    conditionOut:='';
    for i:=0 to subC_old.Count-1 do
    begin
      if (pos(lowercase(replace_condition),lowercase(subC_old[i]))=0) then
        conditionOut:=conditionOut+' OR '+subC_old[i]
      else
      for j:=0 to subC_insert.Count-1 do
      begin
        conditionOut:=conditionOut+' OR '+StringReplace(subC_old[i],replace_condition,subC_insert[j],[rfReplaceAll,rfIgnoreCase])
      end;
    end;
    
    conditionOut:=copy(conditionOut,5,999);
  end;
  
  result:=conditionOut;
end;


//! Splits a condition string into two arrays containing the right hande side and left hand side of the contion
{!
   \private
   \param s condition string of the format "cond1=val1; cond2=val2; cond3/=val3; ..."
   \param lhs string array with the left hand side of the conditions "('cond1=', 'cond2=', 'cond3/=', ...)"
   \param rhs string array with the right hand side of the conditions "(val1, val2, val3, ...)"
   \return lhs and rhs
   \author Hagen Radtke, hagen.radtke@io-warnemuende.de
   }
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;

//! will append ".0" if no dot is in the string, if possible before an "E"
{!
  \private
  \param s string representing a number, to which '.0' should be appended
  \return s with appended '.0' if s did not contain a '.'
  }
function appendDot(s: String):String;
//will append ".0" if no dot is in the string, if possible before an "E"
begin
  if pos(copy(s,1,1),'-0123456789')>0 then
  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
  else
    result:=s;
end;


//! Get in-/decrement (+/-[0-9]*) of a tag-part-String starting with "[+-][0-9]{1,}"
{!
  \private
  \param s string one part of a tag: "+123 TEXT>", "+123>", ">", or "TEXT>"
  \param val in-/decremental value at the beginning of the string (return)
  \param len length of the in-/decrement
  \return boolean: true if s starts with +/- followed by a natural number followed by " "/">"; else false
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  <b>Example:</b> A tag looks like "<abc+23 condition>". Then "+23 condition>" has to 
   be provided as input argument "s" to this function. It will set "val" to 23
   and "len" to 3 ("+23" has a length of 3).

  "val" and "len" are set to 0 and 'false' is returned in these cases:
   - s does not start with "+" or "-" then "val" and "len" are set to 0. 
   - "+" or "-" are not followed by a natural number (n > 0 digits)
   - the natural number is not followed by " " or ">"
   
   Otherwise, 'true' is returned. 
 }
function NumTagPlusMinus(s: String; var val: integer; var len: integer):Boolean;
var stPos, enPos, sLen, tLen: Integer;
    frstChar: String;
    isPlusMinus: Boolean;
begin
  val:=0;
  len:=0;
  isPlusMinus:=false;
  frstChar:=copy(s,1,1);
  
  if ((frstChar='-') or (frstChar='+')) then
  begin
    enPos:=2;
    while ((copy(s,enPos,1)<>'>') and (copy(s,enPos,1)<>' ')) do
      enPos:=enPos+1;
    if (enPos > 2) then
    begin
      len:=enPos-1;
      val:=StrToInt(copy(s,2,enPos-2));
      isPlusMinus:=true;
    end;
    
    if (frstChar='-') then
      val:=(-1) * val;
  end;
  result:=isPlusMinus;
end;


//! Replace a tag representing a number +/-X
{!
  \private
  \param s string one line of the template file
  \param tag string begin of a tag in the format '<numAbcTracer' (no '>')
  \param value integer of the unmodified tag
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  There might be the demand that tags representing the number (of a group) of
  tracers should be incremented or decremented by 1, 2 or another integer
  number. The basic tag looks <numMovingTracers> and a tag for incrementing it
  by 2 looks <numMovingTracers+2>, whereas a tag for decrementing it by 3 looks
  like <numMovingTracers-3>. In previous version, these increment and decrement
  tags were hard-coded. Now this functions resolves it.
 }
function StringReplaceTagPlusMinus(s: String; tag: String; val: integer):String;
var stPos, enPos, sLen, tLen: Integer;
begin
  stPos:=pos(tag,s);
  tLen:=length(tag);
  sLen:=length(s);
  if (sLen > tLen) and (stPos > 0) then
  begin
    if copy(s,stPos+tLen,1)='>' then
      s:=StringReplace(s,tag+'>',IntToStr(val),[rfReplaceAll, rfIgnoreCase])
    else
    begin
      enPos:=stPos+tLen;
      while copy(s,enPos,1)<>'>' do
        enPos:=enPos+1;
      s:=StringReplace(s,copy(s,stPos,enPos-stPos+1),IntToStr(val+StrToInt(copy(s,stPos+tLen,enPos-stPos-tLen))),[rfReplaceAll, rfIgnoreCase]);
    end;
  end;
  result:=s;
end;



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

//! Tests whether a process fullfills the conditions
{!
  \private
  \param p integer index of the process
  \param tProduct boolean indicating whether we are on the product-side (true) or the educt-side (false)
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke

  Possible conditions tested:
  - vertloc: WAT (whole water column), SED (sediment only), SUR (surface only), FIS (fish=like)
  - isflat: deprecated, see vertloc
  - isinporewater:
  - isproduct:
  - iseduct:

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function RateCondition(p: Integer; tProduct: Boolean; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    k,j: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],lhs,rhs);
      for j:=0 to lhs.Count-1 do
      begin
        // if (not tProduct)  then isOk:=false;
        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;
        if (lhs[j]='isproduct=') and ((1<>StrToIntVertLoc(rhs[j])) and tProduct) then isOk:=false;
        if (lhs[j]='isproduct/=') and ((1=StrToIntVertLoc(rhs[j])) and tProduct) then isOk:=false;
        if (lhs[j]='iseduct=') and ((1<>StrToIntVertLoc(rhs[j])) and (not tProduct)) then isOk:=false;
        if (lhs[j]='iseduct/=') and ((1=StrToIntVertLoc(rhs[j])) and (not tProduct)) then isOk:=false;
        if (lhs[j]='isinporewater=') and (processes[p].myIsInPorewater<>StrToInt(rhs[j])) then isOk:=false;
        if (lhs[j]='isinporewater/=') and (processes[p].myIsInPorewater=StrToInt(rhs[j])) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOK);
    end;
  end;
  result:=isOkOr;
end;

//! Creates rates / timeTendencies for the current tracer
{!
  \private
  \param s string: line with <timeTendencies ... > tag
  \param Q input text file
  \param Z output text file
  \param t integer index of the parent tracer
  \param origLines StringList containing the lines of the tracer configuration file
  \param l current line number index (integer) in the tracer configuration file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<timeTendencies ...>' is in the template.

  Supports the content tags
  - <timeTendency>
  - <timeTendency2d>
  - <description>
  - <name>
  - <trimName>

  No sub-loop tags are in order.

  <b>Calls</b>
  - RateCondition()
  - convertToOutputLanguage()
  - optional: makeLonger()

  TODO
                                                                    }
procedure RateLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer; var origLines: TStringList; var l: Integer);
// execute the loop <timeTendencies> </timeTendencies> over all processes that impose creation or destruction rates on the tracer t.
const epsilon='0.00000000001';
var conditions: String;
    i, j, tt, tti, ttimax, ll: Integer;
    tfactor: String;
    loopFinished: Boolean;
    ratestring, ratestring2d: String;
begin
 DecimalSeparator:='.';
 conditions:=lowercase(copy(trim(s),17,length(trim(s))-17));
 if tracers[t].isCombined=1 then
   ttimax:=length(tracers[t].contents)-1
 else
   ttimax:=0;
 for tti:=0 to ttimax do
 begin
  if tracers[t].isCombined=1 then
    tt:=tracers[t].contents[tti].myElementNum;
  //output loop
  for i:=0 to length(processes)-1 do
  begin
   if processes[i].isActive=1 then
   begin
    if RateCondition(i,true,conditions) then    //check if conditions for the rates apply
    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
            ll:=l+1;
            loopFinished:=false;
            while not loopFinished do
            begin
              s:=origLines[ll];
              if ll=origLines.Count-1 then LoopFinished:=true;
              if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
              else
              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]
                ratestring2d:=ratestring;
                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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                if pos('<nonewline>',lowercase(s))>0 then
                begin
                  s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                  write(Z,s);
                end
                else
                  writeln(Z,s);
              end;
              ll:=ll+1;
            end;
          end;
        end
        else  //combined tracer, that means, it contains several (active or inactive) other tracers, but no elements
        begin
          tfactor:=tracers[t].contents[tti].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
            ll:=l+1;
            loopFinished:=false;
            while not loopFinished do
            begin
              s:=origLines[ll];
              if ll=origLines.Count-1 then LoopFinished:=true;
              if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
              else
              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]
                ratestring2d:=ratestring;
                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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description+' (produces '+tracers[tt].name+')',[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                if pos('<nonewline>',lowercase(s))>0 then
                begin
                  s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                  write(Z,s);
                end
                else
                  writeln(Z,s);
              end;
              ll:=ll+1;
            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,false,conditions) then
    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
              ll:=l+1;
              loopFinished:=false;
              while not loopFinished do
              begin
                s:=origLines[ll];
                if ll=origLines.Count-1 then LoopFinished:=true;
                if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
                else
                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]
                  ratestring2d:=ratestring;
                  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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                  if pos('<nonewline>',lowercase(s))>0 then
                  begin
                    s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                    write(Z,s);
                  end
                  else
                    writeln(Z,s);
                end;
                ll:=ll+1;
              end;
            end;
          end;
        end
        else
        begin
          tfactor:=tracers[t].contents[tti].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
              ll:=l+1;
              loopFinished:=false;
              while not loopFinished do
              begin
                s:=origLines[ll];
                if ll=origLines.Count-1 then LoopFinished:=true;
                if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
                else
                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]
                  ratestring2d:=ratestring;
                  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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<description>',processes[i].description+' (consumes '+tracers[tt].name+')',[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                  s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                  if pos('<nonewline>',lowercase(s))>0 then
                  begin
                    s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                    write(Z,s);
                  end
                  else
                    writeln(Z,s);
                end;
                ll:=ll+1;
              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
            ll:=l+1;
            loopFinished:=false;
            while not loopFinished do
            begin
              s:=origLines[ll];
              if ll=origLines.Count-1 then LoopFinished:=true;
              if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
              else
              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]
                ratestring2d:=ratestring;
                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+')))';
                ratestring2d:=ratestring2d+'*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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                if pos('<nonewline>',lowercase(s))>0 then
                begin
                  s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                  write(Z,s);
                end
                else
                  writeln(Z,s);
              end;
              ll:=ll+1;
            end;
          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
            ll:=l+1;
            loopFinished:=false;
            while not loopFinished do
            begin
              s:=origLines[ll];
              if ll=origLines.Count-1 then LoopFinished:=true;
              if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true
              else
              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]
                ratestring2d:=ratestring;
                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,'<timeTendency2d>',makeLonger(ratestring2d,30),[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<description>',processes[i].description,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimName>',Tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<name>',makeLonger(Tracers[t].name,15),[rfReplaceAll, rfIgnoreCase]);
                if pos('<nonewline>',lowercase(s))>0 then
                begin
                  s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                  write(Z,s);
                end
                else
                  writeln(Z,s);
              end;
              ll:=ll+1;
            end;
          end;
        end;
      end;
    end;
   end;
  end;
 end;
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 then LoopFinished:=true;
        if lowercase(copy(trim(s),1,16))='</timetendencies' then LoopFinished:=true;
        ll:=ll+1;
      end;
      l:=ll-1;
end;


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

//! Tests whether a tracer has rates / time tendencies
{!
  \private
  \param t integer index of a tracer
  \return true/false: "has rates" / "has no rates"
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de
  }
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;

//! Tests whether a flat tracer has rates / time tendencies
{!
  \private
  \param t integer index of a tracer
  \param sf vertical location (WAT, SED, SUR, FIS)
  \return true/false: "has rates" / "has no rates"
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de
  }
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;


//! Tests whether a tracer fullfills the conditions
{!
  \private
  \param i integer index of the tracer
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - vertloc: WAT (whole water column), SED (sediment only), SUR (surface only), FIS (fish=like)
  - isflat: deprecated, see vertloc
  - isinporewater:
  - a lot more ... (TODO)

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function TracerCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    j, k, l: Integer;
    myHasCETotal, myHasCEAged: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for l:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[l],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]='vertspeedvalue=') and (FloatToStr(tracers[i].myVertSpeedValue)<>rhs[j]) then isOk:=false;
        if (lhs[j]='vertspeedvalue/=') and (FloatToStr(tracers[i].myVertSpeedValue)=rhs[j]) then isOk:=false;
        if (lhs[j]='vertdiff=') and (lowercase(tracers[i].vertDiff)<>rhs[j]) then isOk:=false;
        if (lhs[j]='vertdiff/=') and (lowercase(tracers[i].vertDiff)=rhs[j]) then isOk:=false;
        if (lhs[j]='moldiff=') and (lowercase(tracers[i].molDiff)<>rhs[j]) then isOk:=false;
        if (lhs[j]='moldiff/=') and (lowercase(tracers[i].molDiff)=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]='isinporewater=') and (IntToStr(tracers[i].isInPorewater)<>rhs[j]) then isOk:=false;
        if (lhs[j]='isinporewater/=') and (IntToStr(tracers[i].isInPorewater)=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]='isselected=') and ((tracers[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected/=') and not ((tracers[i].myIsSelected) xor (rhs[j]='1')) 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;
        if (lhs[j]='tracerabove=') and (lowercase(tracers[i].tracerAbove)<>rhs[j]) then isOk:=false;
        if (lhs[j]='tracerabove/=') and (lowercase(tracers[i].tracerAbove)=rhs[j]) then isOk:=false;
        if (lhs[j]='tracerbelow=') and (lowercase(tracers[i].tracerBelow)<>rhs[j]) then isOk:=false;
        if (lhs[j]='tracerbelow/=') and (lowercase(tracers[i].tracerBelow)=rhs[j]) then isOk:=false;
        if (lhs[j]='hasabove=') and ( ( (rhs[j]='1') and (tracers[i].tracerAbove='none') )
                                    or ( (rhs[j]='0') and (tracers[i].tracerAbove<>'none') ) ) then isOk:=false;
        if (lhs[j]='hasabove/=') and ( ( (rhs[j]='1') and (tracers[i].tracerAbove<>'none') )
                                    or ( (rhs[j]='0') and (tracers[i].tracerAbove='none') ) ) then isOk:=false;
        if (lhs[j]='hasbelow=') and ( ( (rhs[j]='1') and (tracers[i].tracerBelow='none') )
                                    or ( (rhs[j]='0') and (tracers[i].tracerBelow<>'none') ) ) then isOk:=false;
        if (lhs[j]='hasbelow/=') and ( ( (rhs[j]='1') and (tracers[i].tracerBelow<>'none') )
                                    or ( (rhs[j]='0') and (tracers[i].tracerBelow='none') ) ) then isOk:=false;
        if (lhs[j]='longname=') and (lowercase(tracers[i].longname)<>rhs[j]) then isOk:=false;
        if (lhs[j]='longname/=') and (lowercase(tracers[i].longname)=rhs[j]) then isOk:=false;
        if (lhs[j]='unit=') and (lowercase(tracers[i].outputUnit)<>rhs[j]) then isOk:=false;
        if (lhs[j]='unit/=') and (lowercase(tracers[i].outputUnit)=rhs[j]) then isOk:=false;
        if (lhs[j]='color=') and (pos('_'+rhs[j]+'_',tracers[i].name)=0) then isOk:=false;
        if (lhs[j]='color/=') and (pos('_'+rhs[j]+'_',tracers[i].name)>0) then isOk:=false;
        if (lhs[j]='contents=') and isOk=true then
        begin
          isOk:=false;
          for k:=0 to length(tracers[i].contents)-1 do
            if lowercase(tracers[i].contents[k].element)=rhs[j] then isOk:=true;
        end;
        if (lhs[j]='contents/=') and isOk=true then
        begin
          for k:=0 to length(tracers[i].contents)-1 do
            if lowercase(tracers[i].contents[k].element)=rhs[j] then isOk:=false;
        end;
        if (lhs[j]='solubility/=') and (lowercase(tracers[i].solubility)=rhs[j]) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;

//! Tests whether a child fullfills the conditions
{!
  \private
  \param p integer index of the tracer/child
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Forwards p and c to TracerCondition().

  <b>Calls</b>
  - TracerCondition().
                                                                    }
function ChildCondition(p: Integer; c: String):Boolean;
begin
  result:=TracerCondition(p, c);
end;

//! Iterates Children of a certain tracer
{!
  \private
  \param s string: line with <children ... > tag
  \param Q input text file
  \param Z output text file
  \param t integer index of the parent tracer
  \param origLines StringList containing the lines of the tracer configuration file
  \param l current line number index (integer) in the tracer configuration file
  \param e integer index of the basic element
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<children ...>' is in the template.

  Supports the content tags
  - <childName>
  - <trimChildName>
  - <name>
  - <trimName>

  No sub-loop tags are in order.

  <b>Calls</b>
  - ChildCondition()

  TODO
                                                                    }
procedure ChildLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer; var origLines: TStringList; var l: Integer; const e: Integer = -1);
var conditions: String;
    i1,ll, i2, i3: Integer;
    loopFinished: Boolean;
    processTracer: Boolean;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),11,length(trim(s))-11));
  //output loop
  for i1:=0 to length(tracers)-1 do
   if tracers[i1].isActive=1 then
   if tracers[i1].myChildOf=t then
   {!
      At least one element of the tracer needs to have the element e (NOT THE
      COLORED ELEMENT!) or e is set to -1: Then we process this tracer.
      Otherwise not.
      }
   begin
     processTracer:=false;
     if (e=-1) then
       processTracer:=true
     else
       for i2:=0 to length(tracers[i1].contents)-1 do
         if (elements[tracers[i1].contents[i2].myElementNum].parentElement=e) then
           processTracer:=true;

     if processTracer then
     begin
      if ChildCondition(i1,conditions) then
      begin
        ll:=l+1;
        loopFinished:=false;
        while not loopFinished do
        begin
          s:=origLines[ll];
          if ll=origLines.Count-1 then LoopFinished:=true;
          if lowercase(copy(trim(s),1,10))='</children' then LoopFinished:=true
          else
          begin
                s:=StringReplace(s,'<childName>',tracers[i1].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<name>',tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimChildName>',tracers[i1].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<trimName>',tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<tracerAbove>',tracers[i1].tracerAbove,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<tracerBelow>',tracers[i1].tracerBelow,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<childLongname>',tracers[i1].longname,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<longname>',tracers[t].longname,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<childUnit>',tracers[i1].outputUnit,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<unit>',tracers[t].outputUnit,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<childStandardname>',tracers[i1].stdname_prefix+tracers[i1].longname+tracers[i1].stdname_suffix,[rfReplaceAll, rfIgnoreCase]);
                s:=StringReplace(s,'<standardname>',tracers[t].stdname_prefix+tracers[t].longname+tracers[t].stdname_suffix,[rfReplaceAll, rfIgnoreCase]);
                if pos('<nonewline>',lowercase(s))>0 then
                begin
                  s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                  write(Z,s);
                end
                else
                  writeln(Z,s);
          end;
          ll:=ll+1;
        end;
      end;
     end;
   end;
  ll:=l+1;
  loopFinished:=false;
  while not loopFinished do
  begin
    s:=origLines[ll];
    if ll=origLines.Count-1 then LoopFinished:=true;
    if lowercase(copy(trim(s),1,10))='</children' then LoopFinished:=true;
    ll:=ll+1;
  end;
  l:=ll-1;
end;


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

//! Tests whether a limitation fullfills the conditions
{!
  \private
  \param i integer index of the limitation
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - isSelected: Whether a process with isSelected=1 is limited by this limitation

  <b>Calls</b>
  - SplitConditions().
                                                                    }
function limitationCondition(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]='isselected=') and (limitations[i].myIsSelected XOR (rhs[j]='1')) then isOk:=false;
    if (lhs[j]='isselected/=') and (not (limitations[i].myIsSelected XOR (rhs[j]='1'))) then isOk:=false;
  end;
  result:=isOk;
end;

//! Creates limitations of the current tracer
{!
  \private
  \param s string: line with <limitations ... > tag
  \param Q input text file
  \param Z output text file
  \param t integer index of the parent tracer
  \param origLines StringList containing the lines of the tracer configuration file
  \param l current line number index (integer) in the tracer configuration file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<limitations ...>' is in the template.

  Supports the content tags
  - <name> (name of limition; not name of tracer)
  - <trimName>
  - <formula>

  No sub-loop tags are in order.

  <b>Calls</b>
  - convertToOutputLanguage()
  - optional: LimitationCondition()
  - optional: makeLonger()

  TODO
                                                                    }
procedure LimitationLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer; var origLines: TStringList; var l: Integer);
// execute the loop <limitations> </limitations> over all limitation functions that depend on the tracer t.
var
  i,ll: Integer;
  loopFinished: Boolean;
  conditions: String;
  myFormula: String;
begin
  conditions:=lowercase(copy(trim(s),14,length(trim(s))-14));
  for i:=0 to length(limitations)-1 do
  begin
    if limitations[i].myTracerNum=t then
    begin
     if LimitationCondition(i,conditions) then
     begin
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 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+')';
          convertToOutputLanguage(myFormula);
          s:=StringReplace(s,'<name>',makeLonger('lim_'+limitations[i].tracer+'_'+IntToStr(i),20),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>','lim_'+limitations[i].tracer+'_'+IntToStr(i),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<formula>',myFormula,[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
        ll:=ll+1;
      end;
     end;
    end;
  end;
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 then LoopFinished:=true;
        if lowercase(copy(trim(s),1,13))='</limitations' then LoopFinished:=true;
        ll:=ll+1;
      end;
      l:=ll-1;
end;

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

//! Tests whether a tracer fullfills the conditions
{!
  \private
  \param i integer index of the tracer
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - vertloc: WAT (whole water column), SED (sediment only), SUR (surface only), FIS (fish=like)
  - isflat: deprecated, see vertloc
  - isinporewater:
  - vertspeed: has given vertical speed

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function ctCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    k, j: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],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]='isinporewater=') and (IntToStr(tracers[i].isInPorewater)<>rhs[j]) then isOk:=false;
        if (lhs[j]='isinporewater/=') and (IntToStr(tracers[i].isInPorewater)=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;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;

//! Iterates all tracers with defined color and element
{!
  \private
  \param s string: line with <containingtracers ... > tag
  \param Q input text file
  \param Z output text file
  \param ce integer index of the colored element
  \param origLines StringList containing the lines of the tracer configuration file
  \param l current line number index (integer) in the tracer configuration file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<containingtracers ...>' is in the template.

  Supports the content tags
  - <ctNumFlat> internal index of a flat tracer
  - <ctIndexNum> internal index of a tracer
  - <ct> tracer name
  - <cpI> tracer's parents
  - <ctAmount> tracer's amount
  - <ctNumMoving> internal index of a moving tracer

  No sub-loop tags are in order.

  <b>Calls</b>
  - ctCondition()
  - optional: makeLonger()

  Loops through all tracers with same element and color as defined by ce.
                                                                    }
procedure ctLoop(s: String; var Q: TextFile; var Z: TextFile; ce: Integer; var origLines: TStringList; var l: Integer);
var conditions: String;
    i,ll: Integer;
    loopFinished: Boolean;
    Current3dTracerNum, CurrentFlatTracerNum, CurrentMovingTracerNum: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),20,length(trim(s))-20));
  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
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 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,'<cpI>',tracers[i].childOf,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctAmount>',tracers[i].contents[0].amount,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctNumMoving>',IntToStr(CurrentMovingTracerNum),[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctName>',makeLonger(tracers[i].name,20),[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctTrimName>',tracers[i].name,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctLongname>',tracers[i].longname,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctUnit>',tracers[i].outputUnit,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctStandardname>',tracers[i].stdname_prefix+tracers[i].longname+tracers[i].stdname_suffix,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctChildof>',tracers[i].childof,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctAbove>',tracers[i].tracerAbove,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctBelow>',tracers[i].tracerBelow,[rfReplaceAll, rfIgnoreCase]);
              s:=StringReplace(s,'<ctDescription>',tracers[i].description,[rfReplaceAll, rfIgnoreCase]);
              if pos('<nonewline>',lowercase(s))>0 then
              begin
                s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
                write(Z,s);
              end
              else
                writeln(Z,s);
        end;
        ll:=ll+1;
      end;
    end;
   end;
  end;
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 then LoopFinished:=true;
        if lowercase(copy(trim(s),1,19))='</containingtracers' then LoopFinished:=true;
        ll:=ll+1;
      end;
      l:=ll-1;
end;

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

//! Tests whether a constant fullfills the conditions
{!
  \private
  \param i integer index of the constant
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - name:
  - dependson:
  - variation:
  - isselected: 
  - ismissingindocumentation:
  - unit: 

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function ConstantCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    j, k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],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;
        if (lhs[j]='dependson=') and (lowercase(constants[i].dependsOn)<>rhs[j]) then isOk:=false;
        if (lhs[j]='dependson/=') and (lowercase(constants[i].dependsOn)=rhs[j]) then isOk:=false;
        if (lhs[j]='variation=') and ((constants[i].minval<-0.9e20) xor (rhs[j]='0')) then isOk:=false; // the xor becomes true if left and right () do not match, left means "constant does not vary", right means "constant shall not vary".
        if (lhs[j]='variation/=') and ((constants[i].minval<-0.9e20) xor (rhs[j]='1')) then isOk:=false; //if a constant has no variation, then its minimum value is -1.0e20
        if (lhs[j]='isselected=') and ((constants[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected/=') and not ((constants[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation=') and ((constants[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation/=') and not ((constants[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='unit=') and (lowercase(constants[i].valUnit)<>rhs[j]) then isOk:=false;
        if (lhs[j]='unit/=') and (lowercase(constants[i].valUnit)=rhs[j]) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;


//! Prints out the number of constants, given specific conditions
{!
  \private
  \param s string: line with <numConstants ... > tag
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<numConstants ...>' is in the template.

  Supports the same conditions as <constants ...> loop-tag. For details see
  documentation of the ConstantCondtion() function.

  <b>Calls</b>
  - ConstantCondition()
  - NumTagPlusMinus()
                                                                    }
function StringReplaceNumConstants(s: String):String;
var numConstants, incrNumVal, incrNumLen: Integer;
    iConstant: Integer;
    conditions: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  baseTagStr:='<numConstants';
  baseTagLen:=length(baseTagStr);
  stPos:=pos(baseTagStr,s);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    // get full tag
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrNumVal is set to the incremental value (negative if decrement)
    // incrNumLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrNumVal, incrNumLen)) then
    begin
      extndTagLen:=baseTagLen+incrNumLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag an no conditions or
    if fullTagLen = extndTagLen+1 then
      numConstants:=length(constants)+incrNumVal
    else
    begin
      // ~~~ count constants ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      // count constants that fulfill the conditions
      numConstants:=incrNumVal;
      for iConstant:=0 to length(constants)-1 do
      begin
        if ConstantCondition(iConstant,conditions) then
          numConstants:=numConstants+1;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(numConstants),[rfReplaceAll, rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    stPos:=pos(baseTagStr,s);
    fullStrLen:=length(s);
  end;
  result:=s;
end;

//! Iterates all constants
{!
  \private
  \param s string: line with <constants ... > tag
  \param Q input text file
  \param Z output text file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<constants ...>' is in the template.

  Supports the content tags
  - <name>
  - <trimName>
  - <value>
  - <minval>
  - <maxval>
  - <varphase>
  - <numVarying>
  - <description>
  - <comment>
  - <unit>

  No sub-loop tags are in order.

  <b>Calls</b>
  - ConstantCondition()
  - convertToOutputLanguage()
  - optional: makeLonger()
                                                                    }
procedure ConstantLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i: Integer;
    s1: String;
    myLinesRead: Integer;
    myValue, myMinval, myMaxval: String;
    origLines: TStringList;
    l: Integer;
    myNumVarying: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  origLines:=TStringList.Create;
  while not (lowercase(copy(trim(s1),1,11))='</constants') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  myNumVarying:=modelinfos.numPhysicalParametersVarying;
  for i:=0 to length(constants)-1 do
  begin
    if constants[i].minval>-0.9e20 then myNumVarying:=myNumVarying+1;
    if ConstantCondition(i,conditions) then
    begin
      myValue:=makeLonger(AppendDot(FloatToStr(constants[i].value)),8);
      myMinval:=makeLonger(AppendDot(FloatToStr(constants[i].minval)),8);
      myMaxval:=makeLonger(AppendDot(FloatToStr(constants[i].maxval)),8);
      convertToOutputLanguage(myValue);
      for l:=0 to origLines.count-1 do
      begin
          s:=origLines[l];
          s:=StringReplace(s,'<name>',makeLonger(constants[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>',constants[i].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<value>',myValue,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<minval>',myMinval,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<maxval>',myMaxval,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<varphase>',IntToStr(constants[i].varphase),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<numVarying>',IntToStr(myNumVarying),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',constants[i].description,[rfReplaceAll, rfIgnoreCase]);
          if constants[i].myDescriptionInDocumentation='' then
            s:=StringReplace(s,'<descriptionInDocumentation>',constants[i].description,[rfReplaceAll, rfIgnoreCase])
          else
            s:=StringReplace(s,'<descriptionInDocumentation>',constants[i].myDescriptionInDocumentation,[rfReplaceAll, rfIgnoreCase]);

          s:=StringReplace(s,'<comment>',constants[i].comment,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<unit>',constants[i].valUnit,[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  OrigLines.Free;
end;


//******************* Elements ************************************//

//! Tests whether an element fullfills the conditions
{!
  \private
  \param i integer index of the element
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Possible conditions tested:
  - element: the name of the uncolored element
  - hascoloredcopies: 0 or 1; 1: colored versions of this element exist

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function ElementCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    j,k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],lhs,rhs);
      for j:=0 to lhs.Count-1 do
      begin
        if ((lhs[j]='element=') and (lowercase(elements[i].name)<>rhs[j])) then isOk:=false;
        if ((lhs[j]='element/=') and (lowercase(elements[i].name)=rhs[j])) then isOk:=false;
        if ((lhs[j]='hascoloredcopies=') and (IntToStr(elements[i].hasColoredCopies)<>rhs[j])) then isOk:=false;
        if ((lhs[j]='hascoloredcopies/=') and (IntToStr(elements[i].hasColoredCopies)=rhs[j])) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;


//! Prints out the number of elements, given specific conditions
{!
  \private
  \param s string: line with <numElements ... > tag
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<numElements ...>' is in the template.

  Supports the same conditions as <elements ...> loop-tag. For details see
  documentation of the ElementCondtion() function.

  <b>Calls</b>
  - ElementCondition()
  - NumTagPlusMinus()
                                                                    }
function StringReplaceNumElements(s: String):String;
var numElements, incrNumVal, incrNumLen: Integer;
    iElement: Integer;
    conditions: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  baseTagStr:='<numElements';
  baseTagLen:=length(baseTagStr);
  stPos:=pos(baseTagStr,s);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrNumVal is set to the incremental value (negative if decrement)
    // incrNumLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrNumVal, incrNumLen)) then
    begin
      extndTagLen:=baseTagLen+incrNumLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag an no conditions or
    if fullTagLen = extndTagLen+1 then
      numElements:=length(elements)+incrNumVal
    else
    begin
      // ~~~ count elements ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      // count elements that fulfill the conditions
      numElements:=incrNumVal;
      for iElement:=0 to length(elements)-1 do
      begin
        if ElementCondition(iElement,conditions) then
          numElements:=numElements+1;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(numElements),[rfReplaceAll, rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    stPos:=pos(baseTagStr,s);
    fullStrLen:=length(s);
  end;
  result:=s;
end;



//! Iterates all elements (the uncolored ones) of the current tracer
{!
  \private
  \param s string: line with <elements ... > tag
  \param Q input text file
  \param Z output text file
  \param t integer index of the parent tracer
  \param origLines StringList containing the lines of the tracer configuration file
  \param l current line number index (integer) in the tracer configuration file
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<elements ...>' is in the template.

  Supports the content tags
  - <element>: uncolored name of the element
  - <trimElement>: trimed version of <element>
  - <name>: Name of the tracer of the parent tracer-loop
  - <trimName>: trimed version of <name>

  The following sub-loop tags are in order:
  - <timetendencies ...>: TODO
  - <children ...>: only children of t, which represent amount of element e (all colors)
  - <limitations ...>: no changes in the behaviour compared to a direct call from within the <tracer>-loop

  <b>Calls</b>
  - ElementCondition()
  - ChildLoop() on <children ...> (optional)
  - RateLoop() on <timetendencies ...> (optional)
  - LimitationLoop() on <limitations ...> (optional)

  TODO: Reasons for introducing <elements>
                                                                    }
procedure ElementLoop(s: String; var Q: TextFile; var Z: TextFile; t: Integer; var origLines: TStringList; var l: Integer);
var conditions: String;
    e: Integer;
    i,ll: Integer;
    loopFinished: Boolean;
begin
  DecimalSeparator:='.';
  //
  conditions:=lowercase(copy(trim(s),11,length(trim(s))-11));
  for i:=0 to length(tracers[t].contents)-1 do
  begin
    e:=elements[tracers[t].contents[i].myElementNum].parentElement;
    if ElementCondition(e,conditions) then
    begin
      ll:=l+1;
      loopFinished:=false;
      while not loopFinished do
      begin
        s:=origLines[ll];
        if ll=origLines.Count-1 then LoopFinished:=true;
        if lowercase(copy(trim(s),1,10))='</elements' then LoopFinished:=true
        else if lowercase(copy(trim(s),1,15))='<timetendencies' then
          RateLoop(s,Q,Z,t,origLines,ll)
        else if lowercase(copy(trim(s),1,9))='<children' then
          ChildLoop(s,Q,Z,t,origLines,ll,e)
          // ChildLoop(s,Q,Z,t,origLines,ll,-1)
        else if lowercase(copy(trim(s),1,12))='<limitations' then
          LimitationLoop(s,Q,Z,t,origLines,ll)
        else
        begin
          s:=StringReplace(s,'<element>',elements[e].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimElement>',elements[e].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<name>',tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>',tracers[t].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',tracers[t].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<elementDescription>',elements[e].description,[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
        ll:=ll+1;
      end;
    end;
  end;

  // Iterate the whole section in case that the conditions
  // were never TRUE for any element above.
  ll:=l+1;
  loopFinished:=false;
  while not loopFinished do
  begin
    s:=origLines[ll];
    if ll=origLines.Count-1 then LoopFinished:=true;
    if lowercase(copy(trim(s),1,10))='</elements' then LoopFinished:=true;
    ll:=ll+1;
  end;
  l:=ll-1;

end;


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

//! Checks whether a given tag exists in a string before a given position (foundPos)
{!
  \private
  \param s string: full string with tags
  \param searchTag string: tag to find in s
  \param tagCondition string: condition statement that is related to tag (insert into foundCondition if stPos == pos of tag in s)
  \param stPos integer: starting position of tag in s if it is lower than input value of stPos
  \param foundTag string: unchanged if stPos/= 'pos of tag in s'; otherwise '= tag'
  \param foundCondition string: unchanged if stPos/= 'pos of tag in s'; otherwise '= tagCondition'
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  <b>Use Case</b>
  - Our string s: "<numFlatTracers> CODE <numTracers> CODE <numAtmosDepTracers>"
  - We want to know which of the tags appears first, we want to know its position, and we want to assign a specific condition string depending on the tag that appears first

  <b>Example 1</b>
  - s: "<numFlatTracers> CODE <numTracers> CODE <numAtmosDepTracers>"
  - foundPos: 1
  - searchTag: '<numTracers' => position is behind one => no changes
  - return UNCHANGED: foundPos, foundTag, foundCondition

  <b>Example 2</b>
  - s: "<numFlatTracers> CODE <numTracers> CODE <numAtmosDepTracers>"
  - foundPos: 42
  - searchTag: '<numTracers' => position is before 42 (it is 23)
  - return: foundPos=23, foundTag='<numTracers', foundCondition=tagCondition

  Called from within \ref SubstituteNumTracersTag() and SubstituteIdxTracerTag()

  <b>Calls</b> no other functions or procedures.
  }
procedure CheckTag(const s: String; const searchTag: string; const tagCondition: String;
                   var foundPos: Integer; var foundTag: String; var foundCondition: String);
var tmpPos: integer;
begin
  tmpPos:=pos(searchTag,s);
  if ((tmpPos>0) and (tmpPos<foundPos)) then
  begin
    foundPos:=tmpPos;
    foundCondition:=tagCondition;
    foundTag:=searchTag;
  end
end;


//! Replace first occurance of a <numXXXTracers tag by <numTracers and print out its position and the XXX condition
{!
  \private
  \param s string: line with <numTracers ...> or <numXXXTracers ...> tag
  \param stPos integer: starting position of first <numXXXTracers ...> tag
  \param condition string: condition for replacing XXX: <numTracers condition> = <numXXXTracers>
  \return modified s string
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Looks for the first occurance of one of the following tags
  - <numTracers
  - <num3DTracers
  - <numFlatTracers
  - <numWatTracers
  - <numSurTracers
  - <numSedTracers
  - <numFisTracers
  - <numRiverDepTracers
  - <numAtmosDepTracers
  - <numMovingTracers,
  replaces them by '<numTracers', prints out the starting position of the tag (stPos) and prints out the condition associated with the tag-part between 'num' and 'Tracers', i.e. 'vertLoc=WAT' for '<numWatTracers'.

  Called from within \ref StringReplaceNumTracers()

  <b>Calls</b>
  - CheckTag()
  }
function SubstituteNumTracersTag(s: String; var stPos: Integer; var condition: String): String;
var firstNumTracersTag: String;
    tmpTag: String;
    tmpPos: Integer;
    sOUt: String;
begin
    stPos:=32767;
    tmpTag:='';
    condition:='';
    sOut:=s;

    CheckTag(s,'<numTracers',        '',            stPos,tmpTag,condition);
    CheckTag(s,'<numFlatTracers',    'vertloc/=FIS;vertloc/=WAT',stPos,tmpTag,condition);
    CheckTag(s,'<num3DTracers',      'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<numWatTracers',     'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<numSurTracers',     'vertloc=SUR', stPos,tmpTag,condition);
    CheckTag(s,'<numSedTracers',     'vertloc=SED', stPos,tmpTag,condition);
    CheckTag(s,'<numFisTracers',     'vertloc=FIS', stPos,tmpTag,condition);
    CheckTag(s,'<numRiverDepTracers','riverdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<numAtmosDepTracers','atmosdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<numMovingTracers',  'vertspeed/=0',stPos,tmpTag,condition);

    if (stPos<>32767) then
      sOut:=StringReplace(s,tmpTag,'<numTracers',[rfIgnoreCase]) // replace only the first occurance
    else
      stPos:=0;

    result:=sOut;
end;


//! Prints out the number of tracers, given specific conditions
{!
  \private
  \param s string: line with <numTracers ... > tag
  \return modified s string
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<numTracers ...>' is in the template.

  Supports the same conditions as <tracers ...> loop-tag. For details see
  documentation of the TracerCondtion() function.

  <b>Calls</b>
  - TracerCondition()
  - SplitConditions()
  - NumTagPlusMinus()
  - MergeConditionPrefix()
  - SubstituteNumTracersTag()
                                                                    }
function StringReplaceNumTracers(s: String):String;
var numTracers, incrNumVal, incrNumLen: Integer;
    iTracer: Integer;
    conditions, condition_prefix: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  // call constructors
  baseTagStr:='<numTracers';
  baseTagLen:=length(baseTagStr);
  s:=SubstituteNumTracersTag(s,stPos,condition_prefix);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    // get full tag
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrNumVal is set to the incremental value (negative if decrement)
    // incrNumLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrNumVal, incrNumLen)) then
    begin
      extndTagLen:=baseTagLen+incrNumLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag and no conditions (also no numXXXTracers tag)
    if ((fullTagLen = extndTagLen+1) and (condition_prefix = '')) then
    begin
      numTracers:=incrNumVal;
      for iTracer:=0 to length(tracers)-1 do
        if tracers[iTracer].isActive=1 then numTracers:=numTracers+1;
    end
    else
    begin
      // ~~~ count tracers ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      conditions:=MergeConditionPrefix(conditions,condition_prefix);

      // count tracers that are active and fulfill the conditions
      numTracers:=incrNumVal;
      for iTracer:=0 to length(tracers)-1 do
      begin
        if ((TracerCondition(iTracer,conditions)) and (tracers[iTracer].isActive=1)) then
          numTracers:=numTracers+1;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(numTracers),[rfReplaceAll, rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    s:=SubstituteNumTracersTag(s,stPos,condition_prefix);
    fullStrLen:=length(s);
  end;
  result:=s;
end;


//! Replace first occurance of a <numTracer, <idxTracer, <numXXX, or <idxXXX tag in a <tracers loop by <numTracer and print out its position and the XXX condition
{!
  \private
  \param s string: line with <numTracer ...>, <numXXX ...>, or <idx... > tag
  \param stPos integer: starting position of first <numTracer ..>, <numXXX ...>, or <idx...> tag
  \param condition string: condition for replacing XXX: <numTracer condition> = <numXXX>
  \return modified s string with <numTracer ...>
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Looks for the first occurance of one of the following tags
  - <numTracer, <idxTracer,
  - <num3D, <idx3D,
  - <numFlat, <idxFlat,
  - <numWat, <idxWat,
  - <numSur, <idxSur
  - <numSed, <idxSed
  - <numFis, <idxFis
  - <numRiverDep, <idxRiverDep
  - <numAtmosDep, <idxAtmosDep
  - <numMoving, <idxMoving
  replaces them by '<numTracer', prints out the starting position of the tag (stPos) and prints out the condition associated with the tag-part between 'num' and 'Tracers', i.e. 'vertLoc=WAT' for '<numWat'.

  Called from within \ref StringReplaceIndexTracers()

  <b>Calls</b>
  - CheckTag()
  }
function SubstituteIdxTracersTag(s: String; var stPos: Integer; var condition: String): String;
var firstNumTracersTag: String;
    tmpTag: String;
    tmpPos: Integer;
    sOUt: String;
begin
    stPos:=32767;
    tmpTag:='';
    condition:='';
    sOut:=s;

    CheckTag(s,'<numTracer',  '',            stPos,tmpTag,condition);
    CheckTag(s,'<idxTracer',  '',            stPos,tmpTag,condition);
    CheckTag(s,'<num3D',      'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<idx3D',      'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<numFlat',    'vertloc/=FIS;vertloc/=WAT',stPos,tmpTag,condition);
    CheckTag(s,'<idxFlat',    'vertloc/=FIS;vertloc/=WAT',stPos,tmpTag,condition);
    CheckTag(s,'<numWat',     'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<idxWat',     'vertloc=WAT', stPos,tmpTag,condition);
    CheckTag(s,'<numSur',     'vertloc=SUR', stPos,tmpTag,condition);
    CheckTag(s,'<idxSur',     'vertloc=SUR', stPos,tmpTag,condition);
    CheckTag(s,'<numSed',     'vertloc=SED', stPos,tmpTag,condition);
    CheckTag(s,'<idxSed',     'vertloc=SED', stPos,tmpTag,condition);
    CheckTag(s,'<numFis',     'vertloc=FIS', stPos,tmpTag,condition);
    CheckTag(s,'<idxFis',     'vertloc=FIS', stPos,tmpTag,condition);
    CheckTag(s,'<numRiverDep','riverdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<idxRiverDep','riverdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<numAtmosDep','atmosdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<idxAtmosDep','atmosdep=1',  stPos,tmpTag,condition);
    CheckTag(s,'<numMoving',  'vertspeed/=0',stPos,tmpTag,condition);
    CheckTag(s,'<idxMoving',  'vertspeed/=0',stPos,tmpTag,condition);

    if (stPos<>32767) then
      sOut:=StringReplace(s,tmpTag,'<numTracer',[rfIgnoreCase]) // replace only the first occurance
    else
      stPos:=0;

    result:=sOut;
end;


//! Prints out the index of a tracer, given specific conditions
{!
  \private
  \param s string: line with <idxTracer ... > tag
  \param jTracer: internal index of the tracer, which external index - given
                   some conditions - we want to print out
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<idxTracers ...>' is in the template. The tag
  <idxTracers ...> has to be nested in a <tracers ...> loop. However, it prints
  out the tracer indices according to the conditions given to himself and not
  given to the <tracers> tag. Supports the same conditions as <tracer ...>
  loop-tag. For details see documentation of the TracerCondtion() function.

  <b>Example:</b> <br>
   We have three tracers:
    - dia (vertLoc=WAT)
    - no3 (vertLoc=WAT)
    - nit (vertLoc=SED)

  The code
  \code
    <tracers vertLoc=SED>
      index_<trimName> = <idxTracer>
    </tracers>
  \endcode
   prints out
   \code
      index_nit = 3
   \endcode
   . <br>
   In contrast, the code
   \code
    <tracers vertLoc=SED>
      index_<trimName> = <idxTracer verLoc=SED>
    </tracers>
   \endcode
   prints out
   \code
      index_nit = 1
   \endcode
   . <br>
   The code
   \code
    <tracers>
      index_<trimName> = <idxTracer vertLoc=SED>
    </tracers>
   \endcode
   prints out
   \code
      index_dia = 0
      index_no3 = 0
      index_nit = 3
   \endcode
   . <br>

  It can deal with this expression:
  \code
   <tracers vertLoc=SED OR vertLoc=SUR>
     <numTracer loopConditions>
       ! converted to <numTracer vertLoc=SED OR vertLoc=SUR>
     <numSed loopConditions>
       ! converted to <numTracer vertloc=SED;vertLoc=SED OR vertloc=SED;vertLoc=SUR>
     <numWat loopConditions>
       ! converted to <numTracer vertloc=WAT;vertLoc=SED OR vertloc=WAT;vertLoc=SUR>
     <numTracer childOf=none OR color=grey;loopConditions>
       ! covnerted to <numTracer childof=none OR color=grey;vertloc=sed OR color=grey;vertloc=sur>
   </tracers>
  \endcode

  <b>Calls</b>
  - TracerCondition()
  - SplitConditions()
  - NumTagPlusMinus()
                                                                    }
function StringReplaceIndexTracers(s: String; jTracer: Integer; loopConditions: String = ''):String;
var idxTracer, incrIdxVal, incrIdxLen: Integer;
    iTracer, iCondition: Integer;
    conditions, condition_prefix: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  baseTagStr:='<numTracer';
  baseTagLen:=length(baseTagStr);
  s:=SubstituteIdxTracersTag(s,stPos,condition_prefix);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    // get full tag
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrIdxVal is set to the incremental value (negative if decrement)
    // incrIdxLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrIdxVal, incrIdxLen)) then
    begin
      extndTagLen:=baseTagLen+incrIdxLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag and no conditions or
    if ((fullTagLen = extndTagLen+1) and (condition_prefix = '')) then
    begin
      idxTracer:=incrIdxVal;
      for iTracer:=0 to jTracer do
        if tracers[iTracer].isActive=1 then idxTracer:=idxTracer+1;
    end
    else
    begin
      // ~~~ count tracers ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      conditions:=MergeConditionPrefix(conditions,condition_prefix);
      if (loopConditions<>'') then
        conditions:=ReplaceSubCondition(conditions,'loopConditions',loopConditions);

      // count tracers that are active and fulfill the conditions
      idxTracer:=incrIdxVal;
      if TracerCondition(jTracer,conditions) then
      begin
        for iTracer:=0 to jTracer do
        begin
          if ((TracerCondition(iTracer,conditions)) and (tracers[iTracer].isActive=1)) then
            idxTracer:=idxTracer+1;
        end;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(idxTracer),[rfIgnoreCase]);
    //debugging
    // s:=StringReplace(s,fullTagStr,IntToStr(idxTracer)+'+++'+conditions,[rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    s:=SubstituteNumTracersTag(s,stPos,condition_prefix);
    fullStrLen:=length(s);
  end;
  result:=s;
end;


//! Iterates all tracers
{!
  \private
  \param s string: line with <tracers ... > tag
  \param Q input text file
  \param Z output text file
  \param backward iterate tracers in inverted order
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<tracers ...>' is in the template.

  Supports the content tags
  - a lot of ... (TODO)
  - <idxTracer [CONDITION[;CONDITION][;+|-X]]|[+|-X]>

  The following sub-loop tags are in order:
  - <timetendencies>
  - <children>
  - <limitations>
  - <elements>

  <b>Calls</b>
  - TracerCondition()
  - RateLoop()
  - ChildLoop()
  - LimitationLoop()
  - StringReplaceIndexTracers()

  The following <b>sub-loops</b> are in order:
  - <constants ...>
  - <tracers ...>
  - <backwardtracers ...>
  - <auxiliaries>
  - <processes>
  - <celements>
  - optional: StringReplaceIndexTracers()
  - optional: makeLonger()

  <b> CONDITION</b> <br>
  The set of accepted CONDITIONs in the <idxTracer ...> tags is the same as the
  set in the <tracers> loop. <b>However,</b> <idxTracers ...> does not
  necessarily index the tracers in the <tracers> loop from 1 to n. Instead, it
  has its own set of conditions. If <idxTracer> should exactly index the tracers
  in this specific tracer loop, one needs to use the keyword 'loopConditions'
  like a CONDITION. Examples for <idxTracers ...> are shown below. Each
  CONDITION needs to be separated by a ';' from the next one. Increments and
  decrements (+/-X) are treated like conditions (separate by ':') and can be at
  any position in the tag. When more than one in-/decrement is included, only
  the first is considered.

  <b>Calls</b>
  - optional: ConstantLoop()
  - optional: TracerLoop()
  - optional: TracerLoop()
  - optional: AuxiliaryLoop()
  - optional: ProcessLoop()
  - optional: cElementLoop()
  - optional: StringReplaceIndexTracers()
  - optional: StringReplaceIndexConstants()
  - optional: StringReplaceIndexAuxiliaries()
  - optional: StringReplaceIndexElements()
  - optional: StringReplaceIndexProcesses()
  - optional: makeLonger()

  Uses <b>global variables</b>:
  - \ref codegenVersion

  If a XML file is processed, the tags are enclosed by [] instead of <>.

  <b>Examples</b>:
  - <b>Example:</b> iterate all tracers, index all tracers <br>
      <i>Code:</i>
        \code
        <tracers>
          <idxTracers>
        </tracers>
        \endcode
      <i>Result:</i> 1, 2, 3, 4, 5, 6, ...
  - <b>Example:</b> iterate all tracers, index all tracers (+1) <br>
      <i>Code:</i>
        \code
        <tracers>
          <idxTracers +1>
        </tracers>
        \endcode
      <i>Result:</i> 2, 3, 4, 5, 6, 7, ...
  - <b>Example:</b> iterate WAT tracers, index all tracers <br>
      <i>Code:</i>
        \code
        <tracers vertLoc=WAT>
          <idxTracers>
       </tracers>
        \endcode
      <i>Result:</i> 1, 3, 6, ... <br>
        <i>Assuming,</i> that tracers 1, 3 and 6 (with respect to original order) are
        WAT tracers. <br>
      <i>Explanation:</i> only WAT tracers are iterated but all tracers are considered
                   for the indexing
  - <b>Example:</b> iterate all tracers, index WAT tracers <br>
      <i>Code:</i>
        \code
        <tracers>
          <idxTracers vertLoc=WAT>
        </tracers>
        \endcode
      <i>Result:</i> 1, -1, 2, -1, -1, 3, ...  <br>
        <i>Assuming,</i> that tracers 1, 3 and 6 (with respect to original order) are
        WAT tracers.              <br>
      <i>Explanation:</i> all tracers are iterated but only WAT tracers are considered
                   for the indexing; all non-indexd tracers have the value -1
  - <b>Example:</b> iterate WAT tracers, index WAT tracers <br>
      <i>Code:</i>
        \code
        <tracers vertLoc=WAT>
          <idxTracers vertLoc=WAT>
          <idxTracers loopConditions>
          <idxTracers>
        </tracers>
        \endcode
      <i>Result (1st):</i> 1, 2, 3, ...  (only WAT indices)    <br>
      <i>Result (2nd):</i> 1, 2, 3, ...  (same as <tracers> == only WAT)  <br>
      <i>Result (3rd):</i> 1, 3, 6, ...  (original indices)
  - <b>Example:</b> another example <br>
      <i>Code:</i>
        \code
        <tracers vertLoc=WAT; childOf=none>
          <idxTracers vertLoc=WAT; childOf=none>
          <idxTracers loopConditions>
        </tracers>
        \endcode
      <i>Result (1st):</i> 1, 2, 3, ... <br>
      <i>Result (2nd):</i> 1, 2, 3, ...
                                                                    }
procedure TracerLoop(s: String; var Q: TextFile; var Z: TextFile; backward: Boolean=false);
var conditions: String;
    i, j, ii, n, ifs: Integer;
    s1: String;
    myLinesRead:                    Integer;
    myCETotalIndex: String;
    myCEAgedIndex:  String;
    myCETotalName:  String;
    myCEAgedName:   String;
    myCEAmount:     String;
    myChildOfNumMoving, myChildOfNumMovingTemp: Integer;
    parentI: Integer;
    origLines: TStringList;
    l: 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:='';
  origLines:=TStringList.Create;
  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;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  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
    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;
      ifs:=0;
      l:=0;
      while l<=origLines.count-1 do
      begin
        s:=origLines[l];
        if ifs>0 then
        begin
          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
        else if lowercase(copy(trim(s),1,15))='<timetendencies' then
          RateLoop(s,Q,Z,i,origLines,l)
        else if lowercase(copy(trim(s),1,9))='<children' then
          ChildLoop(s,Q,Z,i,origLines,l)
        else if lowercase(copy(trim(s),1,12))='<limitations' then
          LimitationLoop(s,Q,Z,i,origLines,l)
        else if lowercase(copy(trim(s),1,9))='<elements' then
          ElementLoop(s,Q,Z,i,origLines,l)
        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;
          end;
        end
        else if lowercase(copy(trim(s),1,4))='</if' then
          //do nothing, ignore this line
        else
        begin
          s:=StringReplace(s,'<trimName>',Tracers[i].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<name>',makeLonger(Tracers[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',Tracers[i].description,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<comment>',Tracers[i].comment,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<vertSpeed>',AppendDot(Tracers[i].vertSpeed),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<vertDiff>',AppendDot(Tracers[i].vertDiff),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<molDiff>',Tracers[i].molDiff,[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,'<vertSpeedValueMperS>',AppendDot(FloatToStr(Tracers[i].myVertSpeedValue/24.0/3600.0)),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<-vertSpeedValueMperS>',AppendDot(FloatToStr(-Tracers[i].myVertSpeedValue/24.0/3600.0)),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<opacityValue>',AppendDot(FloatToStr(Tracers[i].myOpacityValue)),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<opacity>',AppendDot(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]);
          // below, we need to replace each numXXx Tag twice:
          //  1) <numXXx> => <idxTracer conditionXXx>
          //  2) <numXXx +Y> => <idxTracer conditionXXx; +Y>
          // replace <idxTracer CONDITIONS> tag
          s:=StringReplaceIndexTracers(s,i,conditions);
          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]);
          s:=StringReplace(s,'<tracerAbove>',Tracers[i].tracerAbove,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<tracerBelow>',Tracers[i].tracerBelow,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<longname>',Tracers[i].longname,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<unit>',Tracers[i].outputUnit,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<standardname>',Tracers[i].stdname_prefix+Tracers[i].longname+Tracers[i].stdname_suffix,[rfReplaceAll, rfIgnoreCase]);
          for n:=0 to length(tracers[i].contents)-1 do
            s:=StringReplace(s,'<contentOf'+tracers[i].contents[n].element+'>',AppendDot(FloatToStr(tracers[i].contents[n].myAmount)),[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
        l:=l+1;
      end;
    end;
   end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  OrigLines.Free;
end;

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

//! Tests whether an auxiliary fullfills the conditions
{!
  \private
  \param i integer index of the auxiliary
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - vertloc: WAT (whole water column), SED (sediment only), SUR (surface only), FIS (fish=like)
  - isflat: deprecated, see vertloc
  - a lot more ... (TODO)

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function AuxiliaryCondition(i: Integer; c: String):Boolean;
var isOk,isOkOr: Boolean;
    lhs,rhs,subC: TStringList;
    j, k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],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;
        if (lhs[j]='isselected=') and ((auxiliaries[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected/=') and not ((auxiliaries[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation=') and ((auxiliaries[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation/=') and not ((auxiliaries[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;


//! Prints out the number of auxiliaries, given specific conditions
{!
  \private
  \param s string: line with <numAuxiliaries ... > tag
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<numAuxiliaries ...>' is in the template.

  Supports the same conditions as <auxiliaries ...> loop-tag. For details see
  documentation of the AuxiliaryCondition() function.

  <b>Calls</b>
  - AuxiliaryCondition()
  - NumTagPlusMinus()
                                                                    }
function StringReplaceNumAuxiliaries(s: String):String;
var numAuxiliaries, incrNumVal, incrNumLen: Integer;
    iAuxiliary: Integer;
    conditions: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  baseTagStr:='<numAuxiliaries';
  baseTagLen:=length(baseTagStr);
  stPos:=pos(baseTagStr,s);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    // get full tag
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrNumVal is set to the incremental value (negative if decrement)
    // incrNumLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrNumVal, incrNumLen)) then
    begin
      extndTagLen:=baseTagLen+incrNumLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag an no conditions or
    if fullTagLen = extndTagLen+1 then
      numAuxiliaries:=length(auxiliaries)
    else
    begin
      // ~~~ count auxiliaries ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      // count auxiliaries that fulfill the conditions
      numAuxiliaries:=incrNumVal;
      for iAuxiliary:=0 to length(auxiliaries)-1 do
      begin
        if AuxiliaryCondition(iAuxiliary,conditions) then
          numAuxiliaries:=numAuxiliaries+1;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(numAuxiliaries),[rfReplaceAll, rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    stPos:=pos(baseTagStr,s);
    fullStrLen:=length(s);
  end;
  result:=s;
end;


//! Iterates all auxiliaries
{!
  \private
  \param s string: line with <auxiliaries ... > tag
  \param Q input text file
  \param Z output text file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<auxiliaries ...>' is in the template.

  Supports the content tags
  - <temp[k]> (with a integer k)
  - <formula>
  - <description>
  - <comment>
  - <iterations>
  - <iterinit>

  No sub-loop tags are in order.

  <b>Calls</b>
  - AuxiliaryCondition()
  - convertToOutputLanguage()
  - optional: makeLonger()
                                                                    }
procedure AuxiliaryLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, j, k, ifs: Integer;
    s1: String;
    myLinesRead: Integer;
    myFormula: String;
    mytemp: Array[1..9] of String;
    deleteLine: Boolean;
    origLines: TStringList;
    l: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),14,length(trim(s))-14));
  myLinesRead:=0;
  s1:='';
  origLines:=TStringList.Create;
  while not (lowercase(copy(trim(s1),1,13))='</auxiliaries') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  for i:=0 to length(Auxiliaries)-1 do
  begin
    if AuxiliaryCondition(i,conditions) then
    begin
      ifs:=0;
      myformula:=Auxiliaries[i].formula;
      convertToOutputLanguage(myformula,auxiliaries[i].name);
      for j:=1 to 9 do
      begin
        mytemp[j]:=Auxiliaries[i].temp[j];
        convertToOutputLanguage(mytemp[j],auxiliaries[i].name);
      end;
      for l:=0 to origLines.count-1 do
      begin
        s:=origLines[l];
        if ifs>0 then
        begin
          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
        else if lowercase(copy(trim(s),1,3))='<if' then
        begin
          if auxiliaryCondition(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;
          end;
        end
        else if lowercase(copy(trim(s),1,4))='</if' then
          //the if which is fulfilled; do nothing, ignore this line
        else
        begin
          deleteLine:=false;
          s:=StringReplace(s,'<name>',makeLonger(Auxiliaries[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>',Auxiliaries[i].name,[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]);
          if auxiliaries[i].myDescriptionInDocumentation='' then
            s:=StringReplace(s,'<descriptionInDocumentation>',auxiliaries[i].description,[rfReplaceAll, rfIgnoreCase])
          else
            s:=StringReplace(s,'<descriptionInDocumentation>',auxiliaries[i].myDescriptionInDocumentation,[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
            if pos('<nonewline>',lowercase(s))>0 then
            begin
              s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
              write(Z,s);
            end
            else
              writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  OrigLines.Free;
end;

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

//! Tests whether a process fullfills the conditions
{!
  \private
  \param i integer index of the process
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - vertloc: WAT (whole water column), SED (sediment only), SUR (surface only), FIS (fish=like)
  - isflat: deprecated, see vertloc
  - isinporewater:
  - name:
  - isOutput:
  - processtype:

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function ProcessCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs, subC: TStringList;
    j, k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],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]='isinporewater=') and (processes[i].myIsInPorewater<>StrToInt(rhs[j])) then isOk:=false;
        if (lhs[j]='isinporewater/=') and (processes[i].myIsInPorewater=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;
        if (lhs[j]='isselected=') and ((processes[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected/=') and not ((processes[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation=') and ((processes[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation/=') and not ((processes[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected=') and ((processes[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='isselected/=') and not ((processes[i].myIsSelected) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation=') and ((processes[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
        if (lhs[j]='ismissingindocumentation/=') and not ((processes[i].myIsMissingInDocumentation) xor (rhs[j]='1')) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;


//! Prints out the number of processes, given specific conditions
{!
  \private
  \param s string: line with <numProcesses ... > tag
  \author Daniel Neumann, daniel.neumann@io-warnemuende.de

  Is called when the tag '<numProcesses ...>' is in the template.

  Supports the same conditions as <processes ...> loop-tag. For details see
  documentation of the ProcessCondition() function.

  <b>Calls</b>
  - ProcessCondition()
  - NumTagPlusMinus()
                                                                    }
function StringReplaceNumProcesses(s: String):String;
var numProcesses, incrNumVal, incrNumLen: Integer;
    iProcess: Integer;
    conditions: String;
    stPos: Integer; // column / position of the tag start
    baseTagStr, extndTagStr, fullTagStr: String;
    baseTagLen, extndTagLen, fullTagLen, fullStrLen: Integer;
begin
  // ~~~ initialization ~~~
  baseTagStr:='<numProcesses';
  baseTagLen:=length(baseTagStr);
  stPos:=pos(baseTagStr,s);
  fullStrLen:=length(s);

  // ~~~ start the process ~~~
  // Proceed if baseTagStr exists in s (stPos > 0).
  // Do this as often as there are tags of this type.
  while ( stPos >= 1 ) do
  begin
    // get full tag
    fullTagLen:=pos('>',copy(s,stPos,fullStrLen-stPos+1));
    fullTagStr:=copy(s,stPos,fullTagLen);

    // look whether 'number' is in-/decremented:
    // incrNumVal is set to the incremental value (negative if decrement)
    // incrNumLen is the length of the "+/-NUMBER" expression in the tag
    if (NumTagPlusMinus(copy(fullTagStr,baseTagLen+1,fullStrLen-baseTagLen), incrNumVal, incrNumLen)) then
    begin
      extndTagLen:=baseTagLen+incrNumLen;
      extndTagStr:=copy(s,stPos,extndTagLen);
    end
    else
    begin
      extndTagStr:=baseTagStr;
      extndTagLen:=baseTagLen;
    end;

    // We just have <TAG> as tag an no conditions or
    if fullTagLen = extndTagLen+1 then
      numProcesses:=length(processes)+incrNumVal
    else
    begin
      // ~~~ count processes ~~~
      // get conditions
      conditions:=lowercase(copy(fullTagStr,extndTagLen+2,fullTagLen-extndTagLen-2));
      // count processes that fulfill the conditions
      numProcesses:=incrNumVal;
      for iProcess:=0 to length(processes)-1 do
      begin
        if ProcessCondition(iProcess,conditions) then
          numProcesses:=numProcesses+1;
      end;
    end;

    // ~~~ Do the replacement ~~~
    s:=StringReplace(s,fullTagStr,IntToStr(numProcesses),[rfReplaceAll, rfIgnoreCase]);

    // ~~~ for restart of the while-loop ~~~
    stPos:=pos(baseTagStr,s);
    fullStrLen:=length(s);
  end;
  result:=s;
end;


//! Iterates all processes
{!
  \private
  \param s string: line with <processes ... > tag
  \param Q input text file
  \param Z output text file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<processes ...>' is in the template.

  Supports the content tags
  - <name>, makeLonger(Processes[i].name,15),[rfReplaceAll, rfIgnoreCase]);
  - <trimName>, Processes[i].name,[rfReplaceAll, rfIgnoreCase]);
  - <description>, Processes[i].description,[rfReplaceAll, rfIgnoreCase]);
  - <turnover>, makeLonger(myRate,30),[rfReplaceAll, rfIgnoreCase]);
  - <comment>, Processes[i].comment,[rfReplaceAll, rfIgnoreCase]);
  - <processtype>, Processes[i].processType,[rfReplaceAll, rfIgnoreCase]);
  - <stifffactor>, myStiffFactor,[rfReplaceAll, rfIgnoreCase]);
  - <stifftracer>, myStiffTracer,[rfReplaceAll, rfIgnoreCase]);

  No sub-loop tags are in order.

  <b>Calls</b>
  - ProcessCondition()
  - convertToOutputLanguage()
  - optional: makeLonger()
                                                                    }
procedure ProcessLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, ifs: Integer;
    s1: String;
    myLinesRead: Integer;
    myRate, myStiffFactor, myStiffTracer: String;
    origLines: TStringList;
    l: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  origLines:=TStringList.Create;
  while not (lowercase(copy(trim(s1),1,11))='</processes') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  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:='';
      ifs:=0;
      for l:=0 to origLines.count-1 do
      begin
        s:=origLines[l];
        if ifs>0 then
        begin
          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
        else if lowercase(copy(trim(s),1,3))='<if' then
        begin
          if processCondition(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;
          end;
        end
        else if lowercase(copy(trim(s),1,4))='</if' then
          //the if which is fulfilled; do nothing, ignore this line
        else
        begin
          s:=StringReplace(s,'<name>',makeLonger(Processes[i].name,15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>',Processes[i].name,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',Processes[i].description,[rfReplaceAll, rfIgnoreCase]);
          if processes[i].myDescriptionInDocumentation='' then
            s:=StringReplace(s,'<descriptionInDocumentation>',processes[i].description,[rfReplaceAll, rfIgnoreCase])
          else
            s:=StringReplace(s,'<descriptionInDocumentation>',processes[i].myDescriptionInDocumentation,[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]);
          s:=StringReplace(s,'<equation>',GetInputOutputEquation(Processes[i]),[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
      end;
    end;
   end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  OrigLines.Free;
end;

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

//! Tests whether a colored element fullfills the conditions
{!
  \private
  \param i integer index of the colored element (cElement)
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - istracer:
  - isaging:

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function cElementCondition(i: Integer; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs, subC: TStringList;
    j, k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],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;
        if (lhs[j]='element=') and (lowercase(cElements[i].element)<>lowercase(rhs[j])) then isOk:=false;
        if (lhs[j]='element/=') and (lowercase(cElements[i].element)=lowercase(rhs[j])) then isOk:=false;
        if (lhs[j]='color=') and (cElements[i].color<>rhs[j]) then isOk:=false;
        if (lhs[j]='color/=') and (cElements[i].color=rhs[j]) then isOk:=false;
        if (lhs[j]='atmosdep=') and (IntToStr(cElements[i].atmosDep)<>rhs[j]) then isOk:=false;
        if (lhs[j]='atmosdep/=') and (IntToStr(cElements[i].atmosDep)=rhs[j]) then isOk:=false;
        if (lhs[j]='riverdep=') and (IntToStr(cElements[i].riverDep)<>rhs[j]) then isOk:=false;
        if (lhs[j]='riverdep/=') and (IntToStr(cElements[i].riverDep)=rhs[j]) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;

//! Creates rates / timeTendencies for the current tracer
{!
  \private
  \param s string: line with <celements ... > tag
  \param Q input text file
  \param Z output text file
  \param active Boolean stating whether the loop is performed (true) or not (false)
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<celements ...>' is in the template.

  Supports the content tags
  - <total> "total_COLOR_ELEMENT"
  - <totalTop> "total_COLOR_ELEMENT_at_top"
  - <totalBottom> "total_COLOR_ELEMENT_at_bottom"
  - <totalBottomNumFlat>
  - <aged> "aged_COLOR_ELEMENT"
  - <agedTop> "aged_COLOR_ELEMENT_at_top"
  - <agedBottom> "aged_COLOR_ELEMENT_at_bottom"
  - <agedBottomNumFlat>
  - <totalIndex> "idx_total_COLOR_ELEMENT"
  - <totalIndexTop> "idx_flat_total_COLOR_ELEMENT_at_top"
  - <totalIndexBottom> "idx_flat_total_COLOR_ELEMENT_at_bottom"
  - <agedIndex> "idx_aged_COLOR_ELEMENT"
  - <agedIndexTop> "idx_flat_aged_COLOR_ELEMENT_at_top"
  - <agedIndexBottom> "idx_flat_aged_COLOR_ELEMENT_at_top"
  - <totalIndexNum> index number
  - <totalIndexTopNum> index number
  - <totalIndexBottomNum> index number
  - <agedIndexNum> index number
  - <agedIndexTopNum> index number
  - <agedIndexBottomNum> index number

  The following sub-loop tags are in order:
  - <containingtracers>

  <b>Calls</b>
  - cElementCondition()
  - optional: makeLonger()
                                                                    }
procedure cElementLoop(s: String; var Q: TextFile; var Z: TextFile; active: Boolean=true);
var conditions: String;
    i, j: Integer;
    s1: String;
    myLinesRead: Integer;
    totalIndexNum, totalIndexTopNum, totalIndexBottomNum: Integer;
    agedIndexNum, agedIndexTopNum, agedIndexBottomNum: Integer;
    Current3dTracerNum, CurrentFlatTracerNum: Integer;
    origLines: TStringList;
    l: Integer;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  origLines:=TStringList.Create;
  while not (lowercase(copy(trim(s1),1,11))='</celements') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  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;
      l:=0;
      while l<=origLines.count-1 do
      begin
        s:=origLines[l];
        if lowercase(copy(trim(s),1,18))='<containingtracers' then
          ctLoop(s,Q,Z,i,origLines,l)
        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]);

          s:=StringReplace(s,'<element>',cElements[i].element,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<color>',cElements[i].color,[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<description>',cElements[i].description,[rfReplaceAll, rfIgnoreCase]);

          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
        l:=l+1;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  origLines.Free;
end;

//******************* Process types ********************************//

//! Tests whether a process type fullfills the conditions
{!
  \private
  \param pt String containing the process type
  \param c String containing the conditions
  \return true/false depending on whether the conditions are fullfilled or not
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Possible conditions tested:
  - name

  <b>Calls</b>
  - SplitConditions().
  - SplitOR().
                                                                    }
function ProcessTypeCondition(pt: String; c: String):Boolean;
var isOk, isOkOr: Boolean;
    lhs,rhs, subC: TStringList;
    j, k: Integer;
begin
  if (trim(c)='') then isOkOr:=true
  else
  begin
    lhs:=TStringList.Create; rhs:=TStringList.Create;
    subC:=TStringList.Create;
    isOkOr:=false;
    SplitOR(c,subC);
    for k:=0 to subC.Count-1 do
    begin
      isOk:=true;
      splitConditions(subC[k],lhs,rhs);
      for j:=0 to lhs.Count-1 do
      begin
        if (lhs[j]='name=') and (lowercase(pt)<>rhs[j]) then isOk:=false;
        if (lhs[j]='name/=') and (lowercase(pt)=rhs[j]) then isOk:=false;
      end;
      isOkOr:=(isOkOr or isOk);
    end;
  end;
  result:=isOkOr;
end;

//! Iterates all process types
{!
  \private
  \param s string: line with <processTypes ... > tag
  \param Q input text file
  \param Z output text file
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Is called when the tag '<processes ...>' is in the template.

  Supports the content tags
  - <name>, makeLonger(Processes[i].name,15),[rfReplaceAll, rfIgnoreCase]);
  - <trimName>, Processes[i].name,[rfReplaceAll, rfIgnoreCase]);

  No sub-loop tags yet.

  <b>Calls</b>
  - ProcessTypeCondition()
  - convertToOutputLanguage()
  - optional: makeLonger()
                                                                    }
procedure ProcessTypeLoop(s: String; var Q: TextFile; var Z: TextFile);
var conditions: String;
    i, j, ifs: Integer;
    s1: String;
    myLinesRead: Integer;
    origLines: TStringList;
    l: Integer;
    pt_list: Array of String;
    found: Boolean;
begin
  DecimalSeparator:='.';
  conditions:=lowercase(copy(trim(s),12,length(trim(s))-12));
  myLinesRead:=0;
  s1:='';
  origLines:=TStringList.Create;
  while not (lowercase(copy(trim(s1),1,14))='</processtypes') and not EOF(Q) do
  begin
    readln(Q,s1);
    myLinesRead:=myLinesRead+1;
    origLines.Add(s1);
  end;
  origLines.Delete(OrigLines.Count-1);
  //generate an array pt_list now, as this loop is rare and the array
  //is typically not needed
  setLength(pt_list,0);
  for i:=0 to length(processes)-1 do
  begin
    found:=false;
    for j:=0 to length(pt_list)-1 do
      if lowercase(pt_list[j])=lowercase(processes[i].processType) then found:=true;
    if (not found) then
    begin
      setLength(pt_list,length(pt_list)+1);
      pt_list[length(pt_list)-1]:=processes[i].processType;
    end;
  end;

  for i:=0 to length(pt_list)-1 do
  begin
    if ProcessTypeCondition(pt_list[i],conditions) then
    begin
      ifs:=0;
      for l:=0 to origLines.count-1 do
      begin
        s:=origLines[l];
        if ifs>0 then
        begin
          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
        else if lowercase(copy(trim(s),1,3))='<if' then
        begin
          if processTypeCondition(pt_list[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;
          end;
        end
        else if lowercase(copy(trim(s),1,4))='</if' then
          //the if which is fulfilled; do nothing, ignore this line
        else
        begin
          s:=StringReplace(s,'<name>',makeLonger(pt_list[i],15),[rfReplaceAll, rfIgnoreCase]);
          s:=StringReplace(s,'<trimName>',pt_list[i],[rfReplaceAll, rfIgnoreCase]);
          if pos('<nonewline>',lowercase(s))>0 then
          begin
            s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
            write(Z,s);
          end
          else
            writeln(Z,s);
        end;
      end;
    end;
  end;
  LinesRead:=LinesRead+MyLinesRead;
  OrigLines.Free;
end;

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

//! Main routine: Processes a file and replaces tags
{!
  \public
  \param inputfile a string representing the name of the input file
  \param outputfile a string representing the name of the output file
  \param coloredElements boolean stating whether 'split colored elements' was ticked in the cgt GUI [true]
  \author Hagen Radtke, hagen.radtke@io-warnemuende.de

  Called from within \ref TForm1.BitBtn1Click()

  Supports the content <b>tags</b>:
  - <name>
  - <trimName>
  - <version>
  - < description >
  - <author>
  - <contact>
  - <codegen_version>
  - <now>
  - <numTracers [CONDITION[;CONDITION][;+|-X]]|[+|-X]>
  - <numElements [CONDITION[;CONDITION][;+|-X]]|[+|-X]>
  - <numConstants [CONDITION[;CONDITION][;+|-X]]|[+|-X]>
  - <numAuxiliaries [CONDITION[;CONDITION][;+|-X]]|[+|-X]>
  - <numProcesses [CONDITION[;CONDITION][;+|-X]]|[+|-X]>
  - <numFlatTracers> (deprecated, use <numTracers vertLoc/=WAT; vertLoc/=FIS>)
  - <numFlatTracers+X> (deprecated)
  - <numFlatTracers-X> (deprecated)
  - <num3DTracers> (deprecated, use <numTracers vertLoc=WAT>)
  - <num3DTracers+X> (deprecated)
  - <num3DTracers-X> (deprecated)
  - <numRiverDepTracers> (deprecated, use <numTracers riverDep=1>)
  - <numRiverDepTracers+X> (deprecated)
  - <numRiverDepTracers-X> (deprecated)
  - <numMovingTracers> (deprecated, use <numTracers vertSpeed/=0>)
  - <numMovingTracers+X> (deprecated)
  - <numMovingTracers-X> (deprecated)
  - <maxIterations>
  - </ifParam>
  - </ifNoParam>

  The following <b>sub-loops</b> are in order:
  - <constants ...>
  - <tracers ...>
  - <backwardtracers ...>
  - <auxiliaries>
  - <processes>
  - <celements>

  <b> CONDITION</b> <br>
  The set of accepted CONDITIONs in the <numXXX> tags is the same as the set in
  the <tracers>, <constants>, etc. loops. Examples for <numTracers ...> are
  shown below. Each CONDITION needs to be separated by a ';' from the next one.
  Increments and decrements (+/-X) are treated like conditions (separate by ':')
  and can be at any position in the tag. When more than one in-/decrement is
  included, only the first is considered.
                                       s
  <b>Calls</b>
  - optional: ConstantLoop()
  - optional: TracerLoop()
  - optional: TracerLoop()
  - optional: AuxiliaryLoop()
  - optional: ProcessLoop()
  - optional: cElementLoop()   s
  - optional: StringReplaceNumTracers()
  - optional: StringReplaceNumConstants()
  - optional: StringReplaceNumAuxiliaries()
  - optional: StringReplaceNumElements()
  - optional: StringReplaceNumProcesses()

  Uses <b>global variables</b>:
  - \ref codegenVersion

  If a XML file is processed, the tags are enclosed by [] instead of <>.

  <b>Examples</b>:
  - <numTracers>
  - <numTracers +1>
  - <numTracers vertLoc=WAT; childOf=none; +2>
  - <numTracers vertLoc=SED; childOf=none>
  - <numTracers vertLoc=SUR; +3; childOf=none>
  }
procedure generateCode(inputfile, outputfile: String; coloredElements:Boolean=true);
var Q, Z: TextFile;
    s: String;
    nowstring: AnsiString;
    i: Integer;
    myinputfile, myoutputfile: String;
begin
  //first, do some initialization to e.g. know how many tracers are active
  DateTimeToString(nowstring,'yyyy-mmm-dd hh":"mm',now);

  //Now we need to know which input file to read.
  //Typically, we use inputfile of course.
  //However, if the input file is .xml file, we need to switch between <> and [] brackets,
  //as we use [tracers] etc. in .xml files to avoid confusion of CGT and XML tags.
  //therefore, we generate an "outputfile_temp1" file which is the inputfile with <> and [] switched.
  //we assume that no bell sign (chr(7)) is used.
  if outputLanguage='xml' then
  begin
    AssignFile(Q,inputfile);
    reset(Q);
    myinputfile:=outputfile+'_temp1';
    myoutputfile:=outputfile+'_temp2';
    AssignFile(Z,myinputfile);
    Rewrite(Z);
    while not EOF(Q) do
    begin
      readln(Q,s);
      s:=StringReplace(s,'<',chr(7),[rfReplaceAll]); s:=StringReplace(s,'[','<',[rfReplaceAll]); s:=StringReplace(s,chr(7),'[',[rfReplaceAll]);
      s:=StringReplace(s,'>',chr(7),[rfReplaceAll]); s:=StringReplace(s,']','>',[rfReplaceAll]); s:=StringReplace(s,chr(7),']',[rfReplaceAll]);
      writeln(Z,s);
    end;
    closefile(Q);
    closefile(Z);
  end
  else
  begin
    myinputfile:=inputfile;
    myoutputfile:=outputfile;
  end;

  //Now do the replacing of the CGT tags
  AssignFile(Q,myinputfile);
  SetTextBuf(Q,InputBuffer);   //this accelerates reading
  reset(Q);
  LinesRead:=0;
  AssignFile(Z,myoutputfile);
  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 if lowercase(copy(trim(s),1,13))='<processtypes' then
      processTypeLoop(s,Q,Z)
    else
    begin
      s:=StringReplace(s,'<name>',ModelInfos.name,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<trimName>',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,'<selectedProcessType>',modelinfos.mySelectedProcessType,[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'<now>',nowstring,[rfReplaceAll,rfIgnoreCase]);
      // replace <numTracers CONDITIONS>, <numConstants ...>, <numElements ...>,
      //   <numAuxiliaries ...> and <numProcesses ...> tags
      // StringReplaceNumTracers(), below, does not only replace <numTracers but also <numXXxTracers Tags:
      //  1) <numXXxTracers> => <numTracers conditionXXx>
      //  2) <numXXxTracers+Y> => <numTracers+Y conditionXXx>
      //  3) <numXXxTracers conditions> => <numTracers conditionXXx;conditions>
      //  4) <numXXxTracers+Y conditions> => <numTracers+Y conditionXXx;conditions>
      //  5) <numXXxTracers conditions1 OR conditions2> => <numTracers conditionXXx;conditions1 OR conditionXXx;conditions2>
      s:=StringReplaceNumTracers(s);
      s:=StringReplaceNumConstants(s);
      s:=StringReplaceNumElements(s);
      s:=StringReplaceNumAuxiliaries(s);
      s:=StringReplaceNumProcesses(s);
      s:=StringReplace(s,'<maxIterations>',IntToStr(MaxIterations),[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'</ifParam>','',[rfReplaceAll, rfIgnoreCase]);
      s:=StringReplace(s,'</ifNoParam>','',[rfReplaceAll, rfIgnoreCase]);
      if pos('<nonewline>',lowercase(s))>0 then
      begin
        s:=StringReplace(s,'<noNewLine>','',[rfReplaceAll, rfIgnoreCase]);
        write(Z,s);
      end
      else
        writeln(Z,s);
    end;
  end;

  closefile(Q);
  closefile(Z);

  //now, if output was .xml file, we need to change <> and [] back and delete the temporary files.
  if outputLanguage='xml' then
  begin
    AssignFile(Q,myoutputfile);
    reset(Q);
    AssignFile(Z,outputfile);
    Rewrite(Z);
    while not EOF(Q) do
    begin
      readln(Q,s);
      s:=StringReplace(s,'<',chr(7),[rfReplaceAll]); s:=StringReplace(s,'[','<',[rfReplaceAll]); s:=StringReplace(s,chr(7),'[',[rfReplaceAll]);
      s:=StringReplace(s,'>',chr(7),[rfReplaceAll]); s:=StringReplace(s,']','>',[rfReplaceAll]); s:=StringReplace(s,chr(7),']',[rfReplaceAll]);
      writeln(Z,s);
    end;
    closefile(Q);
    closefile(Z);
    DeleteFile(myinputfile);
    DeleteFile(myoutputfile);
  end;
end;

end.
